☰
从Function Calling到Code Mode:大模型复杂任务执行的新范式
2026/11/3 7:30:15 网站建设 项目流程

1. 一场关于“工具调用”的范式转移

1.1 从“请神”到“抄家伙”,Function Calling 统治了两年

过去两年,只要你在搞 LLM 应用,没人能绕开 Function Calling。GPT-4 刚开放那阵,各家大模型API 都在卷 function call 能力,朋友圈晒的最多的就是“我拿函数调用接了个飞书机器人”“我们电商客服通过 function call 接上了 ERP”。当时这套东西解决的是个什么痛点呢?就是让模型不只会聊天,会“动手”了。你给模型描述一个查询需求,它返回一个结构化 JSON,里面指定了要调用哪个函数、传什么参数,你再拿这个 JSON 去执行真实的业务逻辑。本质上,Function Calling 像一个“传话筒”,帮你在自然语言和代码世界之间搭了个桥。

但这个桥搭得并不稳。我自己在实战里踩过不少坑。多轮对话里,模型可能连续两次调同一个函数,导致库存被扣两次;函数参数一多,模型就开始抽风,数字类型参数偶尔给你传个字符串;更离谱的是,当有两个函数长得比较像——比如“查订单”和“查物流单号”——模型经常选错。你要修这些问题,就得反复调 prompt、调结构化输出约束、写一大段兼容层。可以说,Function Calling 是“能干活,但干活的方式很费劲”。

1.2 为什么我突然想把它“判死刑”

真正让我开始思考 Function Calling 是不是该退休的,是一次处理 Excel 批处理的线下需求。当时客户要求从一份几千行的对账单里自动提取异常条目,再按规则更新到报销系统。我用 Function Calling 设计了一套流程:定义“读取单元格”“公式计算”“筛选行”“更新记录”四个函数,然后让模型自己组合调用。结果呢?模型在第四步开始频繁出错,先是把筛选条件写反,后来又三番两次漏算某些分类小计。我花了整整两天在调参数约束和增加校验逻辑,最后虽跑通了,但很不爽。

就在那个星期的社区周报里,我注意到 Code Mode 这个词开始高频出现。越来越多开发者在讨论:与其让模型像差遣实习生一样去调用别人写好的函数,不如直接让它自己写一段代码,跑一个程序,完成一整件事。这个思路跟我那个 Excel 场景太贴合了。你不需要一个“传话筒”了,你需要的是一个“临时程序员”。我顺着这个思路重构了一遍那个批处理,结果不仅实现过程顺畅得多,而且容错性也好了很多。那一刻我心里就冒出一个强烈的念头:Function Calling 该让位了,新神的名字叫 Code Mode。

2. Code Mode 的本质:让模型从“工具调度员”升级为“程序员”

2.1 一个被忽略的认知转变

先说清楚 Code Mode 到底是什么。市面上对它的理解很混乱,有人觉得这是“增强版 Function Calling”,有人觉得是“代码解释器的变种”。我倾向于用一个更底层的视角去理解:Function Calling 的逻辑是先枚举“能做什么”,再让模型去匹配“该做什么”;而 Code Mode 的逻辑是先理解“要做什么”,再让模型“写代码去实现这个做什么”。一个是路由分发,一个是直接创作。

打个生活化的比方吧。以前你雇了个管家,管家不会做饭,但他知道哪家外卖能送什么菜,你跟他报菜名,他帮你下单。这就是 Function Calling——它的上限就是“调用已经存在的服务”。Code Mode 呢?你直接雇了个厨师,厨房里的食材随便用,你告诉他你想吃什么口味的菜,他临时开火、搭配、炒菜。他不会做你指定的那道菜时,还能即兴创新。

这个区别放在工程上非常关键。Function Calling 极度依赖调用目标的明确性。一旦业务逻辑复杂到一定程度,比如涉及多步骤判断、条件循环、递归,你需要定义的函数数量会爆炸,函数之间的依赖关系会变得难以维护。而 Code Mode 允许模型自己写 if-else、写循环、写类、写异常处理,它在面对一个复杂目标时,不是在“选路径”,而是在“铺路”。

2.2 关键机制:为什么 Code Mode 更适应当下需求

再往深一层看,Code Mode 能跑通的背后有几个核心机制在支撑。

第一是“可执行环境”。Code Mode 通常运行在沙箱容器里,模型生成的 Python 或者 JS 代码会被直接解释执行。这意味着模型的错误可以被实时的运行时反馈修正。代码跑挂了?把错误信息塞回模型,让它自己改。一次不行改两次,直到通过为止。这种“执行—反馈—修正”闭环,在 Function Calling 的世界里几乎不存在。Function Calling 的 bug 要靠开发者去手工排查,而 Code Mode 的 bug 可以让模型自己消化掉。

