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(選正確一組)