LangChain Agent 接入 CubeSandbox MicroVM:用cubesandboxSDK 构建安全代码执行工具实战指南
【免费下载链接】CubeSandboxInstant, Concurrent, Secure & Lightweight Sandbox for AI Agents.项目地址: https://gitcode.com/GitHub_Trending/cu/CubeSandbox
CubeSandbox 为 AI Agent 提供"即时、并发、安全、轻量"的沙箱运行环境,而 LangChain 是构建 Agent 的主流编排框架。本文以仓库examples/langchain-integration/下的完整示例为核心,讲解如何让 LangChain Agent 的代码执行工具运行在 CubeSandbox MicroVM 内部:包括 0.x(AgentExecutor)与 1.x(create_agent)两套主流 API 变体、模板镜像的构建与注册、环境变量配置、run_python工具的实现细节与提示词防数据泄漏设计。读完本文,你将能够在一套真实部署上,用 Python SDK 把 LangChain Agent 的数据分析/建模任务安全地下沉到 MicroVM 中执行。
示例概览:同一个 Agent,两种 LangChain 版本
examples/langchain-integration/目录同时提供了两个大版本变体,两者共享同一个代码执行工具(run_python,基于cubesandboxSDK 构建)和同一个沙箱模板,唯一区别在于 LangChain Agent 的构建 API 与 Python 版本要求:
| 变体 | LangChain | Python | Agent API | 路径 |
|---|---|---|---|---|
| 0.x | 0.3.x(旧版) | 3.9+ | AgentExecutor+create_react_agent | examples/langchain-integration/0.x |
| 1.x | 1.x(新版) | 3.10+ | langchain.agents.create_agent+@tool | examples/langchain-integration/1.x |
选择建议(原文明确给出):
- 运行在Python < 3.10(>= 3.9),或已有 LangChain 0.3.x 存量代码库 → 使用
0.x/; - 运行在Python 3.10+且从零开始、或已在 LangChain 1.x → 使用
1.x/。
两个变体的演示效果一致:Agent 通过run_python工具在 MicroVM 内加载预置的sales.csv,计算每月总收入并输出结果。从 1.x 示例说明 可知,1.x 采用create_agent构建(基于 LangGraph),默认不打印逐步推理轨迹;0.x 的AgentExecutor设置了verbose=True,会额外打印完整推理链路。
共享资源:模板镜像、环境变量模板与种子数据
两个变体共用的资源位于目录顶层(见 examples/langchain-integration/README.md):
| 文件 | 用途 |
|---|---|
Dockerfile | 数据科学模板镜像(基于cubesandbox-base构建) |
.env.example | 环境变量模板(复制为0.x/.env或1.x/.env后填写) |
sales.csv | 预置到模板镜像中的种子数据集 |
模板镜像 Dockerfile 详解
examples/langchain-integration/Dockerfile 的完整内容如下:
# examples/langchain-integration/Dockerfile # Stacks a Python>month,product,units,price 2024-01,Widget,140,10.0 2024-01,Gadget,138,10.0 2024-02,Widget,200,10.0 2024-02,Gadget,131,10.5 2024-03,Widget,236,9.5 2024-03,Gadget,163,10.0该数据集足够 Agent 演示"按月聚合收入"与"线性回归预测"两类典型分析任务。
快速开始:从镜像构建到 Agent 运行
以下为两种变体通用的完整流程(来自 examples/langchain-integration/README.md):
# 1. 构建并推送共享模板镜像(目录顶层) docker build --platform linux/amd64 -t <your-registry>/langchain-cube:latest . docker push <your-registry>/langchain-cube:latest # 2. 注册为 Cube 模板,并记下输出的 template_id cubemastercli tpl create-from-image \ --image <your-registry>/langchain-cube:latest \ --writable-layer-size 2G --expose-port 49983 --probe 49983 --probe-path /health # 3. 选择变体,以 1.x 为例: cd 1.x cp ../.env.example .env # 填写各项值 pip install -r requirements.txt python langchain_agent_demo.py各步骤要点:
- 模板注册:
cubemastercli tpl create-from-image会把镜像登记为 Cube 模板。--writable-layer-size 2G指定可写层大小;--expose-port 49983 --probe 49983 --probe-path /health声明暴露端口并配置健康探针(模板内预置/health健康检查端点)。注册成功后输出template_id,需填入.env的CUBE_TEMPLATE_ID。该命令的完整实现位于 CubeMaster/cmd/cubemastercli/commands/cubebox/template.go。 - 依赖安装:0.x 变体的依赖文件为 examples/langchain-integration/0.x/requirements.txt,固定
langchain==0.3.23与langchain-openai==0.3.12,并要求cubesandbox>=0.6.0(示例依赖其 envd 进程的 commands/files API);1.x 变体为 examples/langchain-integration/1.x/requirements.txt,要求langchain>=1.3.14,<2.0、langchain-openai>=1.0,<2.0,并显式声明langgraph>=0.2(因为create_agent基于 LangGraph 构建,显式声明可保证示例自包含)。
前置条件
两个变体的 README(0.x、1.x)列出的依赖一致:
- 一套 CubeSandbox 部署,CubeAPI 可从宿主机通过
http://<node>:3000访问; - 一个预装 Python + pandas/numpy/matplotlib/scikit-learn 的沙箱模板(用顶层
Dockerfile构建); - 一个 OpenAI 兼容的 LLM 端点(示例中使用 TokenHub)。
环境变量配置详解
examples/langchain-integration/.env.example 是完整的环境变量模板,覆盖控制面、数据面与 LLM 三部分配置:
# CubeSandbox 原生 SDK(from cubesandbox import Sandbox) # (可选)默认 http://127.0.0.1:3000 CUBE_API_URL="http://<your-node-ip>:3000" # (可选)仅在 CubeAPI 开启鉴权时需要;未设置时 SDK 不发送鉴权头 CUBE_API_KEY="<your-cube-api-key>" # (必填)示例使用的沙箱模板 CUBE_TEMPLATE_ID="<your-template-id>" # (可选)直达 CubeProxy 的 IP;未设置时走常规 DNS CUBE_PROXY_NODE_IP="<your-node-ip>" # (可选)CubeProxy HTTP 端口,默认 80。仅在代理监听非默认端口时取消注释 # CUBE_PROXY_PORT_HTTP=8081 # LLM(OpenAI 兼容) # (可选)默认 https://tokenhub.tencentmaas.com/v1 OPENAI_BASE_URL="<your-openai-compatible-endpoint>" # (必填)LLM API Key OPENAI_API_KEY="<your-api-key>" # (可选)默认 deepseek-v3 CHAT_MODEL="<your-chat-model>" # (可选)CubeAPI HTTPS 端点的自签名 CA 证书 PEM 路径。 # 示例会将其导出为 SSL_CERT_FILE(进程全局生效),因此证书包必须 # 同时包含公共根 CA,否则 LLM 端点也会受到影响。 # CUBE_SSL_CERT_FILE="/path/to/ca-cert.pem"其中CUBE_PROXY_NODE_IP(以及可选的CUBE_PROXY_PORT_HTTP)用于配置数据面代理——SDK 通过它访问 MicroVM 内部服务(如 Jupyter 端口 49999 等),未设置时使用常规 DNS 解析。
自签名 CA 的进程级处理
若 CubeAPI 使用自签名 HTTPS 证书,两个演示脚本(0.x 源码、1.x 源码)都会在启动时统一处理:将CUBE_SSL_CERT_FILE同时导出为SSL_CERT_FILE与REQUESTS_CA_BUNDLE,使控制面客户端(requests →REQUESTS_CA_BUNDLE)与数据面客户端(httpx/envd RPC →SSL_CERT_FILE)都信任该 CA。注意注释中的警告:该导出是进程全局的,也会作用于 LLM 客户端,因此证书包必须包含公共根 CA(或仅在 LLM 使用同一 CA 时才设置该变量)。
run_python工具源码剖析:MicroVM 内执行 Python
两个变体的核心工具实现几乎一致,区别仅在 LangChain 侧如何注册工具。以 0.x 为例(langchain_agent_demo.py):
def build_agent(llm, sandbox: Sandbox): _script_counter = itertools.count() def run_python(code: str) -> str: """Execute Python inside the Cube Sandbox MicroVM; return stdout + stderr. The MicroVM is preinstalled with pandas / numpy / matplotlib / scikit-learn. Each call writes the snippet to a unique /workspace/_agent_<n>.py and runs it, so concurrent tool calls don't overwrite each other. Charts can be saved under /workspace (e.g. /workspace/revenue.png). """ script = f"/workspace/_agent_{next(_script_counter)}.py" sandbox.files.write(script, code) result = sandbox.commands.run(f"python3 {script}", timeout=120, cwd="/workspace") out = result.stdout # Keep stderr delimited from stdout so library warnings (exit_code 0) # don't blur the real output seen by the LLM. if result.stderr: out += "\n--- stderr ---\n" + result.stderr if result.exit_code != 0: out += f"\n[non-zero exit code: {result.exit_code}]" return out tools = [Tool( name="run_python", func=run_python, description="Execute Python code in a Cube Sandbox MicroVM with pandas/numpy/matplotlib " "preinstalled. Returns stdout, with stderr (if any) delimited below it, plus " "the exit code on failure. Save charts under /workspace.", )]这段实现中有四个值得注意的工程细节:
- 一次 Agent 运行 = 一个 MicroVM:
Sandbox.create(template=..., timeout=600)在with上下文管理器内创建沙箱,整个 Agent 执行期间复用同一个沙箱,所有run_python调用共享其文件系统与运行环境。SDK 的Sandbox类定义于 sdk/python/cubesandbox/sandbox.py,其上下文管理器在退出时自动销毁沙箱。 - 并发安全:每次调用使用
itertools.count()生成递增序号,把代码写入唯一路径/workspace/_agent_<n>.py,避免并发工具调用互相覆盖脚本文件。 - stderr 与 stdout 分离:库的告警(exit_code 为 0 时)不会混入 LLM 看到的真实输出——stderr 通过
--- stderr ---分隔行拼接在 stdout 之后,让模型能清晰区分正常输出与告警。 - 错误显式化:非零退出码会以
[non-zero exit code: N]追加到返回文本,把失败信号显式交给 LLM 判断,而不是静默吞掉。
1.x 变体使用 LangChain 的@tool装饰器完成同样的注册(1.x 源码),并通过create_agent(llm, [run_python], system_prompt=SANDBOX_CONTEXT)直接构建 Agent。
输出拼装为什么重要
LLM 工具调用返回的文本会原样进入模型的上下文。run_python的返回值把 stdout、stderr、退出码三类信息统一编码进一段文本,同时避免库告警淹没真实结果——这是"工具输出即模型观测"这一环节的关键设计,直接决定模型后续推理的正确性。
提示词设计:防数据泄漏与防幻觉的建模约定
两个变体都把环境事实与建模约定注入提示词。0.x 使用PromptTemplate(0.x 源码),1.x 使用system_prompt(1.x 源码),内容一致,包含:
- 环境事实:工作目录
/workspace;数据集/workspace/sales.csv(列:month,product,units,price,6 行 = 3 月 × 2 产品,收入定义为units * price);预装 pandas/numpy/matplotlib/scikit-learn;图表保存到/workspace。 - 路径约定:用户提到"数据集"而未给路径时,默认使用
/workspace/sales.csv。 - 建模约定(针对这个微型演示数据集):
- 回归/预测目标是每月总收入,先按月聚合成一行,再以数字月索引(0, 1, 2, ...)作为唯一特征;
- 严禁把目标本身或其直接分量(units、price)当作特征——这会造成数据泄漏,得到无意义的 0 RMSE;
- 数据集太小、不适合 train/test 划分,在全量数据上拟合与评估,并明确说明指标为样本内(in-sample);
- 只报告代码实际打印的数字,绝不臆造或估算指标。
0.x 还内置了 ReAct 格式模板(Thought / Action / Action Input / Observation / Final Answer),并设置AgentExecutor(verbose=True, handle_parsing_errors=True),前者输出推理轨迹,后者在模型输出无法解析时自动容错。
这些约定本质上把"小数据集的建模伦理"(防泄漏、防过拟合误判、防幻觉数字)编码进了系统提示,是让 Agent 在受限数据上产出可信结果的关键,值得在自建任务中复用。
LLM 客户端与运行入口对比
两个变体的主入口结构相同(0.x、1.x):
llm = ChatOpenAI( model=os.getenv("CHAT_MODEL") or "deepseek-v3", api_key=_llm_key, base_url=os.getenv("OPENAI_BASE_URL", "https://tokenhub.tencentmaas.com/v1"), timeout=60, max_retries=2, temperature=0, )- API Key 支持
OPENAI_API_KEY或TOKENHUB_API_KEY二选一(缺任一环境变量会直接报错退出); temperature=0保证输出确定性,适合数据分析任务;- 默认问题为:"Load sales.csv from /workspace, compute total revenue per month, and report the month -> revenue numbers in your final answer.",也支持命令行传参自定义任务(见下)。
运行与自定义任务:
python langchain_agent_demo.py # 自定义任务: python langchain_agent_demo.py "Train a linear model on the dataset and report RMSE."沙箱生命周期由上下文管理器接管:
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"], timeout=600) as sandbox: print(f"Sandbox {sandbox.sandbox_id} created. Running agent...") executor = build_agent(llm, sandbox) result = executor.invoke({"input": question}) # 0.x:输入 dict print(result["output"])1.x 的调用形态不同——通过agent.invoke({"messages": [{"role": "user", "content": question}]})传入消息,并逆序遍历result["messages"]取最后一个非空content作为最终答案(因为末轮消息可能是工具调用或空内容)。
预期输出与清理行为
两个变体 README 对运行结果的描述:
- 0.x:默认提示词下,宿主在stdout打印最终答案——月 → 收入数字(2780.0 / 3375.5 / 3872.0);由于
AgentExecutor设置了verbose=True,还会在控制台打印完整推理轨迹(thought → tool call → observation)再输出答案。 - 1.x:
create_agent非 verbose 模式,只在stdout打印最终答案,不显示逐步推理轨迹。
(读者可自行对照 sales.csv 校验:1 月收入 = 140×10.0 + 138×10.0 = 2780.0,2 月 = 200×10.0 + 131×10.5 = 3375.5,3 月 = 236×9.5 + 163×10.0 = 3872.0。)
日志与清理行为:
- 最终答案 →stdout;0.x 的推理轨迹 → 控制台(verbose)。
- 销毁为尽力而为(best-effort):若
kill()失败,SDK 上下文管理器会静默吞掉该错误,不会向stderr打印告警。这意味着 MicroVM 的释放不依赖 Agent 的正常退出路径,从 SDK 的Sandbox实现(sdk/python/cubesandbox/sandbox.py)可以看到其销毁逻辑与生命周期参数(如on_timeout可选kill/pause、auto_resume等)由上层统一管理,Agent 侧无需关心。
从示例到生产:可迁移的设计要点
examples/langchain-integration/虽以演示数据集为目标,但其架构可直接迁移到真实任务:
- 代码执行与业务逻辑隔离:Agent 编排在宿主进程,代码执行在 MicroVM,天然获得进程/网络/文件系统层面的隔离,这是 CubeSandbox "Secure" 能力的直接体现(可结合 docs/architecture/overview.md 了解整体设计)。
- 模板化环境:把依赖预装进模板镜像(如本文的
Dockerfile),Agent 首次调用即可使用,无需安装等待;种子数据(sales.csv)同样通过镜像预置,保证离线可演示。 - 一次会话一个沙箱:
with Sandbox.create(...)复用沙箱、退出即销毁,配合 SDK 的尽力清理,避免资源泄漏。 - 模型可读的工具输出:stdout/stderr/退出码统一编码、防泄漏与防幻觉的提示词约定,是让 LLM 正确使用工具并产出可信结果的通用最佳实践。
选择 0.x 还是 1.x,取决于你的 Python/LangChain 技术栈:存量 0.3.x 代码库选 0.x,全新项目或已在 1.x 选 1.x。两者的run_python工具与模板完全复用,未来从 0.x 迁移到 1.x 时,只需替换 Agent 构建层与调用形态,工具层与沙箱层无需改动。
【免费下载链接】CubeSandboxInstant, Concurrent, Secure & Lightweight Sandbox for AI Agents.项目地址: https://gitcode.com/GitHub_Trending/cu/CubeSandbox
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考