后端开发

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