业务故事站
P1 AIP

NLQ 入门:第一次自然语言查数

从"查询所有订单"到聚合统计与追问改条件,看业务人员如何不写 SQL、用一句话查数,并读懂平台返回的意图、置信度、SQL、结果表格与图表推荐。看完这 4 个故事,你就能自己对着演示数据问出第一句话。

业务分析师 销售总监 NLQ Text2SQL 意图识别 可视化 共 4 个故事

能 / 不能速览

✅ 这个主题能做
  • 用中文一句话查演示数据:明细、聚合(SUM / COUNT / GROUP BY)都支持
  • 看到完整链路:意图 / 置信度 / 生成 SQL / 结果表格 / 图表推荐 / RAG 上下文 / 修复次数
  • SQL 执行失败自动修复(带错误 + 可用列让 LLM 重写,最多 2 次)
  • 聚合结果自动推荐图表(指标卡、柱状图、折线图、饼图、表格),并附完整图表 DSL 与业务摘要
  • 同一对话窗口内追问、改条件,重新出结果
  • 每次查询写双层审计:NLQ_QUERY(谁查了什么)+ AI_DECISION(retrieve→generate→execute 决策链,/ai-audit 可回放)
⛔ 这个主题做不了
  • 多轮上下文记忆:每句查询独立处理,追问必须把条件说全
  • 知识问答:问"什么是 X"只返回占位提示(知识库未接入)
  • 意图识别是关键词规则,复杂口语、省略句可能判不准
  • 结果最多返回 100 行;不支持跨数据源 JOIN(多表 JOIN 限同一数据源)
  • Text2SQL 依赖外部 LLM,无 API key / 离线时生成不了 SQL(本地规则无法生成 SQL)

适用角色

本主题面向两个核心角色:

  • 业务分析师:核心使用者,用自然语言自助查数、核对口径,是"一句话取数"的最大受益者。
  • 销售总监 / 业务经理:消费聚合结论(总金额、订单量、月度对比),看结果图表即可。

数据工程师负责保证数据源与元数据就绪(见"数据源接入"主题);IT 管理员负责账号与权限(见"安全治理"主题),本主题不涉及。

能力速览(能做什么)

智能查询(/chat)

自然语言入口:输入一句话即可查数,自带三个示例问题(查询所有订单 / 查询订单总金额 / 查询每个客户的订单数量)。

意图识别

关键词规则识别 analysis / data_query / knowledge_qa 三类意图,返回置信度,为后续链路定基调。

RAG 上下文

按关键词从 table_schemas / column_schemas 目录中匹配相关表与列,构造 Text2SQL 的 schema 上下文;无命中回退全量 schema。

Text2SQL

经平台 LLM 网关调用 deepseek-v4-flash 把自然语言转成 SQL,输出 <SQL> 与 <EXPLANATION> 中文解释。

执行 + 自修复 + 可视化

经连接器在真实 SQLite 上执行 SQL(最多 100 行);执行失败自动修复 ≤2 次;按结果列类型推荐 chart_type,失败回退 table。

图表 DSL + 业务摘要

返回完整 ChartDSL(图表块 + LLM 业务摘要,摘要失败回退规则摘要),前端渲染图表的同时展示解读。

双层审计留痕

事件级 NLQ_QUERY(query / intent / sql / rows / 脱敏标记)+ 决策级 AI_DECISION(retrieve→generate→execute 决策链,/ai-audit 回放)。

调整指南(怎么调整)

  • 改查询表述:问题里尽量带上表名、字段名或同义词(订单、客户、销售额、城市),命中率高;说得越口语,RAG 越容易回退全量 schema。
  • 改条件:要换筛选条件(时间、城市、分类)就在对话里重新说全一句话,如"查询上海的客户的订单"。
  • 改结果范围:系统默认最多返回 100 行,想在 SQL 里控制请用聚合或明确条件,不要指望一次拉全量。
  • 改元数据质量:结果列对不上或查不出,去检查 table_schemas / column_schemas 的 description 与 synonyms,可用 ai_generate_descriptions=true 重新导入元数据。
  • 换数据源:切换查询目标数据源后,务必先重新导入该数据源的元数据,否则 RAG 用旧 schema 会误导。

做得好的场景

NLQ 最小链路在"单数据源、结构化演示数据、直白问法"下体验最顺:
  • 30 秒出报表:业务人员从"提需求给 IT、等 1~3 天"到"一句话 30 秒出数",明细与聚合都覆盖。
  • 口径即问即得:SUM / COUNT / GROUP BY 自动生成,总金额、订单量这类口径问出来就是对的。
  • 链路全透明:返回意图、置信度、SQL、RAG 上下文,结果不对可自查、可解释,不是"黑盒"。

