跳转至

第 8 章 工具调用与代码解释器

本章学习目标 - 深入理解 Tool Use / Function Calling 的演进脉络(手写 prompt → 原生 Function Calling → MCP) - 掌握 TTD 中"工具"的多种形态与混合范式(@tool / 节点即工具 / ML 注册表 / 代码解释器) - 深度理解代码解释器范式(OpenAI Code Interpreter)与沙箱隔离级别 - 理解 GraphRAG NL2Cypher、数据面执行器、可视化推荐引擎的设计 - 认清当前工具实现的工程权衡与改进方向

前置知识第 4 章 Agent 编排 关联代码backend/app/tools/ml/backend/app/tools/interpreter/backend/app/skills/graph_rag.pybackend/app/skills/sql_executor.pybackend/app/skills/visualization.py


8.1 背景与动机

8.1.1 Agent 为什么需要工具

纯 LLM 只能基于训练知识"说",不能"做"——它不能执行计算、不能查询数据库、不能生成图表。工具(Tool Use)让 Agent 从"语言模型"变为"能行动的 Agent":

能力 工具 TTD 实现
执行计算 ML 工具集 clustering/forecasting/attribution/anomaly/regression/correlation
执行代码 代码解释器 LLM 生成 Python 代码 + 沙箱执行
查询图谱 GraphRAG NL2Cypher 查 Apache AGE
执行 SQL 数据面执行器 RedshiftExecutor / pg_mooncake
生成图表 可视化推荐引擎 规则推荐 + ECharts 渲染

8.1.2 TTD 的三种"工具"范式

TTD 混合使用了三种工具范式——不同任务用不同形态是工程取舍:

范式 形态 代表 特点
LangChain @tool 装饰器 LLM 自主调用 GraphRAG、SQL Executor 标准 Function Calling,LLM 决定何时调
LangGraph 节点即工具 图节点,状态传递 大多数 Sub-agent 确定性调度,图决定何时调
ML 工具注册表 Function Calling 风格 6 个 ML 工具 工具注册 + LLM 选择

AI Agent 理论关联:Tool Use 演进

工具使用(Tool Use)是 Agent 从"对话"走向"行动"的关键能力。按时间脉络演进如下:

第一代:手写 Prompt 描述工具(2020-2023) - 方法:在 prompt 中描述工具的用法,让 LLM 输出特定格式的文本(如 CALL tool_name(args)),再用正则解析调用。 - 问题:脆弱——LLM 输出格式不稳定,正则解析易失败。代表:早期 ReAct 论文中的工具调用。

第二代:原生 Function Calling(2023-06) - 方法:OpenAI/Anthropic 在 API 层面支持 function calling——开发者用 JSON Schema 描述工具,LLM 输出结构化的工具调用(tool_calls),SDK 自动解析。 - 优势:稳定、结构化、SDK 支持。代表:OpenAI Function Calling、LangChain @tool 装饰器。 - 局限:每个 LLM provider 的 API 不同,工具定义不可跨模型复用。

此后演进出现两条并行分支——一条追求"更灵活的计算",一条追求"更开放的工具生态":

分支 A · 代码解释器 Code Interpreter(2023-03,OpenAI) - 方法:不预设具体工具,让 LLM 生成代码在沙箱执行,覆盖任意计算。LLM 写 Python 调用 pandas/matplotlib 等,沙箱返回结果。 - 优势:最灵活——一个"写代码"工具覆盖所有计算需求。代表:OpenAI Code Interpreter、ChatGPT Data Analyst。 - 劣势:安全风险(代码可做任何事)、延迟高(代码执行)、不确定性大。

分支 B · MCP(Model Context Protocol,2024-11,Anthropic) - 方法:开放协议,标准化工具/资源的暴露方式。MCP server 暴露工具,任意 MCP-compatible 的 LLM client 都能调用。 - 优势:跨模型/跨应用复用——同一个 MCP server 可被 Claude、ChatGPT、本地模型调用。代表:modelcontextprotocol.io。 - 意义:工具生态从"provider 锁定"走向"开放标准"。

时间线澄清

