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 · 加一個 glob 工具給 agent 要改什麼

假設要加一個新工具 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(选正确一组)