Function Calling 与 Code Mode:AI Agent 工具调用的原理、对比与选型指南

Function Calling vs Code Mode:AI Agent 工具调用的原理、差异与选型指南

本文面向正在设计 AI Agent、工具调用层或 MCP 集成的开发者,系统介绍 Function Calling 与 Code Mode,比较两者的执行模型、性能和安全边界,并给出可落地的选型方法。

一、先说结论

Function Calling 和 Code Mode 不是完全对等的两种 API 功能,更准确地说,它们位于不同层次:

  • Function Calling 是调用协议:模型返回结构化的函数名和参数,由应用程序执行函数,再把结果交回模型。
  • Code Mode 是编排模式:模型生成一段受控代码,在沙箱中组合多个工具,完成循环、分支、过滤、聚合等中间处理,再返回精简结果。

可以先按下面的规则选择:

任务特征 默认选择
单次查询、单次动作、工具数量少 Function Calling
每一步都需要模型重新判断下一步 Function Calling
多个调用之间有确定的数据流 Code Mode
需要循环、并行、过滤、排序、聚合大量结果 Code Mode
涉及写入、删除、支付、发邮件等副作用 Function Calling,默认直连并加审批
工具目录很大 先用 Tool Search/渐进式发现;编排阶段再考虑 Code Mode

工程上最稳妥的方案通常是混合模式:让 Function Calling 负责意图判断、审批和关键动作,让 Code Mode 负责边界清晰的数据编排。

二、为什么需要工具调用

大语言模型本身只能生成内容,不能天然读取业务数据库、查询订单或修改账户。应用需要把外部能力以"工具"形式提供给模型,例如:

  • get_customer_profile(customer_id):查询客户资料;
  • search_orders(customer_id, status):搜索订单;
  • create_refund(order_id, amount):发起退款;
  • search_document(query):检索知识库。

这里的工具可以是应用内函数、HTTP API、MCP Server,也可以是平台提供的内置工具。

关键问题不在于"模型能不能调用工具",而在于:

  1. 工具定义如何传给模型;
  2. 谁决定调用哪个工具;
  3. 多个工具如何传递中间结果;
  4. 哪些操作必须经过审批;
  5. 如何控制上下文、延迟、成本和风险。

Function Calling 和 Code Mode 的区别,主要就体现在这些问题的答案上。

三、Function Calling 是什么

3.1 定义

Function Calling(也称 Tool Calling)是让模型按照预先给定的 JSON Schema,输出一个结构化的函数调用请求。

模型不会直接执行你的函数。它只负责提出调用意图和参数,真正执行函数的是你的应用程序。

以 OpenAI Responses API 为例,官方定义的基本流程是:

  1. 应用向模型发送用户输入和可调用工具;
  2. 模型返回一个或多个 function_call
  3. 应用解析参数并执行对应函数;
  4. 应用把 function_call_output 连同 call_id 回传给模型;
  5. 模型生成最终回答,或继续提出新的工具调用。

完整流程可表示为:

text 复制代码
用户问题
   |
   v
模型 + 工具定义
   |
   +-- 普通文本回答 ----------------------> 返回答案
   |
   +-- function_call(name, arguments)
             |
             v
       应用程序执行函数
             |
             v
       function_call_output
             |
             +---------- 回到模型,继续判断

官方参考:OpenAI Function Calling 指南

3.2 最小伪代码

下面是与具体 SDK 无关的伪代码,展示直接工具调用的核心循环:

javascript 复制代码
const tools = [
  {
    name: "get_customer_profile",
    description: "根据客户 ID 查询客户资料",
    parameters: {
      type: "object",
      properties: {
        customer_id: { type: "string" }
      },
      required: ["customer_id"],
      additionalProperties: false
    },
    strict: true
  }
];

let input = [{ role: "user", content: "查询客户 CUST-1001 的资料" }];

while (true) {
  const response = await model.generate({ input, tools });
  input.push(...response.output);

  const calls = response.output.filter(item => item.type === "function_call");
  if (calls.length === 0) {
    return response.output_text;
  }

  for (const call of calls) {
    const args = JSON.parse(call.arguments);
    const result = await dispatch(call.name, args);

    input.push({
      type: "function_call_output",
      call_id: call.call_id,
      output: JSON.stringify(result)
    });
  }
}

