ChatGPT 代码解释器沙盒中途超时 —— 排查与修复

Code Interpreter 跑到一半就报 execution timed out —— 通常是 CPU 密集循环、挂起的网络调用,或者沙盒 60-120 秒窗口内的内存暴涨。

你让 ChatGPT 处理一个 200MB 的 CSV、拟合一个模型、再画残差图。二十秒过去,响应单元变红:“The code execution timed out” 或者 “Sandbox terminated. Retrying…”。模型重试一次,撞上同样的墙,然后干脆改打印伪代码,不再实际运行。Code Interpreter(在部分入口里叫 Advanced Data Analysis)是把你的 Python 跑在每条消息独立的临时容器里 —— 通常每个 cell 有 60-120 秒的硬墙钟上限,以及很紧的内存和 CPU 限制。大多数超时不是 “ChatGPT 坏了”,而是一个 cell 想一口气干完所有事,把整个 dataframe 全部塞进内存,或者卡在沙盒根本走不出去的网络调用上。

常见原因

按实际会话中遇到的频率排序。

1. 单个 cell 想把端到端的活全干完

模型写了一个巨型 cell:读 CSV → 清洗 → 特征工程 → 训练 → 交叉验证 → 画图。哪怕只是一个 50MB 的文件,这个组合也经常突破 60 秒上限。Code Interpreter 不会流式返回中间结果,cell 一挂中间状态就全没了。

怎么判断:出错的 cell 超过约 40 行 Python,里面有多个对 dataframe 的 for 循环,且至少出现一次 fit / cross_val_score 调用。

2. 沙盒里发起了网络请求

绝大多数账号下,沙盒默认是离线的。requests.get("https://api.example.com/...")yfinance.download(...) 这种写法不会立即失败 —— 它会一直挂到 60 秒看门狗把它杀掉。pip install 在限制环境下也是同样表现。

怎么判断:cell 里 import 了 requests / urllib / httpx / yfinance / pandas_datareader,或者直接 pd.read_csv("http...")。超时之前没有任何进度输出。

3. 没等到时间到,内存先爆了

大表 join 或者宽表 df.pivot() 容易突破约 1-2GB 的 RAM 上限。内核被 OOM-killed,ChatGPT 给你显示的是一个通用的超时提示 —— 哪怕实际上根本没耗多少时间。

怎么判断:cell 不到 10 秒就挂出超时。换成小切片 (df.head(1000)) 再跑就秒回。

4. 在宽 dataframe 上用 pandas 的链式 lambda

df.apply(lambda row: ...) 跑 100 万+ 行就是单线程纯 Python。向量化版本 2 秒搞定,lambda 版本 60 秒超时。模型经常写 lambda 版本,因为代码更短。

怎么判断:对超过 ~10 万行的 frame 用了 .apply(lambda ...).iterrows()

5. 上传文件在 cell 之间丢了,代码每次都重新读

新的对话回合可能会拉起一个全新的沙盒。如果第 3 轮要用第 1 轮上传的文件,模型可能每轮都重新读、重新解析、重新生成特征 —— 把超时预算全浪费在重做准备工作上。参考 ChatGPT 附件在刷新后丢失 里关于沙盒状态的细节。

怎么判断:每个 cell 开头都是 pd.read_excel("/mnt/data/<file>.xlsx") 加新鲜的 df.head()。之前 cell 里的变量全部不见。

6. 画图时点数太多

matplotlib 散点图画 50 万+ 个点,要在沙盒 CPU 上把整个图序列化成 PNG。光这一步就能吃掉 30 秒以上。

怎么判断:超时前最后能看到的动作是 plt.show() 或者 sns.scatterplot。把点数砍到 5000 就立刻不超时了。

开始之前

  • 确认你的套餐 —— Free 的沙盒限制比 Plus / Team / Pro / Enterprise 都更紧。
  • 看清楚超时是确定性的(每次都在同一个 cell)还是偶发的(有时能跑完)。
  • 重新提示之前先把出问题的 cell 代码留底,ChatGPT 经常在重试时改写代码,你就丢失证据了。
  • 检查上传文件的体积 —— 默认下,任何超过 ~100MB 的文件都有风险。

需要收集的信息

  • 出现的具体报错文本(“execution timed out”、“sandbox terminated”、“kernel restarted”)。
  • 超时前的大约挂钟时间(用秒表 —— 这能区分 OOM 和墙钟超时)。
  • Dataframe 形状(df.shape)和按 dtype 算的内存占用(df.memory_usage(deep=True).sum() / 1e6 MB)。
  • cell 是否有任何网络 I/O 或 pip install
  • 当前使用的模型(GPT-5.5、GPT-5.4 等) —— 模型选择会影响 planner 怎么把活拆进各个 cell。

一步一步修

按 ROI 从高到低排。

第 1 步:逼模型把活拆成有 checkpoint 的多个 cell

在提示里加一段:

分多个 cell 跑这次分析,每个 cell 控制在 30 秒以内:
Cell 1: 读取并检查文件,只打印 shape 和 dtypes。
Cell 2: 清洗并 downcast,把 df 保存到 /mnt/data/clean.parquet。
Cell 3: 特征工程,保存 features.parquet。
Cell 4: 训练并评估,打印指标。
Cell 5: 只画一张图。

不要合并 cell。中间状态用 parquet 文件持久化。

这能把一个 120 秒的巨型 cell 拆成五个 20 秒的小 cell,中间的 parquet 文件还能熬过沙盒重启。

第 2 步:在重计算之前先 downcast 和切片

在训练或 pivot 之前,先把 dataframe 压小:

import pandas as pd
df = pd.read_csv("/mnt/data/input.csv")
for col in df.select_dtypes("float64"):
    df[col] = pd.to_numeric(df[col], downcast="float")