Code Interpreter(2023-03)实际上早于 MCP(2024-11)。二者并非线性替代,而是解决不同问题的并行演进:Code Interpreter 追求"计算灵活性",MCP 追求"工具标准化与互操作"。TTD 同时借鉴了两者思想——代码解释器用于通用计算,@tool/节点抽象用于结构化工具调度。

TTD 的混合范式反映了"不同任务用不同工具形态":确定性任务用节点(可审计),探索性任务用 @tool(灵活),自定义分析用代码解释器(通用)。但缺少 MCP 的统一抽象——改进方向见 8.4。


8.2 设计原理

8.2.1 工具全景

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#e8f0fe','primaryTextColor':'#202124','primaryBorderColor':'#1a73e8','lineColor':'#5f6368','secondaryColor':'#e0f2f1','tertiaryColor':'#f8f9fa'}}}%%
flowchart TB
    Agent["Analytical Agent / SQL Pipeline<br/>根据意图 + 数据形状分发"]

    subgraph ML_Tools["ML 工具集 · Function Calling"]
        direction TB
        ML["MLToolRegistry · 6 工具"]
        CL["clustering_tool<br/>KMeans 客户分群"]
        FC["forecasting_tool<br/>Prophet 销量预测"]
        AT["attribution_tool<br/>statsmodels 归因"]
        AN["anomaly_tool<br/>IsolationForest 异常检测"]
        RG["regression_tool<br/>statsmodels 回归"]
        CO["correlation_tool<br/>scipy 相关性"]
        ML --> CL & FC & AT & AN & RG & CO
    end

    subgraph CodeInterp["代码解释器 · 沙箱执行"]
        direction LR
        CG["code_generator<br/>LLM 生成 Python 代码"]
        SB["sandbox<br/>exec + 模块白名单<br/>60s 超时 · L1 隔离"]
        DB["dataframe_bridge<br/>SQL 结果 ↔ DataFrame"]
        CG --> SB --> DB
    end

    subgraph Decorators["@tool 装饰器 · LLM 自主调用"]
        GR["graph_rag<br/>NL2Cypher → AGE 图"]
        SE["sql_executor<br/>DataPlaneExecutor"]
    end

    subgraph NodeAsTool["节点即工具 · 确定性调度"]
        VZ["visualization<br/>规则推荐引擎"]
    end

    Agent -->|"原始数据(>200 行)"| ML
    Agent -->|"聚合数据(≤200 行)"| CG
    Agent --> GR
    Agent --> SE
    Agent --> VZ

    classDef bpProcess  fill:#e8f0fe,stroke:#1a73e8,stroke-width:2px,color:#202124
    classDef bpData     fill:#e0f2f1,stroke:#0d7377,stroke-width:2px,color:#202124
    classDef bpInfo     fill:#f3e8fd,stroke:#9334e6,stroke-width:2px,color:#202124
    classDef bpGroup    fill:#f8f9fa,stroke:#dadce0,stroke-width:1px,color:#3c4043

    class Agent bpProcess
    class ML,CL,FC,AT,AN,RG,CO bpData
    class CG,SB,DB bpInfo
    class GR,SE bpProcess
    class VZ bpProcess

8.2.2 数据形状感知分流

Analytical Agent(v5.0)根据查询结果的数据形状分流——这是"用对工具"的关键设计:

数据形状 分流 工具 理由
聚合数据(≤200 行,BI 典型输出) LLM 生成 pandas/scipy 统计分析代码 代码解释器 聚合数据量小,代码解释器够用且灵活
原始/粒度数据(>200 行) 派发到 ML 工具 ML 工具注册表 原始数据量大,专用 ML 工具更可靠高效

8.3 实现详解

8.3.1 ML 工具集

backend/app/tools/ml/base.py 定义 MLToolRegistry + 抽象 MLTool(fit_predict/explain),注册 6 个工具:

工具 用途 依赖 典型场景
clustering_tool 客户分群 scikit-learn (KMeans) "把客户分成几类"
forecasting_tool 销量预测 prophet "预测下月销量"
attribution_tool 归因分析 statsmodels "增长主要由什么驱动"
anomaly_tool 异常检测 scikit-learn (IsolationForest) "哪些交易异常"
regression_tool 回归分析 statsmodels "价格对销量的影响"
correlation_tool 相关性分析 scipy "哪些指标相关"