3.3 Function Calling 的优点

  • 边界清晰:模型只能请求已经暴露的函数;
  • 容易审计:每次调用都有函数名、参数和调用 ID;
  • 适合副作用操作:可以在执行前做鉴权、审批、幂等和风控;
  • 容易接入已有服务:函数内部可以调用数据库、HTTP API 或消息队列;
  • 控制权在应用侧:应用决定是否执行、如何重试以及返回哪些数据。

3.4 Function Calling 的局限

直接调用的每一步通常都要把结果交回模型,再由模型决定下一步。当任务包含大量工具调用时,可能出现:

  • 中间结果反复进入上下文,增加输入 Token;
  • 多次模型往返带来更高延迟;
  • 模型需要处理本来可以由代码完成的过滤、排序和聚合;
  • 工具定义过多,模型选择工具的难度上升。

这并不意味着 Function Calling 不适合多步任务。只要每一步都需要模型进行语义判断,它反而更合适。

四、Code Mode 是什么

4.1 术语说明

Code Mode 目前不是一个所有厂商都严格遵循的统一标准名称。它通常描述一种工具使用模式:

模型不再逐个请求工具,而是生成一段代码;代码在受控运行时中调用工具、处理结果,并输出最终需要交回模型的数据。

Cloudflare Agents 文档将 Code Mode 定义为"模型编写代码,而不是分别请求每个操作",并强调它适合组合依赖调用、循环、分支、结果过滤和结果整形。Cloudflare Code Mode 文档

OpenAI 的对应能力称为 Programmatic Tool Calling :模型生成并运行 JavaScript,通过 tools.* 编排工具调用。官方指南说明,这种方式可以并行调用工具、使用循环和条件,并把中间结果保留在托管运行时中。OpenAI Programmatic Tool Calling 指南

因此,本文所说的 Code Mode,主要指"代码编排工具"的架构模式;在 OpenAI 语境下,可将其理解为 Programmatic Tool Calling 的同类实现,而不是普通的 Code Interpreter 或让模型随意执行服务器代码。

4.2 Code Mode 的执行流程

text 复制代码
用户问题
   |
   v
模型 + 一个代码执行入口 + 工具类型定义
   |
   v
模型生成编排代码
   |
   v
受控沙箱执行代码
   |        \
   |         +-- 调用工具 A
   |         +-- 调用工具 B
   |         +-- 过滤、排序、聚合、循环
   |
   v
返回精简结果
   |
   v
模型生成最终回答

4.3 最小伪代码

例如,用户要求"找出库存不足的商品,并计算缺口"。如果库存查询和需求查询之间没有复杂的语义判断,可以让代码完成编排:

javascript 复制代码
const skus = ["SKU-001", "SKU-002", "SKU-003"];

const rows = await Promise.all(
  skus.map(async sku => {
    const [inventory, demand] = await Promise.all([
      tools.get_inventory({ sku }),
      tools.get_demand({ sku })
    ]);

    return {
      sku,
      available_units: inventory.available_units,
      requested_units: demand.requested_units,
      shortage_units: Math.max(
        demand.requested_units - inventory.available_units,
        0
      )
    };
  })
);

const shortages = rows.filter(row => row.shortage_units > 0);
text(JSON.stringify(shortages));

这里的关键不是"让模型写更多代码",而是把确定性的中间处理从模型上下文中移到运行时中:

  • 多个独立请求可以并行;
  • 后续参数可以由前一步结果直接计算;
  • 大量原始结果可以在沙箱中先过滤;
  • 最终只返回必要的结构化结果。

4.4 Code Mode 的优点

  • 适合组合调用:可以在一段代码中串联多个工具;
  • 减少中间上下文:原始结果可以留在运行时,模型只接收摘要或聚合结果;
  • 支持程序控制流:循环、条件、并行、映射和排序由代码执行;
  • 更适合大工具目录:可以配合渐进式工具发现,只加载当前任务需要的能力;
  • 可复用编排逻辑:稳定的多步流程可以形成模板或片段。

4.5 Code Mode 的局限

  • 安全边界更复杂:必须运行在沙箱中,并限制可调用工具和副作用;
  • 调试难度更高:需要同时观察模型生成的代码、工具调用和运行时状态;
  • 模型仍可能生成错误代码:类型、字段、循环终止条件和异常处理都要验证;
  • 不适合所有自适应任务:如果每个结果都需要模型进行语义判断,代码编排可能过早固定流程;
  • 运行时存在限制:不同平台的沙箱在网络、文件系统、依赖安装、持久化和超时方面差异很大;
  • 部分实现仍处于实验阶段:例如 Cloudflare 文档明确提示其 Code Mode 可能发生破坏性变更,生产使用需谨慎。

