大模型工具使用的三次进化:从 Function Calling 到程序化编排

大模型的工具使用(Tool Use)是大模型落地中最有价值、也最具挑战的路径之一:它让模型从”会说”跃升到”能做”,将自然语言理解与真实系统的查询、计算、执行真正连接起来。这条路径的标志性起点,是 OpenAI 在 2023 年推出的 Function Calling——模型在对话中自主决定调用哪些工具,并基于返回结果继续推理与决策。

随着 AI Agent 尝试解决的任务复杂度持续攀升(更多步骤、更长链路、海量数据、强时序依赖),工具使用范式也在快速演化。本文将这条演进路径划分为三个阶段:

  • 阶段一:循环式工具选择(Function Calling) —— 单次调用 + 模型逐轮决策下一步
  • 阶段二:计划驱动执行(Plan-then-Execute) —— 先生成全局计划,再串行/并行执行,支持动态调整
  • 阶段三:程序化工具编排(Programmatic Tool Calling) —— 将多工具调用逻辑下沉到可执行代码,由代码完成循环、并发、过滤与聚合,只将关键摘要返回模型

这条演进路径揭示了一个趋势:编排逻辑从”模型逐步决策”转向”计划驱动、代码驱动”,中间结果的处理从”全部塞进上下文”转向”执行侧预先消化”。这不仅显著提升了复杂任务的吞吐效率和成本控制,也带来了新的工程挑战——如何在可控性与灵活性之间找到平衡。

本文将通过直观的流程图和对比表,梳理这三种模式的核心差异、适用场景与工程实践要点,期待与大家探讨。

[TOC]

什么是工具使用,为什么需要演进

工具使用的核心思想很直观:让大模型在生成文本之外,获得与外部系统交互的能力。当用户问”今天北京天气如何”,模型不再只能说”我不知道实时信息”,而是可以调用天气 API 获取数据后再作答;当用户要求”帮我分析这份销售数据的趋势”,模型可以调用 Python 解释器执行计算,而不是凭空臆测数字。这种”理解意图 → 选择工具 → 获取结果 → 综合回答”的闭环,是工具使用的基本形态。

早期实践中,单次查询、单个工具调用就能解决大部分问题——查天气、查汇率、简单计算。但随着应用场景深入,任务复杂度迅速攀升:用户可能要求”对比过去三年的销售数据,找出增长最快的品类,并生成可视化报告”,这需要模型依次调用数据库查询、数据处理、图表生成等多个工具,甚至需要根据中间结果动态决定下一步。传统的”一问一答式”工具调用开始暴露出效率瓶颈、成本压力和可控性问题。正是这些痛点,推动了工具使用范式从简单到复杂的演进。

本文将这条演进路径归纳为三个阶段,但需要强调的是:这三个阶段并非线性替代关系,而是针对不同复杂度任务的最优解。对于简单的单步查询(如查天气、查汇率),循环式工具选择依然是最直接、成本最低的方案;对于需要明确步骤但步骤间依赖较弱的任务(如”查资料 + 总结 + 翻译”),计划驱动执行能显著提升并行效率;而对于涉及大规模数据处理、复杂过滤聚合的任务(如”从数万条日志中提取异常模式并生成报告”),程序化工具编排则能避免上下文爆炸、大幅降低延迟与成本。选择哪种模式,取决于任务的步骤数量、数据规模、依赖关系与实时性要求,而不是简单的”新技术替代旧技术”。

接下来,我们将逐一拆解这三个阶段的技术实现方式与核心特点,并通过对比表和实际案例,帮助你在工程实践中做出更合理的选择。

模式一:循环式工具选择(Function Calling)

循环式工具选择是最直观的工具使用范式,其核心逻辑可以用一句话概括:模型决策 → 执行工具 → 模型再决策,如此循环往复。用户输入问题后,模型会收到一份可用工具清单(包含工具名称、参数描述、功能说明),然后判断是否需要调用工具。如果需要,模型会输出结构化的调用请求(比如 get_weather(city="北京", date="明天")),应用层拿到这个请求后去执行真实的 API 调用或代码运行,再把结果以文本形式追加回对话历史。模型看到新结果后继续推理,决定是调用下一个工具,还是直接生成最终答案。

OpenAI 的 Function Calling API 就是这种模式的经典实现。开发者在请求中声明工具列表,模型返回 tool_calls 字段告诉你要调哪个工具、传什么参数,你执行完把结果以 tool 角色消息送回去,模型继续处理。整个过程就像一场”乒乓对话”:你来我往,每一轮模型只能看到过去的所有对话和上一次工具的结果。