第二是“程序化能力密度”。一段 50 行的代码可以表达的复杂度,如果用函数调用来编排,可能需要十几个 JSON Schema 定义、上百行工具描述、外加一堆状态管理代码。通常一个中等复杂度的数据清洗任务,我用 Code Mode 写循环加正则也就70行代码,而用 Function Calling 从接口定义到调度逻辑需要两百行起步。代码本身就是最高效的“工具”。

第三是“任务边界的自由延展”。Function Calling 里你很难让模型“顺便加个日志轮转”或者“把结果做成图表附件”,因为没定义这个函数它就不会做。Code Mode 里模型所有可用的库都是它的工具,几乎就没有边界。这不是说模型什么都会,而是在通用开发能力的加持下,它能覆盖更长的任务链路。

这一段不是要否定 Function Calling 的全部价值。在极简场景下,比如查个天气、翻译一句话,Function Calling 依然轻量,简单,调试起来也快。但如果你在做的是一个需要具备“完整性”的智能体系统,那么让模型直接掌握“写代码”这个元能力,确实比“组合一堆小工具”靠谱得多。

3. 实操对比:同一个业务,两种实现方式的天壤之别

3.1 一个真实的“发票数据提取与报销核对”场景

为了让你有直观感受,我把前面提到的那次客户需求简化后重新呈现。假设给你一批发票 PDF(一堆电子票据),每张票上写着金额、开票日期、发票代码、销售方名称。你要做的任务是:提取所有发票的金额,跟 Excel 里的报销单逐笔匹配,标记出有差异的记录,最后输出一份核对报告。

我用 Function Calling 实现的时候,需要设计这么几个函数:

  • extract_invoice_from_pdf(pdf_path) -> dict:抽取 PDF 字段
  • read_excel_row(sheet_name, row_index) -> dict:读取 Excel 某一行
  • compare_amount(invoice_amount, expense_amount) -> str:比对金额差异
  • mark_status(row_id, status) -> bool:在 Excel 里写入标记
  • generate_report(records) -> str:生成最终报告

定义这五个函数还算轻松,但真正写 prompt 约束模型“先读 PDF,再读 Excel,然后逐行比对,有差异的记录标记,最后生成报告”这个顺序编排时,问题就来了。模型经常不按顺序执行,或者跳过比对直接生成报告。你必须给每个函数加极其详细的“何时调用”说明,还得在业务层写状态机去兜底。

换成 Code Mode 之后,我只需要给出指令:“用 Python 写一个脚本,把所有 PDF 提取成结构化字段,然后读取 Excel 报销单,逐笔匹配金额,匹配一致的标记 PASS,不一致的标记 FAIL,最后输出一份 Markdown 报告,并打印差异明细。”

模型自己就会生成这样一段逻辑:

import os import pdfplumber import openpyxl invoices = [] pdf_dir = "./invoices" for fname in os.listdir(pdf_dir): if fname.endswith(".pdf"): with pdfplumber.open(os.path.join(pdf_dir, fname)) as pdf: page = pdf.pages[0] text = page.extract_text() # 从文本里解析关键字段 amount = parse_amount(text) invoices.append({"file": fname, "amount": amount}) workbook = openpyxl.load_workbook("reimbursement.xlsx") sheet = workbook.active row_mapping = {} for row in sheet.iter_rows(min_row=2, values_only=False): invoice_id = row[0].value expense_amount = row[1].value row_mapping[invoice_id] = row results = [] for inv in invoices: matched = row_mapping.get(get_invoice_id(inv["file"])) if matched is None: results.append({"invoice": inv["file"], "status": "NOT_FOUND"}) elif abs(matched[1].value - inv["amount"]) < 0.01: results.append({"invoice": inv["file"], "status": "PASS"}) else: results.append({"invoice": inv["file"], "status": "FAIL", "expected": matched[1].value, "actual": inv["amount"]}) # 标记 Excel 行 matched[2].value = "FAIL" workbook.save("reimbursement.xlsx") print_report(results)

注意一点,这个代码不是预先写好的模板,是模型根据我的指令和目录里的文件结构现场生成的。它自己判断需要用pdfplumber提取文本,自己决定用openpyxl读 Excel,自己写循环遍历所有 PDF,自己处理异常情况。这就是 Code Mode 的威力——你给的是目标,不是步骤。代码实现细节、异常处理逻辑、库的选择,全由模型在生成过程中自主决策。对我这种“提需求的人”来说,交付物从“跟模型反复对话”变成了“直接可跑可查的程序”。

