Pular para o conteúdo

O prompt que você não escreveu

No capítulo do agente sem framework, o system prompt era seu. Você escreveu à mão o texto que explicava o ciclo, listava as Tools e mandava o modelo parar em Final Answer:. Estava tudo num arquivo que você abria e editava.

Depois veio o CodeAgent, e esse texto sumiu do seu código. Ele não sumiu do programa — só saiu das suas mãos.

Este capítulo mostra onde ele foi parar, como olhar para ele, e o que acontece quando alguém entrega um arquivo prompts.yaml e diz que é assim que se configura o agente.

Onde o prompt foi parar

Acrescente uma linha ao alfred_smolagents.py, logo depois de criar o agente:

print(agente.system_prompt)

Saem 9.036 caracteres que você não digitou. O smolagents monta esse texto a partir de um template que vem instalado junto com a biblioteca:

.venv\Lib\site-packages\smolagents\prompts\code_agent.yaml

É um arquivo YAML comum. Abra, se quiser — é o mesmo tipo de texto que você escreveu no capítulo do agente do zero, só que mais longo e mais cuidadoso.

Nota

O template é um arquivo do pacote, não do seu projeto. Se você atualizar o smolagents, ele muda. Essa é a diferença que importa: no agente do zero, o prompt era seu e ficava parado; aqui, ele é da biblioteca e acompanha a versão dela.

A parte que você já conhece

Procure o nome de uma das suas Tools no meio dos 9.036 caracteres:

you only have access to these tools, behaving like regular python functions:
<code>
def consultar_assunto(termo: string) -> integer:
    """Converte o nome de um assunto judicial no código numérico correspondente.

    Args:
        termo: nome (ou parte do nome) do assunto, por exemplo "violência doméstica".
    """

Reconheça o que está aí: é a sua docstring. Palavra por palavra.

O capítulo das Tools disse que a docstring é o contrato. Aqui está a prova material: o @tool leu a assinatura e a docstring, e o smolagents as colou dentro do prompt como se fossem uma declaração de função. O modelo nunca vê o corpo da sua Tool. Ele vê essas seis linhas.

Repare também nos tipos: termo: string, -> integer. Não são os tipos do Python (str, int) — são os tipos do esquema JSON que o smolagents usa por dentro. É o mesmo vocabulário que você vai reencontrar na Unidade 3, quando a Tool atravessar a fronteira do MCP.

O prompts.yaml que vem no material do HF

Quem faz o curso original da Hugging Face recebe, no espaço de exemplo, um arquivo prompts.yaml de 317 linhas e um app.py que o carrega assim:

import yaml

with open("prompts.yaml", "r") as stream:
    prompt_templates = yaml.safe_load(stream)

agente = CodeAgent(
    tools=[final_answer, buscar_processos_cnj],
    model=modelo,
    max_steps=6,
    prompt_templates=prompt_templates,   # <-- aqui
)

A pergunta natural, e é uma boa pergunta: isso ainda funciona? E eu preciso disso?

Duas respostas diferentes, e é importante não confundir uma com a outra.

Funciona? Funciona.

O parâmetro prompt_templates= existe, está vivo, e o arquivo é lido. Dá para medir sem sair do lugar: monte o mesmo agente duas vezes, uma com o YAML e outra sem, e compare o tamanho do prompt resultante.

import yaml
from smolagents import CodeAgent

sem  = CodeAgent(tools=[consultar_assunto], model=modelo)
com  = CodeAgent(tools=[consultar_assunto], model=modelo,
                 prompt_templates=yaml.safe_load(open("prompts.yaml", encoding="utf-8")))

print(len(sem.system_prompt))   # 9036
print(len(com.system_prompt))   # 8788

Números diferentes, mesmo agente. O arquivo entrou.

Preciso? Não precisa.

O prompt_templates= é opcional. Sem ele, o smolagents usa o template que vem instalado — o mesmo que você acabou de imprimir. O agente da Unidade 1, o da Unidade 2 e o da Unidade 3 rodam todos sem prompts.yaml, e é por isso que você nunca viu esse arquivo nos exemplos do curso.