举个具体例子:用户问”帮我查一下北京明天的天气,如果会下雨就提醒我带伞”。第一轮,模型决定调用 get_weather(city="北京", date="明天"),工具返回 {"temperature": 15, "condition": "小雨"};第二轮,模型看到这个结果,生成最终回答:”北京明天15°C,有小雨,记得带伞哦”。整个过程清晰可控,调试起来也很直观。

这种模式的优势和局限都很明显。先说优势:

  • 简单直观:逻辑清晰,几乎所有主流大模型都原生支持,开发门槛很低
  • 灵活性强:模型可以根据每一步的实际结果动态调整策略,如果某个 API 返回错误可以尝试其他工具,如果数据不符合预期可以追加查询
  • 适合探索性任务:这种”走一步看一步”的特性,让它特别适合处理高度不确定的任务,比如调试代码、探索性数据分析

但局限也同样突出:

  • 串行执行瓶颈:每次只能调用一个工具,无法并行,多步任务的延迟会线性累加
  • 上下文膨胀:每次工具调用的完整结果都要塞回上下文,当工具返回大量数据(比如数据库查询返回几百条记录)时,token 消耗会快速飙升
  • 效率冗余:每一步都需要等待模型推理,对于步骤已经很明确的任务(比如”查数据 → 计算均值 → 生成图表”),反复等待显得有些浪费

因此,循环式工具选择最适合以下场景:

  • 单步或少步查询:查天气、查汇率、简单计算、单次数据库查询
  • 高度不确定的任务:需要根据每一步结果灵活调整策略的场景
  • 工具返回数据量小:每次调用返回的结果简洁(几十到几百 tokens),不会撑爆上下文

典型案例包括智能客服(查订单 → 查物流)、简单的数据查询助手、代码执行与纠错等。但当任务步骤增多、数据规模增大时,这种”逐步决策”的模式就开始暴露效率问题了——这也正是阶段二”计划驱动执行”要解决的痛点。

模式二:计划驱动执行(Plan-then-Execute)

循环式工具选择的一个核心问题在于”边走边看”:模型每次只能看到上一步的结果,然后决定下一步。这对于高度不确定的任务是优势,但对于步骤相对明确的任务却是效率杀手。比如用户要求”对比过去三年的销售数据,找出增长最快的品类,并生成可视化报告”——这个任务的步骤其实很清楚:查询数据 → 计算增长率 → 排序筛选 → 生成图表。但在循环模式下,模型必须等第一步查完数据、看到结果后,才能决定第二步;第二步算完增长率后,才能决定第三步。每一步都要完整推理一次,串行等待,延迟线性叠加。

计划驱动执行试图打破这个瓶颈:让模型先看清全局,生成一个完整的执行计划,然后再按计划执行,并支持并行调度与动态调整。具体流程是这样的:模型收到任务后,先不急着调用工具,而是基于任务需求和可用工具,输出一个结构化的执行计划(plan),明确列出需要哪些步骤、每一步调用什么工具、步骤之间的依赖关系(哪些可以并行、哪些必须串行)。应用层拿到这个计划后,交给执行引擎(executor)按依赖关系调度:没有依赖的步骤可以并行执行,有依赖的步骤等前置完成后再执行。所有步骤完成后,将结果汇总返回模型,模型基于全部结果生成最终答案——如果发现计划有问题或结果不符合预期,还可以要求模型调整计划、重新执行。

以刚才的销售数据分析为例。在计划驱动模式下:

第一阶段(Planning):模型生成计划

1. query_database(sql="SELECT category, year, sales FROM sales WHERE year >= 2022")
2. calculate_growth_rate(data=step1.result)  # 依赖步骤1
3. sort_and_filter(data=step2.result, top_n=5)  # 依赖步骤2
4. generate_chart(data=step3.result, type="bar")  # 依赖步骤3

第二阶段(Execution):执行引擎按依赖关系调度,步骤1执行完后,步骤2、3、4依次执行(如果有多个无依赖步骤可以并行)

第三阶段(Synthesis):将所有结果返回模型,模型生成最终报告:”根据数据分析,过去三年增长最快的品类是电子产品(年均增长32%),以下是可视化图表……”

这种模式相比循环式的改进是显著的:

  • 并行执行:没有依赖关系的步骤可以同时调用(比如同时查询多个数据库、同时调用多个 API),大幅降低总延迟
  • 减少推理轮次:模型只需要推理两次(生成计划 + 生成答案),而不是每一步都推理一次,节省了 token 和时间
  • 全局视角:模型在规划时能看到完整任务需求和所有可用工具,可以做出更优的资源分配决策

