跳到主要内容

Function-Calling设计

核对日期:2026-05-09。

1. 定义与边界

函数调用(Function Calling)是模型生成结构化函数调用意图的能力,通常包括函数名和 JSON 参数。它解决的是“让模型把自然语言意图转成可执行接口参数”的问题。

Function Calling 不等于工具执行。模型只负责输出调用意图,运行时仍需要:

  • 校验参数。
  • 决定是否允许执行。
  • 调用真实函数或远程服务。
  • 处理错误和重试。
  • 把结果作为 observation 回传模型。

OpenAI 官方文档把 function calling 放在 tools 机制下;Anthropic Messages API 以 tool_use / tool_result 内容块表达工具调用。不同平台格式不同,但工程抽象可以统一为 ToolCall{name, arguments, id}

2. 为什么重要

没有结构化函数调用时,工程系统常通过正则、文本解析或提示约束让模型输出 JSON。这会带来高脆弱性:

  • 参数字段拼写不稳定。
  • 枚举值漂移。
  • 多工具调用难以关联结果。
  • 错误恢复依赖自然语言解析。

Function Calling 的价值是把“模型语言输出”收敛成“可验证的接口契约”。

3. 核心机制

3.1 四段式流程

3.2 统一调用结构

{
"id": "call_01",
"name": "calendar.create_event",
"arguments": {
"title": "项目复盘",
"start_time": "2026-05-10T10:00:00+08:00",
"duration_minutes": 60,
"attendees": ["alice@example.com"]
}
}

3.3 严格模式与结构化输出

生产系统应优先启用平台支持的严格 schema 能力,例如 OpenAI 的 Structured Outputs / strict schema、Anthropic 的严格工具使用能力。严格模式能降低参数漂移,但不能替代服务端校验,因为:

  • 模型可能选择了不该选的工具。
  • 合法 JSON 不代表业务含义合法。
  • 用户上下文可能不允许执行该动作。
  • 工具返回仍可能包含恶意内容。

4. 架构模式

4.1 Direct Function Call

模型调用业务服务中的函数。

适用于内部自动化、低风险读操作。实现简单,但工具注册、权限和日志容易耦合在业务代码里。

4.2 Adapter Function Call

模型只看到稳定的工具 schema,后端 adapter 再调用多个内部 API。

Model Tool Schema -> Adapter -> Internal API A/B/C

适用于旧系统、微服务聚合、字段需要转换的场景。

4.3 Workflow Function Call

函数不是单个 API,而是触发一个可观测工作流,例如审批流、异步任务、批处理。

适用于长耗时、高风险、需要回滚或人工审批的任务。

5. 工程实现

5.1 函数 schema 示例

{
"type": "function",
"name": "ticket.create",
"description": "创建一条内部工单。只在用户明确要求创建、提交、登记问题时使用。",
"parameters": {
"type": "object",
"additionalProperties": false,
"required": ["title", "priority", "description"],
"properties": {
"title": {
"type": "string",
"minLength": 5,
"maxLength": 80
},
"priority": {
"type": "string",
"enum": ["low", "medium", "high", "urgent"]
},
"description": {
"type": "string",
"maxLength": 2000
},
"assignee": {
"type": "string",
"description": "内部用户名;未知时不要猜测。"
}
}
}
}

5.2 运行时校验

const tool = registry.get(call.name);
if (!tool) throw new ToolError("unknown_tool");

const parsed = tool.inputSchema.safeParse(call.arguments);
if (!parsed.success) {
return {
status: "error",
code: "invalid_arguments",
details: parsed.error.issues
};
}

await policy.assertAllowed(user, tool, parsed.data);
return await tool.handler(parsed.data);

6. 生产实践

  • 函数名采用领域前缀,例如 calendar.create_eventcrm.search_customer
  • 描述只写触发条件和边界,不写长篇推理策略。
  • 参数字段使用业务稳定名,不暴露内部数据库字段。
  • 对时间、金额、地区、ID 使用明确格式。
  • 对 destructive action 使用 dry_run 或 preview 工具,再由确认工具执行。
  • 每次 schema 变更要有版本记录和评测回归。

7. 常见反模式

反模式问题
函数名过泛,例如 processhandle模型难以选择,审计不可读
参数用单个 query 字符串承载所有内容失去结构化校验价值
schema 和真实 API 直接绑定内部 API 变更会破坏模型契约
让模型生成权限字段权限应由系统计算,不能由模型自报
把执行结果当最终答案仍需模型或模板解释结果、处理失败

8. 评测方法

Function Calling 评测不应只看最终回答。建议至少分四层:

层级测什么
选择该调用时调用,不该调用时不调用
参数字段完整、格式正确、枚举合法
业务参数符合用户真实意图和权限
恢复缺字段、歧义、失败时能澄清或降级

测试集示例:

{
"input": "明天上午十点给张三约一个项目会",
"tools": ["calendar.create_event", "email.send"],
"expected_tool": "calendar.create_event",
"expected_arguments": {
"start_time": "2026-05-10T10:00:00+08:00",
"attendees_contains": ["张三"]
},
"requires_clarification": true
}

9. 安全与治理

  • 函数 schema 中不要放密钥、内部 URL、数据库表名等敏感实现细节。
  • 不允许模型选择自身权限范围;权限由运行时根据用户身份和资源策略计算。
  • 对写操作使用 preview/confirm 两阶段。
  • 对参数做注入防护,例如 SQL、shell、路径、模板注入。
  • 对工具结果做脱敏,避免把敏感字段再次喂给模型。

10. 设计决策表

决策推荐做法不推荐做法
函数粒度一个函数对应一个清晰业务动作一个 execute 承载所有动作
写操作preview/confirm 两阶段模型直接调用最终写接口
参数校验JSON Schema + 业务校验只相信模型输出合法
缺失信息澄清或调用读工具补齐让模型猜 ID、时间、金额
返回结果结构化 observation把原始 API 响应全量塞回
兼容多厂商内部统一 ToolCall 抽象业务层依赖厂商私有字段

11. Function Calling Eval Gate

建议评测集至少覆盖:

  • 正确调用:用户明确要求执行某动作。
  • 不调用:用户只是询问概念,不应调用工具。
  • 相似工具:多个函数描述相近时是否选对。
  • 参数歧义:缺时间、缺 ID、缺金额时是否澄清。
  • 安全拒绝:用户诱导越权或注入参数时是否阻断。

12. 权威资料