Skip to content

Function Calling

Function Calling 原理、一次完整调用的闭环、协议参数、生产实践与面试追问。

Updated View as Markdown
For humans

Function Calling

大模型只会输出文本。要查天气、查数据库、下单、发邮件,它做不到。Function Calling 补上这一环:模型不执行代码,只输出结构化的调用请求(调哪个工具、传什么参数),由你的应用去执行,再把结果回灌给它。这一来一回,模型从“聊天机器人”变成了“能干活”的 Agent。

原理:模型负责决策,应用负责执行

能力从哪来。 模型在监督微调(SFT)阶段被喂了大量工具调用对话样本,学会三件事:

  1. 意图识别:判断用户这句话要不要调用工具
  2. 工具选择:从工具列表里挑最合适的那个
  3. 参数生成:按工具定义的 JSON Schema 产出合法参数

可靠性靠什么保证。 普通模式下,模型是“尽力遵循” schema,参数可能写错类型、漏掉必填字段。开 strict: true 后,推理引擎用上下文无关文法(CFG)或有限状态机约束逐 token 采样,只允许生成符合 schema 的输出,格式错误“不可能发生”。前提是你的 schema 满足两个硬性要求:所有对象都要 additionalProperties: false,所有字段都要标 required

记住一个底线:模型生成 JSON 本质是逐 token 预测,不是先有结构再序列化。所以无论开不开 strict,生产代码都要在工具边界再做一次校验。

一次完整调用

标准流程是一个循环,官方叫 Agentic Loop:

Function Calling 的 Agentic Loop

几个细节决定成败:

  • arguments 是 JSON 字符串(为了流式传输方便),用之前要 json.loads 再校验
  • 回灌时必须把模型上一条带 tool_calls 的 assistant 消息一起带上,否则上下文断层,模型会“失忆”
  • 循环必须有上限,一般 5 到 10 轮,否则模型反复调用工具能把 token 烧穿

协议关键参数

参数 取值 用途
tool_choice auto / required / none / 指定函数名 默认 auto 让模型自己判断;required 强制至少调一次(适合结构化抽取);指定函数名用于固定路径
parallel_tool_calls true(默认)/ false 是否允许一次返回多个调用。有依赖关系的任务必须关掉
strict true / false 是否启用受约束解码,生产环境建议永远开

并行调用能省延迟,但有两个前提:工具之间没有依赖,且都是只读或低风险。执行时用 asyncio.gather 才是真并发,for 循环里逐个 await 跟串行没区别。失败的调用也要把错误包装成结果回灌,模型看到“天气成功、汇率失败”会自己决定重试还是降级。

生产实践

工具 Schema 设计

模型的 description 是它选工具的唯一依据,怎么写直接决定调用准确率:

  • description 写清“何时调用、何时不调用、边界在哪”,收尾那句“不返回什么”往往最重要
  • 离散参数用 enum 约束,别让模型猜
  • required 精简,optional 给默认值;嵌套不超过 3 层,参数不超过 8 个
  • 参数和字段名与业务代码保持一致,不一致会静默失败

错误处理

工具一定会失败,按类型分策略:

工具失败的三道防线

错误信息本身也是产品。回灌给模型的内容应该包含错误码、可恢复建议、正确的调用示例,一个 Python 堆栈对它毫无用处,一句“failed”也一样。{"error": "rate_limited", "retry_after": 60} 这种结构,模型才知道该等 60 秒而不是立刻再试。

写操作必须幂等。重试是常态,不传幂等键,一次失败重试就能重复下单、重复发邮件。每个写工具都要求 idempotencyKey,首次执行缓存结果,相同 key 直接返回。

安全

  • 高风险工具(删除、转账、发布)要有权限校验和人工确认,别全自动
  • 防 Prompt Injection:工具返回的内容不能当指令,外部文档和系统指令要隔离

可观测性

记下每次调用的工具名、参数、耗时、成败,按工具分桶统计成功率。没有日志,出了问题只能对着用户骂街。

和相关概念的边界

面试常被追问,一句话各说清一个:

概念 关系
ReAct Function Calling 是 ReAct 的工程化。循环结构一样,但 ReAct 靠 prompt 约定文本格式、用正则解析,脆弱;Function Calling 是 API 协议层直接吐结构化字段,稳定
Structured Output 都基于 JSON Schema。Function Calling 用于触发动作循环;Structured Output 只约束最终输出格式,不触发执行
MCP Function Calling 是模型侧的“遥控器”(决策层);MCP 是工具怎么暴露、发现、通信的协议层。MCP Server 暴露工具,模型仍靠 tool calling 选工具、填参数

常见坑

  • 并行调用的结果拆成多条消息回灌,模型会悄悄退化成串行。同一轮的多个结果要一起回
  • 模型调用了未注册的工具,回灌 {"error": "unknown_tool"} 引导自纠,别直接抛异常
  • 大段检索结果不截断直接回灌,上下文被撑爆,关键信息反而淹没。先摘要再回
  • 依赖关系交给模型判断,不如收进工具内部:write_file 内部自动 mkdir -p,比在 prompt 里写“必须先建文件夹”可靠一个数量级

面试追问

  1. 为什么模型不能直接执行函数? 模型是概率生成器,不该碰真实系统。执行、鉴权、校验、日志都在应用层,这是职责边界,也是安全边界
  2. 参数校验在哪做? 三层:LLM 输出层(JSON 解析)→ Schema 层(契约校验)→ 业务层(权限、规则)。前两层在框架内自动做,第三层在工具实现里
  3. parallel_tool_calls 关不关? 有依赖、有写操作、下游限流紧张就关;独立只读查询才开
  4. 三家厂商差异? OpenAI 的 arguments 是字符串、结果用独立 tool role;Anthropic 的 input 是对象、结果包在 user role 里且多个结果必须同一条消息;Gemini 早期靠函数名配对,顺序错就崩
Navigation

Type to search…

↑↓ navigate↵ selectEsc close