论文精读 06|CodeAct:以可执行代码统一动作空间

论文档案

项目内容
论文Executable Code Actions Elicit Better LLM Agents
作者Xingyao Wang 等
发表ICML 2024
原文PMLR / arXiv
代码xingyaoww/code-act
本文定位将 Python 作为 Agent 与环境交互的统一动作语言

核心命题

JSON function call 适合一次调用一个工具,但遇到循环、条件、变量和多个工具之间的数据传递时,会迅速变成多轮、冗长且难组合的调用序列。CodeAct 让模型直接生成可执行 Python:

items = search_products(query)
eligible = [x for x in items if x.price < budget and x.in_stock]
best = min(eligible, key=lambda x: x.price)
reserve(best.id)

解释器执行代码,把结果或异常作为 Observation 返回,模型下一轮继续修改。这不是“让模型写最终程序”,而是用代码作为动作协议。

为什么代码是特殊的动作空间

自带控制流和数据流

JSON 描述一次调用很清楚,却不能自然表示“遍历这些结果并筛选”“失败后走另一分支”“把 A 返回值传给 B”。Python 原生表达变量、函数、循环和条件,因此复杂动作可以在一次执行中完成。

复用现成软件生态

代码动作可以直接调用 Python 包、解释器与已有 API,不需要为每种组合人工包装新工具。论文演示了数据处理、可视化、模型训练等任务,Agent 能导入库并根据错误信息自我调试。

模型已经见过大量代码

作者假设预训练中的代码数据使模型比自定义文本协议更熟悉 Python。对 17 个模型的原子 API 调用实验表明,CodeAct 在多数模型上与 JSON/文本相当或更好,开源模型中的优势更明显;但并非每个模型都如此。

解释器提供高密度反馈

语法错误、类型错误、堆栈和返回值都是具体反馈,比“动作无效”更容易修正。代价是执行能力大幅扩大,安全边界必须更强。

M³ToolEval:专门测多工具组合

现有工具基准多聚焦一次原子调用,难以显示代码控制流优势。论文构造 82 个人工任务,覆盖网页浏览、金融、行程等场景,通常需要多次调用多个工具并在多轮中修正。

对 17 个模型比较 CodeAct、JSON 与文本动作,指标是成功率和平均回合数。CodeAct 在 12/17 个模型上成功率最高,也在 12/17 个模型上平均回合最少。

模型CodeActJSONTextCodeAct 平均轮数
GPT-4-1106-preview74.4%52.4%53.7%5.5
GPT-4-061367.1%56.1%45.1%6.6
GPT-3.5-turbo-061351.2%26.8%20.7%7.0
Claude 254.9%39.0%29.3%7.2
最佳开源模型 Lemur-70B13.4%15.9%12.2%9.1

GPT-4-1106 上 CodeAct 相对次优格式提高 20.7 个百分点,平均少 2.1 轮。但最佳开源模型的绝对成功率仅约 15.9%,说明表达能力不是全部瓶颈。

CodeActInstruct 与 CodeActAgent

作者进一步收集 7,139 条、约 1,058 万 token 的多轮交互轨迹:

能力来源保留实例
信息检索HotpotQA1,664
数学与软件包MATH1,732
代码与自调试APPS647
外部表格记忆WikiTableQuestions1,065
机器人规划ALFWorld2,031

生成轨迹经过筛选,特别保留能根据执行反馈改善后续动作的多轮样本。作者把 CodeActInstruct 与一般对话数据混合,对 Llama-2 7B 和 Mistral 7B 做全参数监督微调,得到 CodeActAgent。

混合一般对话数据很关键:消融显示 CodeActInstruct 提升 Agent 任务,但一般对话数据有助维持知识问答、指令遵循等通用能力。附录还报告 Llama-2 与 Mistral 底座反应不同,不能假设同一训练配方对所有模型等效。

CodeAct 与 function calling 不是二选一

可以把动作空间看成三层:

层表达力易验证性典型用途
单个结构化工具调用低高发信、付款、数据库写入
受限代码/DSL中到高中浏览、数据变换、多工具组合
任意 Python/Bash极高低隔离沙箱中的研发与分析

最合理的 Harness 往往混合使用:模型可在沙箱里用 Python 处理数据,但真正产生外部副作用的操作仍只能通过有 schema、权限与审批的工具。

安全代价:表达力越强,能力泄露越危险

可执行 Python 不只支持循环,也支持文件、网络、进程、反射和依赖安装。CodeAct 论文证明性能收益,但不是完整沙箱安全方案。生产 Harness 至少需要:

  • 容器或更强隔离,非宿主直接执行;
  • 默认禁网,只开放显式代理;
  • 只读文件系统与限定工作目录;
  • CPU、内存、磁盘、进程数和时间限额;
  • 秘密不进入环境变量或 prompt;
  • 高风险 API 通过 capability token 暴露;
  • 完整记录代码、依赖、stdout/stderr、返回值和副作用;
  • 执行后销毁或回滚环境。

“代码可读”也不等于“行为可预测”。动态导入、网络响应和包版本都可能让同一代码产生不同结果。

Harness 实现建议

将代码解析与执行分离

先从模型响应中提取代码块,做语法解析、静态策略扫描和资源预估,再送往沙箱。解析失败应返回结构化错误,不能用不受控正则拼接命令。

把执行事件完整建模

{
  "action_id": "a17",
  "language": "python",
  "code_hash": "...",
  "sandbox_image": "...",
  "policy_decision": "allow",
  "exit_code": 1,
  "stdout_ref": "blob://...",
  "stderr_ref": "blob://...",
  "duration_ms": 412,
  "side_effects": []
}

模型可见的 Observation 只是一段裁剪视图,审计日志必须保留完整引用。

对循环设置双重预算

模型轮数与单次代码内部循环是两种预算。需要同时限制 Agent step 数和代码执行时间/资源;否则“更少交互轮次”可能只是把大量计算藏进一次动作。

可复现实验

用同一组多工具任务比较三种动作格式:

  1. 每个任务需要至少两种工具和一次中间结果变换;
  2. 为 JSON、文本、代码提供等价工具能力;
  3. 固定模型与总 token,同时限制代码 CPU 时间;
  4. 记录成功率、模型轮数、实际工具调用数、执行时间和安全拒绝数;
  5. 注入工具异常,测量能否根据 traceback 恢复;
  6. 加入恶意工具返回,检查代码路径是否会执行未授权副作用;
  7. 对 CodeAct 再消融原生 Python、受限 AST 和高层 DSL。

只有这样才能判断收益来自代码语法、少轮交互,还是更大的隐性计算预算。

局限

  • M³ToolEval 只有 82 个任务,规模较小;
  • 成功率依赖论文的工具实现、prompt 与模型快照;
  • 代码动作扩大安全面,论文性能评估不能替代生产威胁模型;
  • 平均轮数没有完全反映单轮内部计算量;
  • 训练数据由强模型生成并筛选,可能继承 teacher 偏差;
  • 开源模型绝对表现仍低,底座能力限制明显;
  • 并非所有任务都值得用图灵完备动作,简单高风险操作更适合窄工具。

CodeAct 的核心遗产是把“动作语言”提升为关键设计变量。对 Harness 工程来说,正确结论不是全面改用 Python,而是让表达力按任务逐级开放,并让越强的动作空间对应越严格的隔离、权限和审计。

延伸阅读