后端开发

AI Function Calling:从概念到生产的完整指南

2026年,Function Calling已经成为LLM应用开发的基础设施级能力。但"能跑通Demo"和"能上生产"之间,隔着巨大的鸿沟。这篇文章将系统性地覆盖从协议理解到生产部署的完整链路。

Function Calling的本质不是"让AI调用函数",而是让LLM输出结构化JSON而非自由文本——函数是否真的执行,完全由你的代码决定。


Function Calling的本质:结构化输出而非函数执行

这是最容易被误解的概念。让我们澄清:

用户提问 → LLM推理 → 输出JSON(函数名+参数)→ 你的代码决定是否执行 → 返回结果给LLM → LLM生成最终回答
           ↑                                                    |
           └──────────────── 结果注入上下文 ─────────────────────┘

关键洞察:LLM从不直接执行任何函数。 它只是"建议"应该调用哪个函数、传什么参数。你的应用代码才是真正的执行者。

为什么这很重要?

  1. 安全性:你可以拒绝执行任何危险操作
  2. 可控性:你可以在执行前修改参数、添加校验
  3. 可观测性:每次调用都有完整的日志和审计记录
  4. 可测试性:LLM的输出是纯JSON,可以断言验证

三大协议对比:OpenAI / Anthropic / Google

OpenAI Function Calling

{
    "model": "gpt-4o",
    "messages": [{"role": "user", "content": "北京今天天气怎么样?"}],
    "tools": [{
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "获取指定城市的天气信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "城市名称"},
                    "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
                },
                "required": ["city"]
            }
        }
    }]
}

LLM响应:

{
    "choices": [{
        "message": {
            "tool_calls": [{
                "id": "call_abc123",
                "type": "function",
                "function": {
                    "name": "get_weather",
                    "arguments": "{\"city\": \"北京\", \"unit\": \"celsius\"}"
                }
            }]
        }
    }]
}

Anthropic Tool Use

{
    "model": "claude-3.5-sonnet",
    "messages": [{"role": "user", "content": "北京今天天气怎么样?"}],
    "tools": [{
        "name": "get_weather",
        "description": "获取指定城市的天气信息",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {"type": "string"},
                "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
            },
            "required": ["city"]
        }
    }]
}

Google Gemini Function Calling

{
    "tools": [{
        "functionDeclarations": [{
            "name": "get_weather",
            "description": "获取指定城市的天气信息",
            "parameters": {
                "type": "OBJECT",
                "properties": {
                    "city": {"type": "STRING"},
                    "unit": {"type": "STRING", "enum": ["celsius", "fahrenheit"]}
                },
                "required": ["city"]
            }
        }]
    }]
}

协议差异对比

维度 OpenAI Anthropic Google Gemini
声明位置 tools tools tools.functionDeclarations
Schema字段 parameters input_schema parameters
类型格式 JSON Schema JSON Schema Gemini Schema
并行调用 ✅ Parallel Tool Calls ✅ 多tool_use块 ✅ 多函数调用
流式返回
强制调用 tool_choice: "required" tool_choice: {"type": "tool", "name": "..."} function_calling_config
最大调用数 128 无硬限制 无硬限制

Spring AI + Function Calling实战

Spring AI 1.0已经正式发布,提供了Java生态最优雅的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());
    }

    @Bean
    @Description("创建新的工单,用于用户反馈问题或请求服务")
    public Function<TicketCreateRequest, TicketCreateResponse> createTicket() {
        return request -> ticketService.createTicket(
            request.userId(), request.title(), request.description(), request.priority()
        );
    }
}

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();
    }

    public Flux<String> chatStream(String userMessage) {
        return chatClient.prompt()
            .user(userMessage)
            .stream()
            .content();
    }
}

请求/响应Record

public record WeatherRequest(
    @JsonProperty(required = true) @Description("城市名称") String city,
    @Description("温度单位") String unit
) {}

public record WeatherResponse(
    String city,
    double temperature,
    double humidity,
    String condition,
    String unit
) {}

LangChain4j Tool注解驱动开发

LangChain4j提供了更细粒度的控制,适合复杂Agent场景:

@Tool("搜索知识库中与查询相关的文档")
public List<DocumentResult> searchKnowledge(
    @P("搜索关键词") String query,
    @P("返回结果数量,默认5") int topK,
    @P("最小相似度阈值,0-1之间") double threshold
) {
    return vectorStore.similaritySearch(query, topK, threshold);
}