Analytical Agent 根据 _INTENT_TO_TOOLS 映射选择工具,或让 LLM 生成调用代码。每个工具实现 fit_predict()(执行分析)和 explain()(生成结果解释)。

8.3.2 代码解释器沙箱

backend/app/tools/interpreter/ 实现 LLM 生成代码 + 沙箱执行——这是 Code Interpreter 范式的工程实现:

组件 职责
code_generator.py LLM 生成调用 ML 工具的 Python 代码,注入 DataFrame schema + 工具签名;或确定性生成(已知工具+参数时无需 LLM)
sandbox.py 白名单模块执行(pandas/numpy/sklearn/prophet/statsmodels/scipy),禁 exec/eval/open/网络,60s 超时,50K 输出限制,全审计
dataframe_bridge.py SQL 执行结果 ↔ pandas DataFrame 转换

沙箱隔离级别理论

代码沙箱的安全隔离有几个级别,从弱到强:

级别 机制 隔离性 性能 TTD 状态
L1 Python exec + 模块白名单 低(可绕过) 当前
L2 进程隔离(subprocess + 资源限制)
L3 容器隔离(Docker,独立文件系统/网络)
L4 托管沙箱(E2B/Modal,云隔离)
L5 WASM 沙箱(浏览器级隔离)

TTD 当前用 L1(Python exec + 白名单),这是最弱的隔离——白名单绕过(如通过 __import____builtins____subclasses__)仍可能逃逸。生产级沙箱至少应到 L2(进程隔离)或 L3(容器隔离)。

代表性托管沙箱方案: - E2Be2b.dev):云原生代码沙箱,每个执行独立微 VM - Modal:无服务器代码执行,容器级隔离 - Pyodide(WASM):Python 在浏览器 WASM 沙箱执行

8.3.3 GraphRAG(NL2Cypher)

backend/app/skills/graph_rag.py@tool 装饰器,LLM 将自然语言转 Cypher,执行于 Apache AGE ttd_governance 图:

  • 图 schema:Table/Column/Metric/Term/FewShot/BusinessRule/BusinessContext 节点 + 13 种边类型
  • 返回 {nodes, links, rows, cypher} 供可视化
  • 失败自动降级到 SQL 流水线(_route_after_graph_rag → fallback_sql)——优雅降级

图 Schema

ttd_governance 图在 data-plane/sql/001_create_schema.sql 中由 create_graph('ttd_governance') 创建,节点与边由语义层发布管道从 semlayer.* 关系表 MERGE 而来:

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#e8f0fe','primaryTextColor':'#202124','primaryBorderColor':'#1a73e8','lineColor':'#5f6368','secondaryColor':'#e0f2f1','tertiaryColor':'#f8f9fa'}}}%%
flowchart LR
    T["Table 节点<br/>table_id · certification_status<br/>domain"] -->|"HAS_COLUMN"| C["Column 节点<br/>column_id · semantic_type<br/>sensitivity_level · pii_policy"]
    T -->|"HAS_METRIC"| M["Metric 节点<br/>metric_id · sql_definition<br/>certification_status"]
    T -->|"JOINABLE_TO"| T2["Table 节点<br/>(另一张表)"]
    BT["BusinessTerm 节点<br/>term_id · canonical_term<br/>mapped_asset_id"] -->|"MAPS_TO"| M
    BT -->|"MAPS_TO"| C
    M -->|"DEFINED_ON"| T
    BR["BusinessRule 节点<br/>rule_id · rule_type<br/>bound_assets"] -->|"CONSTRAINS"| T
    BR -->|"CONSTRAINS"| M

    classDef bpData     fill:#e0f2f1,stroke:#0d7377,stroke-width:2px,color:#202124
    classDef bpInfo     fill:#f3e8fd,stroke:#9334e6,stroke-width:2px,color:#202124

    class T,T2,C,M bpData
    class BT,BR bpInfo
