代码语言

知识点思维导图

30 个知识节点

项目实战(02) - 项目:AI 数据分析助手

读完后,你应能完成以下任务:

  • 绘制“项目实战(02) - 项目:AI 数据分析助手 / 别让模型生成 SQL 字符串,让它生成"查询计划"”的关键对象与数据流,解释“模型生成的 SQL 是一段不可控文本,你很难逐条校验它安全。”,并用源码位置、日志或 Trace 标注证据。
  • 为“项目实战(02) - 项目:AI 数据分析助手 / 校验是这个项目的灵魂”设计正常与异常输入,验证“这四层和 28 的工具校验是同一种思想:把"模型想干什么"和"系统允许干什么"分开,后者由你硬编码。”,输出首个偏差位置与回归测试结果。
  • 实现“项目实战(02) - 项目:AI 数据分析助手 / 理解不了就追问,别瞎查”的最小代码或配置,检验“这时候模型应该追问澄清,而不是随便编一个计划去查。”,输出命令、结果与 Diff,并说明不适用边界。

一、AI 数据分析助手的真实应用场景

运营在群里问:"上个月哪个城市销售额最高?"

这本来要数据同学写条 SQL、跑一下、截图发群里。数据分析助手想做的就是把这一步自动化:运营用大白话问,助手直接给数字和结论。

但这里藏着一个比客服项目更尖锐的安全问题。客服调的是你写死的工具(lookup_orders 就查订单,参数固定)。数据分析助手要让模型自己组装查询——查哪张表、选哪些列、怎么过滤。一旦模型能"自由查询",麻烦就来了:

  • 运营随口问"各员工工资多少",模型生成了一条查工资表的 SQL,越权了。
  • 模型把 WHERE 写漏,全表扫描,慢查询拖垮库。
  • 更糟的:用户诱导模型生成 DROP TABLE

所以这一篇的核心不是"怎么让模型写 SQL",而是怎么不让模型直接碰数据库

二、别让模型生成 SQL 字符串,让它生成"查询计划"

最常见的错误做法:

用户问题 → 模型生成 SQL 字符串 → 直接丢给数据库执行   ❌ 危险

模型生成的 SQL 是一段不可控文本,你很难逐条校验它安全。正确做法是让模型输出结构化的查询计划,后端拿计划去执行:

用户问题 → 模型生成查询计划(JSON) → 后端校验计划 → 后端自己安全执行   ✓

计划长这样,是受约束的 JSON,不是自由文本:

这个差别是本质的。SQL 字符串你要靠正则去防 DROP、防注入,防不胜防;查询计划你只要校验"table 在不在白名单、metric 这列能不能查、agg 是不是允许的聚合",每一项都是确定的判断。把模型的输出从"自由文本"收窄成"结构化选项",安全性立刻可控。

三、校验是这个项目的灵魂

模型生成的计划,后端要逐项过校验,缺一不可:

这四层和 28 的工具校验是同一种思想:把"模型想干什么"和"系统允许干什么"分开,后者由你硬编码。其中第 3 条最能体现数据助手的特色——同一个查询,普通分析师看不到成本,管理者能看到。权限差异不靠模型自觉,靠 permissions 列表硬卡。

四、理解不了就追问,别瞎查

还有一条容易被忽略的路径:模型理解不了问题怎么办。

用户只说"帮我分析一下",没说分析什么。这时候模型应该追问澄清,而不是随便编一个计划去查。demo 里 nl_to_plan 返回 None 就是这个分支——宁可承认"我没听懂",也不能拿一个瞎猜的计划去跑。这和 RAG 的"检索不到就拒答"是同一种工程克制。

五、工程上真正会踩的坑

  • 图省事让模型直接出 SQL:看起来快,但你失去了对查询的控制权。坚持"计划 + 后端执行",哪怕计划表达能力弱一点。
  • 敏感列权限写在 prompt 里:"不要返回成本数据"这种提示,模型会被绕过。敏感列必须在 validate_plan 里硬拦。
  • 没有结果行数/超时限制:模型生成一个扫全表的计划,库就卡住了。计划执行前要加 LIMIT、超时、最大扫描行数。
  • 把空结果当成功:查出来 0 行,助手说"分析完成",用户一脸懵。空结果要明确告诉用户"没有符合条件的数据",并提示可能的原因。
  • 结论越界:数据只有销售额,模型却"分析"出了利润率。结论只能基于实际查到的列,不能脑补。

六、一句话面试答法

