
選工具時可以先問一個問題:這個能力是不是你自己系統內的業務能力?

如果是,例如計算會員消費、查訂單、套用優惠規則,通常適合做成 domain tool。若能力來自外部系統、跨語言 runtime 或標準化工具服務,MCP 會比較合適。若對方本身就是一個完整 agent,才考慮 subagent。
重點不是讓 LLM 看到所有工具,而是每個 action 只暴露它真的需要的能力。工具越少,錯用機率越低,測試也越明確。
當工具越來越多,真正的問題不是怎麼全部接上,而是怎麼分類。Domain tool 適合本地 domain object 的精準行為;MCP 適合跨應用、跨語言、可重用的工具;Subagent 則適合把一段專門能力封裝成另一個 agent。
Agentic Tool 更進一步,工具內部本身也可能需要 LLM 推理。這在複雜查詢、資料探索或多步工具操作時有用,但也會增加成本與不可預測性,因此要搭配 observability 與 guardrail。
本系列後面接 generative UI 時,這個分類仍然成立:後端 agent 可以用 MCP 或 subagent 拿資料,但最後輸出的 UI spec 必須遵守前端可渲染的 catalog contract。
這裡有一個很常見的誤解:MCP 不等於「給目前這個 action 內的 LLM 用工具」。MCP 的重點是把工具或能力發布成標準協定,讓外部 MCP client、另一個系統,或另一個已接上 MCP 的 agent 來呼叫。它比較像「對外發布」,不是 Day 14 那種「在本次 LLM 呼叫裡掛上工具」。
這一段先聚焦在「工具該放在哪裡」。費用追蹤與事件監聽屬於可觀測性層,會另外處理。
withToolObject(...) 是本地 LLM 呼叫的引用點McpToolExport 是對外發布點,不是本地 action 的引用點record TravellerActivity(String name, Instant from, Instant to, List<Trip> trips) {
@Tool(description = "Total travel spend in the selected period")
public float totalSpend() {
return trips.stream().map(Trip::amount).reduce(0f, Float::sum);
}
}
@Action
ActivitySummary summarize(TravellerActivity activity, Ai ai) {
return ai.withDefaultLlm()
.withToolObject(activity)
.createObject("Summarize: " + activity, ActivitySummary.class);
}
@Configuration
class RagMcpTools {
@Bean
McpToolExport ragTools(SearchOperations searchOperations) {
var toolishRag = new ToolishRag("docs", "Embabel 文件", searchOperations);
return McpToolExport.fromLlmReference(toolishRag);
}
}
TravellerActivity.totalSpend() 是 domain tool:它需要物件本身的 trips 資料,適合留在 Java domain object,並在 action 裡透過 withToolObject(activity) 交給當次 LLM 呼叫。RagMcpTools 則是把某個能力包成 MCP tool 對外發布,讓別的 MCP client 或別的系統重用。
真正要記住的是:
Ai / PromptRunner + withToolObject(...)
McpToolExport
如果某段程式只看到 McpToolExport,卻沒看到哪個 ai.with... 去引用它,那代表那段程式在講「發布能力」,不是在講「本地 action 內如何使用工具」。
把你系統中的外部能力分成 domain tool、MCP tool、subagent 三類,並說明每類的安全邊界。
如果你也想進一步學習如何透過 AI 開發 Spring Framework 應用,讓 AI 協助理解框架、撰寫程式、除錯與驗>證,歡迎到 Hahow 看凱文大叔的最新課程【駕馭 AI 的全端實戰養成班:從零打造企業級智慧應用系統】。一起>學習如何駕馭 AI,提升 Spring 應用的開發效率與品質。
課程連結