@Tool("执行SQL查询并返回结果(只读,SELECT语句)")
public QueryResult executeQuery(
    @P("SQL查询语句,仅允许SELECT") String sql
) {
    if (!sql.trim().toUpperCase().startsWith("SELECT")) {
        throw new IllegalArgumentException("仅允许SELECT查询");
    }
    return databaseService.executeQuery(sql);
}

@Tool("发送通知给指定用户")
public NotificationResult sendNotification(
    @P("目标用户ID") String userId,
    @P("通知内容") String message,
    @P("通知类型:EMAIL/SMS/PUSH") String channel
) {
    return notificationService.send(userId, message, NotificationChannel.valueOf(channel));
}

Agent编排

public class CustomerServiceAgent {

    private final ChatLanguageModel model;
    private final List<ToolSpecification> tools;

    public AgentResponse handleRequest(String userMessage, String userId) {
        List<ChatMessage> messages = new ArrayList<>();
        messages.add(SystemMessage.from("""
            你是智能客服Agent。根据用户问题选择合适的工具。
            规则:
            1. 涉及订单查询 → 使用queryOrders
            2. 涉及退款 → 先queryOrders确认订单,再processRefund
            3. 涉及投诉 → 先createTicket记录,再notifyAgent通知人工
            """));
        messages.add(UserMessage.from(userMessage));

        for (int i = 0; i < 5; i++) { // 最多5轮工具调用
            Response<AiMessage> response = model.generate(messages, tools);

            if (response.content().hasToolExecutionRequests()) {
                for (ToolExecutionRequest req : response.content().toolExecutionRequests()) {
                    var result = executeTool(req.name(), req.arguments());
                    messages.add(ToolExecutionResultMessage.from(req, result));
                }
            } else {
                return new AgentResponse(response.content().text());
            }
        }
        return new AgentResponse("抱歉,问题处理超时,请转人工客服。");
    }
}

并行函数调用的调度策略

OpenAI的Parallel Tool Calls允许LLM在一次响应中请求多个函数调用。调度策略至关重要:

串行 vs 并行 vs 混合

public class ToolExecutionOrchestrator {

    private final ExecutorService executor;
    private final ToolRegistry toolRegistry;

    public List<ToolResult> executeParallel(List<ToolCall> calls, String userId) {
        // 1. 依赖分析:构建调用DAG
        var dag = buildDependencyGraph(calls);

        // 2. 按拓扑层级并行执行
        List<ToolResult> results = new ArrayList<>();
        for (List<ToolCall> layer : dag.getLayers()) {
            List<CompletableFuture<ToolResult>> futures = layer.stream()
                .map(call -> CompletableFuture.supplyAsync(
                    () -> executeWithSafety(call, userId), executor
                ))
                .toList();

            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

            for (var future : futures) {
                results.add(future.join());
            }
        }
        return results;
    }

    private DependencyDAG buildDependencyGraph(List<ToolCall> calls) {
        // 示例:createTicket依赖queryOrders的结果
        // queryOrders → createTicket(串行)
        // getWeather(无依赖,可与queryOrders并行)
        var dag = new DependencyDAG();
        for (ToolCall call : calls) {
            Set<String> deps = toolRegistry.getDependencies(call.name());
            dag.addNode(call, deps);
        }
        return dag;
    }
}

调度策略对比

策略 适用场景 优点 缺点
全串行 有严格依赖关系 简单可靠 性能差
全并行 函数间无依赖 最快 可能违反业务约束
DAG调度 混合依赖 兼顾性能与正确性 实现复杂
限流并行 有API速率限制 避免触发限流 需要限流配置

错误处理与重试:优雅降级

多层错误处理

public class ResilientToolExecutor {

    private final ToolRegistry registry;
    private final CircuitBreakerManager circuitBreakers;
    private final RetryPolicy retryPolicy;

