iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0

前面幾天掛上去的工具,tools/list 列得出來、連線測試也通過。這些只證明工具到得了模型面前。實際跑一次「關閉前後門風扇」,模型花了三分鐘做別的事,該用的那個工具一次都沒有呼叫。今天拆這次失敗,以及它帶出來的工具設計問題。


三分鐘裡發生的事

當時的架構是兩層,上層收到指令後委派給一個下層 worker,風扇控制工具掛在 worker 身上。那三分鐘的動作統計:

動作 次數
查知識庫 iot_kb 7
檔案系統操作 8
呼叫 control_fans 0

最後收到的是 Error: Model generated invalid tool call: transfer_task。

工具在工具清單裡,模型選擇了別的路徑。 三件事同時成立才湊出這個結果:

  • worker 的 instruction 寫著「先查知識庫與檔案」,這條指示排在任務前面,模型照著做
  • 知識庫查不到有用的東西,所以它一直換詞重查
  • 指令裡的「前後門」在工具參數裡沒有對應,工具吃的是 fan_0 與 fan_1,模型無從判斷哪個是前門

知識庫查不到的原因在斷詞

知識庫用的是 bm25 關鍵字檢索。pkg/rag/strategy/bm25.go 的 tokenize() 做四件事:

  1. strings.ToLower 轉小寫
  2. 用一個 strings.Replacer 把標點換成空白
  3. strings.Fields 依空白切成 token
  4. 丟掉長度兩個 byte 以內的 token 與二十個英文停用詞

第二步那個替換表列了十六組,內容是 . , ! ? ; : 括號四種、引號兩種,加上換行與 tab,全部是 ASCII。中文的全形標點不在表上,中文句子裡也沒有空白,一整句中文切完之後是一個巨大的 token。檢索詞與文件內容除非逐字一模一樣,否則對不上。模擬檢索的結果是中文詞 0/5 命中、英文詞 5/10 命中。

這帶出知識庫的兩條寫作規則:

  • 關鍵事實旁邊放 ASCII 錨點詞,讓英文檢索詞有東西可以命中
  • 粗體會黏進 token,**grafana 與 grafana 是兩個不同的字串,關鍵詞要在表格或程式碼區塊裡有一個沒加粗的出現位置

檢索工具這一側的修法是在 instruction 裡明寫檢索詞用英文或 ASCII 關鍵字,查不到就換詞重查。治本的做法是換成 embedding 或混合檢索。


三個修正各自拆掉一個條件

修正 拆掉的是
工具從 worker 移到上層,取消這一段委派 委派過程中任務描述失真
不給那一層知識庫與檔案工具 「先查知識庫」這條指示沒有對象可以執行
工具描述裡寫明 fan_0 是後門、fan_1 是前門 指令用語與參數之間缺一段對照

三個一起上之後,迷路這個現象消失。

第三條是工具設計層面的問題,與這次故障的其他兩條性質不同。工具的參數名取自底層 API,底層 API 取自硬體編號,而人下指令時說的是前門與後門。這段對照要嘛寫進工具描述,要嘛模型自己猜。

這也是自建 MCP 那兩天把工具切成 control_door_fans 而非 set_gpio(pin, value) 的理由。語意層的工具名把對照關係固定在協定裡,低階參數工具則把它留給模型。

工具的命名與描述屬於送進模型的 prompt,它決定模型有沒有辦法把人的說法對應到參數


驗證方式要能排除「它只是猜對了」

改完之後重跑,正確的行為長這樣:先讀感測狀態,接著反問是哪一台設備,印出前後門與欄位的對照,在得到答案之前沒有動任何風扇。同一個情境連跑十次,十次都是這個流程。

驗證方法這一側有個細節,問一個能從常識答出來的問題時,模型即使跳過工具也可能答對。兩種證據的強度差距很大:

證據 證明得了什麼
回答看起來正確 模型產出了合理的文字
回答裡帶著只有真實資料才有的讀值 那個值來自一次實際取得
日誌裡出現 tool=<工具名> 工具被呼叫過,次數可數

查詢的內容要限定成只有真實資料答得出來的細節,例如某個具體讀值、某台設備目前的吹向。日誌那一條最直接,工具名出現幾次與回答像不像無關。


另一種掛不上去:副檔名

同一段時間還遇到一個與模型無關的失敗。stdio 型的 MCP server 以 npx 啟動,在 Windows 上回:

mcp(stdio cmd=npx ...) start failed: server unavailable

手動在終端機跑同一個 npx 指令正常。差別在於終端機是 cmd shell,它會自動補副檔名找到 npx.cmd,而原生程式直接 spawn 沒有副檔名的 npx 找不到可執行檔。

兩個修法:

  1. 改用遠端 HTTP 端點,該 MCP 有提供的話這是首選,本機不需要 npx 也不需要容器
  2. 用 cmd /c 包一層,command: cmd、args: ["/c", "npx", "-y", "<套件>"],讓 cmd.exe 負責解析

走容器的 MCP 不受這個問題影響,它 spawn 的是 docker 而非 npx。


心得

最初的判斷方向是模型能力不足,於是去直接打推論端點,帶著同一份工具定義送一次請求。回來的是合法的 tool call,finish_reason 是 tool_calls,格式與內容都對。

那個結果把問題範圍縮掉一大半。模型選得出工具,選不出來的是在那個情境底下。差別在於直接打端點時,送過去的只有工具定義與那句指令,而實際跑的時候前面還有一段 instruction 要它先查知識庫。

同一個模型、同一組工具,前面多一段指示,行為就換一套。工具清單決定它能做什麼,前面那段文字決定它會先做什麼。

工具到得了模型面前,與模型會不會用它,由 prompt 裡的不同位置分別決定


明天

四個指令名字裡帶 serve 或 peer,只有一個是 agent 對 agent。


上一篇
【Day 23】OAuth 的 client 換人當:接外部 MCP 之後組態少掉兩個區塊
下一篇
【Day 25】打開 mcp serve 以為會拿到 agent,拿到的是一座訊息橋
系列文
打造具備記憶與執行能力的常駐 AI Agent:Hermes Agent × Gemini × MCP 的 Harness 設計 共 25 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言