昨天介紹了專案經理的骨架。今天打開它手上的工具箱——17 支 FunctionTool,每一支都是「呼叫生產線上某道既有工序」的薄薄一層包裝。
| 比喻 | 術語 | 白話 |
|---|---|---|
| 工具箱裡的一支工具 | FunctionTool |
ADK 把一個 Python 函式包裝成「AI 可以呼叫」的介面 |
| 工具的使用說明 | docstring | 函式開頭那段註解,ADK 會直接讀出來給 AI 看 |
| 工具交回的工作回報單 | tool-envelope | 固定格式的回傳值:{tool, pack, ok, output, error, needs_approval} |
| 工具只能在自己的置物櫃裡拿東西 | AssetDir(資產目錄沙箱) |
工具讀寫檔案時,路徑一律被鎖在那份資產自己的資料夾內 |
| 工具箱本身 | agents/tools/catalog.py |
17 支工具的實作都在這一支檔案 |
每支工具的 docstring 直接變成 AI 看到的工具使用說明,跑一下 python -m tools 就能看到完整清單:
$ cd agents && uv run python -m tools
節錄幾支:
{"name": "ingest_cad", "doc": "把資產目錄內的 STEP 轉成 geometry/raw.glb(Z-up、mm),保留零件名稱、顏色與階層。"}
{"name": "render_views", "doc": "Blender Workbench headless 出 6 個視角的 PNG 到 reports/views/,並存成 artifacts 供視覺判讀。"}
{"name": "request_rebuild", "doc": "排一次自癒重跑:調整面數預算/焊接容差,下一步會重跑 normalize(可選)→ decimate → pack → qa_check。"}
17 支分四組:
| 組別 | 工具 |
|---|---|
| 系統 | list_packs、set_pipeline_params、request_rebuild |
| 幾何 | inspect_cad、ingest_cad、normalize_mesh、compute_explode_vectors、decimate_mesh、pack_web |
| QA | qa_check、render_views、record_vision_verdict |
| 語意/場景 | list_parts、set_part_semantics、build_scene、write_hotspots、write_faq |
前 15 天介紹過的每一道工序,幾乎都在這裡被包成了一支工具——不是重寫一遍邏輯,是薄薄包一層。
1. 工具不寫邏輯,只呼叫已有的確定性程式(Day 07–15 介紹過的那些 packages/)加上讀寫狀態。
2. 路徑一律鎖在資產自己的資料夾裡。每個工具操作的路徑都要通過這套檢查,故意餵一個想逃出去的路徑(例如試圖往上跳出資料夾,或直接指到系統檔案)會被直接擋下來,不會真的去動那個檔案。
3. 可預期的失敗要老實回報,不能讓整個經理當機:找不到資產、檔案格式不對這類情況,工具回傳的是一份「失敗,代號是什麼,給人看的原因是什麼」的回報單,不是讓整個程式當場爆掉。
def ok(tool, output, *, pack=None):
return {"tool": tool, "pack": pack, "ok": True, "output": output, "error": None, "needs_approval": False}
def fail(tool, error, detail="", *, pack=None, output=None):
return {"tool": tool, "pack": pack, "ok": False, "output": output, "error": error,
"detail": detail, "needs_approval": False}
error 是給程式判斷用的代號(例如 asset_not_found),detail 是給人看的一句話。needs_approval 這個欄位是留給「這個動作太重大、要先問過人」的高風險操作用的——目前還沒有任何一支工具真的把它設成 True,設計先留著,之後真的需要時才會用上。
write_faq(寫展場常見問答)的回傳:
doc = {"asset_id": asset_id, "generated": bool(faq), "count": len(faq), "items": list(faq)}
if not faq:
doc["reason"] = "no_model_credentials"
如果這次完全沒有產生任何問答(例如還沒有申請 AI 門票),它不會留一個空白、裝作什麼都沒發生,而是明確寫「generated: false,原因是沒有 AI 門票」。write_hotspots 也有類似的防呆:熱點只能用「場景包」已經算好、真實存在的候選 id,自己編一個聽起來合理的新熱點 id 會被直接拒絕(回傳 unknown_hotspot)——錨點與相機角度是靠幾何算出來的,不是靠文案決定要不要存在。
寫測試的時候,刻意造過幾種故意搗亂的情境:
../../ 往上跳)→ 全部被擋下。這些測試不需要真的接上 AI,純粹驗證「工具本身的防呆有沒有守住」。
needs_approval 機制設計上留著,但目前還沒有任何工具真的觸發它。下一篇:建置期總管第一步,讀 CAD 檔先判斷該用多細的參數轉檔。