限制与不足

以下是明确的边界,使用前先知道:
  • 意图识别是关键词规则:识别不了隐含意图;问"什么是 X"会命中知识问答占位提示。
  • RAG 是关键词匹配不是向量:元数据没写描述/同义词时,中文业务词常匹配不上,直接回退全量 schema。
  • Text2SQL 依赖外部 LLM:无 API key 或离线时生成不了 SQL,链路中断(无本地规则兜底生成 SQL)。
  • 执行能力有限:最多 100 行;不支持跨数据源 JOIN(多表 JOIN 仅限同一数据源);NLQ 本身不带多轮记忆,复杂分析要自己把条件说全。
  • 上下文默认走 RAG:本体/OAG 未建模或未启用时,注入 Text2SQL 的是 RAG 四路召回(关键词)而非业务语义,问得口语容易回退全量 schema。
  • 演示数据重启重建:aip_demo_warehouse 每次启动重建,订单日期相对启动时刻生成,人工改动会还原。

场景故事

故事 1 业务分析师第一次用自然语言查出全部订单明细
背景
王姐是华东区的业务分析师,Excel 玩得很溜但完全不会 SQL。周一晨会前,销售同事问她要一份订单明细,她想起平台上线时的演示:在"智能查询"里直接打字就能查数。她打开 http://127.0.0.1:18080,登录 admin / admin1,进入 /chat 页面。
传统做法对比
以前要发邮件给数据组提需求,IT 排期 1~3 天才能拿到一张临时表,中途口径改一次再等一天。现在点开示例"查询所有订单",30 秒就能看到完整结果。
角色
业务分析师(登录即可查询,无需建模或数据源管理权限)。
操作步骤
  1. 登录平台后进入"智能查询"页(/chat)
  2. 点击页面自带的示例问题"查询所有订单"(或手动输入)
  3. 按回车,等待链路返回
  4. 查看返回的意图、SQL、结果表格
系统响应
页面返回完整链路,核心字段如下:
{
  "message": "查询成功",
  "intent": "data_query",
  "confidence": 0.8,
  "data_source_name": "aip_demo_warehouse",
  "sql_query": "SELECT order_id, customer_id, order_date, product_id, quantity, price, sales_amount FROM orders LIMIT 100",
  "sql_explanation": "查询 orders 表中的全部订单字段",
  "query_result": {
    "columns": ["order_id", "customer_id", "order_date", "product_id", "quantity", "price", "sales_amount"],
    "rows": [["1","1","2026-06-09","1","10","99.5","995"],["2","2","2026-07-09","2","5","199","995"],["3","1","2026-07-30","1","20","99.5","1990"],["4","3","2026-08-06","3","2","499","998"],["5","2","2026-08-09","1","15","99.5","1492.5"]]
  },
  "visualization": { "chart_type": "table", "reason": "无明确图表特征,默认表格", "confidence": 0.6, "alternatives": [], "x_axis": "order_id", "series": ["order_id","customer_id","order_date","product_id","quantity","price","sales_amount"] },
  "fix_attempts": 0,
  "dsl": { "version": "1.0", "recommended_chart": "table", "confidence": 0.6, "alternatives": [], "blocks": [{"type":"chart","chart_type":"table","title":"查询所有订单","data_mapping":{"x":"order_id","y":["order_id","customer_id","order_date","product_id","quantity","price","sales_amount"]},"options":{"pagination":true}},{"type":"summary","text":"查询共返回 5 行、7 列数据。"}] },
  "rag_channels_used": ["metadata"],
  "ontology_hit": false
}
结果洞察
5 条订单一目了然:字段名都是"能看懂"的,总额可心算约 6470.5。王姐发现 intent 是 data_query、置信度 0.8、fix_attempts 是 0(一次执行成功)——平台把"怎么理解、生成了什么 SQL、查出什么"全摊开了。
调整建议
只想要关键列,就说得更具体,如"查询订单号和销售额";若出现不认识的中文列名,是导入元数据时生成的描述,可找数据工程师核对。
动手试一试
登录:http://127.0.0.1:18080,账号 admin / admin1。页面路径:/chat 智能查询。输入内容:点击示例"查询所有订单"。预期结果:返回 5 行订单数据,intent=data_query、confidence=0.8,图表推荐 table。
限制提示
结果最多返回 100 行,全量明细会被截断;演示数据 aip_demo_warehouse 每次启动重建,日期相对启动时刻生成。
故事 2 销售总监问"查询订单总金额",拿到一个数一张指标卡
背景
李经理是销售总监,月底要给 CEO 汇报一个"总盘子"。他懒得看明细表,就在 /chat 里输入"查询订单总金额",想要一个确定的数字。
传统做法对比
以前要助理把 5 张 Excel 汇总加总,再来回对口径,至少折腾半小时;现在一句话,平台自动生成 SUM 并把结果直接算出来。
角色
销售总监(只读查询权限即可,不需要懂 SQL)。
操作步骤
  1. 进入 /chat 智能查询
  2. 输入"查询订单总金额"并回车
  3. 查看返回的 SQL 与数值结果
