Text-to-SQL 的核心挑战
自然语言问数看似简单,工程上却要同时解决:
| 挑战 | 表现 | 本专题策略 |
|---|---|---|
| 语义歧义 | 「收入」指毛利还是营收 | 指标元数据 + 域分类 |
| 时间缺失 | 「销售收入多少」没写周期 | 主动追问 missing_time_period |
| 口径不一 | 完成率分母不同 | bi_metric_extract_logic 固化 |
| SQL 安全 | 注入、删库 | validate_sql 四层校验 |
| 幻觉 | 模型编造表名 | 禁止 LLM 直接产 SQL |
为什么用结构化元数据,而非纯 LLM 生成 SQL
LLM 生成 SQL 在 Demo 里很炫,在生产里常见问题:
- 表名、字段名随 prompt 波动,不可复现
- 同一问题两次可能生成不同 SQL,审计困难
- 企业宽表字段多、命名晦涩,上下文窗口装不下
因此本平台的生产路径是:检索命中指标 → 查 bi_metric_extract_logic → 规则拼接 SQL。LLM 留给解释与未来的复杂推理。
五步流水线
1 | |
步骤 1:域分类 classify_domain
从 bi_domain.domain_keywords 与关联指标名构建关键词表,对用户问题打分:
- 完全匹配关键词:+3 分
- 部分匹配:+1.5 分
- 同分按域优先级排序
- 无命中 →
未知域,走更宽泛检索
虚构示例:问题含「销售收入、华东区」→ 命中「销售运营域」。
步骤 2:指标检索 retrieve_all
并行检索 metrics / bi_models / business_rules。metrics 优先走 MySQL:
1 | |
命中第一个指标 M_SALES_REVENUE(销售收入)后进入取数逻辑。
步骤 3:取数逻辑 _build_sql_from_meta_logic
查 bi_metric_extract_logic:
1 | |
关键字段(虚构宽表 dws_sales_metric_wide):
| 字段 | 示例值 | 含义 |
|---|---|---|
table_code |
dws_sales_metric_wide |
结果表 |
index_name_value |
销售收入 |
宽表中 index_name 过滤值 |
period_value |
季度 |
默认统计周期 |
period_field |
period |
周期类型字段 |
time_field |
stat_date |
统计日期字段 |
default_filter |
is_valid = '1' |
有效数据标记 |
value_field_map |
{"当期值":"value","同比":"val_yoy"} |
展示字段映射 |
org_field |
region_name |
组织/区域维度 |
步骤 4:时间解析
函数 _extract_time_period 将自然语言转为内部表示,再由 _resolve_result_table_time_condition 生成 WHERE 片段。
| 用户表述 | 解析结果 | SQL 条件(示例) |
|---|---|---|
| 今年 Q1 / 2026 年一季度 | 2026Q1 | period='季度' AND stat_date='2026-03-31' |
| 2026 年 | 2026年 | period='年' AND stat_date='2026-12-31' |
| 上月 | 2026-05 | period='月' AND stat_date='2026-05-31' |
| 本周 | 2026W23 | period='周' AND stat_date=... |
| (未提及) | — | 触发 missing_time_period 追问 |
步骤 5:SQL 组装
维度解析:用户提到「华东区」→ region_name 过滤;未提及则按 granularity 默认到省级汇总。
取值字段:默认「当期值」;若含「同比」追加 val_yoy,含「完成率」追加完成率字段。
完整虚构 SQL 示例(用户问:「华东区 2026 年一季度销售收入是多少?」):
1 | |
代码级拼接逻辑(简化)
1 | |
缺时间周期时的追问
若命中指标但 _has_time_period(question) 为 false,不执行 SQL,返回:
1 | |
与站内延伸
- 元数据设计可参考 RAG 离线部分:元数据增强
- Agent 工具链注册见 Agent 自定义工具开发与接入方法
本章小结
- 生产 Text-to-SQL = 检索指标 + 元数据模板,不是 LLM 写 SQL
- 五步链路每一步可单测、可日志、可回放
- 时间周期是最常见澄清点,必须产品化
思考题
- 若同一指标存在月表与季表两套取数逻辑,优先级应如何配置?
value_field_map为什么要 JSON 化,而不是在代码里写死字段名?
系列导航
| 篇 | 主题 |
|---|---|
| 01 | 架构全景 |
| 02 | 本篇 |
| 03 | RAG 指标库 |
| 04 | 多轮澄清 |
| 05 | 安全校验 |
| 06 | Vue3 工作台 |
| 07 | 演示验证 |
| 08 | 反馈闭环 |