Aula 02 · Fundamentos

O loop não muda — só as ferramentas aumentam

"O loop não mudou nada — eu só adicionei itens ao array TOOLS." — s02_tool_use.py, textualmente.

⏱ ~10 min · 📝 3 componentes interativos · 🧑‍💻 Baseado em shareAI-lab · s02_tool_use.py

O que precisa mudar para adicionar uma ferramenta?

O agent do S01 só sabe usar bash. Para que ele também consiga read_file / write_file / edit_file, o que você mudaria?

A primeira reação de muitos seria: mudar o loop. Errado. O loop não precisa de nenhuma alteração. São apenas três coisas:

  1. Escreva uma função handler em Python (run_read(path, limit)).
  2. Registre-a no mapa TOOL_HANDLERS ("read_file": lambda **kw: run_read(...)).
  3. Adicione uma declaração de JSON schema no array TOOLS (informando ao modelo o nome da ferramenta e seus parâmetros aceitos).

O loop vê um bloco tool_use, consulta o dispatch map pelo block.name, executa a função e insere a saída em tool_result. Exatamente o mesmo caminho que o bash percorre.

# Dispatch map: name → handler lambda
TOOL_HANDLERS = {
    "bash":       lambda **kw: run_bash(kw["command"]),
    "read_file":  lambda **kw: run_read(kw["path"], kw.get("limit")),
    "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
    "edit_file":  lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
}

Como o dispatch roteia

O widget abaixo deixa você clicar em um request tool_use que o modelo poderia enviar e ver como ele é roteado para a função Python correspondente. Atenção: é o block.name que determina qual caminho seguir.

safe_path: uma linha de defesa que não pode faltar

Dar ao agent acesso ao sistema de arquivos traz um risco de segurança fácil de ignorar: path traversal. O modelo deveria trabalhar em /home/user/project/, mas envia read_file("../../etc/passwd").

O s02 tem uma pequena função que resolve isso:

def safe_path(p: str) -> Path:
    path = (WORKDIR / p).resolve()  # normaliza, resolve .. e symlinks
    if not path.is_relative_to(WORKDIR):
        raise ValueError(f"Path escapes workspace: {p}")
    return path

A chave está em .resolve() + .is_relative_to() — converter para caminho absoluto antes de verificar se ainda está dentro do sandbox. Sem o primeiro passo, algo como foo/../../etc passaria; sem o segundo, a verificação nem acontece.

Classifique a segurança dos caminhos

Os 5 caminhos abaixo são parâmetros que o modelo poderia enviar para read_file. Quais seriam aceitos pelo safe_path e quais seriam rejeitados? Considere WORKDIR = /home/user/project.

Não adicione ferramentas perigosas ao agent

Adicionar ferramentas é simples — mas lembre-se: cada ferramenta que você adiciona amplia o raio de ação do modelo. No s02, o bash já tem uma lista negra (rm -rf /, sudo, shutdown) e o write_file tem a restrição do safe_path. Antes de adicionar uma ferramenta em produção, responda três perguntas:

  • Essa ferramenta permite que o modelo faça algo irreversível? (rm, enviar e-mail, git push)
  • Ela pode vazar dados sensíveis? (env vars, .ssh, cookies)
  • A saída de erro pode ser injetada como instrução? (prompt injection via tool output)

A lição s07 vai adicionar uma "camada de permissão" que tira essas decisões do código e as torna declarativas.

Interativo

Widget 1 · Tool Dispatch · como o tool_use chega ao handler

Clique em um request tool_use e veja como o block.name no loop encontra a função correspondente, executa e insere a saída no bloco tool_result.

Requests tool_use do modelo
TOOL_HANDLERS dispatch map

        
Resultado da execução · bloco tool_result
(clique em qualquer tool_use acima para acionar o roteamento)
Interativo

Widget 2 · Safe Path · detecção de path traversal em 5 caminhos

Cada caminho será processado pelo safe_path(). Considerando WORKDIR = /home/user/project, clique em "Permitir" ou "Bloquear" — veja se sua intuição sobre path escape está certa.

Acertos: 0 / 5
Interativo

Widget 3 · Add a Tool · o que precisa mudar para adicionar glob ao agent

Suponha que você quer adicionar uma nova ferramenta glob(pattern) para o agent localizar arquivos em lote. Preencha os três pontos obrigatórios — nenhum pode ser omitido.

Ponto 1 · Escreva uma função handler
def run_glob(pattern: str) -> str:
    import glob
    return "\n".join(glob.glob(pattern))
Ponto 2 · Registrar no dispatch map (escolha a linha correta)
Ponto 3 · Adicionar schema no array TOOLS (escolha o conjunto correto)