数据分析助手怎么防止模型生成危险查询? 我不让模型直接生成 SQL 字符串去执行,而是让它输出结构化的查询计划——查哪张表、哪些列、怎么聚合。后端拿到计划后做四层校验:表白名单、列权限、敏感列额外授权、聚合方式白名单,全过了才由后端自己安全执行,模型从头到尾碰不到数据库。这样把模型输出从不可控文本收窄成有限选项,注入和越权问题就从"难防"变成"可校验"。

七、动手实践:45 AI 数据分析助手

自然语言 → 结构化查询计划 → 权限校验 → 在内存数据上执行 → 出结论。这是 Text2SQL 的最小骨架,但刻意不让模型直接生成 SQL 去执行——模型只输出"查哪张表、选哪些列、怎么聚合"的计划,后端校验合法、有权限再执行。模型碰不到数据。

7.1 在线运行

零依赖,纯标准库。

7.2 预期输出

=== 场景 1:各城市销售额(普通权限,成功)===
问:上个月哪个城市销售额最高?
  生成计划:{"table": "sales", "group_by": "city", "agg": "sum", "metric": "amount"}
  数据:[{"city": "北京", "amount": 1900}, {"city": "上海", "amount": 1800}, ...]
  结论:city 维度 amount 最高的是 北京(1900)。建议用柱状图展示。

=== 场景 3:查成本(敏感列,普通权限被拦)===
  拦截:列 cost 为敏感数据,缺少权限 read:sensitive

=== 场景 4:查成本(管理者有权限,成功)===
  数据:[{"city": "北京", "cost": 1150}, ...]

=== 场景 5:查工资表(表不在白名单,被拦)===
  拦截:表不存在或未授权:users

=== 场景 6:模型理解不了(追问澄清)===
  无法理解问题,请换种问法或补充字段。

场景 3 和 4 是同一句"查成本":普通分析师被拦,管理者放行。权限不在模型里,在 validate_plan

7.3 代码对应文章的哪些点

概念 在 main.py 哪里
自然语言 → 查询计划(Text2SQL) nl_to_plan
表白名单 / 列权限 / 敏感列 / 聚合白名单 validate_plan
后端安全执行(模型不碰数据) execute_plan
结论 + 图表建议 summarize
理解不了就追问澄清 nl_to_plan 返回 None

7.4 动手改

  • nl_to_plan 换成真实模型,要求它只输出 JSON 计划,绝不输出可执行 SQL。
  • validate_plan 加一条「行级过滤」:让分析师只能看自己区域的数据。
  • SCHEMA 加一张表,体会白名单怎么挡住越权查询。

7.5 可运行源码:项目:AI 数据分析助手

main.py

八、总结

  • 别让模型生成 SQL 字符串,让它生成"查询计划":模型生成的 SQL 是一段不可控文本,你很难逐条校验它安全。
  • 校验是这个项目的灵魂:这四层和 28 的工具校验是同一种思想:把"模型想干什么"和"系统允许干什么"分开,后者由你硬编码。
  • 理解不了就追问,别瞎查:这时候模型应该追问澄清,而不是随便编一个计划去查。
  • 工程上真正会踩的坑:敏感列必须在 validate_plan 里硬拦。
  • 一句话面试答法:我不让模型直接生成 SQL 字符串去执行,而是让它输出结构化的查询计划——查哪张表、哪些列、怎么聚合。

学完自测

选择所有正确答案;提交后逐项核对判断依据。

1在“项目:AI 数据分析助手”中,需要同时满足“AI 数据分析助手的真实应用场景”与“别让模型生成 SQL 字符串,让它生成"查询计划"”。给定正文约束“所以这一篇的核心不是"怎么让模型写 SQL",而是怎么不让模型直接碰数据库。”,哪些判断保持了原有处理机制?多选
2“项目:AI 数据分析助手”出现偏差:“在“项目:AI 数据分析助手 / 校验是这个项目的灵魂”中,即使不满足“把"模型想干什么"和"系统允许干什么"分开,后者由你硬编码”,结果与副作用仍会保持不变。”已成为实际行为。围绕“校验是这个项目的灵魂”与“理解不了就追问,别瞎查”,哪些判断能定位被改变的职责或边界?多选
3评审“项目:AI 数据分析助手”方案时,验收条件包含“数据分析助手怎么防止模型生成危险查询? 我不让模型直接生成 SQL 字符串去执行,而是让它输出结构化的查询计划——查哪张表、哪些列、怎么聚合。”。关于“一句话面试答法”与“动手实践:45 AI 数据分析助手”的哪些决策符合正文机制?多选