第35章 Text-to-Pandas / Text-to-Python¶
第34章解决结构化取数。用户继续问“品类结构有没有关系”“价格还是销量导致”“排除新品以后还成立吗”,问题就进入二次分析。SQL 可以表达其中一部分,但复杂中间计算、临时列、统计检验和图表前处理往往更适合 Python。
这并不意味着给模型一个自由 Notebook。生产 DataAgent 中,Python 只能读取上游已授权、已裁剪的数据引用,在受限沙箱中执行,并输出可追溯 Artifact。需要重新取数时必须回到 sql_executor。
Text-to-Python 不是给模型一个 Notebook,而是把灵活分析压进一个只能读取受控数据引用、受资源和依赖限制、输出可追溯 Artifact 的沙箱工具。
35.1 SQL 与 Python:权威取数和二次分析分工¶
SQL 负责 Metric 聚合、Join、过滤和数据权限,Python 负责在已经授权的结果集上继续分析。
表35-1:SQL 与 Python 的边界。来源:本书整理。
| 任务 | 首选路径 | 原因 |
|---|---|---|
| 单指标聚合、Top-N | SQL | 口径明确、执行成本可控 |
| 多维过滤与排序 | SQL | 数据库擅长 |
| 品类贡献度 | SQL 取数 + Python | 中间计算和解释更清晰 |
| 量价分解、分布、统计检验 | Python | 多步公式/临时列较多 |
| 临时小文件探查 | Python 沙箱 | 数据未入仓,但必须限大小/权限 |
| 图表前处理 | Python + chart renderer | 生成结构化图表数据 |
“华东下滑”案例中,SQL 先返回 sku_id、category、两期 GMV 和差额;Python 再计算各品类对下滑的贡献比例。
自然语言入口不能改变物理计算边界。若任务要求数千万行明细进入 pandas,应改成数据库预聚合、异步离线任务或专用分析服务,而不是给沙箱加更多内存。
Python 负责分析已经授权的数据,不负责重新发现它还能去哪里找更多数据。
35.2 SQL → Python 的证据合同¶
Python 读取的不是聊天历史,而是上游 Tool Result:
{
"dataframe_ref": "artifact://run-1/sql-result-3.parquet",
"content_hash": "sha256:...",
"row_count": 1542,
"columns": ["sku_id", "category", "gmv_last_week", "gmv_prior_week", "gmv_delta"],
"metric_context": [
{"metric_id": "gmv_ops", "version": "2025Q1"}
]
}
其中:
dataframe_ref:明确读哪份数据;content_hash:防止输入在修复/恢复时被替换;metric_context:说明这些数字按什么口径产生。
这三项是 SQL 与 Python 之间最小证据合同。自修复可以改变代码,不能悄悄换输入;如果分析需要新增列,Planner 回到 SQL 重新生成新的 Artifact,再创建新的分析步骤。
图35-2:分析 Tool 链时序。来源:本书自绘。Alt text:时序图展示 Planner 先用 SQL 取数、再调 Python Tool 做统计建模、最后生成图表产物,箭头表示 SQL 与 Python 工具在一次分析中接力协作。
分析链路可信的前提,是每个后续产物都能明确指出“我基于哪一份输入、哪一版 Metric、哪一段代码得到”。
35.3 模型生成代码默认不可信:安全边界必须由沙箱实现¶
Prompt 里写“不要访问网络”“不要读取文件”不是安全机制。模型生成代码应像处理外部脚本一样默认不可信。
基础策略可以是:
allowed_imports: [pandas, numpy, scipy, sklearn, matplotlib]
max_memory_mb: 512
max_cpu_seconds: 30
max_python_retries: 2
network: false
生产沙箱至少应满足:
- 无外网和内网横向访问;
- 不暴露数据库连接、Secret 和宿主环境变量;
- 每个 Run 独立临时目录,输入只读;
- CPU、内存、运行时长、磁盘和输出大小有硬限制;
- 依赖白名单、固定版本,禁止运行时
pip install; - 禁止
subprocess、socket、任意文件路径、数据库连接; - PII 在进入沙箱前由上游脱敏/裁剪;
- 运行完成按 TTL 清理临时文件。
Docker/容器是较现实的通用方案;WASM/Pyodide 更轻,但科学计算生态有限;远程 Jupyter Kernel 适合开发,不应默认作为多租户生产执行面。
静态 AST 扫描是第一道门,容器/cgroup 等运行时隔离是第二道门。两者不能互相替代。
代码是否由企业 Prompt 生成,与代码是否值得信任是两件事。真正的安全来自运行环境,即使生成了危险代码,它也只能失败在边界内。
35.4 代码生成、静态审计、执行与自修复¶
一次 Python Tool 调用可以拆成:
- Planner 提供分析目标、列摘要和
dataframe_ref; - Gateway 生成代码;
- Tool 做 AST/依赖静态审计;
- 沙箱加载受控数据并执行;
- 回收结构化输出、Artifact、资源统计和错误;
- Runtime 把 Observation 返回 Planner。
图35-1:Python Tool 沙箱执行流程。来源:本书自绘。Alt text:流程从生成代码、静态审计、注入只读数据、在受限沙箱执行、回收产物与日志,到超时或越权即终止。
生成 Prompt 不需要完整 DataFrame,只需要 schema、类型、统计摘要和极少量脱敏样例。完整数据在沙箱里通过引用读取。
允许自修复的错误包括:列名、类型转换、轻微语法和库 API 使用错误。比如 KeyError: gmv_change,Observation 可以提供合法列名列表,允许一次修正。
不允许让模型通过重试探索的错误包括:网络访问、非白名单 import、读宿主文件、读取 Secret、进程调用、越权数据和资源策略拒绝。
{
"code": "PYTHON_COLUMN_NOT_FOUND",
"retryable": true,
"available_columns": ["gmv_delta", "category"],
"attempt": 1,
"max_attempts": 2
}
自修复后仍使用同一 dataframe_ref 与 hash。如果修复需要扩大数据范围,就已经不是代码修复,而是新的数据任务。
35.5 输出必须结构化:不要让报告层从 stdout 里“抠数字”¶
Python 产物可以包含统计 JSON、DataFrame、图表数据、PNG 或 CSV 摘要,但关键分析结果应有结构化契约。
例如品类贡献度:
import json
import pandas as pd
df = pd.read_parquet(inputs["dataframe_ref"])
by_cat = (
df.groupby("category", as_index=False)["gmv_delta"]
.sum()
.assign(share_of_decline=lambda x: x["gmv_delta"] / x["gmv_delta"].sum())
.sort_values("gmv_delta")
)
result = {
"metric": "gmv_ops@2025Q1",
"categories": by_cat.to_dict(orient="records"),
"top3_share": float(
by_cat.nsmallest(3, "gmv_delta")["gmv_delta"].sum()
/ by_cat["gmv_delta"].sum()
)
}
print(json.dumps(result, ensure_ascii=False))
真实 Tool Result 更适合直接返回:
{
"artifact_ref": "artifact://run-1/category-contrib.json",
"input_hash": "sha256:...",
"code_hash": "sha256:...",
"environment": "py-analysis-v3",
"metrics": {"top3_share": 0.58},
"warnings": [],
"resource_usage": {"cpu_s": 1.8, "peak_mem_mb": 126}
}
第36章报告层引用 top3_share=0.58,不能让模型凭代码意图重新计算一个比例。
Python 负责计算事实,LLM 负责解释事实;如果报告中的数字不在结构化 Python/SQL 产物里,就应视为无证据数字。
35.6 数值校验:代码运行成功不等于分析正确¶
Text-to-Python 的错误很多不是语法错误,而是重复行、分母错、缺失值处理、类型转换、时区、采样和排序截断。
执行后至少可以做一组确定性检查:
- 关键字段/类型符合契约;
- 汇总值与上游 SQL 总量在容差内一致;
- 占比和概率在合法范围;
- 分组行数/时间窗口符合预期;
- 图表数据与分析 Artifact 同源;
- 截断/采样被明确标记;
- 含随机过程时固定 seed 和参数。
高风险分析应保留关键中间结果,而不是只保存最终一句“下降主要来自某品类”。
统计模型、聚类和异常检测还要记录方法、参数、环境和限制。一次沙箱分析可以用于探索,但长期预测/评分模型应进入正式模型治理,而不是一直由临时代码承担。
35.7 高频分析应从生成代码沉淀成版本化 Tool/Playbook¶
Text-to-Python 适合长尾和探索任务,不代表每次都应该重新生成代码。
若“量价分解”“贡献度 waterfall”“留存 cohort”“异常检测”每周重复出现,更合适的演进是:
模型只负责选择模板和参数,稳定算法由平台实现。这样可解释性、性能和测试都会提高。
反过来,高频 Tool 也应允许退役。上游 Metric 变化、业务规则失效、失败率上升或长期无人使用,都需要重新验证或下线。
一个成熟的 DataAgent 不会让生成代码越来越多;它会把高价值、反复出现的临时代码逐渐沉淀成可治理能力。
35.8 Artifact 生命周期、权限和回放¶
Python Artifact 的可信范围只覆盖当次输入、Metric、代码和环境。它不是长期事实表。
关键 provenance 至少包含:
run_id
input dataframe_ref + hash
metric_context
code_hash
python/environment version
library versions
random seed (if any)
resource limit + actual usage
outputs / artifact hashes
created_at
报告发布后,如果上游数据回补或代码发现错误,应能定位所有引用旧 Artifact 的报告并生成新版本;旧产物可以冻结/撤回,但审计证据不应静默覆盖。
查看历史 Artifact 仍要按当前用户权限重新授权。历史执行者当时有权读取明细,不代表所有复盘人员今天都能打开底层 Parquet。
中间文件也需要 TTL。交互式临时结果可以短期清理,正式报告引用的产物跟随报告生命周期,高风险审计只保留必要证据。对象存储不是“所有 notebook 文件永久保存”的垃圾场。
35.9 python_sandbox 与生产验收¶
推荐作为 Registry Tool:
mini-platform/tools/python_sandbox/
├── handler.py
├── runner/docker_runner.py
├── static_scan.py
└── policy.yaml
Planner 传入代码和数据引用,Tool 返回结构化结果、Artifact、资源统计和错误。业务前端只显示“正在做品类贡献度分析”,技术细节放在 Evidence/Trace 中。
上线前至少测试:
- 正确贡献度/分组/统计结果;
- 缺列、空数据、单位变化;
- SQL 总量与 Python 汇总不一致;
- 非白名单 import、网络、文件、进程访问;
- CPU/内存/输出超限;
- 修复时输入 hash 不变化;
- 多输入 Artifact 的来源/权限;
- 失败产物不会进入报告;
- 用户权限变化后历史 Artifact 不能越权查看。
运营上观察重试率、超时率、内存失败率、Artifact 体积和“SQL/Python 汇总不一致率”。某类任务持续失败时,优先考虑预聚合或固化 Tool,而不是不断给沙箱更多自由度。
本章小结¶
SQL 是 DataAgent 的权威取数层,Python 是已授权结果集上的受控分析层。dataframe_ref、content_hash 和 metric_context 把两者连成证据链;模型生成代码经过静态审计和隔离环境执行,不能访问网络、数据库和宿主 Secret。
代码运行成功仍需数值校验;输出应结构化为可追溯 Artifact,报告只引用这些事实。高频稳定分析则应从临时代码升级为版本化 Tool/Playbook。
Text-to-Python 的成熟度不看模型能写多复杂的 pandas,而看代码即使出错或被恶意诱导,也不会越过数据边界,并且每个最终数字都能复现到确定输入、代码和环境。
参考文献¶
Tang, Z., et al. (2025). LLM/Agent-as-Data-Analyst: A survey.
OpenAI. (2023). Introducing ChatGPT Code Interpreter.
PandasAI. (2024). PandasAI documentation.
WebAssembly Community. (2024). WebAssembly System Interface (WASI).
Jupyter Development Team. (2024). Jupyter Kernel Gateway.
Li, J., et al. (2023). Chain-of-code: Reasoning with language model-generated programs.

