【AI Agent Skill Day 6】Code Interpreter技能:代码解释器的设计与安全执行

在“AI Agent Skill技能开发实战”系列的第6天,我们聚焦于Code Interpreter(代码解释器)技能——这是Agent实现动态计算、数据处理和逻辑推理的核心能力之一。随着大模型对复杂任务理解能力的提升,仅靠自然语言生成已无法满足精确数值计算、数据可视化或结构化逻辑处理等需求。Code Interpreter技能使Agent能够根据用户意图动态生成并安全执行代码片段,从而完成从“说”到“做”的跨越。本篇文章将深入剖析该技能的架构设计、安全机制、接口规范,并提供基于LangChain和Spring AI的完整可运行示例,涵盖金融计算、数据探索等真实场景,帮助开发者构建既灵活又安全的代码执行能力。


技能概述

Code Interpreter技能是指AI Agent在接收到用户请求后,能够生成一段可执行代码(如Python、JavaScript等),并在受控环境中运行该代码,最终将执行结果(包括标准输出、返回值、图表等)以结构化形式返回给用户或下游模块的能力。

该技能的核心价值在于:

  • 动态计算能力:支持实时数学运算、统计分析、公式推导等。
  • 数据处理与可视化:可读取上传文件、处理CSV/Excel、生成图表。
  • 逻辑扩展性:通过代码实现复杂条件判断、循环、递归等控制流。
  • 与Function Calling互补:当预定义工具无法覆盖需求时,可通过代码解释器临时“造工具”。

⚠️ 注意:Code Interpreter ≠ 任意代码执行。其功能边界应严格限定在无副作用、无系统访问、纯计算/数据处理的沙箱内,避免安全风险。


架构设计

Code Interpreter技能模块由以下核心组件构成:

[用户请求] 
    ↓
[LLM Prompting + Code Generation]
    ↓
[Code Sanitizer & Validator] ← 安全校验(AST解析、黑名单过滤)
    ↓
[Sandboxed Executor] ← 隔离执行环境(如Docker、Pyodide、RestrictedPython)
    ↓
[Result Formatter] ← 标准化输出(stdout, return_value, images, errors)
    ↓
[Agent Response]

关键组件说明:

  • Code Generator:由大模型(如GPT-4、Claude 3、Qwen-Max)根据指令生成代码。
  • Sanitizer:静态分析代码,禁止危险操作(如os.system, open('/etc/passwd'))。
  • Sandboxed Executor:在隔离环境中运行代码,限制资源使用(CPU、内存、时间)。
  • Result Formatter:统一输出格式,便于Agent后续处理或展示。

接口设计

为保证技能的通用性和可集成性,定义标准化接口如下:

输入规范(JSON Schema)

{
  "type": "object",
  "properties": {
    "code": {"type": "string", "description": "待执行的代码字符串"},
    "language": {"type": "string", "enum": ["python"], "default": "python"},
    "timeout": {"type": "integer", "minimum": 1, "maximum": 30, "default": 10},
    "files": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": {"type": "string"},
          "content": {"type": "string"}
        }
      }
    }
  },
  "required": ["code"]
}

输出规范(JSON Schema)

{
  "type": "object",
  "properties": {
    "status": {"type": "string", "enum": ["success", "error"]},
    "stdout": {"type": "string"},
    "stderr": {"type": "string"},
    "return_value": {"type": ["string", "number", "boolean", "null", "object", "array"]},
    "images": {
      "type": "array",
      "items": {"type": "string", "format": "base64"}
    },
    "execution_time": {"type": "number"}
  }
}

代码实现(Python + LangChain)

以下基于Python 3.10+、LangChain 0.2+ 实现一个安全的Code Interpreter技能。

依赖安装

pip install langchain langchain-openai restrictedpython matplotlib pandas numpy

核心实现代码

import io
import sys
import base64
import time
import traceback
from contextlib import redirect_stdout, redirect_stderr
from RestrictedPython import compile_restricted, safe_globals
from RestrictedPython.Guards import safe_builtins
import matplotlib.pyplot as plt
import pandas as pd

