Day 14 處理的是哪些文件能進入 RAG。今天輪到另一份同樣會塞進模型 context 的東西:工具定義。
我先故意列出 20 個 Helpdesk 候選工具,裡面不只搜尋與工單功能互相重疊,還混了重設密碼、對外傳資料、任意 HTTP request 和 shell command。不過這篇不會拿單次模型輸出就宣稱「20 個工具一定比較差」。我先問一個更實際的問題:眼前這個任務,真的需要讓模型看到全部 20 個嗎?
這一天的 Live LLM 不是拿 20 個工具做一場運氣測試。真正的 create_agent() 只收到 AGENT_TOOLS:
AGENT_TOOLS = [search_it_sop, create_ticket]
self.agent = create_agent(
model=model,
tools=AGENT_TOOLS,
...
)
因此模型呼叫發生時,它的 tool schema 裡只有兩個能力。reset_password、share_sop_excerpt、任意 HTTP 與 shell 根本沒有送給模型,不是藏在 system prompt 裡叫它「最好別用」。
Day 15 Live 實驗會先留下 tool_catalog_scope 20_to_2,再把兩個 proposal schemas 交給模型。trace 因此能同時看到集合如何縮小,以及真實模型在這個集合裡先選了哪個工具。固定資料版本則保留在回歸測試區。
這份 catalog 是刻意做出來的 metadata,不代表專案背後真的接了 20 個 handler。分類之後,問題很快就浮出來:
| 類型 | 數量 | 問題 |
|---|---|---|
| SOP 搜尋 | 5 | 名稱不同,用途卻高度重疊。 |
| 建立工單 | 3 | create_ticket、template 和 incident 的責任不清楚。 |
| 修改工單 | 4 | 目前的 triage 流程根本用不到。 |
| 帳號管理 | 3 | 風險高,而且還沒有人工核准。 |
| 對外傳送/上傳 | 3 | 一旦選錯,可能直接造成資料外洩。 |
| 任意 HTTP/shell | 2 | 能做的事情太廣,不能只用 prompt 管。 |
如果使用者只說「VPN 連不上,請先查 SOP,無法排除再開單」,模型需要的其實只有兩種能力:查 SOP、建立 mock 工單。其餘 18 個與其說是備用功能,不如說是額外的混淆和攻擊面。
Anthropic 的工具設計文章也建議先做少數、明確,而且直接對應重要流程的工具。功能互相重疊時,Agent 反而更難選出有效路徑。Anthropic:Writing effective tools for AI agents
我把完整 catalog 留在應用程式端,再由 tools_for_helpdesk_triage() 決定這次執行要暴露哪些 schema:
HELPDESK_TRIAGE_TOOL_NAMES = frozenset({
"search_it_sop",
"create_ticket",
})
def tools_for_helpdesk_triage() -> tuple[CatalogTool, ...]:
return tuple(
tool
for tool in TOOL_CATALOG
if tool.name in HELPDESK_TRIAGE_TOOL_NAMES
)
本機 demo 會留下四筆 trace:
full_tool_catalog 20 個候選工具
task_capability_scope 只需要 SOP search 與 ticket create
model_visible_tools 2 tools
unneeded_tools 18 個工具未註冊
真正的 LangChain Agent 原本就只有 search_it_sop 和 create_ticket。這次新增的 20 項 catalog,是拿來驗證「有很多候選功能時,程式是否仍會只挑出必要工具」,不是先把 20 個都餵給模型,再在 system prompt 裡拜託它別亂用。
LangChain 文件提到,工具的名稱、description 與 type hints 會形成模型判斷何時呼叫、如何填參數的依據。既然這些定義都要進模型 context,工具清單就不是免費的功能選單。LangChain:Tools
「工具要小」很容易被做成另一種災難。假設我把 SOP 搜尋拆成 search_vpn_sop、search_network_sop、lookup_helpdesk_article 和 find_it_policy,每個函式看起來都很單純,模型卻得先猜這次問題該算 VPN、network、article 還是 policy。
我最後留下的分法比較接近使用者意圖:
search_it_sop:查找核准的 IT 排障內容。create_ticket:建立一張 mock 工單,後端仍會檢查 SOP-first 與原始使用者意圖。工具內部可以串幾個確定性的 API,但對模型暴露的用途要清楚,而且不要和旁邊的工具搶同一件事。單一責任指的是一個能說清楚的工作,不是把後端每個 endpoint 原封不動交給 Agent。
我用五個問題篩選候選工具:
第一題答不出必要性,我就先不註冊。後面幾題還沒處理好,也不能拿「請勿濫用」的 system prompt 充當防線。
OWASP 將不必要的功能列為 Excessive Agency 的成因之一,也特別提醒要避免任意 shell、任意 URL 這類 open-ended extensions。OWASP:LLM06:2025 Excessive Agency

Day 15 的單元測試會確認三件事:catalog 剛好有 20 個不同名稱、Helpdesk triage 只留下兩個工具,而且 reset_password、http_request 與 run_shell_command 都不在模型可見集合。Promptfoo 的 Day 15 案例則從 provider 輸出核對 catalog_count 是 20、model_visible_count 是 2。
本次完整單元測試是 99 個通過;Day 15 的 Promptfoo 篩選結果是 1 passed、0 failed、0 errors。
這只能證明工具暴露規則確實把 20 縮成 2,不能證明所有模型看到 20 個工具時會多錯幾次。若要比較選擇準確率,我還得固定模型版本、準備任務資料集,再進行多次抽樣。今天先完成不需要依賴模型運氣的部分:用不到的工具,根本不讓它看見。
Day 16 會把外寄功能放進獨立、受限的 policy demo。我會繼續檢查 tool schema、收件者 allowlist,以及 read、write、execute 為什麼不該混成同一種權限。