节点类型 代表资产 关键属性
Table gold_hm.fact_transaction table_id, certification_status, domain
Column fact_transaction.price column_id, semantic_type, sensitivity_level
Metric met_hm_transaction_count metric_id, sql_definition
BusinessTerm "GMV""销量" term_id, canonical_term, mapped_asset_id
BusinessRule "退货金额为负" rule_id, rule_type, bound_assets

NL2Cypher 查询演练

以"GMV 指标关联了哪些表和列?"为例,展示完整调用链:

# backend/app/skills/graph_rag.py(@tool 装饰器)
@tool
def graph_rag(query: str) -> dict:
    """自然语言 → Cypher → 图遍历 → 返回节点/边/行"""
    # 1. LLM 将自然语言转为 Cypher
    cypher = llm.generate_cypher(query)
    # 例:MATCH (t:Term {canonical_term: 'GMV'})-[:MAPS_TO]->(m:Metric)
    #     -[:DEFINED_ON]->(tbl:Table)-[:HAS_COLUMN]->(c:Column)
    #     RETURN t, m, tbl, c

    # 2. 在 AGE 图 ttd_governance 执行 Cypher
    result = age_graph.execute(cypher)

    # 3. 返回结构化结果供可视化
    return {"nodes": [...], "links": [...], "rows": [...], "cypher": cypher}

查询返回的 nodes/links 直接驱动前端 @antv/g6 图谱可视化,rows 供表格展示,cypher 供审计与可解释性。

GraphRAG 与 NL2Cypher

GraphRAG 是用知识图谱增强检索的方法(Microsoft GraphRAG 是代表)。TTD 的 GraphRAG 专注于元数据图谱(表/列/术语/规则的关系网),而非文档知识图谱。两者区别详见 第 5 章 5.1.2 注。

NL2Cypher 是 NL2SQL 的图数据库版本——把自然语言转 Cypher 查询。挑战与 NL2SQL 类似:LLM 可能生成语法错误的 Cypher、可能查询不存在的节点/边。但 TTD 的 GraphRAG 没有像 SQL 那样的五层护栏——这是改进点(见 8.4)。

ML 工具深度剖析:Forecasting

forecasting_tool(Prophet 销量预测)为例,展示一个 ML 工具的完整生命周期:

# backend/app/tools/ml/forecasting.py(简化)
class ForecastingTool(MLTool):
    name = "forecasting_tool"
    description = "基于 Prophet 的时间序列预测,用于销量/指标趋势预测"

    args_schema = {
        "df": "pandas.DataFrame",  # 含 ds(日期) + y(值) 两列
        "periods": "int",          # 预测未来多少天
        "frequency": "str",        # 'D'/'W'/'M'
    }

    def fit_predict(self, df, periods=30, frequency='D') -> dict:
        """执行 Prophet 预测,返回预测值 + 置信区间。"""
        model = Prophet()
        model.fit(df)
        future = model.make_future_dataframe(periods=periods, freq=frequency)
        forecast = model.predict(future)
        return {
            "forecast": forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']],
            "components": model.plot_components(forecast),
        }

    def explain(self, result) -> str:
        """生成结果解释(供 LLM 洞察使用)。"""
        return f"Prophet 预测未来 {periods} 天,趋势{'上升' if trend_up else '下降'}..."

调用链:Analytical Agent 检测意图为"预测" → _INTENT_TO_TOOLS 映射到 forecasting_toolcode_generator 注入 DataFrame schema + 工具签名 → LLM 生成调用代码 → sandbox 执行 → dataframe_bridge 转换结果 → explain() 生成解释 → 可视化推荐引擎推荐折线图。

8.3.4 数据面执行器

backend/app/skills/sql_executor.py 抽象 DataPlaneExecutor,两后端:

后端 用途 特点
RedshiftExecutor 生产 redshift-connector,支持 EXPLAIN 估算
pg_mooncake 开发 psycopg 标准协议

抽象接口:

class DataPlaneExecutor:
    async def execute(sql, max_rows, timeout_ms) -> {columns, rows, row_count, has_more, execution_time_ms}
    async def explain(sql) -> {plan, estimated_rows, estimated_cost}

