后端开发
AI Function Calling:從概念到生產的完整指南
2026年,Function Calling已經成為LLM應用開發的基礎設施級能力。但「能跑通Demo」和「能上生產」之間,隔著巨大的鴻溝。
Function Calling的本質不是「讓AI呼叫函式」,而是讓LLM輸出結構化JSON而非自由文字——函式是否真的執行,完全由你的程式碼決定。
本質:結構化輸出而非函式執行
使用者提問 → LLM推理 → 輸出JSON(函式名+參數)→ 你的程式碼決定是否執行 → 回傳結果給LLM → 最終回答
關鍵洞察:LLM從不直接執行任何函式。 它只是「建議」應該呼叫哪個函式、傳什麼參數。
三大協定對比:OpenAI / Anthropic / Google
| 維度 | OpenAI | Anthropic | Google Gemini |
|---|---|---|---|
| Schema欄位 | parameters |
input_schema |
parameters |
| 型別格式 | JSON Schema | JSON Schema | Gemini Schema |
| 並行呼叫 | ✅ | ✅ | ✅ |
| 強制呼叫 | tool_choice: "required" |
tool_choice: {type, name} |
function_calling_config |
Spring AI + Function Calling實戰
自動函式註冊
@Configuration
public class ToolConfig {
@Bean
@Description("取得指定城市的目前天氣資訊")
public Function<WeatherRequest, WeatherResponse> getWeather() {
return request -> weatherService.getCurrentWeather(request.city(), request.unit());
}
@Bean
@Description("查詢使用者最近的訂單記錄")
public Function<OrderQueryRequest, OrderQueryResponse> queryOrders() {
return request -> orderService.queryRecentOrders(request.userId(), request.limit());
}
}
Spring AI的魔法:只需定義
Function<Req, Resp>Bean並加@Description,框架自動將其註冊為LLM的tool,自動生成JSON Schema,自動處理呼叫迴圈。
聊天服務
@Service
public class ChatService {
private final ChatClient chatClient;
public ChatService(ChatClient.Builder builder) {
this.chatClient = builder
.defaultSystem("你是智慧客服助手。")
.defaultFunctions("getWeather", "queryOrders", "createTicket")
.build();
}
public String chat(String userMessage) {
return chatClient.prompt().user(userMessage).call().content();
}
}
LangChain4j Tool註解驅動開發
@Tool("搜尋知識庫中與查詢相關的文件")
public List<DocumentResult> searchKnowledge(
@P("搜尋關鍵詞") String query,
@P("回傳結果數量") int topK
) {
return vectorStore.similaritySearch(query, topK);
}
@Tool("執行SQL查詢(唯讀,SELECT語句)")
public QueryResult executeQuery(@P("SQL查詢語句") String sql) {
if (!sql.trim().toUpperCase().startsWith("SELECT")) {
throw new IllegalArgumentException("僅允許SELECT查詢");
}
return databaseService.executeQuery(sql);
}
並行函式呼叫的排程策略
| 策略 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
| 全串行 | 有嚴格依賴 | 簡單可靠 | 效能差 |
| 全並行 | 無依賴 | 最快 | 可能違反業務約束 |
| DAG排程 | 混合依賴 | 兼顧效能與正確性 | 實作複雜 |
錯誤處理與重試:優雅降級
public class ResilientToolExecutor {
public ToolResult executeWithResilience(ToolCall call, String userId) {
// 第1層:熔斷器檢查
if (circuitBreakers.isOpen(call.name())) {
return ToolResult.fallback(call.name(), "服務暫時不可用");
}
// 第2層:參數校驗
ValidationResult validation = registry.validate(call.name(), call.arguments());
if (!validation.isValid()) {
return ToolResult.error(call.name(), "參數校驗失敗");
}
// 第3層:權限檢查
if (!registry.hasPermission(call.name(), userId)) {
return ToolResult.error(call.name(), "無權限執行此操作");
}
// 第4層:重試執行
try {
return retryPolicy.execute(() -> registry.execute(call.name(), call.arguments(), userId));
} catch (RetryExhaustedException e) {
circuitBreakers.recordFailure(call.name());
return ToolResult.fallback(call.name(), getFallbackMessage(call.name()));
}
}
}
安全邊界:權限控制與沙箱執行
權限模型
ToolPermissionManager.builder()
.role("USER", Set.of("getWeather", "queryOrders", "searchKnowledge"))
.role("VIP", Set.of("getWeather", "queryOrders", "searchKnowledge", "createTicket", "processRefund"))
.role("ADMIN", Set.of("*"))
.dangerousOperations(Set.of("processRefund", "deleteAccount"))
.build();
輸入校驗與注入防護
- SQL注入防護:檢測UNION、DROP等關鍵字
- 路徑遍歷防護:阻止
../等路徑跳轉 - 命令注入防護:過濾shell特殊字元
- 長度限制:字串上限1000字元
生產案例:智慧客服多輪函式呼叫編排
使用者:「我昨天下的單怎麼還沒出貨?如果今天不出貨我就要退款。」
第1輪:LLM → queryOrders(userId, "yesterday")
第2輪:LLM分析訂單狀態 → processRefund(orderId)
第3輪:LLM → createTicket(userId, "退款申請", ...)
第4輪:LLM → 生成最終回覆
@Service
public class SmartCustomerService {
public CustomerServiceResponse handle(String userMessage, String userId, String sessionId) {
var response = chatClient.prompt()
.system(buildSystemPrompt(userId))
.messages(conversationStore.getHistory(sessionId))
.user(userMessage)
.functions("queryOrders", "processRefund", "createTicket", "sendNotification")
.advisors(
new LoggingAdvisor(),
new RateLimitAdvisor(),
new SafetyCheckAdvisor(),
new MetricsAdvisor()
)
.call();
conversationStore.append(sessionId, userMessage, response.content());
return new CustomerServiceResponse(response.content(), extractToolUsage(response));
}
}
除錯技巧:Function Calling日誌追蹤
┌──────────────────────────────────────────────────┐
│ Session: sess_abc123 User: user_456 │
├──────────────────────────────────────────────────┤
│ Turn 1: User → "訂單沒出貨,要退款" │
│ ├─ LLM → queryOrders(userId, days=1) │
│ │ └─ Result: [Order#1234 未出貨] 120ms │
│ ├─ LLM → processRefund(orderId="1234") │
│ │ └─ Result: 退款成功 850ms │
│ └─ LLM → "已為您處理訂單1234的退款..." │
│ │
│ Total: 2 tool calls, 970ms tool time │
└──────────────────────────────────────────────────┘
總結與最佳實踐
| 檢查項 | 重要性 | 說明 |
|---|---|---|
| 函式描述精確 | ⭐⭐⭐ | LLM完全依賴描述來選擇函式 |
| 參數校驗 | ⭐⭐⭐ | 永遠不信任LLM的輸出 |
| 權限控制 | ⭐⭐⭐ | 按角色限制可呼叫函式 |
| 熔斷降級 | ⭐⭐ | 外部API不可用時優雅降級 |
| 呼叫輪次限制 | ⭐⭐ | 防止無限迴圈(5-10輪) |
| 日誌追蹤 | ⭐⭐ | 全鏈路traceId追蹤 |
最重要的原則:Function Calling是「建議」不是「指令」。你的程式碼必須在執行前做校驗、做鑑權、做降級——永遠不信任LLM的輸出。
本站提供瀏覽器本地工具,免註冊即可試用 →
#Function Calling#AI Agent#LLM#工具调用#Spring AI