AI Function Calling:从概念到生产的完整指南
2026年,Function Calling已经成为LLM应用开发的基础设施级能力。但"能跑通Demo"和"能上生产"之间,隔着巨大的鸿沟。这篇文章将系统性地覆盖从协议理解到生产部署的完整链路。
Function Calling的本质不是"让AI调用函数",而是让LLM输出结构化JSON而非自由文本——函数是否真的执行,完全由你的代码决定。
Function Calling的本质:结构化输出而非函数执行
这是最容易被误解的概念。让我们澄清:
用户提问 → LLM推理 → 输出JSON(函数名+参数)→ 你的代码决定是否执行 → 返回结果给LLM → LLM生成最终回答
↑ |
└──────────────── 结果注入上下文 ─────────────────────┘
关键洞察:LLM从不直接执行任何函数。 它只是"建议"应该调用哪个函数、传什么参数。你的应用代码才是真正的执行者。
为什么这很重要?
- 安全性:你可以拒绝执行任何危险操作
- 可控性:你可以在执行前修改参数、添加校验
- 可观测性:每次调用都有完整的日志和审计记录
- 可测试性: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系统。
本站提供浏览器本地工具,免注册即可试用 →