Python经营分析与问数

项目目录

项目简介

电商问数是一个面向经营分析的自然语言数据查询项目。用户输入“统计华北地区的销售总额”等问题,系统检索相关字段、指标和真实取值,生成并执行 SQL,返回查询结果与处理进度。

如何写进简历

数见 · 连锁零售经营问数平台 | 场景化示例

项目描述:面向连锁零售区域经理和门店运营,汇集订单、实收及退款数据,支持按区域、门店和商品查询净销售额,返回 SQL 与查询结果。

技术栈:Python、FastAPI、LangGraph、MySQL、Qdrant、Elasticsearch

主要工作:

  • 三路并发召回:基于 Qdrant 与 Elasticsearch 分别召回字段、指标和真实取值,各路扩词后以独立状态并行返回候选,兼顾经营术语的语义匹配与地区、商品等筛选值检索。
  • 指标依赖与关联键补齐:按字段标识合并去重,补齐指标依赖、取值所属字段及候选表主外键,再按表组织生成上下文,为跨门店查询提供计算字段、真实条件值和连接信息。
  • SQL 生成与纠错:基于 LangGraph 编排生成、预检查和执行,将 EXPLAIN 错误与原 SQL、相关表结构反馈模型进行一次修正,处理生成语句中的语法及字段引用问题。

面试题与回答

  1. 如何检索召回数据库的表和字段?

    展开收起

    核心回答

    电商问数把召回拆成字段、指标和字段取值三路:字段找列,指标找计算口径,取值找筛选条件里的真实值。字段和指标使用 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,为连接提供信息。合并节点源码展示了这段处理。

    这里补的是候选表上的键字段,并没有沿全库关系图自动寻找所有桥接表。若必要的中间表从未进入候选,还要增加关系路径补表设计;补齐字段也不等于已经确认连接方向和聚合粒度。

    筛选与取舍

    合并后,表字段与指标还会分别经模型精筛。表过滤节点让模型返回保留的表名、字段名,程序再裁剪原结构。这能控制上下文长度,但当前实现没有强制保留全部指标依赖和连接字段;进一步完善时,可以在精筛后做依赖复核,缺失时补回或终止本次生成。

    验证时应分别看字段、指标和取值是否命中,合并与过滤后必要依赖是否仍在,以及最终查询结果是否正确。例如金额字段漏召回可以靠指标依赖补齐;地区真实值缺失却不能靠多补几个主键解决。增加召回数量会带来更多噪声,应与完整相关结构、不同候选范围在同一组问题上比较。

    对应教程

    参考资料

  2. 生成 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 的结构化结果,由独立步骤提取计划信息,再决定是否执行:

    1. 检查扫描与索引。 看 type、key、估算 rows 与 filtered,区分可能使用的索引和实际选择的索引;不能只看 possible_keys。
    2. 检查整个查询。 看连接条件、中间结果、排序和临时表。即使每张表都用了索引,多次匹配和大量回表也可能让整条查询变慢。
    3. 按业务预算处理。 结合表规模、数据分区、时间范围与允许延迟判断。风险过高时可以要求缩小范围或使用预聚合数据;改写查询后重新检查口径与计划。

    例如“统计华北地区销售总额”需要关联订单事实表与地区维表。地区表很小时,ALL 可能是合理选择;订单事实表若缺少预期过滤、扫描范围很大,则应继续核查连接和索引。不能为了减少扫描,擅自给用户原本查询全部历史的问题加一个月份条件。

    这些是进一步设计;当前项目还需要增加计划读取、规则判断和失败出口。执行侧的只读权限、超时与结果大小限制也应独立落实,LIMIT 只能约束返回规模,不能保证聚合或排序只处理少量数据。

    怎样验证判断有效

    用同一组查询保留计划、真实耗时和返回结果,比较小维表扫描、大范围事实表查询、缺少连接条件和大量排序等情况。预算阈值应基于实际数据与资源确定,不直接把估算行数当成真实扫描量。

    普通 EXPLAIN 的估算依赖统计信息;需要测实际行数和耗时时,可在受控环境使用 EXPLAIN ANALYZE,但它会真实执行查询。计划判断还应与运行时超时、取消机制配合,避免把估算通过当作运行一定达标。

    对应教程

    参考资料

    • 国内:JavaGuide:MySQL 执行计划分析:采用访问方式、索引、估算行数和附加操作的解释,组织计划判断所需的信息。
    • 国内:JavaGuide:MySQL 索引详解:参考小表扫描、回表成本与覆盖索引的取舍,解释不能仅凭 ALL 或“用了索引”判断整条查询。
    • 国内:阿里云 DMS:SQL 审核:借鉴规则审核与优化建议的独立分工,说明计划产生后还需要程序判断,而非数据库不报错就直接判定达标。
    • 国内:阿里云 DMS:访问控制权限:参考查询、变更及数据范围权限的区分,说明执行授权需要独立于执行计划检查。
    • 国外:MySQL 8.4:EXPLAIN Statement:核对普通计划输出、统计信息与 EXPLAIN ANALYZE 的真实执行行为,限定预检查和运行测量各能说明什么。
    • 国外:PostgreSQL 18:Using EXPLAIN:借鉴计划估算、实际测量和成本单位的区分,解释为何计划成本不能直接当成毫秒或运行保证。
  3. 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,并记录本次候选查询与反馈,避免沿用旧错误。权限、允许的语句及资源约束若已接入,也要对修正后的新查询重新检查。

    连接故障或服务超时也不能一律归入语义纠错:临时连接问题可按调用预算处理,权限拒绝应停止,字段或口径错误则需要修输入或上下文。没有新依据时重复生成同一问题,只会增加模型调用与数据库负载。

    对应教程

    参考资料

    • 国内:NineData:Text2SQL / Chat2SQL:借鉴表列、时间状态、聚合与关联的核对方式,解释查询可执行但结果不符时怎样定位上下文问题。
    • 国内:阿里云 Quick BI:数据准备:参考字段描述、维值匹配和结构变化后的更新要求,说明字段或取值错误应修相应知识,而非盲目再生成。
    • 国内:飞致云:SQLBot 最佳实践:采用查询范围、表关系、标准查询和术语的准备方式,解释缺表、错误关联和口径混淆的处理分工。
    • 国内:JavaGuide:常见 SQL 优化手段:参考连接、执行计划与同条件复测,说明修正后的查询既要保持业务含义,也要检查实际访问代价。
    • 国外:LangChain:Build a SQL agent:借鉴错误反馈、查结构、改写后检查再执行的循环,组织本文进一步提出的复验和停止条件;其中模型检查与数据库验证仍需区分。
    • 国外:MySQL 8.4:EXPLAIN Statement:依据执行计划和实际运行的区别,说明数据库预检查不能判定真实取值、指标口径与结果是否正确。