Um prompts.yaml no seu projeto quer dizer uma coisa só: você decidiu substituir o texto da biblioteca pelo seu. É uma decisão legítima. Só não é gratuita, e a próxima seção mostra o preço.

O preço: o template congela, a biblioteca não

O prompts.yaml do material do HF foi escrito para uma versão anterior do smolagents. Passar ele para a versão 1.26 não dá erro. Não dá aviso. O agente sobe e responde.

O que muda é o texto que o modelo lê. Três diferenças medidas, todas silenciosas.

1. A tag de código está errada — e o agente sobrevive por acaso

A versão 1.26 delimita código com <code> e </code>:

print(agente.code_block_tags)   # ('<code>', '</code>')

O prompts.yaml do HF ensina outra coisa. Ele manda o modelo fechar o bloco com <end_code>, e insiste: a expressão aparece 14 vezes no prompt montado.

prompt padrão da 1.26 prompt do prompts.yaml
ocorrências de </code> 15 0
ocorrências de <end_code> 0 14

Ou seja: o modelo é instruído 14 vezes a escrever uma marca que o interpretador não procura, e zero vezes a escrever a que ele procura.

Deveria quebrar sempre. Não quebra — porque o smolagents tem uma segunda tentativa:

# smolagents/utils.py, dentro de parse_code_blobs {: #smolagentsutilspy-dentro-de-parse-code-blobs }
matches = extract_code_from_text(text, code_block_tags)
if not matches:  # Fallback to markdown pattern
    matches = extract_code_from_text(text, ("```(?:python|py)", "\n```"))