同步驱动用 asyncio.to_thread() 包装避免阻塞事件循环——这是 Python async 生态处理同步库的标准模式。

8.3.5 可视化推荐引擎

backend/app/skills/visualization.py + skills/chart_templates/ 实现 v6.0 推荐引擎——这是"规则优先、LLM 兜底"的典型设计:

主路径(95%+ 无需 LLM)

recommend_visualization() → ChartMatcher.recommend(top_k) → TemplateRenderer → EChartsAdapter
组件 职责
DataShapeAnalyzer 分析数据形状(时间序列/分类/分布/排名...)
ChartMatcher 规则匹配 top-k 候选图表
adapters/ EChartsAdapter 渲染
模板库 bar/line/pie/kpi/ranking/trend/combo/specialized,35+ 模板

LLM 回退(边缘场景):推荐得分 < 0.3 或渲染失败时用 LLM 生成 ECharts 配置,后验证 series 类型白名单。

产出 chart_type + echarts_option + template_ref + confidence + checkpoint_status(auto_pass/needs_review/blocked)。低置信度触发前端 HITL 候选确认——这是 human-in-the-loop 在可视化的体现。

可视化推荐:规则 vs LLM

可视化推荐有两条路线:

  • 规则推荐:根据数据形状(列类型、行数、时间维度存在性)用规则匹配图表类型。快、可控、无需 LLM。但覆盖面受规则数量限制。
  • LLM 推荐:让 LLM 看数据决定图表。灵活、覆盖广。但慢、贵、不稳定。

TTD 用混合路线——规则为主(95%+),LLM 回退(边缘)。这是工程上的最优取舍:大部分 BI 可视化需求是标准化的(趋势用折线、占比用饼图、排名用条形),规则足够;少数复杂场景用 LLM 兜底。


8.4 方案选型对比

8.4.1 工具范式对比

范式 控制力 灵活性 适用 TTD 用法
@tool 装饰器 低(LLM 自主调) 探索性任务 GraphRAG、SQL Executor
节点即工具 高(确定性调度) 标准化流程 大多数 Sub-agent
MCP 跨应用工具生态 未用
代码解释器 最高 任意计算 Analytical Agent

8.4.2 沙箱方案对比

方案 隔离级别 性能 运维 TTD 状态
Python exec + 白名单 L1 低 当前
进程隔离(subprocess) L2 中
容器隔离(Docker) L3 高
E2B / Modal(托管沙箱) L4 高
WASM 沙箱 L5 高

8.4.3 可视化推荐路线对比

路线 优势 劣势 TTD 状态
规则推荐 快、可控、无需 LLM 覆盖面有限 主路径(95%+)
LLM 生成 灵活、覆盖广 慢、贵、不稳定 回退(边缘)
混合(TTD) 兼顾速度与覆盖 实现复杂

性能与质量指标(设计目标)

以下为工具系统的设计目标值,TTD 尚未进行系统化实测

指标 设计目标 说明
ML 工具执行延迟 1-5s 视数据量与算法复杂度
代码解释器沙箱延迟 2-10s 含 LLM 生成代码 + 沙箱执行
GraphRAG NL2Cypher 延迟 1-3s LLM 生成 Cypher + 图遍历

| 可视化推荐延迟(规则路径) | < 50ms | 纯规则匹配,无 LLM | | 可视化推荐延迟(LLM 回退) | 1-3s | 仅边缘场景触发 | | 沙箱逃逸风险 | L1(当前) | 生产应升至 L2+ |

评估基础设施见 [第 15 章](15-quality-evaluation.md),改进路线见 [第 16 章](16-roadmap-and-improvements.md)。

