
【實戰】Day 21:Agent Tool Layer 封裝:把業務 API 變成 AI 聽得懂的專屬武器
延續五層式架構,本篇帶你實作第 3 層「Agent Tool Layer」。我們將示範如何為不同的業務需求(毛利分析、訂單預測)編寫獨立的方法,並透過強型別與 Docstring,完美封裝成 AI Agent 專用的工具。
WRITTEN BY

- Name
- Harry Chang
在上一篇的「五層式企業級架構」中,我們確立了一個非常重要的觀念:「AI 不負責算數學,它只負責呼叫正確的方法 (Method)。」
既然我們已經在 Day 19 的 Business API Layer 寫好了各種商業邏輯(如算毛利、拉歷史訂單),那麼問題來了:AI 怎麼知道有這些方法存在?它又怎麼知道該傳入什麼參數?
這就是本篇的主角 - Agent Tool Layer(工具封裝層)要解決的問題。我們必須把生硬的程式碼,包裝成帶有「說明書」與「防護罩」的專屬武器,親手交到 AI Agent 的手上。
為什麼需要特地「包裝」一層?
許多初學者會直接把原始的 Python Function 丟給大模型,這往往會導致兩個災難:
- 幻覺參數:AI 隨便編造了參數格式,導致程式報錯。
- 意圖混淆:遇到「要評估 NIKE 訂單」時,AI 搞不清楚該呼叫「毛利查詢」還是「庫存查詢」。
在 Agent Tool Layer 中,我們必須透過「強型別 (Type Hints)」與「詳盡的註解 (Docstring)」,為每一個工具寫下嚴格的說明書。在主流的 Pydantic AI 或 LangChain 框架中,這通常是透過 @tool 裝飾器來完成的。
實作解析:封裝不同目的的決策方法
假設針對鞋墊廠的決策系統,我們需要兩個截然不同的工具:「毛利率分析」與「訂單預測」。以下是我們如何封裝它們的範例:
工具一:訂單毛利率分析 (analyze_gross_margin)
這個工具的核心是調用 Day 19 的 API 來精算利潤。它的註解(Docstring)寫得越清楚,AI 呼叫的準確率就越高。
from pydantic import BaseModel, Field
from typing import Literal
# 1. 嚴格定義參數格式 (防呆機制)
class MarginQueryInput(BaseModel):
brand_name: Literal["Nike", "Adidas", "Puma", "New Balance", "Asics", "Under Armour"] = Field(
..., description="要查詢毛利的鞋墊品牌名稱"
)
quarter: str = Field(
..., description="查詢的季度,必須符合格式,例如:'2024-Q1'"
)
# 2. 封裝工具 (交給 AI 的說明書)
@tool("analyze_gross_margin", args_schema=MarginQueryInput)
def analyze_gross_margin(brand_name: str, quarter: str) -> str:
"""
用途:計算特定品牌在特定季度的訂單毛利率。
使用時機:當主管詢問「利潤」、「毛利」、「賺不賺錢」時呼叫此工具。
回傳值:一段包含總營收與毛利率百分比的文字報告。
"""
# 這裡呼叫 Day 19 寫好的 Business API Layer
# margin_data = call_fastapi("/api/margin", brand_name, quarter)
# 假設 API 回傳計算結果
margin_percentage = 18.5
return f"{quarter} 季度 {brand_name} 的訂單毛利率為 {margin_percentage}%。"
工具二:下一季訂單預測 (predict_next_quarter_orders)
這是一隻牽涉到預測模型的工具,我們同樣把它獨立包裝。
class PredictQueryInput(BaseModel):
brand_name: str = Field(..., description="要預測的品牌名稱")
insole_type: Literal["PU 發泡鞋墊", "EVA 成型鞋墊"] = Field(..., description="鞋墊材質類型")
@tool("predict_next_quarter_orders", args_schema=PredictQueryInput)
def predict_next_quarter_orders(brand_name: str, insole_type: str) -> str:
"""
用途:預測特定品牌與鞋墊類型在「下一季」的預估訂單量。
使用時機:當主管詢問「下一季產能」、「未來訂單預估」、「要不要備料」時呼叫此工具。
限制:無法預測超過一年的數據。
"""
# 這裡呼叫底層的預測模型 (Business API Layer)
# predicted_qty = call_forecast_model(brand_name, insole_type)
predicted_qty = 150000
return f"系統預測下一季 {brand_name} 的 {insole_type} 訂單量約為 {predicted_qty} 雙。"
AI 大腦的路由判斷
當我們把這兩個 @tool 註冊給 Agent 後,只要業務主管問:「幫我評估 NIKE 下一季 EVA 成型鞋墊要不要提早備料?」
大腦(Claude 3.5)會先閱讀這兩個工具的 Docstring。它會發現 predict_next_quarter_orders 的「使用時機」明確寫著「要不要備料」,於是它就會自動構造符合 PredictQueryInput 格式的 JSON 參數去呼叫工具二,完美實現意圖路由。
實用建議:三個起步行動
在撰寫 Agent Tool Layer 時,請務必養成以下三個工程紀律:
步驟 1:把 Docstring 當作 Prompt 來寫
在傳統軟體工程中,註解是給人類同事看的;但在 Agent 架構中,註解就是 AI 的 Prompt。你必須在 """用途、使用時機、限制""" 裡面寫得極度白話且具體,AI 才能精準命中工具。
步驟 2:使用 Enum 或 Literal 限制選項
就像我們在上面的 MarginQueryInput 中使用了 Literal["Nike", "Adidas", ...]。這能強迫大語言模型「只能從這幾個選項裡挑一個輸出」,從根本上解決了 AI 憑空捏造不存在品牌的幻覺問題。
步驟 3:為工具加上優雅的失敗處理
如果 API 發生錯誤(例如資料庫連線失敗),工具千萬不能直接拋出 Exception 讓整個 Agent 程式當機。你應該用 try-except 包起來,並 return "無法連線至資料庫,請告知主管稍後重試"。Agent 看到這段文字,就會用自然語言轉述給主管聽,這才是良好的體驗。
我的反思
「寫一個方法,包裝一個工具」,這種看似繁瑣的解耦過程,其實正是企業級 AI 開發的分水嶺。
如果你把所有邏輯全塞在同一個超長 Prompt 裡面,那個 Agent 永遠只能是個「玩具」,牽一髮而動全身。但透過今天的 Agent Tool Layer 設計,我們把每一個商業邏輯都變成了一塊獨立的樂高積木。
未來鞋墊廠若要增加「員工出缺勤查詢」的功能,你只需要再寫一個獨立的 @tool,把它掛上 Agent,系統就無縫升級了。掌握了工具的封裝,我們接下來就可以更進一步,讓 Agent 讀取不同主管的「個人化偏好 (Context Layer)」了!