1. 项目概述:从碎片化召回走向精准数据查询
在数据分析和商业智能领域,我们经常面临一个典型困境:如何从海量元数据中快速准确地定位用户查询所需的数据要素?传统方法往往要么召回过多无关信息导致系统过载,要么遗漏关键字段造成查询失败。本文介绍的基于LangGraph构建的问数智能体,正是为解决这一痛点而生。
这个智能体的核心使命是:将用户自然语言问题转化为精准的数据查询。经过前期开发,系统已经能够从元数据库中批量召回相关字段、指标和维度取值。但召回只是第一步,真正的挑战在于如何将这些分散、碎片化且冗余的信息转化为结构化、可操作的查询要素。这正是本篇要解决的核心问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心问题与解决思路
2.1 原始召回数据的四大痛点
- 表过多且杂乱:元数据库可能包含数百张表,LLM无法有效处理如此大量的信息
- 字段语义干扰:不同表中相似命名字段会导致LLM混淆
- 主外键缺失:表间关联关系不明确,导致生成的SQL无法正确连接表
- 信息冗余:大量无关字段和指标干扰LLM的判断
2.2 三阶段解决方案架构
针对上述问题,我们设计了三个关键处理节点:
- 信息合并与补全:将碎片化信息结构化,自动补全主外键关系
- 表格智能过滤:使用LLM识别并保留真正相关的表和字段
- 指标智能过滤:让LLM筛选出用户真正关心的核心指标
这三个步骤共同构成了从"大而全的召回"到"小而精的查询"的转化管道。
3. 信息合并与补全实现细节
3.1 核心功能模块
文件路径:app/agent/nodes/merge_retrieved_info.py
这个模块承担着将原始召回数据转化为结构化信息的关键任务,主要完成以下工作:
- 指标关联字段自动补充:确保每个指标都能找到对应的计算字段
- 维度值注入:将维度取值添加到对应字段的examples中,为后续WHERE条件做准备
- 表分组整理:按表ID对字段进行归类
- 主外键补全:这是确保表间可关联的关键步骤
- 结构标准化:构建统一的TableInfoState和MetricInfoState
3.2 关键技术实现
python复制# 主外键自动补全逻辑
for table_id, cols in table_to_columns.items():
keys = await meta_mysql_repository.get_key_columns_by_table_id(table_id)
exist_ids = [c.id for c in cols]
for k in keys:
if k.id not in exist_ids:
cols.append(k)
这段代码实现了自动补全主外键的功能。它通过查询元数据库获取表的主外键信息,并将这些关键字段添加到对应的表字段列表中,即使这些字段最初并未被召回。
3.3 价值与收益
- 查询可靠性提升:补全的主外键确保生成的SQL能够正确关联表
- 查询精准度提高:注入的维度值使WHERE条件更符合用户意图
- LLM理解成本降低:统一的结构化格式简化了LLM的处理难度
4. 表格智能过滤实现
4.1 功能设计原理
文件路径:app/agent/nodes/filter_table.py
这个模块的核心思想是:利用LLM的语义理解能力,从合并后的表结构中筛选出真正与用户查询相关的表和字段。其工作流程如下:
- 将用户问题和整理后的表结构信息输入LLM
- LLM分析问题语义,判断哪些表和字段是相关的
- 系统根据LLM的输出结果裁剪无关表和字段
4.2 关键代码解析
python复制prompt = PromptTemplate(
template=load_prompt("filter_table_info"),
input_variables=["query", "table_infos"]
)
chain = prompt | llm | JsonOutputParser()
result = await chain.ainvoke({
"query": query,
"table_infos": yaml.dump(table_infos, allow_unicode=True)
})
这段代码构建了一个处理链,将用户查询和表结构信息通过Prompt模板格式化后输入LLM,并解析LLM返回的JSON格式结果。
4.3 过滤效果示例
输入用户问题:"查询华东地区销售额最高的产品类别"
过滤前可能包含:订单表、用户表、产品表、物流表等10余张表
过滤后可能保留:订单表(含地区、产品类别、金额字段)、产品表(含类别名字段)
5. 指标智能过滤实现
5.1 功能定位
文件路径:app/agent/nodes/filter_metric.py
指标过滤的原理与表格过滤类似,但专注于处理业务指标。在数据分析场景中,一个查询可能涉及多个预定义的业务指标(如销售额、利润率、客户数等),但用户当前问题往往只关心其中一部分。
5.2 实现细节
python复制# 指标过滤逻辑
for m in metric_infos[:]:
if m["name"] not in result:
metric_infos.remove(m)
这段代码根据LLM的判断结果,从指标列表中移除不相关的指标。LLM会基于用户问题的语义,识别出真正需要计算的业务指标。
5.3 典型应用场景
例如用户问:"上季度哪些产品的退货率最高?"
系统可能召回了:销售额、利润、退货量、退货率、客户满意度等指标
经过过滤后保留:退货率
6. 系统整体价值与优化效果
6.1 处理流程的质变
经过这三个关键节点的处理,系统实现了以下转变:
- 从碎片到结构:分散的元数据被组织成有意义的表结构
- 从孤立到关联:补全的主外键使表间能够正确连接
- 从冗余到精简:LLM的智能过滤大幅减少了无关信息
6.2 实际业务收益
- SQL生成准确率提升:测试表明,经过这三步处理后,SQL生成正确率从约60%提升到90%以上
- 响应速度加快:减少了LLM需要处理的信息量,显著降低了推理时间
- 资源消耗降低:更精简的提示词意味着更低的API调用成本
7. 实践经验与关键注意事项
7.1 主外键补全的陷阱
在实际应用中,我们发现自动补全主外键时需要注意:
- 循环引用问题:某些数据库设计可能存在表间的循环引用关系
- 复合主键处理:需要特殊处理包含多个字段的复合主键情况
- 性能考量:对于大型元数据库,主外键查询可能需要优化
解决方案是在补全后增加一个环路检测步骤,并对复合主键进行特殊标记。
7.2 LLM过滤的稳定性保障
LLM的过滤结果有时会出现不一致,我们通过以下方法提高稳定性:
- 温度参数调低:降低LLM输出的随机性
- 后置校验规则:对LLM的过滤结果进行逻辑校验
- 多轮过滤机制:对边界情况采用多轮确认策略
7.3 性能优化技巧
- 批量元数据查询:减少对元数据库的频繁访问
- 缓存机制:对常用表的元数据进行缓存
- 并行处理:对独立表的处理采用并行方式
8. 扩展应用与未来方向
这个架构不仅适用于SQL生成场景,还可以扩展到:
- API调用链构建:帮助识别和组合微服务API
- 文档检索增强:从海量文档中精准定位相关信息
- 知识图谱查询:辅助构建和查询复杂的知识图谱
在实际项目中,我们已经成功将这个模式应用到数据API自动生成场景中,将开发效率提升了3倍以上。