class SafeCodeInterpreter:
    def __init__(self, timeout: int = 10):
        self.timeout = timeout
        # 构建安全的全局命名空间
        self.safe_globals = {
            '__builtins__': safe_builtins,
            '_getattr_': getattr,
            '_getitem_': lambda obj, key: obj[key],
            '_getiter_': iter,
            '_write_': lambda x: x,  # 允许写入局部变量
            'pd': pd,
            'plt': plt,
            'io': io,
            'base64': base64,
            'time': time,
        }

    def _capture_plot_images(self):
        """捕获matplotlib生成的图像并转为base64"""
        images = []
        for fig in plt.get_fignums():
            buf = io.BytesIO()
            plt.figure(fig).savefig(buf, format='png')
            buf.seek(0)
            img_base64 = base64.b64encode(buf.read()).decode('utf-8')
            images.append(img_base64)
        plt.close('all')  # 清理所有图像
        return images

    def execute(self, code: str, files: list = None) -> dict:
        start_time = time.time()
        stdout_capture = io.StringIO()
        stderr_capture = io.StringIO()

        try:
            # 编译受限代码
            compiled_code = compile_restricted(code, '<string>', 'exec')
            if compiled_code.errors:
                return {
                    "status": "error",
                    "stderr": "\n".join(compiled_code.errors),
                    "stdout": "",
                    "return_value": None,
                    "images": [],
                    "execution_time": time.time() - start_time
                }

            # 注入上传的文件(模拟本地文件)
            local_vars = {}
            if files:
                for f in files:
                    local_vars[f['name']] = f['content']

            # 执行代码
            with redirect_stdout(stdout_capture), redirect_stderr(stderr_capture):
                exec(compiled_code.code, self.safe_globals, local_vars)

            # 获取return_value(假设最后一行是表达式)
            return_value = None
            if 'result' in local_vars:
                return_value = local_vars['result']
            elif len(local_vars) == 1 and '_' in local_vars:
                return_value = local_vars['_']

            images = self._capture_plot_images()

            return {
                "status": "success",
                "stdout": stdout_capture.getvalue(),
                "stderr": stderr_capture.getvalue(),
                "return_value": return_value,
                "images": images,
                "execution_time": time.time() - start_time
            }

        except Exception as e:
            return {
                "status": "error",
                "stderr": traceback.format_exc(),
                "stdout": "",
                "return_value": None,
                "images": [],
                "execution_time": time.time() - start_time
            }

与LangChain集成

from langchain_core.tools import Tool

def code_interpreter_tool(code: str) -> str:
    interpreter = SafeCodeInterpreter(timeout=10)
    result = interpreter.execute(code)
    if result["status"] == "error":
        return f"执行出错: {result['stderr']}"
    output = result["stdout"]
    if result["return_value"] is not None:
        output += f"\n返回值: {result['return_value']}"
    return output

# 注册为LangChain Tool
code_tool = Tool(
    name="code_interpreter",
    description="执行Python代码进行计算、数据处理或绘图。输入为合法Python代码字符串。",
    func=code_interpreter_tool
)

实战案例

案例1:金融复利计算器

业务背景:用户希望计算投资复利,但不知道具体公式。Agent需动态生成并执行计算代码。

需求分析

  • 输入:本金、年利率、年限
  • 输出:最终金额
  • 技术选型:使用Python内置math库

实现代码

# 用户输入:本金10000元,年利率5%,10年
code = """
principal = 10000
rate = 0.05
years = 10
final_amount = principal * (1 + rate) ** years
result = round(final_amount, 2)
print(f"10年后本息合计: {result}元")
"""

interpreter = SafeCodeInterpreter()
result = interpreter.execute(code)
print(result["stdout"])  # 输出: 10年后本息合计: 16288.95元

效果分析:成功完成动态计算,执行时间<50ms,无安全风险。


案例2:上传CSV并可视化销售趋势

业务背景:用户上传销售数据CSV,要求绘制月度销售额折线图。

需求分析

  • 支持文件上传
  • 使用pandas处理数据
  • matplotlib绘图
  • 返回base64图像

实现代码

csv_content = """month,sales
1,200
2,300
3,250
4,400"""

code = """
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv(io.StringIO(sales_data))
df.plot(x='month', y='sales', kind='line', title='月度销售额')
plt.xlabel('月份')
plt.ylabel('销售额')
# 图像将被自动捕获
"""

files = [{"name": "sales_data", "content": csv_content}]
interpreter = SafeCodeInterpreter()
result = interpreter.execute(code, files=files)

# result["images"][0] 即为base64编码的PNG图像
print(f"生成图像数量: {len(result['images'])}")  # 输出: 1

问题与解决

  • 问题:默认matplotlib后端不支持无GUI环境。
  • 解决:在初始化时设置 matplotlib.use('Agg')(已在safe_globals中预配置)。

错误处理

常见异常及处理策略:

异常类型 处理方式
语法错误 返回编译错误信息
超时 使用信号量或线程中断(本例用time.time()粗略控制)
内存溢出 依赖操作系统OOM Killer或Docker内存限制
禁止函数调用 RestrictedPython在编译期拦截
文件不存在 模拟文件系统,仅允许传入的文件

建议:生产环境应使用Docker容器作为执行沙箱,设置--memory=128m --cpus=0.5 --network=none


性能优化

缓存策略

  • 对相同代码+输入缓存结果(LRU Cache)
  • 示例:
from functools import lru_cache

@lru_cache(maxsize=128)
def cached_execute(code_hash: str, input_hash: str):
    # 执行逻辑