    public ToolResult executeWithResilience(ToolCall call, String userId) {
        String toolName = call.name();

        // 第1层:熔断器检查
        if (circuitBreakers.isOpen(toolName)) {
            return ToolResult.fallback(toolName, "服务暂时不可用,请稍后重试");
        }

        // 第2层:参数校验
        ValidationResult validation = registry.validate(toolName, call.arguments());
        if (!validation.isValid()) {
            return ToolResult.error(toolName, "参数校验失败: " + validation.getErrors());
        }

        // 第3层:权限检查
        if (!registry.hasPermission(toolName, userId)) {
            return ToolResult.error(toolName, "无权限执行此操作");
        }

        // 第4层:重试执行
        try {
            return retryPolicy.execute(() -> {
                ToolResult result = registry.execute(toolName, call.arguments(), userId);
                circuitBreakers.recordSuccess(toolName);
                return result;
            });
        } catch (RetryExhaustedException e) {
            circuitBreakers.recordFailure(toolName);
            return ToolResult.fallback(toolName, getFallbackMessage(toolName));
        } catch (TimeoutException e) {
            return ToolResult.error(toolName, "操作超时,请稍后重试");
        }
    }

    private String getFallbackMessage(String toolName) {
        return switch (toolName) {
            case "getWeather" -> "天气服务暂时不可用,建议查看天气APP";
            case "queryOrders" -> "订单系统繁忙,请稍后再试或联系人工客服";
            case "createTicket" -> "工单系统暂时不可用,请直接拨打客服热线";
            default -> "服务暂时不可用,请稍后重试";
        };
    }
}

熔断器配置

resilience:
  circuit-breaker:
    getWeather:
      failure-rate-threshold: 0.5
      slow-call-rate-threshold: 0.8
      slow-call-duration-threshold: 3s
      wait-duration-in-open-state: 30s
      permitted-number-of-calls-in-half-open-state: 3
    queryOrders:
      failure-rate-threshold: 0.3
      wait-duration-in-open-state: 60s

安全边界:函数权限控制与沙箱执行

权限模型

@Configuration
public class SecurityConfig {

    @Bean
    public ToolPermissionManager permissionManager() {
        return ToolPermissionManager.builder()
            // 普通用户:只读权限
            .role("USER", Set.of(
                "getWeather", "queryOrders", "searchKnowledge"
            ))
            // VIP用户:可创建工单和退款
            .role("VIP", Set.of(
                "getWeather", "queryOrders", "searchKnowledge",
                "createTicket", "processRefund"
            ))
            // 管理员:所有权限
            .role("ADMIN", Set.of("*"))
            // 危险操作需要额外确认
            .dangerousOperations(Set.of(
                "processRefund", "deleteAccount", "executeUpdate"
            ))
            .build();
    }
}

输入校验与注入防护

public class ToolInputSanitizer {

    public SanitizedArgs sanitize(String toolName, Map<String, Object> args) {
        Map<String, Object> sanitized = new HashMap<>();

        for (var entry : args.entrySet()) {
            Object value = entry.getValue();
            if (value instanceof String str) {
                // 防止SQL注入
                str = preventSqlInjection(str);
                // 防止路径遍历
                str = preventPathTraversal(str);
                // 防止命令注入
                str = preventCommandInjection(str);
                // 长度限制
                str = str.substring(0, Math.min(str.length(), 1000));
                sanitized.put(entry.getKey(), str);
            } else {
                sanitized.put(entry.getKey(), value);
            }
        }

        return new SanitizedArgs(sanitized);
    }

    private String preventSqlInjection(String input) {
        // 检测常见SQL注入模式
        Pattern sqlPattern = Pattern.compile(
            "(?i)(\\bUNION\\b|\\bDROP\\b|\\bDELETE\\b|--|;|/\\*|\\*/)"
        );
        if (sqlPattern.matcher(input).find()) {
            throw new SecurityException("检测到潜在的SQL注入攻击");
        }
        return input;
    }
}

沙箱执行

public class SandboxExecutor {

    private final ExecutorService sandboxPool;

    public ToolResult executeInSandbox(ToolCall call, Duration timeout) {
        Future<ToolResult> future = sandboxPool.submit(() -> {
            // 设置安全上下文
            SecurityManager sm = new ToolSecurityManager(
                allowedClasses, allowedPackages, maxMemoryBytes
            );
            return executeWithSecurityManager(call, sm);
        });

        try {
            return future.get(timeout.toMillis(), TimeUnit.MILLISECONDS);
        } catch (TimeoutException e) {
            future.cancel(true);
            return ToolResult.error(call.name(), "沙箱执行超时,已强制终止");
        }
    }
}

生产案例:智能客服多轮函数调用编排

场景描述

用户说:"我昨天下的单怎么还没发货?如果今天不发货我就要退款。"

这需要多轮编排:

第1轮:LLM → queryOrders(userId, "yesterday")
第2轮:LLM分析订单状态 → 如果未发货,processRefund(orderId)
第3轮:LLM → createTicket(userId, "退款申请", ...)
第4轮:LLM → 生成最终回复