Falhou com as tags, tenta o bloco markdown ```python. Como os modelos escrevem markdown por hábito, o código costuma ser encontrado por essa segunda via. E há ainda uma terceira: se nada casar, ele tenta ast.parse no texto inteiro.

Atenção

Repare no formato desta falha, porque ele se repete no curso inteiro: o template está errado, o resultado está certo, e nada no meio do caminho avisa. O agente funciona apoiado num plano B que existe para outra finalidade.

Quando o modelo escrever o bloco sem marcação markdown — e ele às vezes escreve — as três tentativas falham de uma vez e o erro que aparece é este:

Error in code parsing:
Your code snippet is invalid, because the regex pattern <code>(.*?)</code> was
not found in it.

A mensagem cita <code>, que é o que a biblioteca queria. Ela não cita <end_code>, que é o que o seu YAML mandou o modelo escrever. Quem for depurar isso vai procurar o defeito no lugar errado.

2. As Tools aparecem como prosa, não como assinatura

Com o template padrão, a sua Tool chega ao modelo como código:

def consultar_assunto(termo: string) -> integer:
    """Converte o nome de um assunto judicial no código numérico correspondente.

    Args:
        termo: nome (ou parte do nome) do assunto, por exemplo "violência doméstica".
    """

Com o prompts.yaml do HF, a mesma Tool chega como lista:

- consultar_assunto: Converte o nome de um assunto judicial no código numérico correspondente.
    Takes inputs: {'termo': {'type': 'string', 'description': 'nome (ou parte do nome) do assunto, por exemplo "violência doméstica".'}}
    Returns an output of type: integer

A informação é a mesma. A forma não. Você está pedindo a um modelo que escreve Python que deduza a chamada a partir de uma descrição em inglês, em vez de mostrar a ele a assinatura que ele deve chamar. O template mais novo mostra código para quem vai escrever código — e essa mudança foi deliberada.

3. Três chaves que ninguém lê

As chaves de topo dos dois arquivos são idênticas: system_prompt, planning, managed_agent, final_answer. Um diff superficial diria que está tudo certo.

A diferença está um nível abaixo, dentro de planning:

subchave de planning no prompts.yaml do HF usada pela 1.26
initial_plan sim sim
update_plan_pre_messages sim sim
update_plan_post_messages sim sim
initial_facts sim não
update_facts_pre_messages sim não
update_facts_post_messages sim não

Três blocos de texto que você pode editar com todo o cuidado do mundo sem que nada aconteça. Não há erro, não há aviso — a chave simplesmente não é procurada por ninguém.

Nota

Isto é o oposto do problema anterior e vale guardar como par. Na tag <end_code>, você escreve uma instrução e ela chega ao modelo estando errada. Aqui, você escreve uma instrução e ela não chega ao modelo. Nos dois casos, o agente responde normalmente.

Editar um template sem imprimir o resultado é escrever no escuro.

Como olhar, quando for preciso olhar

A ferramenta é uma linha. Guarde-a:

print(agente.system_prompt)

Antes de acusar o modelo de não seguir uma instrução, confira se a instrução chegou até ele. Três verificações que cabem em segundos:

p = agente.system_prompt

print(len(p))                        # o prompt tem o tamanho que você espera?
print(agente.code_block_tags)        # a tag que o template ensina é a que a lib procura?
print("consultar_assunto" in p)      # a sua Tool foi realmente anunciada?

E, para saber se um YAML de terceiro está mesmo sendo lido, compare os dois tamanhos como na seção anterior. Se derem o mesmo número, o arquivo não entrou.

O que fazer com o prompts.yaml

Para este curso: não use. Os exemplos das três unidades passam tools=, model= e max_steps=, e param aí. O template da biblioteca acompanha a versão dela, e é a versão dela que define como o código vai ser lido.

Personalize o prompt quando houver um motivo que você saiba dizer em voz alta — por exemplo, exigir que o agente responda em português, ou proibi-lo de citar número de processo que não tenha vindo de uma Tool. Quando esse dia chegar:

  1. Comece do arquivo instalado, smolagents/prompts/code_agent.yaml, não de um YAML antigo de outra pessoa. Copie e edite.
  2. Anote a versão do smolagents para a qual você copiou, num comentário no topo do arquivo.
  3. Imprima o prompt depois de carregar, e confira as tags de código.
  4. Repita o passo 3 a cada atualização da biblioteca. Nada vai avisar você.
Atenção

Boa parte do que se pede a um agente não precisa de template nenhum. A instrução do agente.run(...) chega ao modelo do mesmo jeito, vale só para aquela pergunta, e não congela com a versão da biblioteca.

Foi assim que o curso resolveu o problema do português e o da forma dos dados na Unidade 3: uma frase a mais na pergunta, e nenhum arquivo de configuração a manter.

Template é para o que vale sempre. Pergunta é para o resto — e o resto é a maioria.

Exercícios

  1. Imprima o prompt do seu alfred_smolagents.py e localize as duas Tools. Confira que o texto que o modelo lê é a sua docstring, sem uma vírgula de diferença.
  2. Mude uma docstring — troque "parte do nome do assunto" por "o nome EXATO do assunto" — e imprima de novo. Meça em quantos lugares do prompt a mudança apareceu.
  3. Apague o bloco Args: de uma das docstrings e rode o arquivo. Você não chega nem a criar o agente:

DocstringParsingException: Cannot generate JSON schema for consultar_assunto because the docstring has no description for the argument 'termo'

O erro estoura no @tool, na hora de importar o módulo — antes do modelo, antes da pergunta. Explique por que essa é a melhor hora possível para essa falha acontecer, e compare com o que aconteceria se ela só aparecesse no meio de uma execução.

  1. Carregue o prompts.yaml do material do HF no seu agente e rode a mesma pergunta três vezes. Conte quantas execuções passaram pelo plano B do markdown e quantas quebraram no parse_code_blobs.

Fixando o capítulo

Q1: Passar prompt_templates= com um YAML escrito para uma versão antiga do smolagents produz o quê, na versão 1.26?

Q2: Você quer que o agente responda sempre em português. Onde essa instrução deve ir?


O prompt já não é seu, mas continua legível — e uma linha basta para lê-lo. Guarde essa linha: na Unidade 3, quando a Tool vier de outro processo e você não tiver mais o @tool na mão, ela vai ser o único jeito de saber o que o modelo está realmente vendo.