for col in df.select_dtypes("int64"):
    df[col] = pd.to_numeric(df[col], downcast="integer")
for col in df.select_dtypes("object"):
    if df[col].nunique() / len(df) < 0.5:
        df[col] = df[col].astype("category")
print(df.memory_usage(deep=True).sum() / 1e6, "MB")

1.8GB 的 frame 经常能压到 300MB。同一个 cell 立刻就装得下了。

第 3 步:把 apply(lambda) 换成向量化操作

直接要求模型:

重写这个 cell,不要用 .apply(),也不要用 .iterrows()。只用向量化的
pandas / numpy 操作。如果某一步无法向量化,就在 df.sample(50_000)
上做,并解释 trade-off。

很多时候,这一次重写就能把 90 秒超时变成 3 秒跑完。

第 4 步:给 matplotlib 限制点数

任何在大 frame 上的散点图都这么写:

sample = df.sample(min(5000, len(df)), random_state=0)
plt.scatter(sample["x"], sample["y"], s=4, alpha=0.4)
plt.show()

或者改用 hexbin / 2dhist,先聚合再渲染。渲染时间从 30 秒掉到 1 秒以内。

第 5 步:把中间状态持久化到 /mnt/data/

沙盒工作目录 /mnt/data/ 在同一对话内多数沙盒重启之间能保留下来。每完成一个重计算步骤就保存:

df_clean.to_parquet("/mnt/data/clean.parquet")
features.to_parquet("/mnt/data/features.parquet")

下一个 cell 用 df = pd.read_parquet(...) 起步 —— 亚秒级。哪怕第 4 个 cell 超时,前面 1-3 个 cell 的成果都还在。

第 6 步:把网络调用挪出沙盒

如果脚本需要外部数据,不要让 Code Interpreter 自己去抓。做法:

  1. 自己在本地下好数据(curl、浏览器都行)。
  2. 把文件上传到对话里。
  3. 让模型只从 /mnt/data/<file> 读取。

对于需要付费墙或登录的来源,可以参考 ChatGPT 联网搜索被付费墙站点拦截 —— Code Interpreter 受同样的边界约束。

第 7 步:实在不行就把沙盒重启一遍

在新一轮对话里:

重置 Python 沙盒。在新 cell 里 print("ok") 确认,
然后重新加载 /mnt/data/clean.parquet。

旧内核的残留状态(挂住的线程、上一轮泄漏的文件句柄)确实是性能下降的真实原因,换个新内核往往立马变快。

验证

  • 之前出错的 cell 现在能完整跑完,输出直接显示在下面。
  • 整个分析的总挂钟时间在沙盒预算内还有富余(目标:每个 cell 不超过 30 秒)。
  • 在新的对话里重跑同一个提示能复现成功 —— 证明不是内核偶尔抽风。
  • downcast 之后内存占用低于 500MB(之前要 1.5GB+)。

长期预防

  • 任何超过 50MB 的文件,对话第一步就让模型先打印 shapedtypes,并先规划 downcast 方案,再动数据。
  • 把”每个 cell 只做一件事”写进你的提示模板。读取、清洗、join、建模、绘图都各占一个 cell。
  • 任何超过 10 秒的步骤之后都要持久化到 parquet;把沙盒当作可丢弃的临时资源。
  • 重复性的工作流优先用本地 Jupyter / Colab,只用 ChatGPT 生成和审阅代码。
  • 永远不要让模型在沙盒里写 requests.get(...),要么提前下好,要么干脆不调。
  • 用 Advanced Data Analysis 处理超大文件时,先把上传切块(split -n l/4 big.csv),分块处理。

常见坑

  • 不给具体预算就说”让它更快”,换来的只是边角的微调,而不是结构性重组。
  • 同一个失败提示重跑 5 次,把消息上限耗光 —— 参考 ChatGPT 消息上限触发
  • 以为 pip install <pkg> 是免费的;某些套餐里它会触发沙盒刷新,耗 10-20 秒。
  • 为了”精度”硬要画 100 万个原始点 —— 你的眼睛区分不出 5000 和 100 万,但沙盒能。
  • 忘了 /mnt/data/ 在开新对话时会被清空。文件在同一线程内能存,跨线程不行。
  • 看到 “completed successfully” 就当成功,实际并没有真的画出图或表 —— 一定要看输出 cell。

FAQ

Q: 60 秒超时在所有套餐上一样吗?

不一样。Free 最紧,Plus / Team 较宽,Pro 和 Enterprise 上限最高但也不是无限。沙盒规格没有公开文档,而且会变 —— 不管你哪个套餐,单个 cell 超过 30 秒都要当作有风险。

Q: 能直接调大超时吗?

不能。要做的是结构调整:文件更小、downcast、向量化、拆 cell、持久化 parquet。真正有用的旋钮只有升级到 Pro / Enterprise,但它只是抬高上限,不会消除。

Q: 我本地 3 秒能跑完的 cell,在 ChatGPT 里就超时,为什么?

沙盒 CPU 核心数比你笔记本少,没有 GPU,磁盘也更慢。被单线程 Python(lambda、循环)主导的运算上限低得多。改成向量化以后差距通常就消失了。

Q: Advanced Data Analysis 和 Code Interpreter 是同一个限制吗?

是 —— 它们就是同一个功能在不同入口下的两个名字。沙盒、包、超时行为完全一致。

Q: 我上传的文件中途消失了,是超时引起的吗?

不是,那是另一个沙盒重启问题。参考 ChatGPT 文件从对话中消失 —— 重新上传,在任何重计算步骤之前先持久化到 parquet。

标签: #ChatGPT #code-interpreter #timeout #python #排查