s02
Outils
Outils et exécutionOne Handler Per Tool
120 LOC4 outilsTool 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
"Ajouter un outil signifie ajouter un gestionnaire" -- le boucle reste le même ; les nouveaux outils s'enregistrent dans la carte de distribution.
Problème
Avec seulement bash, l'agent utilise le shell pour tout. cat tronque de manière imprévisible, sed échoue sur les caractères spéciaux, et chaque appel bash est une surface de sécurité non contrainte. Des outils dédiés comme read_file et write_file permettent d'appliquer le sandboxing de chemin au niveau de l'outil.
L'idée clé : ajouter des outils ne nécessite pas de modifier le boucle.
Solution
+--------+ +-------+ +------------------+
| User | ---> | LLM | ---> | Tool Dispatch |
| prompt | | | | { |
+--------+ +---+---+ | bash: run_bash |
^ | read: run_read |
| | write: run_wr |
+-----------+ edit: run_edit |
tool_result | } |
+------------------+
La carte de distribution est un dict : {nom_outil: fonction_handler}.
Un seul lookup remplace n'importe quelle chaîne if/elif.
Comment Ça Marche
- Chaque outil obtient une fonction handler. Le sandboxing de chemin empêche l'échappement de l'espace de travail.
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]
- La carte de distribution lie les noms d'outils aux gestionnaires.
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"]),
}
- Dans la boucle, recherchez le handler par nom. Le corps de la boucle lui-même est inchangé depuis 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,
})
Ajouter un outil = ajouter un handler + ajouter une entrée de schéma. Le boucle ne change jamais.
Qu'est-ce qui a Changé par Rapport à s01
| Composant | Avant (s01) | Après (s02) |
|---|---|---|
| Outils | 1 (bash seulement) | 4 (bash, read, write, edit) |
| Distribution | Appel bash codé en dur | TOOL_HANDLERS dict |
| Sécurité chemin | None | safe_path() sandbox |
| Boucle agent | Inchangée | Inchangée |
Essayer
python agents/s02_tool_use.py
Lire le fichier requirements.txtCréer un fichier appelé greet.py avec une fonction greet(name)Modifier greet.py pour ajouter un docstring à la fonctionLire greet.py pour vérifier que la modification a fonctionné
