How to Build an AI Agent
How to Build an AI Agent
s02

Ferramentas

Ferramentas e Execucao

One Handler Per Tool

120 LOC4 ferramentasTool dispatch map
The loop stays the same; new tools register into the dispatch map

s01 > [ s02 ] s03 > s04 > s05 > s06 | s07 > s08 > s09 > s10 > s11 > s12

"Adicionar uma ferramenta significa adicionar um handler" -- o loop permanece o mesmo; novas ferramentas se registram no mapa de dispatch.

Problema

Com apenas bash, o agente faz chamadas externas para tudo. cat trunca imprevisivelmente, sed falha em caracteres especiais, e cada chamada bash é uma superficie de segurança não restrita. Ferramentas dedicadas como read_file e write_file permitem aplicar sandboxing de caminho no nível da ferramenta.

A percepção chave: adicionar ferramentas não requer mudar o loop.

Solução

+--------+      +-------+      +------------------+
|  User  | ---> |  LLM  | ---> | Tool Dispatch    |
| prompt |      |       |      | {                |
+--------+      +---+---+      |   bash: run_bash |
                    ^           |   read: run_read |
                    |           |   write: run_wr  |
                    +-----------+   edit: run_edit |
                    tool_result | }                |
                                +------------------+

The dispatch map is a dict: {tool_name: handler_function}.
One lookup replaces any if/elif chain.

Como Funciona

  1. Cada ferramenta recebe uma função handler. O sandboxing de caminho previne escape do workspace.
def safe_path(p: str) -> Path:
    path = (WORKDIR / p).resolve()
    if not path.is_relative_to(WORKDIR):
        raise ValueError(f"Path escapes workspace: {p}")
    return path

def run_read(path: str, limit: int = None) -> str:
    text = safe_path(path).read_text()
    lines = text.splitlines()
    if limit and limit < len(lines):
        lines = lines[:limit]
    return "\n".join(lines)[:50000]
  1. O mapa de dispatch vincula nomes de ferramentas aos handlers.
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"]),
}
  1. No loop, procure o handler pelo nome. O corpo do loop em si é inalterado desde s01.
for block in response.content:
    if block.type == "tool_use":
        handler = TOOL_HANDLERS.get(block.name)
        output = handler(**block.input) if handler \
            else f"Unknown tool: {block.name}"
        results.append({
            "type": "tool_result",
            "tool_use_id": block.id,
            "content": output,
        })

Adicionar uma ferramenta = adicionar um handler + adicionar uma entrada de schema. O loop nunca muda.

O Que Mudou Desde s01

ComponenteAntes (s01)Depois (s02)
Ferramentas1 (apenas bash)4 (bash, read, write, edit)
DispatchChamada bash hardcodedTOOL_HANDLERS dict
Segurança de caminhoNenhumsafe_path() sandbox
Loop do agenteInalteradoInalterado

Experimente

python agents/s02_tool_use.py
  1. Leia o arquivo requirements.txt
  2. Crie um arquivo chamado greet.py com uma função greet(name)
  3. Edite greet.py para adicionar um docstring à função
  4. Leia greet.py para verificar se a edição funcionou