以 OpenAI 的 Programmatic Tool Calling 为例,生成的 JavaScript 运行在隔离的 V8 运行时中,不能直接使用 Node.js、安装包、访问任意网络、使用通用文件系统或启动子进程;它只能通过请求中启用的工具与外部系统交互。具体限制应以当前平台文档为准。

五、两者的核心差异

对比维度 Function Calling Code Mode
抽象层次 工具调用协议 工具编排模式
模型输出 函数名 + 结构化参数 一段受控代码或程序计划
执行者 应用程序执行函数 沙箱执行代码,代码再调用工具
控制流 主要由模型和应用循环共同控制 循环、分支、并行由代码控制
中间结果 通常回到模型上下文 可留在运行时,最后返回摘要
最适合 单次调用、语义决策、审批动作 确定性多步调用、批量处理、聚合
工具边界 应用可以精确控制每个函数 需要控制代码能看到和调用的工具集合
可审计性 调用链直接、容易记录 需同时记录程序、嵌套调用和运行状态
安全重点 参数校验、鉴权、审批、幂等 沙箱隔离、工具权限、资源配额、审批
失败处理 每个调用可单独重试或转人工 需要定义程序级超时、重试和部分失败策略
上下文和 Token 工具定义和中间结果可能较多 通常能减少中间结果回传,但会增加程序执行管理成本
延迟 多次模型往返可能较慢 确定性编排可减少模型往返,但沙箱和工具执行仍有成本
成熟度 主流模型和 SDK 普遍支持 平台实现差异大,部分实现仍较新或实验性较强

六、最容易混淆的三个概念

6.1 Code Mode 不等于 Code Interpreter

两者都涉及"模型生成代码",但目标不同:

  • Code Interpreter:让模型用代码完成计算、数据分析、文件处理等任务;
  • Code Mode:让模型用代码编排已暴露的工具,完成多个业务操作的组合。

Code Mode 的代码通常不能任意访问业务网络或数据库,必须通过受控工具访问外部系统。

6.2 Function Calling 不等于函数自动执行

模型返回调用请求后,应用仍然需要:

  1. 校验函数名和参数;
  2. 检查用户身份、权限和租户范围;
  3. 判断是否需要审批;
  4. 执行函数或拒绝执行;
  5. 将安全、可用的结果回传给模型。

把模型输出的函数名直接映射到任意本地函数,是常见且严重的安全错误。

6.3 Code Mode 也不是"让模型拥有一台服务器"

Code Mode 的正确实现应当是:

  • 在隔离运行时中执行;
  • 只注入明确允许的工具;
  • 对工具参数继续做服务端校验;
  • 对写操作保留审批和审计;
  • 限制执行时间、并发数、内存和输出大小。

如果实现允许生成代码直接访问生产网络、任意文件或操作系统命令,应将其视为高风险远程代码执行面,而不是普通的工具调用功能。

七、如何做选型

7.1 选择 Function Calling 的场景

以下条件满足一项,就应优先考虑直接 Function Calling:

  • 任务只需要一次或少量工具调用;
  • 每个工具结果都需要模型重新理解后再决定下一步;
  • 工具具有副作用,例如创建、修改、删除、支付、发信;
  • 必须在每个动作前进行用户确认或人工审批;
  • 需要保留原始引用、来源、原生文件或其他不可丢失的产物;
  • 团队还没有成熟的沙箱、运行时和程序级观测能力。

典型例子:

text 复制代码
用户:把订单 ORD-1001 退款 50 元

模型判断意图
  -> Function Calling: get_order(ORD-1001)
  -> 应用校验订单归属和可退款金额
  -> 用户确认
  -> Function Calling: create_refund(...)
  -> 返回退款结果

这里每一步都涉及权限和业务规则,不应该为了减少往返而隐藏在模型生成的代码里。

7.2 选择 Code Mode 的场景

以下条件同时成立时,Code Mode 通常更有价值:

  • 调用链的控制流相对确定;
  • 中间结果可以由代码可靠地过滤、排序、连接或聚合;
  • 工具调用数量较多,直接回传所有结果会浪费上下文;
  • 独立调用可以并行执行;
  • 运行时有清晰的沙箱、权限、配额和审计机制。

