PandasAI — 这是什么 / 全景 图 / 主线 / 阅读地图
上游状态(2026-08-18): 本仓库自 2025-10-28 起停止更新(已静默 293 天),sinaptik-ai 的精力转向其商业平台。本拆解按停更前的
bbbb771d写成,作为「让 LLM 生成受限代码来问数」这一范式的教学样本保留发布;选型请看仍在维护的db-gpt/wren-ai/queryweaver。
30 秒导读: PandasAI 让你用大白话向一张表(或一个数据库)提问——"哪个国家收入最高?"、"画一张按月的销量图"。它把你的问题交给大模型,大模型生成一段 Python 代码,这段代码里所有取数都必须走一个叫
execute_sql_query的函数;PandasAI 校验、清洗这段代码后执行(默认在本地 DuckDB 上跑 SQL),最后把结果包装成数字 / 字符串 / 表格 / 图四种响应之一还给你。
本章只做路由与直觉:让你先知道"这是什么、大概怎么转、精华在哪、接下来该读哪一章",不深入任何单个机制的实现。
1. 这是什么(零基础也能懂)
一句话定义: PandasAI 是一个把"自然语言问数据"翻译成"生成并执行代码"的 Python 库——你问,它让 LLM 写代码,它执行代码给你答案。
解决什么问题 / 给谁用:
假设你手里有一个 CSV 或一张数据库表,你想知道"销量最高的三个地区是哪些",但你不想手写 df.groupby(...).sum().sort_values(...), 也不想拼 SQL。PandasAI 让你直接用中文/英文问,它替你把代码写出来并跑掉。既服务不会写代码的业务人,也帮会写代码的人省事。
它能做什么:
- 对一个或多个 dataframe 用自然语言提问、追问(多轮对话)。
- 从 CSV / Excel 读数据,或连接远程 SQL 数据源(Postgres、MySQL 等)。
- 把数据集保存为带 schema 的数据集(语义层),甚至跨表建"视图"。
- 返回四类结果:一个数字、一段文字、一张表、或一张图。
用起来什么样:
最小用法就是"读数据 + .chat()"。下面这段演示两条最常见入口。
# 示意,取自 README 与 pandasai/__init__.py 的公开 API
import pandasai as pai
from pandasai_litellm.litellm import LiteLLM
pai.config.set({"llm": LiteLLM(model="gpt-4.1-mini", api_key="...")}) # 配一个 LLM
df = pai.read_csv("data/companies.csv") # ① 读成 PandasAI 的 DataFrame
df.chat("Which are the top 5 companies by revenue?") # ② 直接用大白话问
# 或者一步到位,一次问一个或多个 df:
pai.chat("Which country has the highest revenue?", df)
pai.read_csv/pai.read_excel把普通数据读成 PandasAI 的DataFrame(pandasai/__init__.py:294read_csv)。DataFrame.chat(...)是最常用的入口,内部懒创建一个Agent来处理这次提问(pandasai/dataframe/base.py:100chat)。pai.chat(query, *dataframes)是模块级快捷方式,同样是新建Agent再.chat()(pandasai/__init__.py:219chat)。pai.create(...)把一个 df 连同列 schema、转换、分组等存成数据集(语义层),pai.load(...)再把它读回来(pandasai/__init__.py:46create、:259load)。
一句话直觉/类比: 把 PandasAI 当成一个**"会写 SQL 的实习生"**——你说人话,它写查询代码,但你给它立了一条铁规矩:取数只能用 execute_sql_query,不许自己直接乱翻 pandas。这条规矩是全库安全性与可控性的地基。
本节不出现底层代码细节。记住三个动词就够了:问(chat)→ 生成代码 → 执行取数。
2. 顶层全景(它大概怎么转)
2.1 主线一张图
下面这张图从用户问题一路走到响应。从上往下读即可,虚线是"出错时的回环"。
用户问题 "top 5 by revenue?"
│
▼
┌───────────────────────────────┐
│ Agent 主循环 (agent/base.py) │ ← 三大支柱之一:主循环
└───────────────────────────────┘
│
① 组 prompt(把表结构 + 允许的 SQL 函数 + 输出格式喂给 LLM)
│
▼
┌───────────────────────────────┐
│ LLM 生成 Python 代码 │
│ (代码里取数只允许调 │
│ execute_sql_query(...) ) │ ← 支柱三:代码即 SQL
└───────────────────────────────┘
│
② 校验:AST 扫一遍,没调 execute_sql_query 就打回 ┄┄┄┄┄┐
│ ┆
③ 清洗:表名白名单、剥掉危险重定义、改图路径等 ┆(重试:
│ ┆ 拿报错重新
▼ ┆ 生成代码)
┌───────────────────────────────┐ ┆
│ 执行代码 │ ┆
│ execute_sql_query → SQL 跑在: │ ┆
│ · 本地 DuckDB(CSV/parquet) │ ┆
│ · 或远程连接器(Postgres…) │ ┆
└───────────────────────────────┘ ┄┄执行报错┄┄┄┄┄┄┄┄┄┄┄┄┘
│
④ 解析 result = {"type":.., "value":..}
│
▼
┌───────────────────────────────┐
│ 4 种响应:number / string / │
│ dataframe / plot │
└───────────────────────────────┘
怎么读这张图: 主干是"问题 → 生成 → 校验/清洗 → 执行 → 解析"这条直线;右侧虚线是容错回环——校验或执行出错时,把错误塞回 prompt 让 LLM 重写,最多重试 max_retries 次(pandasai/agent/base.py:171 generate_code_with_retries、:197 execute_with_retries)。
2.2 三大支柱
PandasAI 的复杂度集中在三处,记住这三根柱子,后面每一章都能对号入座:
- Agent 主循环 —— 把"生成代码 → 校验清洗 → 执行 → 重试 → 解析响应"串起来的中央流程(见 01-agent-loop.md)。
- 语义层(Semantic Layer) —— 用 schema 描述数据集/视图/转换,再用
sqlglot编译成 SQL(见 04-semantic-layer.md)。 - 代码即 SQL 的安全执行 —— 强制 LLM 只能通过
execute_sql_query取数,加上 AST 校验、代码清洗、可选沙箱(见 02-code-as-sql.md)。
2.3 部件一句话职责
| 部件 | 干什么 | 文件 |
|---|---|---|
| 公开 API | chat/read_csv/read_excel/create/load/follow_up 等入口 | pandasai/__init__.py |
DataFrame | 继承 pandas.DataFrame,挂上 schema,提供 .chat() 入口 | pandasai/dataframe/base.py:31 |
Agent | 主循环:生成→执行→重试→解析;并提供 execute_sql_query | pandasai/agent/base.py:34 |
CodeGenerator | 调 LLM 生成代码,再做校验 + 清洗 | pandasai/core/code_generation/base.py:10 |
CodeRequirementValidator | 用 AST 检查代码是否真的调了 execute_sql_query | pandasai/core/code_generation/code_validation.py:7 |
| prompt 构造 | 把表结构、允许的 SQL 函数、输出格式拼成给 LLM 的提示 | pandasai/core/prompts/(get_chat_prompt_for_sql) |
execute_sql_query | 代码里唯一取数入口;分发到 DuckDB 或远程连接器 | pandasai/agent/base.py:137 _execute_sql_query |
DuckDBConnectionManager | 本地把 df 注册进 DuckDB 并跑 SQL | pandasai/data_loader/duck_db_connection_manager.py |
| 语义层 schema | 描述数据集/视图/列/转换 | pandasai/data_loader/semantic_layer_schema.py |
| query builders | 把 schema 编译成 SQL(用 sqlglot) | pandasai/query_builders/base_query_builder.py:12 |
ResponseParser | 把 {type,value} 变成 4 种响应对象 | pandasai/core/response/parser.py:15 |
2.4 主线走一遍(高层,不进代码)
以 df.chat("Which country has the highest revenue?") 为例:
DataFrame.chat懒创建一个Agent,把这张 df 交给它(pandasai/dataframe/base.py:116)。Agent.chat清空上一轮记忆,进入_process_query(pandasai/agent/base.py:92、:271)。generate_code用get_chat_prompt_for_sql组 prompt,把表结构 + 可用 SQL 函数 + 期望输出格式喂给 LLM,拿回一段 Python 代码(:111)。- 代码经 AST 校验(必须调
execute_sql_query)+ 清洗,执行时把execute_sql_query注入运行环境(:123execute_code、:127-130)。 - 代码里的
execute_sql_query(...)落到_execute_sql_query:本地数据就注册进 DuckDB 跑 SQL,远程数据集就交给它自己的执行器(:137)。 - 代码最后产出
result = {"type":.., "value":..},ResponseParser按type包成 number/string/dataframe/plot(pandasai/core/response/parser.py:20)。
3. 巧妙之处(速览)
这是读完这套文档你该带走的"精华"。每条只点直觉,细节留给对应章节。
-
强制 LLM 走 SQL,而非直接操作 pandas。 生成的代码里,取数只准调
execute_sql_query;库会用 AST 遍历函数调用,发现没调它就直接抛ExecuteSQLQueryNotUsed把代码打回重写(pandasai/core/code_generation/code_validation.py:41validate,:62检查)。这把"LLM 能干什么"收窄成"写 SQL",极大压缩了出错面和攻击面。→ 细看 02-code-as-sql.md。 -
schema → SQL 用
sqlglot编译。 语义层不是自己拼字符串,而是用sqlglot(SQL 解析/生成库)构造并parse_one校验查询,还能按数据源方言(DuckDB / Postgres…)输出正确 SQL(pandasai/query_builders/base_query_builder.py:3导入,:19parse_one校验)。→ 细看 04-semantic-layer.md。 -
多层安全 + 容错。 三道闸:AST 校验(必须用
execute_sql_query)→ 代码清洗(表名白名单、剥掉危险重定义、改图路径)→ 可选Sandbox隔离执行(pandasai/agent/base.py:132)。任一步或执行出错,都把报错回填 prompt 让 LLM 重试(:296_regenerate_code_after_error)。→ 细看 01-agent-loop.md 与 02-code-as-sql.md。 -
喂给 LLM 的是"表结构 + 允许的 SQL 函数 + 严格输出格式",不是原始数据。 prompt 模板把每张表的 schema、可用 SQL 函数、以及"最后必须声明
result字典"的要求拼进去(pandasai/core/prompts/templates/generate_python_code_with_sql.tmpl:1)。这既省 token,又把输出约束成可解析的结构。→ 细看 03-prompts.md。 -
响应被收敛成 4 种类型。 LLM 必须把答案写成
{"type": one of number/string/dataframe/plot, "value": ...},解析器逐类型校验值的合法性(数字必须是数、图必须是路径或 base64 等),不合规则报InvalidOutputValueMismatch(pandasai/core/response/parser.py:32_validate_response)。
4. 阅读地图(建议顺序)
先读本章建立全局直觉,然后按"主循环 → 安全执行 → prompt → 语义层"由主干到细节推进:
| 顺序 | 章节 | 一句话 | 什么时候读 |
|---|---|---|---|
| 0 | index.md(本章) | 是什么、全景、主线、路由 | 先读,建立心智模型 |
| 1 | 01-agent-loop.md | Agent 主循环:一问一答怎么跑完,重试逻辑如何回环 | 想搞懂"一次 chat 的完整生命周期" |
| 2 | 02-code-as-sql.md | 代码即 SQL:强制 execute_sql_query、AST 校验、清洗、沙箱执行 | 想搞懂"为什么安全、代码怎么被约束和跑起来" |
| 3 | 03-prompts.md | Prompt 工程:喂给 LLM 的到底是什么(表结构、SQL 函数、输出契约) | 想搞懂"LLM 凭什么写对代码" |
| 4 | 04-semantic-layer.md | 语义层与 SQL 编译:数据集、视图、转换如何用 sqlglot 变成 SQL | 想搞懂"数据集/视 图/转换是怎么落到 SQL 的" |
推荐路径: 只想用 → 读本章 §1、§2 即可。想改代码/排错 → 顺序 1→2。想理解"自然语言到底怎么变 SQL" → 加读 3、4。
5. 代码地图(导航索引)
| 主题 | 文件路径 | 符号名 |
|---|---|---|
| 公开入口 chat | pandasai/__init__.py | chat |
| 公开入口 读 CSV | pandasai/__init__.py | read_csv |
| 公开入口 建数据集 | pandasai/__init__.py | create |
| 公开入口 载数据集 | pandasai/__init__.py | load |
| DataFrame.chat 入口 | pandasai/dataframe/base.py | DataFrame.chat |
| 默认 schema 生成 | pandasai/dataframe/base.py | DataFrame.get_default_schema |
| Agent 类 | pandasai/agent/base.py | Agent |
| 处理一次查询 | pandasai/agent/base.py | Agent._process_query |
| 生成代码(带重试) | pandasai/agent/base.py | Agent.generate_code_with_retries |
| 执行代码(带重试) | pandasai/agent/base.py | Agent.execute_with_retries |
| SQL 取数分发 | pandasai/agent/base.py | Agent._execute_sql_query |
| 出错后重生成 | pandasai/agent/base.py | Agent._regenerate_code_after_error |
| 代码生成器 | pandasai/core/code_generation/base.py | CodeGenerator.generate_code |
| 强制用 SQL 的校验 | pandasai/core/code_generation/code_validation.py | CodeRequirementValidator.validate |
| 组 SQL 提示 | pandasai/core/prompts/__init__.py | get_chat_prompt_for_sql |
| 主 prompt 模板 | pandasai/core/prompts/templates/generate_python_code_with_sql.tmpl | — |
| 响应解析(4 类型) | pandasai/core/response/parser.py | ResponseParser.parse |
| 本地 DuckDB 执行 | pandasai/data_loader/duck_db_connection_manager.py | DuckDBConnectionManager |
| 语义层 schema | pandasai/data_loader/semantic_layer_schema.py | SemanticLayerSchema |
| schema → SQL 编译 | pandasai/query_builders/base_query_builder.py | BaseQueryBuilder |