当前实现可改进之处

  1. 工具与图节点混用范式不统一:@tool、节点即工具、ML 注册表三种范式并存,缺乏统一的工具抽象层。开发者需理解三种调用方式。改进:统一工具接口(参考 MCP),让所有工具可同时作为 @tool 和节点暴露,按场景选择调用方式。
  2. 沙箱安全模型简陋:Python exec + 模块白名单是 L1 隔离(最弱),白名单绕过可能逃逸。生产环境有安全风险。改进:迁移到进程/容器隔离或托管沙箱(E2B/Modal),至少用 subprocess + 资源限制(L2)。
  3. 无工具失败重试策略:工具调用失败(如 ML 工具异常)时无自动重试或降级,直接报错。改进:为关键工具增加重试 + 降级策略(如 forecasting 失败降级为简单移动平均)。
  4. GraphRAG NL2Cypher 无校验:LLM 生成的 Cypher 直接执行,无语法校验或安全护栏(不同于 SQL 有五层护栏)。改进:为 Cypher 增加类似 SQL 的护栏(语法校验 + 只读校验 + 路径深度限制)。

改进方向

工具系统的演进方向是统一抽象 + 强隔离 + 智能容错。短期:统一工具接口、加固沙箱、增加工具重试降级。长期:引入 MCP 协议让工具可跨 Agent 复用,建立工具调用的可观测与评估闭环。代码解释器应支持多语言(Python/SQL/JavaScript)并迁移到强隔离沙箱。


8.5 MCP Server 暴露方案设计

当前 TTD 的三种工具范式(@tool / 节点即工具 / ML 注册表)无统一抽象。本节设计一个基于 MCP 协议的统一暴露方案,让 TTD 的工具可被任意 MCP-compatible client(Claude Desktop、ChatGPT、本地模型)复用。

8.5.1 哪些工具对外暴露

工具 MCP 暴露 权限要求 说明
graph_rag read-only 图谱查询,无副作用
forecasting_tool compute 含 ML 计算,需资源限制
clustering_tool compute 同上
anomaly_detection_tool compute 同上
sql_executor 仅内部调用,不对外暴露(安全风险)
code_generator 仅内部调用

8.5.2 MCP Server 架构

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#e8f0fe','primaryTextColor':'#202124','primaryBorderColor':'#1a73e8','lineColor':'#5f6368','secondaryColor':'#e0f2f1','tertiaryColor':'#f8f9fa'}}}%%
flowchart LR
    subgraph TTD["TTD Backend"]
        Tools["工具注册表<br/>forecasting · clustering<br/>graph_rag..."]
        Auth["权限校验<br/>JWT + scope"]
    end
    subgraph MCP["MCP Server(新增)"]
        Schema["tool schema 暴露<br/>JSON Schema → MCP<br/>list_tools()"]
        Exec["工具执行代理<br/>call_tool()<br/>调用 TTD 内部工具"]
    end
    Client["MCP Client<br/>Claude · ChatGPT<br/>本地模型"] -->|"MCP 协议<br/>list_tools / call_tool"| MCP
    Schema -->|"返回 tool 列表"| Client
    Client -->|"调用工具 + JWT"| Exec
    Exec -->|"鉴权 · JWT + scope"| Auth
    Auth -->|"执行"| Tools
    Tools -->|"结果"| Exec
    Exec -->|"返回"| Client

    classDef bpExternal fill:#f8f9fa,stroke:#dadce0,stroke-width:2px,color:#5f6368
    classDef bpProcess  fill:#e8f0fe,stroke:#1a73e8,stroke-width:2px,color:#202124
    classDef bpData     fill:#e0f2f1,stroke:#0d7377,stroke-width:2px,color:#202124
    classDef bpInfo     fill:#f3e8fd,stroke:#9334e6,stroke-width:2px,color:#202124

    class Client bpExternal
    class Schema,Exec bpProcess
    class Auth bpInfo
    class Tools bpData

8.5.3 Tool Schema 管理

每个工具的 JSON Schema 自动从 Python 类型注解生成(复用现有 args_schema),转为 MCP tool definition:

# mcp_server/server.py(新增)
from mcp import Server
from app.tools.ml.forecasting import ForecastingTool

server = Server("ttd-tools")

@server.list_tools()
async def list_tools() -> list[Tool]:
    return [
        Tool(
            name="forecasting",
            description=ForecastingTool.description,
            inputSchema=ForecastingTool.args_schema_to_json(),  # 复用现有
        ),
        # ... 其他工具
    ]