完整编排实现

@Service
public class SmartCustomerService {

    private final ChatClient chatClient;
    private final ConversationStore conversationStore;

    public CustomerServiceResponse handle(
        String userMessage, String userId, String sessionId
    ) {
        // 加载对话历史
        var history = conversationStore.getHistory(sessionId);

        // 构建上下文
        var context = Map.of(
            "userId", userId,
            "sessionId", sessionId,
            "timestamp", Instant.now().toString()
        );

        // 执行Function Calling循环
        var response = chatClient.prompt()
            .system(buildSystemPrompt(context))
            .messages(history)
            .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),    // 返回工具调用记录
            extractConfidence(response)    // 返回置信度
        );
    }
}

Advisor模式:横切关注点

public class SafetyCheckAdvisor implements CallAroundAdvisor {

    @Override
    public AdvisedResponse aroundCall(AdvisedRequest request, CallAroundAdvisorChain chain) {
        // 检查用户请求是否包含敏感信息
        detectSensitiveInfo(request.userText());

        // 检查工具调用是否在权限范围内
        if (request.functionNames() != null) {
            for (String fn : request.functionNames()) {
                permissionManager.checkPermission(fn, request.context().get("userId"));
            }
        }

        AdvisedResponse response = chain.nextAroundCall(request);

        // 检查LLM响应是否包含危险内容
        validateResponseSafety(response);

        return response;
    }
}

调试技巧:Function Calling日志追踪

结构化日志

@Slf4j
public class FunctionCallLogger {

    public void logToolCall(ToolCall call, String userId, String sessionId) {
        log.info("""
            [Function Call] session={} user={} tool={} args={} traceId={}
            """,
            sessionId, userId, call.name(),
            maskSensitiveArgs(call.arguments()),
            MDC.get("traceId")
        );
    }

    public void logToolResult(ToolCall call, ToolResult result, long durationMs) {
        log.info("""
            [Function Result] tool={} status={} duration={}ms resultSize={}
            """,
            call.name(), result.status(), durationMs,
            result.content() != null ? result.content().length() : 0
        );
    }
}

调用链可视化

┌──────────────────────────────────────────────────────────┐
│ Session: sess_abc123  User: user_456                     │
├──────────────────────────────────────────────────────────┤
│ Turn 1: User → "昨天订单没发货,要退款"                    │
│   ├─ LLM → queryOrders(userId="user_456", days=1)       │
│   │   └─ Result: [Order#1234 未发货, Order#1235 已发货]  │
│   │      Duration: 120ms                                  │
│   ├─ LLM → processRefund(orderId="1234", amount=299.00) │
│   │   └─ Result: 退款成功,预计1-3工作日到账               │
│   │      Duration: 850ms                                  │
│   ├─ LLM → createTicket(userId="user_456", ...)          │
│   │   └─ Result: Ticket#T789 已创建                      │
│   │      Duration: 200ms                                  │
│   └─ LLM → "已为您处理订单1234的退款..."                   │
│                                                          │
│ Total: 3 tool calls, 1170ms total tool time              │
│ Tokens: 450 input + 280 output = 730 total              │
└──────────────────────────────────────────────────────────┘

总结与最佳实践

生产级Function Calling检查清单

检查项 重要性 说明
函数描述精确 ⭐⭐⭐ LLM完全依赖描述来选择函数
参数校验 ⭐⭐⭐ 永远不信任LLM的输出
权限控制 ⭐⭐⭐ 按角色限制可调用函数
熔断降级 ⭐⭐ 外部API不可用时优雅降级
调用轮次限制 ⭐⭐ 防止无限循环(建议5-10轮)
超时控制 ⭐⭐ 每个函数调用设置超时
日志追踪 ⭐⭐ 全链路traceId追踪
成本监控 Token消耗和API调用成本

最重要的原则:Function Calling是"建议"不是"指令"。你的代码必须在执行前做校验、做鉴权、做降级——永远不信任LLM的输出。

2026年的Function Calling生态已经非常成熟。Spring AI和LangChain4j为Java开发者提供了生产级的框架支持,OpenAI/Anthropic/Google三大协议也在逐步收敛。掌握这些模式,你就能构建出可靠、安全、可观测的AI Agent系统。

本站提供浏览器本地工具,免注册即可试用 →

#Function Calling#AI Agent#LLM#工具调用#Spring AI