FDE W5 Day 5 学习手册 · Text2SQL 最小 Demo
W5 Day5 · B 级(重要,需熟练)· 3.5h · 学完能讲透"自然语言如何安全变成只读 SQL 并解释回给用户"
本日定位:W5 聚焦"企业 Agent Harness"的工程化能力。Day5 是其中最小却最易踩坑的一环——把用户的中文问题转成 SQL 查数据库。你是 36 岁、Java+大数据+医疗保险背景的候选人,示例全程围绕"特药理赔 Agent":用户问"我上个月特药报销了多少钱",Agent 要生成安全 SQL 查理赔库。
学完能回答:① Text2SQL 端到端流程每一步做什么;② 为什么 SQL 必须只读+白名单;③ Schema Linking 怎么做才准;④ 结果怎么解释、空结果怎么处理。
使用方法:通读原理 → 重点看「工程含义」「面试话术」「易错点」→ 做自测清单 → 配合《FDE-W5D5-评测题.md》。选中不熟的词可标注(左下★重要 / 右下📌待查)。
一、Text2SQL 定位:特药理赔场景里它做什么
Text2SQL(又称 NL2SQL)是把自然语言问题转换成可在数据库执行的 SQL 的任务。在特药理赔 Agent 里,用户常问"3 月我报的奥希替尼花了多少""哪些保单的特药额度还没用完"——这类结构化、可聚合的问题,最适合走 SQL,而不是 RAG 或硬编。
生产定位:Text2SQL 不是"炫技",是企业里落地最快、价值最实在的 LLM 应用之一。它把"业务人员问数据库"的门槛降到零,但前提是安全——一个写错或越权的 SQL 可能拖垮库或泄露数据。
Text2SQL 是"把自然语言转成 SQL 查数据库"。适合结构化、可聚合的业务问题(如理赔金额统计)。它落地快、价值高,但前提是"只读 + 安全",否则一个错 SQL 能拖库或泄密。
📚 延伸资源(中文/官方,非 OpenAI):① Awesome-Text2SQL 论文与工具集锦 github.com/eosphoros-ai/awesome-text2sql;② LangChain SQL 官方文档(如何接数据库)python.langchain.com/docs/how_to/sql_db;③ 通义/DeepSeek 的 function calling 与 SQL 实践文档(中文社区)。
二、端到端流程全景
最小可运行的 Text2SQL 链路是一条线性流水线,每一步都有明确职责与失败点:
用户问题 → 数据路由判断 → Schema Linking → 生成只读 SQL → SQL 安全校验 → 执行计划 & 超时 → 执行 → 结果解释 → 自然语言回答
- 数据路由判断:先判断该问题是不是该走 SQL(详见 Day6),避免拿聊天问题去查库。
- Schema Linking:把"特药报销"映射到具体表/字段(claim、drug、amount)。
- 生成只读 SQL:大模型产出 SELECT 语句。
- SQL 安全校验:拦截一切非只读、越表、危险操作(核心防线)。
- 执行计划 & 超时:先看执行计划是否全表扫/超成本,再设超时。
- 执行:在受限账号/连接池里跑。
- 结果解释:把行集转成"您 3 月特药报销合计 ¥12,400"。
流水线里安全校验是必须的一道独立关卡,不能和"生成 SQL"合并——生成由 LLM(不可信),校验由确定性规则(可信)。这就是"不可信的生成"与"可信的守门"分离的工程思想,贯穿整个 Agent Harness。
三、数据路由判断(上游闸门,Day6 详述)
进入 Text2SQL 之前,先要判断"这个问题到底该不该用 SQL"。简要规则:问题涉及聚合统计 / 条件筛选 / 跨表关联且数据在结构化库里 → 走 SQL;涉及知识条款 / 文档内容 → 走 RAG;涉及实时状态 / 写操作 → 走业务 API。判断不准会出问题:把"特药目录怎么规定的"拿去查库,必然查不到。
路由的详细判断逻辑、LLM 分类 vs 规则、低置信度回退,见 Day6 三路数据路由。Day5 先假设"已判定走 SQL"。
路由是上游闸门:聚合/筛选/关联→SQL;知识条款→RAG;实时/写→API。判错会查不到或误写。详细见 Day6。
四、Schema Linking 原理(准确率命门)
Schema Linking 是 Text2SQL 的准确率命门:把用户话术里的实体/概念,对齐到数据库真实的表名、字段名、枚举值。
自然语言实体 → 候选表/字段匹配 → 消歧 → 锁定 (table, column) → 拼进 SQL 上下文
- 用户说"报销金额" → 锁定
claim.clm_amt 而非 claim.paid。
- 用户说"奥希替尼" → 锁定
drug.name='奥希替尼',不是模糊 LIKE。
- 用户说"上个月" → 锁定
claim.clm_date BETWEEN 上月首日 AND 上月末。
Schema Linking 不准,后面 SQL 再"语法正确"也是错答案。常见错:① 字段歧义(多个表里都有 amount);② 同义词没映射("花费"≠字段名 cost);③ 时间表达式没归一化("最近"→具体区间)。这些都是"形对义错"。
五、Schema Linking 工程做法:给模型喂对上下文
要让 Linking 准,关键在给模型喂对上下文。三个数据源:
5.1 元数据 / 数据字典
从 INFORMATION_SCHEMA 或自建数据字典抽取表结构:表名、字段名、类型、注释、主键外键、枚举取值。这是 Linking 的"地图"。
5.2 表注释 / 字段注释(中文注释尤其重要)
保险库里字段常是英文缩写(clm_amt),但业务词是中文。把中文注释写进 prompt,让模型知道 clm_amt = 理赔金额。你们 Java+大数据背景最熟:建表时就该有 comment。
5.3 候选裁剪(减少幻觉)
库有几百张表时,别把全库 schema 塞给模型(超长+干扰)。先用问题关键词做表召回(向量检索 / 关键词匹配表注释),只把 Top-K 相关表的结构喂进去。
工程上 Schema Linking 的本质是检索增强:用数据字典做"索引",用问题做"查询",召回相关表结构再生成。它的准确直接决定 SQL 准确——许多 Text2SQL 失败,根因在 Linking 而非生成。
# 伪代码:Schema Linking 召回
def link_schema(question, schema_index):
# 1. 用问题召回相关表(关键词 or 向量)
top_tables = schema_index.search(question, top_k=5)
# 2. 组装成模型能懂的上下文
ctx = "\n".join(format_table(t) for t in top_tables)
# 3. 让模型输出 (table, column) 映射
mapping = llm(f"问题:{question}\n可用表结构:\n{ctx}\n"
f"输出涉及的(表,字段)JSON")
return mapping, ctx # ctx 直接复用给后续 SQL 生成
Schema Linking 准的秘诀:把"中文注释 + 数据字典 + 表召回裁剪"喂给模型。本质是检索增强——先召回相关表结构,再生成。错大多在 Linking 不在生成。
六、SQL 生成:提示词与上下文构造
拿到 Linking 结果和表结构上下文后,构造 prompt 让模型生成 SQL。关键点:
- 显式给 schema:CREATE TABLE 注释版 + 字段中文含义。
- 给 few-shot:2~3 个"问题→SQL"样例,固定输出风格。
- 强制只读:prompt 里写明"只输出 SELECT,禁止 INSERT/UPDATE/DELETE/DDL"。
- 低 Temperature:T=0,SQL 要确定性、可复现。
- 方言对齐:库是 MySQL 就别生成 PostgreSQL 语法。
prompt = f"""
你是保险理赔库 SQL 助手。数据库为 MySQL。
可用表结构(含注释):
{ctx}
要求:
1. 仅生成一条只读 SELECT,禁止任何写操作/DDL;
2. 只能使用上述表;金额用 clm_amt,日期用 clm_date;
3. 输出纯 SQL,不要解释。
问题:{question}
"""
生成阶段就"先声明只读"能挡掉大部分明显违规,但不能只靠 prompt——模型可能被诱导绕过。所以生成后必有独立安全校验(下一节)。prompt 约束是"第一道软防线",规则校验是"硬底线"。
别迷信"我在 prompt 里写了禁止写操作就安全了"。越狱/长上下文注入能让模型吐出 DROP 或 UPDATE。安全必须靠执行前规则校验,不靠模型自觉。
七、SQL 安全校验总览(守门员)
安全校验是流水线的守门员,在 SQL 真正执行前用确定性代码逐条检查。它和"生成"解耦:生成可能错,校验必须 100% 可靠。
| 校验项 | 做法 | 挡住什么 |
| 只读 | AST 解析,禁止 INSERT/UPDATE/DELETE/DROP/ALTER | 数据被改/被删 |
| 白名单表 | SQL 引用的表必须 ∈ 允许集 | 越权访问敏感表 |
| 参数化 | 用户输入走绑定变量,不拼字符串 | SQL 注入 |
| 行数限制 | 自动追加 LIMIT,或 LIMIT 不得超过阈值 | 超大结果集拖库 |
| 执行计划检查 | EXPLAIN 看是否全表扫/笛卡尔积/超成本 | 慢查询卡死 |
安全校验是"守门员",和生成解耦:生成可能错,校验 100% 可靠。五关:只读、白名单表、参数化、行数限制、执行计划检查。每一关都必须是确定性规则,不依赖模型。
八、只读限制 + 白名单表
只读:解析 SQL 的 AST(抽象语法树),只允许 SELECT(及 CTE 里的 WITH...SELECT),拒绝一切 DML/DDL。比正则更稳——正则会被注释、换行绕过。
白名单表:从 SQL 提取所有表名,逐一校验是否在"本 Agent 可读表集合"内。特药理赔 Agent 只允许 claim / policy / drug / member 等,绝不许碰 user_credential / payment_account。
为什么要只读+白名单?① 护数据:LLM 生成的 SQL 出错概率非零,一旦变写操作就是真实资损/泄露,远比"答错"严重;② 最小权限:即便账号被攻破,也只暴露该暴露的表;③ 合规:保险数据受监管,越权访问是事故。只读账号 + 白名单是"纵深防御"的第一层。
① 只读校验别只靠正则(如禁 "drop"/"update" 字符串)——注释、换行、大小写、编码都能绕过。用 SQL 解析器(如 JSqlParser/Apache Calcite)做 AST 校验最稳。② 白名单要防"隐式表":子查询、JOIN 里的表也要算进去。③ 只读账号在数据库层也要配置(GRANT SELECT only),不只在应用层——纵深防御。
为什么必须只读+白名单?答错只是体验差,写错/越权是真实资损和合规事故。做法:AST 解析只允许 SELECT(别靠正则),白名单覆盖 JOIN/子查询里的所有表,且数据库账号本身只 GRANT SELECT——纵深防御。
九、参数化 + 行数限制 + 执行计划检查
9.1 参数化(防注入)
用户原话(如药品名)绝不能字符串拼进 SQL,必须走预编译/绑定变量:WHERE drug_name = ?。这同时防 SQL 注入(用户输入 ' OR '1'='1)和语法错。
9.2 行数限制
自动给查询追加 LIMIT N(如 200),或校验用户写的 LIMIT 不超过阈值。防"SELECT * FROM claim"返回百万行拖垮连接。
9.3 执行计划检查
执行前先 EXPLAIN,看是否全表扫描、是否缺索引、预估行数/成本是否超标。超标则拒绝或改写,避免慢查询占满连接池。
这三关是"防滥用 + 保性能":参数化堵注入(你们 Java 背景最熟 PreparedStatement),行数限制和 EXPLAIN 是"保护数据库不崩"的工程底线。生产 SQL 路由必须和资源配额绑定。
① EXPLAIN 本身也有成本,高并发下别每次都跑,可对简单查询免检或缓存计划。② LIMIT 太低会"悄悄截断"结果导致统计错(用户问"全部保单",你 LIMIT 200 只统计了 200 条)——要在结果解释里提示"结果已截断"。③ 参数化要覆盖所有用户入参,包括 LIMIT 值本身。
十、执行与超时控制(最后兜底)
通过校验的 SQL,在受限连接里执行:独立连接池、低权限账号、语句超时(如 3s)。超时即杀掉查询返回"查询过慢,请缩小范围"。
超时是"最后兜底":即便前面 EXPLAIN 放行,线上数据分布变化也可能让查询变慢。语句级超时 + 连接池隔离,保证一个烂查询拖不垮整个理赔服务。这是你们做过高并发 Java 服务最该有的肌肉记忆。
十一、结果解释:SQL → 自然语言
SQL 跑出的是行集/聚合值,要转成用户能懂的自然语言。原理:把 SQL 结果 + 原始问题,喂给模型生成解释。
answer = llm(f"""
用户问题:{question}
SQL:{sql}
查询结果({len(rows)} 行):{rows}
请用一句中文回答用户,含关键数字;若结果为空见下方规则。
""")
结果解释是"闭环体验":用户不关心 SQL,只关心"我报了多少钱"。把结构化结果用模型组织成自然语言,同时注意数字准确性——解释里出现的金额必须来自真实结果,不能让模型"重新算"或编造。
结果解释把行集转成自然语言,但数字必须来自真实 SQL 结果、不能让模型重算或编。这是"SQL 负责准、语言模型负责顺"的分工。
十二、空结果与异常处理
两种要特殊处理的情形:
- 空结果:SQL 正常但 0 行(如"查不存在的保单")。要如实说"未查到相关记录",不能编造"您报销 0 元"误导(0 元和没记录是两回事)。
- 执行异常:超时/权限/语法(校验漏网)。要兜底:提示用户换种问法、转人工,或走降级(如返回近 30 天概览)。
① 空结果 ≠ 0:把"没查到"说成"报销 0 元"是严重误导,保险场景可能引发投诉。② 异常别把原始报错(含表名/SQL)直接暴露给用户(信息泄露),只给友好提示并记日志。③ 结果解释若基于截断数据,必须明示"仅统计前 N 条"。
空结果要说"未查到",不能编"0 元"(0 和没有是不同的);异常别把 SQL/表名抛给用户(泄露),友好提示+记日志+可转人工。
十三、面试达标线①:完整讲清 Text2SQL 流程(含安全校验每一环)
问题 → 路由判断(走SQL?) → Schema Linking(话术→表/字段) → 生成只读SQL(低T+schema上下文) → 安全校验(只读AST/白名单表/参数化/行数LIMIT/EXPLAIN) → 执行(受限连接+超时) → 结果解释(数字来自真实结果) → 自然语言
- 路由判断:确认问题该走 SQL(聚合/筛选/关联)。
- Schema Linking:用数据字典+中文注释+表召回,把话术映射到(表,字段)。
- 生成:低 T,prompt 含 schema 与"仅 SELECT"约束。
- 安全校验(五关):只读(AST)、白名单表(含JOIN/子查询)、参数化、行数 LIMIT、EXPLAIN 检查。
- 执行:受限连接池 + 语句超时。
- 结果解释:数字来自真实结果,空结果如实说,异常友好兜底。
达标核心:能从头到尾串起流程,并且把安全校验五关逐一道出(只读/白名单/参数化/行数/执行计划),说清每关挡什么。这体现"把不可信生成与可信守门分离"的工程观。
十四、面试达标线②:为什么必须只读+白名单、Schema Linking 怎么做才准
只读+白名单的理由:LLM 生成 SQL 出错概率非零;一旦变写操作/越权表,是真实资损与合规事故,远重于"答错"。纵深防御:AST 校验(应用层) + 数据库只读账号(GRANT SELECT only) + 白名单表(含 JOIN/子查询)。
Schema Linking 怎么做准:① 建数据字典+中文注释(建表 comment);② 用问题召回 Top-K 相关表(向量/关键词),避免全库噪声;③ 让模型先输出(表,字段)映射再生成 SQL;④ 枚举值归一化("奥希替尼"→drug_name 精确匹配);⑤ 时间表达式归一化("上个月"→具体区间)。错因多在 Linking(字段歧义、同义词、时间)而非生成。
只读+白名单:生成会错,写错=资损/合规事故;用 AST+库账号只读+白名单做纵深防御。Schema Linking 准:数据字典+中文注释+表召回+先映射后生成+枚举/时间归一化。多数失败在 Linking 不在生成。
十五、Day 5 自测清单
- 能默写 Text2SQL 端到端流程 8 步,说出每步职责与失败点。
- 能解释"生成"与"安全校验"为什么要解耦(不可信 vs 可信)。
- 能说清 Schema Linking 是什么、为什么是准确率命门。
- 能列出 Schema Linking 的 3 个数据源(元数据/注释/召回裁剪)和工程做法。
- 能讲清 SQL 安全校验五关各自挡什么。
- 能解释为什么只读校验要用 AST 而非正则(绕过风险)。
- 能说清白名单表为什么要覆盖 JOIN/子查询,且数据库账号也要只读。
- 能解释参数化、行数 LIMIT、EXPLAIN 各自防什么。
- 能讲清结果解释中"数字必须来自真实结果"的原因。
- 能区分空结果 vs 0 元、说明异常处理不能泄露 SQL/表名。
- 能讲清为什么必须只读+白名单(资损/合规/纵深防御)。
- 能讲清 Schema Linking 怎么做到准(达标线②)。
十六、高频面试题速记卡
Q:Text2SQL 端到端有哪些步骤?
问题→路由判断→Schema Linking→生成只读SQL→安全校验(只读/白名单/参数化/行数/EXPLAIN)→执行(超时)→结果解释→自然语言。
Q:为什么"生成"和"安全校验"要解耦?
生成由 LLM(不可信、会错/越狱),校验由确定性规则(可信、100%)。守门员不能和生成合并,否则不安全。
Q:Schema Linking 为什么是准确率命门?
它把话术映射到真实表/字段;Linking 错则 SQL 形对义错。多数 Text2SQL 失败根因在 Linking 而非生成。
Q:只读校验为什么用 AST 而不是正则?
正则可被注释/换行/大小写/编码绕过;AST 解析语法树只允许 SELECT,最稳。配合数据库 GRANT SELECT only 纵深防御。
Q:白名单表为什么要覆盖 JOIN/子查询?
越权表可能藏在子查询或 JOIN 里,只查顶层表会漏。所有被引用的表都要 ∈ 允许集。
Q:为什么必须只读+白名单?
LLM 生成会错;一旦变写操作/越权表是真实资损与合规事故,远重於答错。纵深防御:AST+库只读账号+白名单。
Q:参数化、行数 LIMIT、EXPLAIN 各防什么?
参数化防 SQL 注入;LIMIT 防超大结果集拖库;EXPLAIN 防全表扫/慢查询卡死连接池。
Q:空结果和"报销 0 元"一样吗?
不一样。没查到要说"未查到",不能编"0 元",否则误导用户、引发投诉。0 和没有是两回事。
Q:结果解释里数字从哪来?
必须来自真实 SQL 结果,不能让模型重算或编造。SQL 负责准,语言模型负责顺。
Q:SQL 安全校验五关是哪五关?
只读(AST)、白名单表、参数化、行数限制(LIMIT)、执行计划检查(EXPLAIN)。每一关都是确定性规则。
FDE W5D5 学习手册 · Text2SQL 最小 Demo(面试级)· 配合《FDE-W5D5-评测题.md》自测
📌 待查★ 重要