但这种模式也不是万能的,它带来了新的复杂度:

  • 计划质量依赖强:如果模型生成的计划有误(比如步骤遗漏、依赖关系错误),整个执行过程可能失败,而且错误只能等全部执行完才能发现
  • 灵活性下降:一旦计划确定,中间步骤无法根据实际结果动态调整(除非重新规划),不如循环模式灵活
  • 工程复杂度:需要实现一个能解析依赖、调度执行、处理并发的执行引擎,开发成本比简单循环高
  • 中间结果处理:虽然并行提升了效率,但如果某个步骤返回大量数据,仍然需要全部传回模型,上下文压力依然存在

因此,计划驱动执行最适合以下场景:

  • 多步骤但步骤明确的任务:比如数据分析流程、文档处理流程(提取 → 翻译 → 总结 → 格式化)
  • 有明显并行机会的任务:比如同时查询多个数据源、同时调用多个 API 进行对比
  • 对延迟敏感的任务:通过并行和减少推理轮次,显著降低端到端响应时间
  • 工具返回数据量可控:每个步骤的返回结果不会太大,汇总后不会撑爆上下文

典型案例包括:复杂的数据分析任务、多源信息聚合(比如对比多个电商平台的价格)、文档处理流水线等。目前不少 Agent 框架(如 LangGraph、AutoGPT)都在实现类似的计划-执行模式。

但问题还没有完全解决。当任务涉及大规模数据处理(比如”从1万条日志中提取异常模式”),即使做了并行优化,把所有中间结果都传回模型依然不现实——这就引出了第三阶段的核心思想:既然模型处理大规模数据吃力,为什么不把数据处理逻辑直接交给代码?

模式三:程序化工具编排(Programmatic Tool Calling)

计划驱动执行解决了并行调度的问题,但有一个根本矛盾它始终无法回避:中间结果的上下文爆炸。想象这样一个场景:用户要求”分析过去一个月的服务器日志,找出所有响应时间超过5秒的请求,统计它们的来源IP分布,并生成top10列表”。即使你用了计划-执行模式,查询日志可能返回1万条记录,过滤后还有500条,这些数据全部传回模型会占用几十万tokens——不仅成本高昂,模型处理起来也慢且容易出错。更关键的是,这些”过滤、循环、聚合”的逻辑本身非常机械,根本不需要模型的推理能力,却在消耗大量的推理资源。

程序化工具编排的核心思想很激进:既然模型擅长理解意图和生成逻辑,代码擅长执行循环和处理数据,那就让模型生成可执行代码,把多工具调用的编排逻辑完全交给代码。具体来说,模型不再输出”调用工具A → 调用工具B → 调用工具C”这样的步骤序列,而是直接生成一段Python(或其他语言)代码,这段代码里包含了所有的工具调用、循环、条件判断、数据过滤与聚合逻辑。应用层将这段代码放到沙箱环境中执行,代码可以自由调用工具、处理大规模数据、进行复杂计算,最后只把关键的摘要结果(比如top10列表、汇总统计)返回模型。模型基于这个精简的结果生成最终答案。

还是以刚才的日志分析为例。在程序化工具编排模式下:

第一步(Code Generation):模型生成代码

# 查询日志数据
logs = query_database("SELECT * FROM server_logs WHERE timestamp >= NOW() - INTERVAL 30 DAY")

# 过滤响应时间超过5秒的请求
slow_requests = [log for log in logs if log['response_time'] > 5]

# 统计来源IP分布
ip_counts = {}
for req in slow_requests:
    ip = req['source_ip']
    ip_counts[ip] = ip_counts.get(ip, 0) + 1

# 排序并取top10
top10 = sorted(ip_counts.items(), key=lambda x: x[1], reverse=True)[:10]

# 返回结果摘要
result = {
    "total_slow_requests": len(slow_requests),
    "top10_ips": top10,
    "sample_request": slow_requests[0] if slow_requests else None
}

第二步(Execution):代码在沙箱中执行,处理1万条日志、过滤、聚合,最后只返回一个几百字节的摘要

第三步(Synthesis):模型收到摘要(而不是1万条原始记录),生成报告:”过去30天共有327次慢请求,来源主要集中在以下10个IP……”

这种模式的改进是革命性的:

  • 彻底解决上下文爆炸:代码在执行侧完成所有数据处理,只把精简摘要返回模型,token消耗从几十万降到几千
  • 执行效率大幅提升:循环、过滤、聚合这些操作在代码层面执行,比通过模型逐步决策快几个数量级
  • 支持复杂逻辑:代码可以实现任意复杂的控制流(嵌套循环、条件分支、异常处理),模型只需专注于”生成正确的逻辑”而不是”逐步执行”
  • 成本降低:减少了推理轮次和token传输,对于数据密集型任务,成本可能降低一个数量级