3.2 对照一下维护成本和容错效率

写代码只是一部分,更核心的差距在后续维护。我保留了两次实现的全套代码和排错日志,做了个简单统计。

维度Function Calling 方案Code Mode 方案
所需函数/代码量5个函数 + 约120行业务调度代码1个脚本约96行,无外部调度
模型需要理解的规则prompt 里写满“先…再…然后…”的顺序只需要明确目标和输入输出格式
出错时谁负责调试开发者手工看 JSON 输出、追调用链模型读取报错信息自行修改代码再跑
业务变动时改动量新增字段需额外定义函数和约束直接改描述语句,让模型改代码
可交付性需要再包一层 API 服务才给客户直接交付脚本,客户本地跑

这个表格说实话有点夸张成分,但方向是完全真实的。Function Calling 在需要“多步长链路”的场景里,最大的问题不是模型能力不够,而是外部约定的约束太死。每加一个新能力,都要改 Schema、改 prompt、改校验逻辑,改动成本是线性的。而 Code Mode 环境下,改动成本回归“说一句话”,因为模型自己会把需求翻译成实现。

容错效率的差距则更明显。有一次模型生成的比对逻辑里,没有处理 PDF 金额里的逗号、货币符号,导致几千行里几十条匹配差异。如果是 Function Calling,我得去翻日志,找到具体调用了哪个函数、哪一步出了问题。在 Code Mode 下,我把一行报错文本贴给模型,它立刻意识到需要先清洗金额格式,然后重新生成代码,跑完整流程,五分钟内就修复了。这里的关键不是“模型比我聪明”,而是“它有代码的执行上下文”,它能自己看到哪里挂了、为什么挂,并能直接改代码再试。从“人工排查”变成“自动迭代”,这个效率差是数量级的。

3.3 Windows 怎么落地 Code Mode

很多做开发的读者问过我:Code Mode 会不会要求 Linux 环境?或者只能在云端跑?其实不然。只要你能给模型一个 Python 解释器环境,再给它一个可以写文件、跑命令的工作目录,就能复现 Code Mode 的工作方式。

实操上分三步:

  1. 准备环境。安装 Python 3.9 以上版本,装好openpyxl、pdfplumber这类常用数据处理库。
  2. 提供一个沙箱工作目录。把输入文件放进去,模型生成的代码也保存进去。
  3. 设置“执行循环”。也就是模型输出代码后,自动在当前目录运行,捕获 stdout 和 stderr,再把执行结果回传给模型,用于下一轮修正。这个循环就是 Code Mode 的本质,缺了它,代码生成只是静态的代码片段,毫无意义。

有条件的可以直接用现成框架,比如 LangChain 里的代码解释器 Agent,或者 OpenAI 的函数调用加代码沙箱组合。不过我更推荐先手动搭一次最小的执行循环,体会“代码生成—运行—反馈—再生成”这个闭环的节奏。等你理解了这个循环,再上框架会事半功倍。

4. 我要给 Function Calling 说几句公道话

4.1 哪些场景下它依然是“最靓的仔”

讲这么多 Code Mode 的好处,难免有人觉得我在全盘否定 Function Calling。必须澄清,我没这个意思。事实上有一类场景它是不可替代的——就是对外部系统做精确操作,而且操作接口已经明确封装好了。

举个例子,你的 Agent 需要调用公司内部的支付接口,传入order_id、amount、callback_url,然后返回payment_result。这种场景我不希望模型自己写代码去实现一个 HTTP 调用,因为它根本不该接触内部网络的密钥和地址。你要的是模型精确地抽取出那三个参数,然后交给你自己的签名方法去执行。这就是 Function Calling 的安全价值——它在“动作边界”上是天然可控的。Code Mode 却偏向“开放式生成”,一旦代码可以自由执行,访问控制、权限隔离、预算限制都需要额外设计。

类似的场景还包括:

  • 语音助手要设置手机闹钟,操作极简单,但涉及系统权限
  • CRM 里更新一条客户记录,字段固定且校验严密
  • 物联网平台里下发设备指令,协议固定,参数枚举值有限

这些场景里,Function Calling 足够简单、稳定、安全,你没必要杀鸡用牛刀,非让模型临时写一段代码去操作你家的智能灯泡。

4.2 决定取舍的是“任务形状”

我发现很多人纠结选型,是因为没有真正分析任务的“形状”。这里分享我的一个判断框架。