典型例子:

  • 查询多个仓库后找出共同依赖;
  • 批量读取多个客户的指标并计算排名;
  • 分页拉取数据,去重后只把前 10 条交给模型;
  • 先获取商品列表,再批量获取详情并计算库存缺口;
  • 从大量工具结果中筛选满足条件的记录。

7.3 需要保留 Function Calling 的场景

Code Mode 并不意味着所有工具都应该改成"代码调用"。尤其要注意:

  • 写入和删除操作应默认走直接调用;
  • 审批点应显式存在,不能藏在复杂程序里;
  • 需要模型逐条判断的搜索和研究任务,不要强行改成固定代码流程;
  • 需要最终引用、原生文件或外部凭证时,要确保程序保留并验证这些信息;
  • 如果程序无法可靠处理部分失败,就不要让它批量执行高影响操作。

7.4 大规模工具目录的处理

工具数量增加后,不要简单地把所有 Schema 一次性塞给模型。工具定义本身会占用上下文,也可能降低选择准确率。

可以按顺序采用:

  1. 按领域划分命名空间,例如 crmbillingshipping
  2. 只把当前阶段必需的工具暴露给模型;
  3. 使用 Tool Search 或类似的渐进式发现机制延迟加载工具定义;
  4. 在 Code Mode 中只提供已经发现且明确授权的工具;
  5. 记录工具命中率和无效调用率,再决定是否继续拆分。

OpenAI 官方建议控制初始可用函数数量,并在工具目录较大时考虑 Tool Search;Tool Search 指南还说明了命名空间和延迟加载的使用方式。

八、推荐的混合架构

对于大多数生产 Agent,可以采用下面的分层方式:

text 复制代码
用户请求
   |
   v
模型:识别意图、拆分阶段、判断风险
   |
   +-- 需要确认/有副作用 ------> 直接 Function Calling
   |                                  |
   |                                  +-- 鉴权
   |                                  +-- 审批
   |                                  +-- 幂等和审计
   |
   +-- 可确定性批处理 ----------> Code Mode
                                      |
                                      +-- 并行调用只读工具
                                      +-- 过滤、聚合、排序
                                      +-- 返回小而明确的结果
   |
   v
模型:解释结果并生成最终回答

8.1 路由规则示例

不要只在提示词中写"请高效使用 Code Mode"。应明确规定工具使用边界,例如:

text 复制代码
工具编排规则:

1. 对客户资料、订单状态等只读查询,可以使用 Code Mode 做并行查询和结果聚合。
2. 对退款、删除、发邮件、修改账户等操作,只能使用直接 Function Calling。
3. 任何副作用操作必须在应用侧完成鉴权,并在执行前获得用户确认。
4. Code Mode 只能使用本阶段列出的工具,不得自行发现或调用其他工具。
5. 程序最多重试一次临时错误,不得重复执行已完成的副作用操作。
6. 如果必要字段缺失,返回结构化失败,不要猜测参数。

8.2 工具设计建议

不要只比较"模型看起来会不会调用工具",应使用相同任务集做 A/B 测试,至少关注以下指标:

指标 说明
任务完成率 是否得到业务上正确的最终结果
工具选择准确率 是否调用了正确工具
参数校验通过率 参数是否满足 Schema 和业务约束
无效调用率 是否出现不存在的工具、字段或重复调用
平均和 P95 延迟 用户实际感受到的速度
输入/输出 Token 工具定义和中间结果带来的上下文成本
沙箱执行成本 Code Mode 的运行时、并发和资源成本
审批绕过率 高影响操作是否始终经过审批
重试与部分失败率 批量任务失败后的恢复质量
可审计性 能否重建一次任务的决策和执行链

测试集应覆盖:

  • 正常请求;
  • 缺少参数和格式错误;
  • 多租户越权;
  • 工具超时和部分失败;
  • 重复请求;
  • 恶意提示词和工具描述注入;
  • 大量结果、空结果和边界值;
  • 需要人工审批的高风险动作。

十一、常见反模式

反模式 1:把所有工具都暴露给模型

后果是工具 Schema 占用上下文,模型更容易选错。应使用命名空间、阶段化暴露或 Tool Search。