但这种模式也带来了新的挑战:

  • 代码生成可靠性:模型需要生成语法正确、逻辑正确的可执行代码,容错率比生成工具调用序列更低
  • 调试难度增加:当代码执行出错时,排查问题比简单的工具调用链更复杂,需要完善的错误处理和日志机制
  • 沙箱环境要求:需要一个安全的代码执行环境,防止恶意代码、资源滥用等问题
  • 适配性问题:不是所有模型都擅长生成高质量的可执行代码,对模型的代码能力有更高要求
  • 可解释性下降:相比明确的步骤序列,一段代码的意图对于非技术用户来说更难理解

因此,程序化工具编排最适合以下场景:

  • 大规模数据处理任务:需要对成千上万条数据进行过滤、聚合、分析的场景
  • 复杂的计算密集型任务:涉及大量循环、嵌套逻辑、数学计算的任务
  • 对成本和延迟极度敏感:通过代码执行可以大幅降低token消耗和响应时间
  • 步骤清晰但数据量大:任务逻辑明确,但中间结果体积庞大,不适合直接传回模型

典型案例包括:日志分析、大规模数据清洗与转换、批量文件处理、复杂的数学建模与仿真等。Anthropic最近推出的Extended Thinking + Tool Use、OpenAI的Code Interpreter升级版,都在朝这个方向演进。

值得注意的是,程序化工具编排并不意味着完全替代前两种模式。实际工程中,很多系统会混合使用:对于简单查询用循环式,对于多步明确任务用计划驱动,对于数据密集型任务用程序化编排。选择哪种模式,归根结底取决于任务的复杂度、数据规模、实时性需求以及你愿意承担的工程复杂度。

一张图总结三种模式

这里给一个简单的对比表格总结三种不同的模式:

维度 循环式工具选择 计划驱动执行 程序化工具编排
核心机制 模型逐步决策,每次调用一个工具后再决定下一步 模型先生成完整计划,再按计划调度执行 模型生成可执行代码,代码完成所有工具调用与数据处理
执行方式 严格串行,每步等待模型推理 支持并行,按依赖关系调度 代码层面自由控制流,支持复杂并行与循环
效率(延迟与成本) 延迟高(线性累加),成本中等 延迟中等(并行优化),成本中等 延迟低(代码执行快),成本低(token消耗少)
上下文压力 随工具调用次数线性增长 所有步骤结果汇总后进上下文 极低(中间数据不进上下文,只返回摘要)
灵活性 高(可根据每步结果动态调整) 中(需重新规划才能调整) 中(代码逻辑固化,但可重新生成)
工程复杂度 低(逻辑简单,易调试) 中(需实现依赖解析与调度) 高(需沙箱、错误处理、安全机制)
适合数据规模 小(KB级) 中(KB到MB级) 大(MB到GB级,只返回摘要)
典型场景 查天气/汇率、智能客服、代码调试等简单查询 数据分析流程、多源信息聚合、文档处理流水线 日志分析、大规模数据清洗、批量文件处理

写在最后

从循环式工具选择到计划驱动执行,再到程序化工具编排,大模型的工具使用正在从”会调工具”走向”会编排系统”。这条演进路径背后的核心逻辑很清晰:把模型擅长的事情(理解意图、生成逻辑)和代码擅长的事情(执行计算、处理数据)分离开来,各司其职。循环式给了我们灵活性,让模型可以”走一步看一步”;计划驱动带来了全局视角和并行能力;程序化编排则彻底解放了上下文,让模型不再被数据规模束缚。

但需要再次强调的是:没有一种模式适合所有场景。上面的对比表不是在告诉你”应该用哪个”,而是在帮你理解”什么时候用哪个”。查个天气、问个订单状态,循环式足够了,简单高效;做数据分析、处理文档流水线,计划驱动能帮你省时间;分析海量日志、批量处理文件,程序化编排才能撑得住。实际工程中,很多优秀的 Agent 系统会根据任务特征动态选择模式,甚至在一个任务中混合使用——这才是成熟的工程实践。

工具使用的演进还远未结束。随着模型推理能力持续增强、执行环境越来越安全可靠、多模态工具逐渐成熟,我们可能会看到更激进的模式出现——比如模型直接生成完整的微服务、自主管理长期运行的后台任务、甚至跨系统的自动化编排。但无论技术如何演进,核心问题始终是那个:如何让 AI 更高效、更可靠地与真实世界交互

欢迎关注 DataLearner 官方微信,获得最新 AI 技术推送

DataLearner 官方微信二维码