项目目录
项目简介
电商问数是一个面向经营分析的自然语言数据查询项目。用户输入“统计华北地区的销售总额”等问题,系统检索相关字段、指标和真实取值,生成并执行 SQL,返回查询结果与处理进度。
如何写进简历
数见 · 连锁零售经营问数平台 | 场景化示例
项目描述:面向连锁零售区域经理和门店运营,汇集订单、实收及退款数据,支持按区域、门店和商品查询净销售额,返回 SQL 与查询结果。
技术栈:Python、FastAPI、LangGraph、MySQL、Qdrant、Elasticsearch
主要工作:
- 三路并发召回:基于
Qdrant与Elasticsearch分别召回字段、指标和真实取值,各路扩词后以独立状态并行返回候选,兼顾经营术语的语义匹配与地区、商品等筛选值检索。 - 指标依赖与关联键补齐:按字段标识合并去重,补齐指标依赖、取值所属字段及候选表主外键,再按表组织生成上下文,为跨门店查询提供计算字段、真实条件值和连接信息。
- SQL 生成与纠错:基于
LangGraph编排生成、预检查和执行,将EXPLAIN错误与原SQL、相关表结构反馈模型进行一次修正,处理生成语句中的语法及字段引用问题。
面试题与回答
如何检索召回数据库的表和字段?
展开收起
核心回答
电商问数把召回拆成字段、指标和字段取值三路:字段找列,指标找计算口径,取值找筛选条件里的真实值。字段和指标使用
Embedding与Qdrant,取值使用Elasticsearch;三路并行完成后,由程序按字段 ID 去重,补齐指标依赖、取值所属字段和候选表主外键,再输出按表组织的table_infos与指标列表metric_infos。例如“统计华北地区的销售总额”,需要同时拿到订单金额字段、销售总额对应的指标定义、地区字段中的“华北”,以及订单表和地区表的连接字段。只召回“销售额”和“华北”两个相似文本,还不足以生成正确查询。
三路分别怎样实现
字段和指标的名称、描述、别名在构建期写入各自的向量集合;配置为需要同步的字段取值写入全文索引。查询时,关键词抽取节点先产生通用关键词,各召回节点再结合原问题生成适合本路的扩展词。
- 字段路:把通用词与字段扩展词合并,逐词生成向量,查询字段集合,再按
ColumnInfo.id去重。它返回字段所属表、类型、角色和说明等信息。 - 指标路:检索指标集合,按
MetricInfo.id去重,保留描述、别名和relevant_columns。例如配置中的GMV依赖fact_order.order_amount,不能只留下指标名称。 - 取值路:用扩展后的词查询
Elasticsearch,得到包含column_id和value的候选。用户说“华北地区”,命中的真实值可能是dim_region.region_name中的“华北”;全文命中仍是候选,不代表任意别名都已准确归一。
三路各写一个状态字段,避免并行更新互相覆盖;随后统一合并。固定版本图编排把这组并行分支与合并节点明确连接起来。
指标依赖和主外键为什么要补
合并节点先用
column_id建字典。命中的指标若依赖尚未召回的字段,就按 ID 从元数据库查询补入;命中的取值也先找到所属字段,再合并进该字段的examples。这样,口径、物理字段和条件值才能对上。随后按
table_id分组,为每张候选表查询配置为主键或外键的字段,去重后加入上下文,最后补上表说明。以销售总额为例,即使字段路漏掉order_amount,指标依赖也能把它补回;当订单表与地区表都进入候选后,再补fact_order.region_id和dim_region.region_id,为连接提供信息。合并节点源码展示了这段处理。这里补的是候选表上的键字段,并没有沿全库关系图自动寻找所有桥接表。若必要的中间表从未进入候选,还要增加关系路径补表设计;补齐字段也不等于已经确认连接方向和聚合粒度。
筛选与取舍
合并后,表字段与指标还会分别经模型精筛。表过滤节点让模型返回保留的表名、字段名,程序再裁剪原结构。这能控制上下文长度,但当前实现没有强制保留全部指标依赖和连接字段;进一步完善时,可以在精筛后做依赖复核,缺失时补回或终止本次生成。
验证时应分别看字段、指标和取值是否命中,合并与过滤后必要依赖是否仍在,以及最终查询结果是否正确。例如金额字段漏召回可以靠指标依赖补齐;地区真实值缺失却不能靠多补几个主键解决。增加召回数量会带来更多噪声,应与完整相关结构、不同候选范围在同一组问题上比较。
对应教程
- 电商问数:关键词抽取与多路召回:回看字段、指标和真实取值的职责,以及各路检索与去重。
- 电商问数:召回信息合并与上下文构建:回看指标依赖、取值归属、主外键补齐,以及两个输出对象。
参考资料
- 国内:飞致云:SQLBot 最佳实践:借鉴表字段说明、关联关系和术语配置,解释只靠字段相似度为什么不足以组织查询上下文。
- 国内:阿里云 Quick BI:数据准备:采用字段描述与真实维值准备的做法,说明语义找列与取值匹配解决不同问题。
- 国内:阿里云 Quick BI:问数知识管理:参考业务定义、同义词、数据范围和选表约束的分工,解释召回前需要限定范围,指标定义也不能由相似度代替。
- 国内:NineData:Text2SQL / Chat2SQL:借鉴字段注释、枚举与表关系的准备和核对方式,说明召回上下文需同时支持筛选、关联与计算。
- 国外:AWS 与 Cisco:Enterprise-grade natural language to SQL generation using LLMs:参考按业务域缩小结构范围、补充描述和业务规则的方案,解释完整结构与按需检索的取舍。
- 国外:Wang 等:RAT-SQL:采用问题与表字段的名称、取值及关系需要共同对齐的分析,解释为何字段命中后仍要补连接信息。
- 字段路:把通用词与字段扩展词合并,逐词生成向量,查询字段集合,再按
生成 SQL 后如何检测全表扫描等慢 SQL?
展开收起
核心回答
电商问数已有的是数据库预检查,还没有自动识别慢查询。生成
SQL后,校验节点向MySQL提交EXPLAIN,利用报错发现部分语法、表名或字段名问题;仓储没有读取返回的执行计划,因此“校验通过”不能说明没有全表扫描或查询足够快。进一步实现慢查询检测,需要读取计划中的访问方式、实际选择的索引、估算行数和连接情况,再结合表规模与查询范围判断风险;执行时还要设置时间和资源限制。小维表全表扫描可能合理,大事实表扫描、关联放大或大量排序才需要结合具体场景处理,不能见到
ALL就一律拒绝。当前预检查怎样接入
generate_sql先把候选查询写入state["sql"],validate_sql调用数仓仓储的validate()。仓储的核心操作是:sql = f"explain {sql}" await self.session.execute(text(sql))没有异常时,校验节点返回
error=None;报错时返回错误文字,条件边据此选择执行或校正。当前图在校正一次后直接进入run_sql,没有再次预检查。数据库仓储与图编排分别说明了检查行为和真实执行顺序。怎样增加计划判断
可以让仓储返回普通
EXPLAIN的结构化结果,由独立步骤提取计划信息,再决定是否执行:- 检查扫描与索引。 看
type、key、估算rows与filtered,区分可能使用的索引和实际选择的索引;不能只看possible_keys。 - 检查整个查询。 看连接条件、中间结果、排序和临时表。即使每张表都用了索引,多次匹配和大量回表也可能让整条查询变慢。
- 按业务预算处理。 结合表规模、数据分区、时间范围与允许延迟判断。风险过高时可以要求缩小范围或使用预聚合数据;改写查询后重新检查口径与计划。
例如“统计华北地区销售总额”需要关联订单事实表与地区维表。地区表很小时,
ALL可能是合理选择;订单事实表若缺少预期过滤、扫描范围很大,则应继续核查连接和索引。不能为了减少扫描,擅自给用户原本查询全部历史的问题加一个月份条件。这些是进一步设计;当前项目还需要增加计划读取、规则判断和失败出口。执行侧的只读权限、超时与结果大小限制也应独立落实,
LIMIT只能约束返回规模,不能保证聚合或排序只处理少量数据。怎样验证判断有效
用同一组查询保留计划、真实耗时和返回结果,比较小维表扫描、大范围事实表查询、缺少连接条件和大量排序等情况。预算阈值应基于实际数据与资源确定,不直接把估算行数当成真实扫描量。
普通
EXPLAIN的估算依赖统计信息;需要测实际行数和耗时时,可在受控环境使用EXPLAIN ANALYZE,但它会真实执行查询。计划判断还应与运行时超时、取消机制配合,避免把估算通过当作运行一定达标。对应教程
- 电商问数:SQL 生成与执行闭环:回看候选查询、数据库反馈、校正与执行;计划读取和风险判断是本文进一步设计。
参考资料
- 国内:JavaGuide:MySQL 执行计划分析:采用访问方式、索引、估算行数和附加操作的解释,组织计划判断所需的信息。
- 国内:JavaGuide:MySQL 索引详解:参考小表扫描、回表成本与覆盖索引的取舍,解释不能仅凭
ALL或“用了索引”判断整条查询。 - 国内:阿里云 DMS:SQL 审核:借鉴规则审核与优化建议的独立分工,说明计划产生后还需要程序判断,而非数据库不报错就直接判定达标。
- 国内:阿里云 DMS:访问控制权限:参考查询、变更及数据范围权限的区分,说明执行授权需要独立于执行计划检查。
- 国外:MySQL 8.4:EXPLAIN Statement:核对普通计划输出、统计信息与
EXPLAIN ANALYZE的真实执行行为,限定预检查和运行测量各能说明什么。 - 国外:PostgreSQL 18:Using EXPLAIN:借鉴计划估算、实际测量和成本单位的区分,解释为何计划成本不能直接当成毫秒或运行保证。
- 检查扫描与索引。 看
AI 生成的 SQL 不符合预期时如何处理?
展开收起
核心回答
先把数据库报错和业务结果错误分开处理。电商问数已有的分支能把
EXPLAIN返回的错误连同原问题、原SQL、表字段、指标和数据库环境交给模型,生成一次修正后的查询;当前图随后直接执行,没有纠错后复验。如果查询能执行但取值、指标或关联不对,就要沿三路召回与上下文合并定位原因,不能只靠数据库报错。进一步完善时,可把修正结果重新送入校验,保留一次纠错上限:复验通过再执行,仍失败则结束并返回具体原因;业务结果另外用参考查询和样例数据核对。
数据库错误怎样反馈给模型
validate_sql调用数仓仓储执行EXPLAIN,成功返回error=None,失败则把异常文字写入error。条件边读取这个字段,选择run_sql或correct_sql。correct_sql同时读取query、table_infos、metric_infos、date_info、db_info、原sql和error,将上下文序列化后组成修正提示词,再用字符串解析器取得纯SQL,覆盖原来的sql。保留原问题与指标定义,是为了让模型修字段或语法时仍有业务依据。校正节点源码展示了这些输入和输出。例如查询华北地区销售总额时,模型若生成不存在的
fact_order.sales_amount,数据库可能反馈未知列。若上下文已经包含配置中的fact_order.order_amount及GMV依赖,修正节点就有依据改回正确字段;若上下文本身缺失金额字段,应先检查指标依赖补齐和过滤结果,而不是要求模型猜一个替代列。能执行但结果不对,查哪一步
仍用“华北地区销售总额”这个问题,可以沿中间结果区分三类问题:
- 条件值不对。 生成了
region_name = '华北地区',而数据保存的是“华北”。这类查询可能通过EXPLAIN但返回空结果,应检查取值路是否命中,以及命中值是否进入所属字段的examples。 - 口径不对。 金额字段存在,却采用了错误的计算或过滤条件。应检查指标定义、依赖字段和生成上下文;业务定义不清时先澄清,数据库不会替业务选择口径。
- 关联不对。 缺少订单与地区的连接条件,或连接方式放大了行数。应核对候选表与键字段是否齐备,再检查实际连接和聚合粒度;“补了主外键”并不自动证明查询正确。
定位后修正相应元数据、召回或生成环节,用人工确认的查询和固定数据比较结果。等价查询不要求字符串完全相同;空结果也可能是真实业务事实,不能为了得到非空结果擅自放宽条件。
怎样增加复验和有限重试
当前固定图的边是
correct_sql → run_sql,因此一次修正不经过第二次数据库预检查。图编排源码可以直接看到这个顺序。若补齐这段流程,可以增加纠错计数,把修正后的查询送回校验节点。以最多纠错一次为例:首次校验失败后记录错误并校正;第二次校验成功才执行,再失败就终止。每次校验覆盖当前
error,并记录本次候选查询与反馈,避免沿用旧错误。权限、允许的语句及资源约束若已接入,也要对修正后的新查询重新检查。连接故障或服务超时也不能一律归入语义纠错:临时连接问题可按调用预算处理,权限拒绝应停止,字段或口径错误则需要修输入或上下文。没有新依据时重复生成同一问题,只会增加模型调用与数据库负载。
对应教程
- 电商问数:SQL 生成与执行闭环:回看数据库错误怎样进入校正提示词,以及校正一次后执行的实际流程。
参考资料
- 国内:NineData:Text2SQL / Chat2SQL:借鉴表列、时间状态、聚合与关联的核对方式,解释查询可执行但结果不符时怎样定位上下文问题。
- 国内:阿里云 Quick BI:数据准备:参考字段描述、维值匹配和结构变化后的更新要求,说明字段或取值错误应修相应知识,而非盲目再生成。
- 国内:飞致云:SQLBot 最佳实践:采用查询范围、表关系、标准查询和术语的准备方式,解释缺表、错误关联和口径混淆的处理分工。
- 国内:JavaGuide:常见 SQL 优化手段:参考连接、执行计划与同条件复测,说明修正后的查询既要保持业务含义,也要检查实际访问代价。
- 国外:LangChain:Build a SQL agent:借鉴错误反馈、查结构、改写后检查再执行的循环,组织本文进一步提出的复验和停止条件;其中模型检查与数据库验证仍需区分。
- 国外:MySQL 8.4:EXPLAIN Statement:依据执行计划和实际运行的区别,说明数据库预检查不能判定真实取值、指标口径与结果是否正确。
- 条件值不对。 生成了