Gemini 代码执行是服务器端沙箱,模型写 Python、平台执行、结果内联返回,一个 generateContent 调用完成全流程。
代码执行是一种服务端工具:模型编写 Python 代码,Google 在沙箱中运行它,模型读取输出——所有这一切都发生在一个 generateContent 调用内部。你的客户端从不执行任何代码,运行的记录作为响应中的普通 parts 返回。
与函数声明不同,代码执行无需指定任何参数。启用这个内置工具只需传入一个空对象,具体用法在 Google 的代码执行指南中有文档说明:
curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tools": [{"codeExecution": {}}],
"contents": [{"role": "user", "parts": [{
"text": "What is the sum of the first 50 prime numbers? Compute it, do not estimate."
}]}]
}'
在 REST 的 camelCase 命名中字段是 codeExecution,在 proto 和某些 SDK 层面则是 code_execution。是否使用该工具完全由模型决定,这与函数调用如出一辙:声明只是让工具变得可用,并不强制使用。
Gemini 中的 Part 是一个联合类型——它可以承载 text、inlineData、functionCall、functionResponse,以及这个工具新增的两种类型。使用代码执行的响应中,这些 part 与正文内容交错排列,按照实际发生的顺序返回:
{
"candidates": [{
"content": {
"role": "model",
"parts": [
{ "text": "I'll compute this directly." },
{ "executableCode": {
"language": "PYTHON",
"code": "from sympy import prime\ntotal = sum(prime(n) for n in range(1, 51))\nprint(total)\n"
}},
{ "codeExecutionResult": {
"outcome": "OUTCOME_OK",
"output": "5117\n"
}},
{ "text": "The sum of the first 50 primes is 5117." }
]
},
"finishReason": "STOP"
}]
}
两个字段。language 是一个枚举类型,当前行唯一的文档值是 PYTHON(零值是 LANGUAGE_UNSPECIFIED)。code 是模型编写的源代码,原样保留,在运行之前就已确定。这是可审计的制品:如果答案错了,代码中何处出错一目了然。
outcome 也是一个枚举,文档中记录的值是你必须做分支处理的:
OUTCOME_UNSPECIFIED — 零值,视为未知。
OUTCOME_OK — 代码运行完毕。
OUTCOME_FAILED — 代码抛出了异常。output 携带 traceback,模型通常会读取它并在后续的 executableCode part 中写入修正版本。
OUTCOME_DEADLINE_EXCEEDED — 代码运行时间过长被终止。
output 是进程写入 stdout 和 stderr 的内容,如果非常大则会被截断。没有其他内容会传回来:模型看到的正是被打印出来的内容,因此如果代码计算了一个值却没有打印它,就会产生一个 output 为空的 OUTCOME_OK,此时模型也没什么可报告的。
这是它与普通函数调用的结构性差异,也是它在适用场景下更值得选用的原因。使用声明的函数时,模型发出 functionCall,调用返回给你,你执行它,再把 functionResponse 发回去——至少两次网络往返,以及一段你需要维护的编排逻辑。
而代码执行中,写-运行-读的循环发生在 Google 的基础设施内部。一个请求可以包含多对 executableCode / codeExecutionResult,这代表模型调试了自己的脚本,而你一次性收到整个记录。文档中记载了每次请求内最多迭代次数的上限,超过后它会停止并用已有的结果作答。
由于迭代过程在响应到达之前是不可见的,使用代码执行的请求延迟分布比普通生成要宽得多。如果你为一个文本生成调校了超时时间,在模型编写并重写脚本三次的偶发请求上,这个超时就会触发。
流式传输改变了等待的感受,但改变不了底层发生的事。使用 streamGenerateContent 时,parts 按照产生的顺序到达,因此一个 executableCode part 会在模型写完脚本后立即出现,随后在代码实际运行期间没有任何内容传来。如果你在渲染这个流,那个间隙是一个展示代码和运行指示器的好地方,而不是让光标看起来像卡住了——这值得处理,因为一个需要好几秒的脚本在otherwise看起来和断开的连接一模一样。
代码执行也可以与普通函数声明组合在同一个请求中。同时声明两者,模型就能通过你的函数获取数据,然后在沙箱中对其计算,这正是让"无网络"限制变得可以忍受的组合方式。Parts 返回时是交错的:一个你需要回复的 functionCall,然后在后续请求中是使用了你的数据的 executableCode 和 codeExecutionResult 对。如果模型需要同时调用你的多个函数,同一轮也可以携带多个函数调用,连同它编写的代码一起。
环境是 Python,附带了固定的科学计算库集合——包括 NumPy、SymPy、pandas 和 Matplotlib——并且没有网络访问。你不能安装包,模型也无法调用 API、获取 URL 或读取你的文件系统。这种隔离性是让该工具在不可信输入上可以安全启用的安全属性,也是它的主要限制。
有两个能力值得了解,不仅仅是算术。通过 File API 上传的文件可以让模型在执行环境中加载并用 pandas 处理,这就将"分析这个 CSV"从一道阅读理解题变成了真正的操作。此外,Matplotlib 的输出会作为 image part 返回,因此请求可以返回一张模型从自己计算的数据绘制的图表。
库列表、每次执行的时间限制、文件大小限制,以及每次请求的最大迭代次数,这些都因模型而异,且随功能成熟有过变化。请查阅你要调用的模型 ID 对应的代码执行指南以获取最新数值。
沙箱没有独立的计量表。成本体现在 token 上:模型编写的代码是输出 token,执行结果会被反馈给模型作为下一步的输入 token。因此一个迭代三次的请求计费会比最终答案的篇幅所暗示的高得多。
请读取响应中的 usageMetadata 来获取实际数字,而不是根据可见文本估算。中间步骤都在里面,在调试密集的请求上它们可能占主导。
精确算术。语言模型预测大数乘法的位数是在猜测;同一个模型写 print(a * b) 不是在猜。凡是答案必须正确而非看起来合理的地方,就属于这里。
针对附加文件的数据操作。对 CSV 排序、分组、过滤和求和,是 pandas 精确执行而模型只能近似的事情。
任何有可验证中间结果的任务。日期运算、单位换算、解析、正则验证——模型可以通过运行来验证自己工作的任务。
不适用于调用你的系统。没有网络意味着没有数据库、没有内部 API、没有 webhook。这些是函数调用该做的事,两者可以在同一请求中声明。
Parallel Function Calling in the Gemini API
Grounding With Google Search in the Gemini API
Sending Function Responses Back in a Multi-Turn Gemini Conversation