learn-claude-code/s02_tool_use
2026-08-15 00:03:45 +08:00
..
images Fix empty tool-use response handling 2026-08-15 00:03:45 +08:00
code.py Fix empty tool-use response handling 2026-08-15 00:03:45 +08:00
README.ja.md Fix empty tool-use response handling 2026-08-15 00:03:45 +08:00
README.md Fix empty tool-use response handling 2026-08-15 00:03:45 +08:00
README.zh.md Fix empty tool-use response handling 2026-08-15 00:03:45 +08:00

s02: Tool Use — Add a Tool, Add Just One Line

English · 中文 · 日本語

s01 → s02s03 → s04 → ... → s16 → s17

"Add a tool, add just one handler" — The loop stays the same. Register the new tool in the dispatch map and you're done.

Harness Layer: Tool Dispatch — Expanding the model's reach.


Only One Tool: Bash

The s01 Agent has only one tool: bash. To read a file, cat; to write, echo "..." > file.py; to edit, sed.

The model thinks "read this file" but has to spell out cat path/to/file. An extra layer of translation that wastes tokens and invites errors.


Overview: Tool Dispatch

Tool Dispatch

The s01 loop is fully preserved (LLM call, tool_use block check, message append — not a single word changed). The only change is in that one line of tool execution: run_bash() is replaced with TOOL_HANDLERS[block.name]() dispatch lookup.

Adding a tool to the Agent requires just two things:

  1. Define the tool: Add one entry to the TOOLS array
  2. Register the handler: Add one mapping in the TOOL_HANDLERS dict

From 1 Tool to 5 Tools

s01 had only bash:

TOOLS = [{"name": "bash", ...}]

def run_bash(command): ...

s02 expands to 5 tools, each independently defined:

TOOLS = [
    {"name": "bash",       "description": "Run a shell command.", ...},
    {"name": "read_file",  "description": "Read file contents.",  ...},
    {"name": "write_file", "description": "Write content to file.", ...},
    {"name": "edit_file",  "description": "Replace text in file once.", ...},
    {"name": "glob",       "description": "Find files by pattern.", ...},
]

Each tool has its own implementation function:

def run_read(path, limit=None):
    lines = safe_path(path).read_text().splitlines()
    if limit:
        lines = lines[:limit]
    return "\n".join(lines)

def run_write(path, content):
    safe_path(path).write_text(content)
    return f"Wrote {len(content)} bytes to {path}"

def run_edit(path, old_text, new_text):
    text = safe_path(path).read_text()
    if old_text not in text:
        return "Error: text not found"
    safe_path(path).write_text(text.replace(old_text, new_text, 1))
    return f"Edited {path}"

def run_glob(pattern):
    import glob as g
    return "\n".join(g.glob(pattern, root_dir=WORKDIR))

Tool Dispatch

TOOL_HANDLERS = {
    "bash":       run_bash,
    "read_file":  run_read,
    "write_file": run_write,
    "edit_file":  run_edit,
    "glob":       run_glob,
}

# Only one line changed in the loop — from hardcoded run_bash to dispatch lookup:
for block in tool_calls:
    handler = TOOL_HANDLERS[block.name]    # lookup
    output = handler(**block.input)         # call
    results.append(...)

Adding a tool = one entry in TOOLS array + one line in TOOL_HANDLERS dict. The loop stays the same.


Multiple Tool Calls

The model often returns multiple tool_use calls at once — "read a.py and b.py, then list all .py files".

Calls are executed one by one in their original response.content order.


Quick Reference

Concept One-Liner
TOOL_HANDLERS Tool name → handler function dict. Add a tool = add one mapping line
Tool Definition JSON schema telling the model "what I can do"
Multiple tool calls Model may return multiple tool_use at once; calls execute in their original order
Loop Unchanged s01's while True loop — not a single line changed

Changes from s01

Component Before (s01) After (s02)
Tool count 1 (bash) 5 (+read, write, edit, glob)
Tool execution Hardcoded run_bash() TOOL_HANDLERS dispatch lookup
Path safety None safe_path validation (file tools only)
Loop while True + tool_use block Identical to s01

Try It

cd learn-claude-code
python s02_tool_use/code.py

Try these prompts:

  1. Read the file README.md and tell me what this project is about
  2. Create a file called test.py that prints "hello", then read it back
  3. Find all Python files in this directory
  4. Read both README.md and requirements.txt, then create a summary file

What to watch for: When does the model call just one tool, and when does it call multiple at once? Are multiple tool calls executed in the correct order?


What's Next

The Agent now has 5 specialized tools. File tools are protected by safe_path, but bash is unrestricted — rm -rf / still runs.

→ s03 Permission: Add a gate before tool execution — is this operation safe? Does it need user approval?