Function Calling 是模型与外部世界之间的结构化接口。它的核心价值在于:让模型能够自主决定何时调用工具、调用哪个工具、传什么参数,而不是只能生成文本。
在 Agent 系统中,它承担几个关键角色:
(1)连接模型推理与真实系统
模型本身不能查数据库、不能发邮件、不能调 API。Function Calling 让模型输出一个结构化的调用请求(函数名 + 参数 JSON),由应用代码执行,再把结果回填给模型继续推理。这是 Agent 从“聊天”变成“做事”的第一步。
(2)支撑多步编排
Agent 的规划能力(ReAct、Plan-and-Execute 等)依赖 Function Calling 作为执行层。模型每一步的 Action 就是一次 Function Call,Observation 就是工具返回结果。没有 Function Calling,规划只是空想。
(3)实现可控的集成
模型不直接执行业务逻辑,只负责“点单”,应用代码负责“做菜”。开发者可以校验参数、控制权限、记录日志、处理错误。这种分层是 Agent 系统安全性和可审计性的基础。
一句话:Function Calling 让模型从“文本生成器”变成“系统编排器”,是 Agent 执行能力的底层机制。
工具描述是影响工具调用准确率的最大单一因素。模型决定调不调、调哪个,唯一依据就是描述。
(1)描述必须回答四个问题
一份好的描述需要说清:
比如一个 deactivateUser 的描述:
“禁用用户账号,撤销所有会话并阻止登录。可逆。当客户要求关闭、取消、暂停或挂起账号时使用。不删除数据。如需永久删除,使用 deleteUser,该操作不可逆。”
这段描述同时做了三件事:说明功能、指出适用场景(客户的用词“关闭/取消/暂停”)、指出兄弟工具(deleteUser)避免混淆。
(2)包含用户的词汇
用户说“关掉账号”,API 叫 deactivateUser。描述里要出现“关闭”“取消”“暂停”这些用户实际会用的词,而不是只写“设置 active 为 false”。
(3)指出兄弟工具
当两个工具功能相近时,在描述里直接说“如果你要 X,用 Y 工具,不要用这个”。模型在对比两者的瞬间看到这句话,歧义就被消解了。
(4)命名要具体
search 太模糊。searchCustomersByEmail 同时告诉了模型搜什么、怎么搜。用 verbNoun 风格,保持一致性。
(5)控制工具数量
同时暴露的工具越多,模型选错的概率越大。建议同时活跃的工具控制在 20 个以内。如果工具很多,用工具检索(tool search)按需加载,或者按对话上下文只传相关子集。
参数 Schema 的目标是:让模型很难产出错误的参数。
(1)用枚举约束封闭集合
如果某个参数只有固定几个合法值,用 enum。字符串类型让模型自由发挥,枚举让它只能从列表里选。
"status": {
"type": "string",
"enum": ["pending", "paid", "refunded", "cancelled"]
}
(2)单位写进参数名
amount 有歧义,模型可能猜美元或美分。amount_cents 没有歧义。同理 timeout_seconds、distance_meters。
(3)格式给示例
日期参数写“ISO 8601 格式,例如 2026-08-26”比只写“开始日期”准确得多。
(4)避免可表示的非法状态
toggle_light(on: bool, off: bool) 允许 on=true, off=true,这是逻辑矛盾。改成单个 state 枚举 ["on", "off"],矛盾状态就无法表示。
(5)required 要诚实
标记为 required 的参数必须是模型确实能提供的。如果模型没有信息来填某个参数,不要标记 required,而是在代码里提供默认值或从上下文中注入。反过来,可选参数太多会把失败推到运行时。
(6)扁平优于嵌套
模型填 {"customer": {"address": {"postal_code": "..."}}} 比填 customer_postal_code 更容易出结构错误。在工具边界拍平,在 executor 里重组。
(7)不要要求模型填它不知道的值
内部 ID、租户 UUID、计算出的哈希值,模型无法知道。这些应该由代码从会话或上下文中注入,而不是让模型猜。
错误处理的核心原则:不要让一次坏的工具调用变成一次坏的答案。
(1)永远不要把错误吞掉
如果工具调用失败,代码返回空字符串或默认值,模型会以为工具成功了,然后基于空数据编一个答案。这是最危险的失败模式:用户看到一个自信的错误答案,系统没有任何异常。
(2)把错误以结构化形式返回给模型
错误应该作为 tool result 的一部分返回给模型,让模型能看到“哪里错了、值不值得重试”。常见做法是返回一个 JSON:
{"error": "invalid_arguments", "details": "...", "retryable": true}
模型收到这个信号后,通常会修正参数重试,或者告知用户无法完成。
(3)区分可重试和不可重试
可重试:网络超时、速率限制(429)、临时故障。代码层做指数退避重试,不要把这个决策丢给模型。
不可重试:参数语义错误、权限不足、资源不存在。返回给模型,让它修正参数或告知用户。
(4)设置超时和每轮调用上限
每个工具调用要有超时,每轮对话要有工具调用次数上限。否则模型可能陷入“调用失败→重试→再失败”的循环,消耗 token 和 API 配额。
(5)先验证再执行
不要信任模型传来的参数。用 Pydantic 或类似工具做语义校验(日期先后、数值范围、枚举值),校验失败返回错误给模型,而不是让工具带着错误参数执行。
(6)危险操作要有闸门
删除、支付、发消息这类不可逆操作,不要让模型直接触发。加确认步骤、权限校验、人工审批。模型负责“提议”,代码负责“批准”。
Function Calling 让模型能自主调用外部工具,是 Agent 执行能力的底层机制。提高准确率靠:描述说清做什么/何时用/何时不用/不做什么,参数用枚举和明确单位约束,工具数量控制。提高鲁棒性靠:错误不吞掉、结构化返回给模型、区分可重试、设置超时和上限、危险操作加闸门。
质量:上。 适合中高级岗位,能有效区分“用过 Function Calling”和“真正做过生产级工具调用”的人。建议追问:
“strict 模式能保证什么?不能保证什么?”
“并行调用怎么处理?”
“工具数量多了怎么办?”
“你遇到过模型编造参数的情况吗?怎么处理的?”
“危险操作怎么加闸门?”
“Function Calling 是模型和外部系统之间的结构化接口。模型输出函数名和参数,代码执行,结果回填。它让 Agent 能真正做事,也是 ReAct 等规划模式的执行层。”
“描述设计上,最关键的是说清四件事:做什么、何时用、何时不用、不做什么。要包含用户的词汇,指出兄弟工具避免混淆,工具数量控制在 20 个以内。”
“Schema 设计的目标是让错误参数难以表示。封闭集合用 enum,单位写进参数名,required 要诚实,扁平优于嵌套,不要让模型填它不知道的值。”
“错误处理的核心原则是:永远不要把错误吞掉。失败要结构化返回给模型,区分可重试和不可重试。可重试的代码层退避,不可重试的让模型修正。要有超时和每轮调用上限,危险操作加确认闸门。”
“我们实际遇到过工具失败返回空字符串,模型编了一个答案。后来强制所有错误结构化返回,加上 Pydantic 语义校验,静默失败基本消失了。”
评论专区
评论加载中...登录后即可发表评论