目录

核心关键词: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,更不会自己操作你的本地文件。它真正做的是三件事:

  1. 理解用户意图。
  2. 判断是否需要调用工具,以及调用哪个工具。
  3. 按约定格式提取参数,返回一条工具调用指令。

真正执行函数的,仍然是开发者写的程序。

比如用户问:

北京今天天气怎么样?

模型可能返回的不是最终答案,而是类似这样的结构化调用意图:

{
  "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 往往面对的是一个工具箱,比如:

  • 天气查询
  • 机票预订
  • 数据库查询
  • 文件读取
  • 网页搜索
  • 代码执行
  • 消息发送

这时候难点就变成了:

  1. 工具功能可能重叠,模型容易选错。
  2. 用户一句话里可能包含多个意图。
  3. 工具之间可能有依赖关系,必须按顺序执行。

比如用户说:

帮我查一下北京明天的天气,如果适合出行,就帮我订一张北京到上海的机票。

这就不是一次简单调用,而是一个多步任务:

  1. 先调用 get_weather("北京", "明天")
  2. 把天气结果加入上下文。
  3. 再判断是否适合出行。
  4. 如果适合,再调用 book_flight("北京", "上海", "明天")
  5. 最后整合天气和订票结果,回复用户。

这也是 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 从聊天助手走向真实任务执行的基础能力。

参考资料

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