今天搞懂了 Function Calling:Agent 调工具到底发生了什么?
目录
- 前言
- 一、为什么需要 Function Calling?
- 二、Function Calling 本质上做了什么?
- 三、完整流程:为什么通常需要两次模型调用?
- 四、工具定义的三个关键字段
- 五、多工具场景:Agent 为什么比单次函数调用更复杂?
- 六、LangChain 和 OpenClaw 封装了什么?
- 七、常见避坑总结
- 八、重新理解 Agent 的执行链路
- 总结
- 参考资料
核心关键词:Function Calling、Tool Calling、Agent、工具调用、结构化执行、LangChain、OpenClaw

前言
很多人第一次接触 Agent 时,都会把注意力放在“模型会不会思考”“模型能不能规划”上,但真正让 Agent 从聊天走向执行的关键,其实是工具调用。
所谓 Agent 的“执行力”,并不是模型真的直接去查数据库、调接口、订机票,而是模型先把自然语言请求转成一条结构化的调用指令,再由程序去执行真实函数,最后把执行结果交回模型生成自然语言回答。
一句话总结:
Function Calling 解决的是:如何让大模型从“生成文本”,变成“生成可执行的结构化指令”。
这就是从 Prompt 到 Agent 的关键一步。
一、为什么需要 Function Calling?
在没有 Function Calling 之前,我们也可以用提示词让模型输出 JSON,例如:
如果用户想查天气,请输出:
{"tool": "weather", "city": "beijing"}
如果用户想订机票,请输出:
{"tool": "flight", "from": "changsha", "to": "beijing"}
如果用户想计算数学,请输出:
{"tool": "calc", "expression": "2+6"}
这个方案看起来能用,但问题也很明显。
第一,它不可靠。模型可能输出 JSON,也可能输出一段解释文字;可能字段完整,也可能漏掉引号、漏掉参数,甚至把 JSON 和自然语言混在一起。
第二,它不安全。如果完全相信模型输出的“工具名”和“参数”,就可能出现危险调用。比如模型生成了一个执行系统命令的工具调用,而程序没有做白名单和参数校验,就会带来很大的风险。
第三,它不通用。不同模型供应商、不同框架可能要求不同格式。有的希望输出 JSON,有的可能是 XML 风格,有的又有自己的工具调用协议。如果完全靠提示词约定,项目很难稳定迁移。
所以行业需要一种更标准的方式:把“工具是什么、什么时候调用、参数长什么样”明确告诉模型,让模型返回结构化的工具调用结果,而不是让开发者用 Prompt 硬猜模型输出。
这就是 Function Calling 出现的背景。

