Lesson 02 · 基础

循环没变,只是工具变多了

「循环一点没改,我只是往 TOOLS 数组里加了东西。」——s02_tool_use.py 原话。

⏱ 约 10 分钟 · 📝 3 个可交互组件 · 🧑‍💻 基于 shareAI-lab · s02_tool_use.py

加一个工具要动哪几处?

S01 的 agent 只会 bash。要让它也能 read_file / write_file / edit_file,你会怎么改?

很多人第一反应是:改 loop。错。loop 一行都不用动。只需要三件事:

  1. 写一个 Python handler 函数(run_read(path, limit))。
  2. 把它注册到 TOOL_HANDLERS 映射表("read_file": lambda **kw: run_read(...))。
  3. TOOLS 数组里加一条 JSON schema 声明(告诉模型这个工具叫什么、接受什么参数)。

循环看到一个 tool_use 块,就按 block.name 到 dispatch map 里查函数、执行、把输出塞回 tool_result跟 bash 走的是完全一样的路。

# 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"]),
}

看 dispatch 怎么路由

下面这个 widget 让你点一个模型可能发来的 tool_use 请求,看它如何被路由到具体的 Python 函数。注意:block.name 决定走哪条线。

safe_path:一条不能省的防线

给 agent 文件访问权限,安全上最容易翻车的是路径逃逸:模型本来该在 /home/user/project/ 里工作,结果它发了个 read_file("../../etc/passwd")

s02 里有一个小函数专治这个:

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

关键在 .resolve() + .is_relative_to() 这两步——解析到绝对路径再检查是否仍在沙箱内。少了前者,foo/../../etc 这种能穿过去;少了后者,连检查都没做。

辨别路径安全性

下面 5 个路径都是模型可能发来的 read_file 参数。哪些会被 safe_path 放行,哪些会被拒?假设 WORKDIR = /home/user/project

不要在 agent 里加危险工具

加工具简单,但要记住:你加的每一个工具都是模型的新能力边界。s02 里 bash 已经有黑名单(rm -rf /sudoshutdown),write_filesafe_path 限制。往生产里加工具前问自己三个问题:

  • 这个工具能让模型做出不可逆的事吗?(rm、发邮件、git push)
  • 它能泄漏什么?(env vars、.ssh、cookies)
  • 错误输出会不会被当作指令回灌?(prompt injection via tool output)

s07 课会补上一个「权限层」把这些决定从代码里拉出来,做成声明式的。

Interactive

Widget 1 · Tool Dispatch · tool_use 怎样路由到 handler

点一条 tool_use 请求,看循环里 block.name 如何查到对应函数、执行、把输出塞回 tool_result 块。

模型发来的 tool_use 块
TOOL_HANDLERS 映射表

        
执行结果 · tool_result 块
(点上面任意一条 tool_use 触发路由)
Interactive

Widget 2 · Safe Path · 5 个路径的逃逸检测

每个路径都会被 safe_path() 处理。假设 WORKDIR = /home/user/project,点「放行」或「拒绝」——看你对 path escape 的直觉准不准。

答对 0 / 5
Interactive

Widget 3 · Add a Tool · 给 agent 加一个 glob 工具需要改什么

假设要加一个新工具 glob(pattern) 让 agent 能批量找文件。填空:三处都要改,缺一不可。

第 1 处 · 写一个 handler 函数
def run_glob(pattern: str) -> str:
    import glob
    return "\n".join(glob.glob(pattern))
第 2 处 · 注册到 dispatch map(选正确一行)
第 3 处 · 在 TOOLS 数组里加 schema(选正确一组)