反模式 2:为了少一次模型调用,把副作用藏进 Code Mode

这会模糊审批和审计边界。副作用操作应保持直接、可见、可拦截。

反模式 3:把模型生成代码当作可信代码

代码可能访问不存在的字段、进入无限循环或重复调用。沙箱和运行时限制是必须项,而不是优化项。

反模式 4:只验证最终文本,不验证工具执行链

最终回答看起来正确,不代表中间操作安全或真实。必须核对调用参数、权限、返回值和审批记录。

反模式 5:忽略工具返回值的大小

把数千条原始记录逐条交回模型会造成上下文浪费。应在服务端分页,或用 Code Mode 先过滤和聚合。

反模式 6:使用模糊的编排提示词

"尽量高效""必要时重试"无法定义可执行边界。应明确工具范围、停止条件、重试上限、结果格式和失败处理。

十二、最终决策清单

在架构评审时,可以逐项回答:

  • 这个任务是否只需要一次或少量工具调用?
  • 每一步是否需要模型进行新的语义判断?
  • 中间结果能否由确定性代码处理?
  • 是否需要循环、并行、过滤、排序或聚合?
  • 工具是否包含写入、删除、支付或其他副作用?
  • 是否存在强制审批和人工接管点?
  • 是否有可靠的沙箱、配额、超时和审计能力?
  • 工具定义是否过多,需要渐进式发现?
  • 是否保留了引用、原生文件或其他不可丢失产物?
  • 是否用真实任务集比较了完成率、延迟、Token 和安全指标?

可以用以下简化决策树收尾:

text 复制代码
是否涉及副作用或强审批?
  |-- 是 --> 直接 Function Calling
  |
  |-- 否 --> 是否需要模型在每一步做语义判断?
               |-- 是 --> 直接 Function Calling
               |
               |-- 否 --> 是否存在批量、循环、并行或聚合?
                            |-- 是 --> Code Mode
                            |
                            |-- 否 --> 直接 Function Calling

十三、结语

Function Calling 解决的是"模型如何请求一个外部能力";Code Mode 解决的是"如何用代码把多个外部能力编排成一个受控流程"。

二者不是简单的替代关系:

  • 需要清晰边界、逐步判断、审批和审计时,优先 Function Calling;
  • 需要确定性的数据编排、批量处理和结果压缩时,考虑 Code Mode;
  • 复杂生产 Agent 通常采用混合架构,把高风险动作留在直接调用路径,把只读和可确定的中间处理放进受控程序。

最重要的选型问题不是"哪种模式更先进",而是:

这一步的控制流,应该由模型决定,还是可以由经过限制和验证的代码决定?

回答清楚这个问题,Function Calling 和 Code Mode 的边界通常就清楚了。

参考资料

  1. OpenAI Function Calling 指南
  2. OpenAI Programmatic Tool Calling 指南
  3. OpenAI Tools 总览
  4. OpenAI Tool Search 指南
  5. Cloudflare Agents Code Mode 文档
  6. Cloudflare:Code Mode,the better way to use MCP
  7. Cloudflare:Code Mode,give agents an entire API in 1,000 tokens
相关推荐
就是一顿骚操作21 分钟前
ViT:把图像切成词之后,Transformer 如何进入计算机视觉
人工智能·深度学习·计算机视觉·transformer·论文解读
千里码aicood27 分钟前
PyQt基于卷积神经网络的智慧校园的设计与实现
人工智能·cnn·pyqt
MartinYeung533 分钟前
[论文学习]MPIB:医疗提示注入基准——针对LLM临床安全性的系统性评估
人工智能·python·学习
MartinYeung537 分钟前
[论文学习]医学AI安全风险分类:S1-S4分级框架深度分析
人工智能·学习·安全
stuartevil37 分钟前
AI 小说漫改视频零基础入门:口型同步和字幕匹配怎么调?
人工智能·音视频·语音识别
优化Henry37 分钟前
5G站点BBU功能模块集成化与偶发案例
运维·网络·学习·5g·tdd
lucas_AI44 分钟前
把表格压成 64 个 token,长文档问答反而更准了:先找表,再看数
人工智能·深度学习·算法
博图光电1 小时前
AI视觉引导高反光金属圆环深筐上料(汽车零部件行业)
人工智能·汽车
julyx1 小时前
为什么只靠 Prompt 让 LLM 输出 JSON 不可靠
人工智能