二、Function Calling 本质上做了什么?
Function Calling 并不是让大模型真的执行函数。
模型不会自己访问数据库,也不会自己请求天气 API,更不会自己操作你的本地文件。它真正做的是三件事:
- 理解用户意图。
- 判断是否需要调用工具,以及调用哪个工具。
- 按约定格式提取参数,返回一条工具调用指令。
真正执行函数的,仍然是开发者写的程序。
比如用户问:
北京今天天气怎么样?
模型可能返回的不是最终答案,而是类似这样的结构化调用意图:
{
"name": "get_weather",
"arguments": {
"city": "北京"
}
}
接下来程序读取这条指令,检查函数名是否允许、参数是否合法,然后执行本地函数或远程 API:
def get_weather(city: str) -> dict:
return {
"city": city,
"weather": "晴",
"temperature": "5-15℃"
}
函数执行完之后,程序再把结果交回模型,让模型组织成用户能看懂的话:
北京今天晴,气温 5-15℃,适合出行。
所以 Function Calling 的核心不是“模型会执行”,而是“模型会决定怎么执行”。
三、完整流程:为什么通常需要两次模型调用?
Function Calling 通常至少涉及两次模型调用,这一点很容易被忽略。
第一次调用,是让模型做决策。
这时模型要回答的问题不是“最终怎么回复用户”,而是:
- 要不要调用工具?
- 调用哪个工具?
- 参数应该是什么?
第二次调用,是让模型整合工具结果。
工具返回的通常是原始数据,比如数据库记录、接口 JSON、检索结果、计算结果。这些内容不一定适合直接展示给用户,所以还需要模型把它组织成自然语言。
可以把它理解成下面这条链路:
用户输入
-> 构建带工具定义的第一次请求
-> 模型返回 tool call
-> 程序解析 tool call
-> 程序执行真实函数
-> 函数返回原始结果
-> 构建带工具结果的第二次请求
-> 模型生成最终回答
-> 返回给用户
这个“两次思考”的设计很重要:
- 第一次思考:模型负责理解意图和生成调用指令。
- 第二次思考:模型负责理解工具结果并组织表达。
如果没有第二次调用,用户可能只能看到一段原始 JSON;如果没有第一次调用,程序又不知道该执行哪个工具。
四、工具定义的三个关键字段
Function Calling 能否稳定工作,很大程度取决于工具定义写得好不好。
一个工具定义通常至少包含三部分:
| 字段 | 作用 | 关键点 |
|---|---|---|
name |
工具名称 | 给模型看的函数标识,最好清晰、具体 |
description |
工具描述 | 告诉模型这个工具能做什么、什么时候该用 |
parameters |
参数结构 | 约束参数名、类型、必填项和含义 |
其中最容易被忽略的是 description。
函数描述就是工具的“说明书”。模型并不会读你的函数实现,它主要是根据工具名、描述和参数 Schema 来判断要不要调用这个工具。
一个比较差的描述可能是:
查询信息
这个描述太泛了。模型不知道它是查天气、查股票、查订单,还是查数据库。
更好的描述应该写清楚功能边界和触发条件:
根据城市名称查询实时天气。当用户询问某个城市的天气、气温、是否适合出行时使用。
这样模型更容易做出正确路由。
写 Function Calling 的工具描述,不是在写给人看的接口文档,而是在写给模型看的决策说明。
五、多工具场景:Agent 为什么比单次函数调用更复杂?
单工具场景比较简单:模型只需要判断“要不要调用这个工具”。
但 Agent 往往面对的是一个工具箱,比如:
- 天气查询
- 机票预订
- 数据库查询
- 文件读取
- 网页搜索
- 代码执行
- 消息发送
这时候难点就变成了:
- 工具功能可能重叠,模型容易选错。
- 用户一句话里可能包含多个意图。
- 工具之间可能有依赖关系,必须按顺序执行。
比如用户说:
帮我查一下北京明天的天气,如果适合出行,就帮我订一张北京到上海的机票。
这就不是一次简单调用,而是一个多步任务:
- 先调用
get_weather("北京", "明天")。 - 把天气结果加入上下文。
- 再判断是否适合出行。
- 如果适合,再调用
book_flight("北京", "上海", "明天")。 - 最后整合天气和订票结果,回复用户。
这也是 Agent 框架要做的事情:把工具定义、模型决策、工具执行、上下文管理和多轮循环封装起来。
六、LangChain 和 OpenClaw 封装了什么?
理解 Function Calling 之后,再看 LangChain、OpenClaw 这类 Agent 框架,会更容易分清底层能力和上层封装。
原生 API 更接近底层机制,适合理解工具调用的完整链路;框架则帮我们减少重复代码,适合做复杂 Agent 应用。
LangChain 主要帮我们做了这些事:
- 从
@tool装饰器和文档字符串生成工具定义。 - 自动把工具列表传给模型。
- 自动解析模型返回的工具调用。
- 自动执行对应函数。
- 自动维护
messages对话历史。 - 支持多步工具调用循环。
OpenClaw 的思路更偏“技能系统”:
- 从
SKILL.md读取技能描述。 - 用自然语言调度不同技能。
- 支持多模型路由。
- 让技能可以被复用和共享。
换句话说,Function Calling 是底层能力;LangChain、OpenClaw 这类框架是在底层能力之上做了更高层的工程封装。
七、常见避坑总结
1. 工具描述太模糊
如果两个函数都写成“查询信息”“获取数据”,模型很容易选错。
建议让描述包含三类信息:
- 这个工具能做什么。
- 什么情况下应该调用。
- 参数分别代表什么。
2. 参数类型不匹配
模型可能把数字输出成字符串,也可能漏掉可选参数。
所以真实函数内部一定要做类型转换、默认值处理和参数校验,不要假设模型永远输出完美参数。
3. 忽略错误处理
工具执行失败时,不应该让程序直接崩溃。
更好的做法是把错误包装成结构化结果返回给模型,例如:
{
"success": false,
"error": "城市名称不能为空"
}
然后让模型用用户能理解的方式解释问题。
4. 过度依赖工具调用
不是所有问题都需要 Function Calling。
如果用户只是问一个通用概念,模型自己就能回答,没必要强制调用工具。强制调用反而会增加延迟、成本和系统复杂度。
5. 没有做工具白名单
程序执行工具前必须确认:
- 函数名是否在允许列表里。
- 参数类型是否正确。
- 参数值是否在允许范围内。
- 当前用户是否有权限调用。
模型负责“建议调用”,程序负责“最终把关”。
八、重新理解 Agent 的执行链路
理解 Function Calling 之后,Agent 就不再是一个抽象概念,而是“模型 + 工具 + 控制流程”共同完成任务的系统。
Agent 并不是一个神秘黑盒,它大概可以拆成下面几层:
用户任务
-> 模型理解和决策
-> Function Calling 生成结构化调用
-> 程序执行真实工具
-> 工具结果写回上下文
-> 模型继续决策或生成最终回答
其中 Function Calling 就是连接“自然语言”和“真实程序能力”的桥。
它让大模型不再只是聊天,而是可以通过受控、结构化、可校验的方式参与真实任务执行。
总结
理解 Function Calling 之后,最重要的结论是:
Agent 的关键不只是“会思考”,而是能把思考结果变成可靠、可校验、可执行的结构化动作。
Prompt 工程可以让模型“假装输出指令”,但 Function Calling 让这件事变成了更标准、更稳定、更安全的工程流程。
从这个角度看,Function Calling 不是一个孤立 API,而是 AI Agent 从聊天助手走向真实任务执行的基础能力。
参考资料
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)