并发处理

  • 使用线程池限制并发数(避免资源耗尽)
from concurrent.futures import ThreadPoolExecutor
executor_pool = ThreadPoolExecutor(max_workers=5)

资源管理

  • 自动清理matplotlib图像、pandas DataFrame引用
  • 设置最大代码长度(如5000字符)

安全考量

三层防护体系:

层级 措施
输入层 代码长度限制、字符白名单(可选)
编译层 RestrictedPython AST分析,禁止__import__, exec, eval
执行层 Docker沙箱、资源限制、网络隔离

危险操作黑名单(部分):

  • os, sys, subprocess, socket, importlib
  • 文件系统访问(open, pathlib
  • 环境变量读取(os.getenv

✅ 安全原则:最小权限 + 默认拒绝


测试方案

单元测试(pytest)

def test_safe_math():
    interpreter = SafeCodeInterpreter()
    result = interpreter.execute("result = 2 + 3")
    assert result["return_value"] == 5

def test_dangerous_code_blocked():
    interpreter = SafeCodeInterpreter()
    result = interpreter.execute("import os; os.system('rm -rf /')")
    assert result["status"] == "error"

集成测试

  • 模拟Agent调用Tool,验证端到端流程
  • 注入恶意代码测试沙箱有效性

端到端测试

  • 使用Playwright或Selenium模拟用户上传文件并执行代码
  • 验证图像正确渲染

最佳实践

  1. 永远不要信任生成的代码:即使来自可信LLM,也必须沙箱化。
  2. 明确技能边界:仅支持无副作用的纯函数式代码。
  3. 超时必设:防止无限循环(建议默认10秒)。
  4. 日志脱敏:记录代码执行日志时,过滤敏感信息。
  5. 版本锁定:固定依赖库版本(如pandas==2.0.3),避免行为漂移。
  6. 监控指标:记录执行成功率、平均耗时、错误类型分布。

扩展方向

变体 说明
多语言支持 扩展至JavaScript(使用Node.js沙箱)、SQL
交互式执行 支持多轮代码执行(共享变量上下文)
MCP协议集成 将Code Interpreter封装为MCP工具,供任何兼容Agent调用
GPU加速 在沙箱中启用CUDA(用于科学计算)
代码修复 当执行失败时,让LLM自动修正代码并重试

总结

Code Interpreter技能是AI Agent从“语言模型”迈向“行动智能”的关键一步。本文详细阐述了其架构设计、安全机制、接口规范,并提供了基于RestrictedPython的完整实现。通过两个实战案例,展示了其在金融计算和数据可视化中的强大能力。安全是该技能的生命线,必须通过编译期检查、运行时隔离和资源限制三重保障。在下一篇【Day 7】中,我们将深入探讨Python Executor技能,聚焦更高级的沙箱技术(如gVisor、Firecracker)和性能优化策略。


进阶学习资源

  1. RestrictedPython官方文档
  2. LangChain Tools指南
  3. OpenAI Code Interpreter技术报告
  4. Google Codelabs: Secure Python Execution
  5. MCP Protocol Specification
  6. Pyodide: WebAssembly Python Runtime
  7. OWASP Top 10 for LLM Applications
  8. LangChain4j Code Interpreter Example

技能开发实践要点

  1. 安全第一:代码解释器必须运行在严格隔离的沙箱中。
  2. 输入校验:对生成的代码进行AST静态分析,拦截危险操作。
  3. 资源限制:设置CPU、内存、执行时间上限,防DoS攻击。
  4. 标准化输出:统一返回格式,便于Agent后续处理。
  5. 支持文件上下文:允许用户上传数据文件供代码使用。
  6. 图像自动捕获:无缝集成matplotlib/seaborn可视化。
  7. 错误友好提示:将技术错误转化为用户可理解的语言。
  8. 性能监控:记录执行指标,持续优化响应速度。

文章标签:AI Agent, Code Interpreter, LangChain, 沙箱安全, 动态代码执行, RestrictedPython, MCP协议, 技能开发

文章简述
本文是“AI Agent Skill技能开发实战”系列第6篇,深入讲解Code Interpreter技能的设计与安全实现。Code Interpreter使AI Agent能够动态生成并安全执行代码,完成计算、数据处理和可视化等任务。文章详细剖析了技能架构、安全沙箱机制、标准化接口,并基于LangChain和RestrictedPython提供了完整的可运行代码示例。通过金融复利计算和CSV数据可视化两个实战案例,展示了技能的实际应用价值。同时,文章强调了三层安全防护体系(输入校验、编译拦截、运行隔离),并提供了性能优化、错误处理和测试方案。开发者可直接复用文中的技能模板,快速构建安全可靠的代码执行能力,为Agent赋予真正的“动手”能力。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