系统响应
平台自动生成聚合 SQL 并返回单个数值:
{
  "intent": "data_query",
  "confidence": 0.8,
  "sql_query": "SELECT SUM(sales_amount) AS total_sales FROM orders",
  "sql_explanation": "对 orders 表的 sales_amount 求和,得到订单总金额",
  "query_result": { "columns": ["total_sales"], "rows": [["6470.5"]] },
  "visualization": { "chart_type": "metric_card", "reason": "single numeric value, recommend metric card", "x_axis": "total_sales", "series": ["total_sales"] }
}
前端把这一行渲染成一张醒目的"指标卡"。
结果洞察
总金额 6470.5 元,与明细对得上(995×2 + 1990 + 998 + 1492.5)。李经理注意到图表推荐是 metric_card——因为结果只有一列数值,平台按列类型自动判断用指标卡而非表格,正好符合"要一个数"的需求。
调整建议
想看"每个客户的总金额"就把维度说进去(见故事 3);涉及财务口径时,建议与财务确认 sales_amount 的口径定义(这里是已成交订单金额)。
动手试一试
登录:admin / admin1。页面路径:/chat。输入内容:"查询订单总金额"。预期结果:返回 total_sales=6470.5,图表推荐 metric_card(指标卡)。
限制提示
聚合 SQL 由 LLM 生成,列名必须来自元数据;若 LLM 把金额列写错,结果是 0 或报错——先看返回的 sql_query 再判断是元数据还是模型问题。
故事 3 按客户统计订单数量:一句话完成分组聚合与柱状图
背景
王姐要给销售例会准备"各客户订单量"的柱状图。演示库里订单在 orders、客户在 customers 两张表,她问"查询每个客户的订单数量",想验证平台能不能自己把两张表关联起来。
传统做法对比
以前要 VLOOKUP 把 orders 和 customers 按 customer_id 匹配,再手动 COUNTIF,半小时起步;现在一句话,JOIN 和 GROUP BY 都自动生成。
角色
业务分析师(会"看数",不写 SQL)。
操作步骤
  1. 进入 /chat
  2. 输入"查询每个客户的订单数量"并回车
  3. 观察返回 SQL 中是否自动 JOIN 了 customers
  4. 查看分组结果与图表推荐
系统响应
平台生成带 JOIN 与 GROUP BY 的 SQL 并返回分组结果:
{
  "intent": "data_query",
  "confidence": 0.8,
  "sql_query": "SELECT c.customer_name, COUNT(o.order_id) AS order_count FROM customers c LEFT JOIN orders o ON o.customer_id = c.customer_id GROUP BY c.customer_name",
  "sql_explanation": "按客户名分组,统计每位客户的订单数(LEFT JOIN 保留无订单客户)",
  "query_result": { "columns": ["customer_name", "order_count"], "rows": [["张三","2"],["李四","2"],["王五","1"]] },
  "visualization": { "chart_type": "bar", "reason": "contains one dimension and numeric columns, recommend bar chart", "x_axis": "customer_name", "series": ["customer_name","order_count"] }
}
前端按推荐渲染柱状图:张三 2 单、李四 2 单、王五 1 单。
结果洞察
三条客户记录都有数:张三 2 单、李四 2 单、王五 1 单,合计 5 单对得上。王姐发现平台自己写了 LEFT JOIN(保留无订单客户),还按"一个分类列 + 一个数值列"推荐了柱状图——这正是她想要的例会素材。
调整建议
想按月看某客户订单趋势,把时间维度加进问句(见"复杂分析"主题);如果 SQL 里没有 JOIN 而只有 customer_id,说明 LLM 没识别到关联列,换一种说法重问。
动手试一试
登录:admin / admin1。页面路径:/chat。输入内容:"查询每个客户的订单数量"。预期结果:返回 3 行(张三 2 / 李四 2 / 王五 1),图表推荐 bar(柱状图)。
限制提示
JOIN 是否生成取决于 LLM 与 RAG 上下文;若元数据里 customer_name 没有描述/同义词,RAG 可能回退全量 schema,靠 LLM 自己猜关联——多表复杂查询成功率不如直白问法稳定。
故事 4 追问改条件:在对话窗口里把"查询所有订单"收窄到上海客户
背景
王姐查完全部订单后,销售同事追问:"只要上海的客户。"她不想重新筛选 Excel,就在同一个对话窗口里接着问了一句。
传统做法对比
以前要么重新跑一遍流程再筛,要么下载 5 行数据手动过滤(量大了就费劲);现在在对话里把条件说全,一条新 SQL 直接出结果。
角色
业务分析师(连续查询,仍只需登录权限)。
操作步骤
  1. 保持 /chat 对话窗口,不刷新页面
  2. 输入"查询上海的客户的订单"(把表名与条件带全)
  3. 回车,查看返回的 SQL 与结果