你需要做的事如果满足以下特征,用 Function Calling 更舒服:

  • 动作类型少,通常 5 个以内
  • 参数结构稳定,不会有动态嵌套
  • 执行结果需要强校验,比如支付、写库
  • 外部依赖已存在,比如 API 已经封装好了

如果任务长这样,我建议认真考虑 Code Mode:

  • 涉及多步骤编排,循环、条件分支的组合复杂
  • 输入数据是文件、表格、网页这类非结构化内容
  • 任务目标是“产出物”,比如报告、图表、清洗后的数据集
  • 你需要模型自己去探索库和接口,而不是限制它只能用某几个工具

说白了,一个是“让模型当接线员”,一个是“让模型当临时工”。你的场景缺的是接线还是干活?想清楚这个问题,工具选型就不难了。

而且我多说一句,未来更多 Actor 类 Agent 会走向混合架构:外层用 Function Calling 做权限控制和副驾驶纠偏,内层用 Code Mode 做自由推理和代码执行。两者不是对立关系,是各管一段。标题说“该退休”,那是情绪表达;理智上的结论是“退居二线,不再包打天下”。

5. 落地 Code Mode 的四个避坑指南

5.1 安全边界,先想好再动手

Code Mode 让模型执行任意代码,这是巨大生产力,也是巨大入口。最基础的几道防线:第一,必须在独立的 Docker 容器里跑,容器里不挂宿主机敏感目录,只挂临时工程目录。第二,给容器配 CPU 配额和内存上限,防止模型写出死循环或者一次性申请大内存的代码。第三,默认禁用网络权限,挂一个白名单代理,只有明确指定的域名可以访问。这几个坑我踩过不止一次,尤其“容器里去读/etc/passwd”这种操作,没隔离的话后果不堪设想。

5.2 依赖管理别含糊

Code Mode 生成的代码经常会用到一些你环境里根本没有的库。一旦代码跑起来发现ModuleNotFoundError,执行循环自动装依赖的话,会很慢而且不可控。我的做法是:预先把高频库全部装进基础镜像,比如requests、openpyxl、pandas、pdfplumber、beautifulsoup4、jinja2等,然后在 prompt 里明确说“只能使用环境已有的库”。这样既加快迭代,又避免在沙箱里现场 pip install 引入供应链风险。

5.3 代码质量要靠“测试用例”去兜底

模型生成的代码不是不能有 bug,但我们要让它自己有“发现 bug 的能力”。最好的方式是在需求描述里顺带写清楚验收用例。比如“发票金额等于 1000 的标准样本,提取结果必须是 1000.00”。当模型生成完代码,它会自己拿样本跑一遍,不过就直接改代码。巧的是,这也跟写单测的思维对齐了。我对任何生产级 Code Mode 任务,都要求交付物里至少带 3 组能验证主链路的测试数据。

5.4 别忘了“人工审计”这条底线

哪怕全自动流程跑得再顺,高风险操作,比如删除数据、批量覆盖文件、转账,必须留一个人工确认的卡点。这个思路跟自动驾驶分级有点像,L4 级别在特定场景全自动,但落地时依然有人类驾驶员兜底。Code Mode 的审计日志尤其重要,哪个模型、生成什么代码、在哪个容器里执行过什么命令,全部记录下来。出了问题,你能回溯、能问责,这个能力在团队协作时是安全感来源。

6. 最后讲点实操中的个人心得

我以前是 Function Calling 的忠实拥趸,写过不少“教你定义好看的 JSON Schema”的文章。但过去三个月的项目让我明白了一个道理:模型在复杂任务里的上限,取决于你给它的表达自由度。你给模型一堆函数壳子,它的行为上限就限定在那些壳子里;你给模型一个能写代码的运行时,它就能处理你想要的一切。

现在我在做的内部 Agent 底座,已经把 Code Mode 作为默认执行器。核心业务链路里,凡是“读文件、处理数据、生成报告”这类任务,全部走代码生成加沙箱执行。只有涉及钱、权限、外部系统操作的节点,才会切回人工确认或函数调用兜底。这个架构上线后,之前经常告警的“工具路由错误”“参数解析失败”基本绝迹了。

如果你正准备给自己的 Agent 换代升级,我强烈建议先拿一个你最头疼的“多步数据处理”场景试试 Code Mode。不需要一步到位,先跑通一个最小的“代码生成—运行—反馈”循环,你就能直观感受到两种范式的差距。踩了几次坑之后,你大概率会像我一样,不再想回去定义那些费力不讨好的 function schema 了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询