@server.call_tool()
async def call_tool(name: str, arguments: dict) -> str:
    # 鉴权:校验 MCP client 携带的 JWT + scope
    user = verify_token(arguments.pop("_token"))
    if name == "forecasting":
        result = await ForecastingTool().fit_predict(**arguments)
        return ForecastingTool().explain(result)

8.5.4 权限模型

Scope 允许的工具 说明
ttd:read graph_rag 只读查询
ttd:compute forecasting, clustering, anomaly_detection ML 计算(需资源限制)
ttd:admin 全部 管理员(内部使用)

8.6 L2 沙箱实现方案

当前代码解释器为 L1(exec + whitelist),有逃逸风险。本节给出 L2(subprocess + 资源限制)的具体实现方案。

8.6.1 L1 vs L2 对比

维度 L1(当前) L2(目标)
隔离方式 exec() 同进程 + 模块白名单 subprocess 独立进程
资源限制 CPU/内存/时间/磁盘 限制
文件系统 共享进程 FS 临时目录 + 只读挂载
网络 共享进程网络 可禁用
逃逸风险 高(白名单可绕过) 中(进程隔离)
延迟 ~0ms(无启动开销) ~100ms(进程启动)

8.6.2 L2 实现方案

# backend/app/tools/sandbox_l2.py(新增)
import subprocess
import tempfile
import resource
import json

class L2Sandbox:
    """进程级隔离沙箱:subprocess + 资源限制 + 临时目录。"""

    def __init__(self, *, cpu_seconds=10, memory_mb=512, timeout=30):
        self.cpu_seconds = cpu_seconds
        self.memory_mb = memory_mb
        self.timeout = timeout

    def execute(self, code: str, input_data: dict) -> dict:
        with tempfile.TemporaryDirectory() as tmpdir:
            # 写入代码与输入数据
            code_file = f"{tmpdir}/main.py"
            data_file = f"{tmpdir}/input.json"
            with open(code_file, "w") as f:
                f.write(self._wrap_code(code))
            with open(data_file, "w") as f:
                json.dump(input_data, f)

            # subprocess 执行,设置资源限制
            result = subprocess.run(
                ["python", code_file, data_file],
                capture_output=True,
                timeout=self.timeout,
                cwd=tmpdir,
                env={"PYTHONPATH": tmpdir},  # 最小环境
                preexec_fn=self._set_limits,  # rlimit 设置
            )
            return json.loads(result.stdout) if result.returncode == 0 \
                else {"error": result.stderr.decode()}

    def _set_limits(self):
        """子进程资源限制(preexec_fn 回调)。"""
        resource.setrlimit(resource.RLIMIT_CPU, (self.cpu_seconds, self.cpu_seconds))
        resource.setrlimit(resource.RLIMIT_AS, (self.memory_mb * 1024 * 1024,) * 2)
        resource.setrlimit(resource.RLIMIT_FSIZE, (100 * 1024 * 1024,) * 2)  # 100MB 文件

8.6.3 迁移路径

阶段 目标 工作量
Phase 1 L2 可选(env 切换 L1/L2) ~200 行
Phase 2 L2 默认,L1 仅 debug 测试覆盖
Phase 3 L3(容器隔离,Docker-in-Docker) ~500 行 + CI

8.5 小结与延伸阅读

本章要点

  1. TTD 混用三种工具范式:@tool(GraphRAG/SQL Executor)、节点即工具(多数 Sub-agent)、ML 注册表(6 个 ML 工具)。
  2. Analytical Agent 数据形状感知分流:聚合数据走代码解释器,原始数据走 ML 工具。
  3. 代码沙箱用 Python exec + 白名单(L1 隔离),安全模型需升级到 L2+。
  4. 可视化推荐引擎 v6.0:规则推荐为主(95%+),LLM 回退为辅,产出含置信度与 HITL checkpoint。
  5. Tool Use 演进:手写 prompt → Function Calling(2023)→ 并行分支 Code Interpreter(2023-03)与 MCP(2024-11)。

延伸阅读

下一章第 9 章 Semantic Plane:语义治理——进入第四部子系统设计,详解三层治理模型与 Git+CI/CD 发布管道。