第 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.py、backend/app/skills/sql_executor.py、backend/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(容器隔离)。
代表性托管沙箱方案: - E2B(e2b.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_tool → code_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):
| 组件 | 职责 |
|---|---|
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)。
当前实现可改进之处
- 工具与图节点混用范式不统一:@tool、节点即工具、ML 注册表三种范式并存,缺乏统一的工具抽象层。开发者需理解三种调用方式。改进:统一工具接口(参考 MCP),让所有工具可同时作为 @tool 和节点暴露,按场景选择调用方式。
- 沙箱安全模型简陋:Python exec + 模块白名单是 L1 隔离(最弱),白名单绕过可能逃逸。生产环境有安全风险。改进:迁移到进程/容器隔离或托管沙箱(E2B/Modal),至少用 subprocess + 资源限制(L2)。
- 无工具失败重试策略:工具调用失败(如 ML 工具异常)时无自动重试或降级,直接报错。改进:为关键工具增加重试 + 降级策略(如 forecasting 失败降级为简单移动平均)。
- 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 小结与延伸阅读¶
本章要点:
- TTD 混用三种工具范式:@tool(GraphRAG/SQL Executor)、节点即工具(多数 Sub-agent)、ML 注册表(6 个 ML 工具)。
- Analytical Agent 数据形状感知分流:聚合数据走代码解释器,原始数据走 ML 工具。
- 代码沙箱用 Python exec + 白名单(L1 隔离),安全模型需升级到 L2+。
- 可视化推荐引擎 v6.0:规则推荐为主(95%+),LLM 回退为辅,产出含置信度与 HITL checkpoint。
- Tool Use 演进:手写 prompt → Function Calling(2023)→ 并行分支 Code Interpreter(2023-03)与 MCP(2024-11)。
延伸阅读:
- OpenAI Code Interpreter — openai.com/index/code-interpreter-exploring-chatgpts-new-code-execution-environment(注:ChatGPT Plugins 已于 2024-04 下线,Code Interpreter 现为 Advanced Data Analysis)
- Model Context Protocol (MCP) — modelcontextprotocol.io
- E2B 沙箱 — e2b.dev
- Toolformer: Language Models Can Teach Themselves to Use Tools — arxiv.org/abs/2302.04761
下一章:第 9 章 Semantic Plane:语义治理——进入第四部子系统设计,详解三层治理模型与 Git+CI/CD 发布管道。