系统响应
平台生成带城市过滤的 SQL 并返回 2 行:
{
  "intent": "data_query",
  "confidence": 0.8,
  "sql_query": "SELECT o.order_id, o.sales_amount, c.customer_name, c.city FROM orders o JOIN customers c ON o.customer_id = c.customer_id WHERE c.city = '上海'",
  "sql_explanation": "关联客户表,筛选城市为上海的客户订单",
  "query_result": { "columns": ["order_id","sales_amount","customer_name","city"], "rows": [["1","995","张三","上海"],["3","1990","张三","上海"]] },
  "visualization": { "chart_type": "table", "reason": "default table view", "x_axis": "order_id", "series": ["order_id","sales_amount","customer_name","city"] }
}
结果洞察
上海客户只有张三,2 笔订单合计 2985 元。王姐把这句话和上一句对比,确认"上海的客户"这个条件被正确翻译成了 WHERE city='上海'。窗口里两条消息并列,历史还留着,方便回头核对。
调整建议
追问时把表名、字段、条件一次说全,例如"查询上海的客户的订单金额合计";如果只说"只要上海的"这种省略句,意图识别容易掉到兜底分支(置信度 0.5)。
动手试一试
登录:admin / admin1。页面路径:/chat。输入内容:"查询上海的客户的订单"。预期结果:返回 2 行(订单 1 与订单 3,均为张三·上海),合计 2985 元。
限制提示
当前 MVP 每句查询独立处理、不带上下文记忆,追问不能省略条件;省略句可能命中兜底(confidence=0.5),SQL 结果需要人工核对。

常见问题

为什么返回里既有 SQL 又有表格?

AIP 强调链路透明:intent 告诉你平台怎么理解问题,sql_query 告诉你生成了什么 SQL,query_result 是真实执行结果。这样结果对不上时你能直接排查,而不是对着黑盒猜。

confidence(置信度)是什么?

意图识别给出的把握程度:data_query 命中关键词通常 0.8,兜底时 0.5,知识问答 0.9。置信度低不代表查不了,但建议多核对一次 SQL 与结果。

问"什么是客户"会怎样?

会命中 knowledge_qa(知识问答)意图,返回占位提示"知识问答类问题暂由知识库处理,当前最小链路仅支持数据查询与分析"。因为 MVP 还没接知识库。

换了一个数据源后为什么查不到?

NLQ 默认从第一个活跃数据源取数(activeDataSources 的首个)。切换数据源后需要:确认新数据源 Active 标记、重新导入元数据,再回来查询。

返回里的 fix_attempts 是什么?

是"SQL 失败自动修复次数"。生成 SQL 后执行出错时,平台会把错误信息 + 可用列(schema)回喂给 LLM 重写,最多修 2 次(共最多执行 3 次)。fix_attempts=0 表示一次执行就成功。

"谁在何时查了什么"和"AI 怎么决策的"都能查吗?

都能。管理员在审计日志(/admin/audit)按 NLQ_QUERY 事件查"谁查了什么、查了几行";在 AI 决策审计(/admin/ai-audit)按 trace_id 回放一次查询的 retrieve→generate→execute 三步决策链(输入→检索上下文→生成 SQL→执行结果)。

演示数据会重置吗?

会。aip_demo_warehouse(SQLite)每次启动服务都会删除重建,订单日期相对启动时刻生成;平台元数据与审计日志则持久保存。练习查询放心用,人工改数据没意义。

主题小结

一句话:NLQ 入门就是把"一句话问数"用起来——明细、聚合、分组都能查,链路全透明可核对。记住四个边界:每句独立无上下文、意图靠关键词规则、RAG 靠关键词非向量、Text2SQL 依赖外部 LLM。问得越直白、条件带得越全,体验越稳。