尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

RAG与NL2SQL双通道融合:企业智能问答Agent的架构设计与落地实践

RAG与NL2SQL双通道融合:企业智能问答Agent的架构设计与落地实践 1. 为什么我把RAG和NL2SQL塞进了同一个Agent先说结论如果你正在做企业内部的AI问答助手或者正在纠结知识库和数据库到底该先做哪个这篇文章大概率能帮你少走两个月的弯路。我今年接手了一个内部数据问答项目需求听起来很简单业务人员用自然语言提问系统要能从文档资料和业务数据库两个来源给出准确答案。比如公司今年的差旅报销标准是什么——这属于知识库问题答案在制度文档里而上个月各部门的差旅报销总额是多少——这属于数据库问题答案要从业务表中算出来。一开始我也走过弯路先做了纯RAG方案把文档切碎丢进向量库效果还行。但当用户追问带具体数字和统计口径的问题时RAG就开始一本正经地胡说八道了。后来又单独试了NL2SQL方案让它直接把自然语言转成SQL去查库解决了数字问题但用户问报销标准是什么这种文本问题时NL2SQL根本无从下手。踩了一圈坑之后我确定了一件事真实的业务场景里文本知识和结构化数据是混在一起被提问的。只做RAG数字层面必翻车只做NL2SQL文本层面必翻车。所以最终落地了一个知识库数据库联动的AI Agent方案底层链路是RAG加NL2SQL双通道承载平台选的是PolarDB Agent Express。这篇文章把整个搭建过程、踩坑记录和可以照抄的配置细节全部整理出来。适合谁看正在做企业内部问答助手、报表中心智能问答、客服知识库升级的开发者和架构师。如果你只是刚接触RAG或NL2SQL其中一项这篇文章也能帮你把两块知识串起来。2. 整体架构设计与技术选型2.1 双通道路由先判意图再选通道整个Agent的核心不是某个模型而是意图路由这一层。用户输入的query进来先做一次分类是面向文档文本的知识检索型问题还是面向结构化数据的数据查询型问题还是两者混合的复合型问题。我的做法是用大模型做一次轻量级意图判断给它三段信息用户问题、业务表清单、知识库主题列表让它输出一个JSON结构标明通道选择和置信度。这块不用单独训练模型Prompt设计好配合少量示例就够了。{ intent: database | knowledge | hybrid, confidence: 0.0 ~ 1.0, reason: 简短说明判断依据 }这里有个细节容易被忽略不要用规则去匹配关键词判断意图。比如金额数量不一定就是数据库问题制度标准也不一定是纯知识库问题。规则匹配在样本少的时候看似好用一旦问题变多准确率会断崖式下跌。用大模型做意图分类虽然多了一次模型调用但换来的是稳定性和扩展性。2.2 为什么选PolarDB Agent Express承载整个链路选型阶段我对比过几种方案自建Milvus加MySQL加LangChain用Dify这类低代码平台还有直接用PolarDB Agent Express。最终选了PolarDB Agent Express核心原因是它把向量存储和关系型数据存储放在了同一个数据库里。传统架构里知识库向量通常放Milvus或pgvector业务数据放在MySQL或PostgreSQLAgent要同时访问两个存储中间还得自己做数据一致性管理。而PolarDB Agent Express本身是关系型数据库能力加向量检索能力的一体化方案表和向量索引可以共用一个库知识库分片和业务数据在同一个事务体系下管理。这对于知识库和数据库联动这个场景来说省掉了一大堆中间件。另外它对NL2SQL的支持比较友好。Agent Express能感知库里的表结构、字段注释、枚举值生成SQL时会自动带着这些schema信息做上下文。这意味着我不用手动维护一份表结构说明文档喂给模型数据库层面的元数据会直接参与生成。2.3 向量检索与Embedding模型的选择知识库的召回质量一半取决于切分策略一半取决于Embedding模型。我最后用的是bge-m3系列中文场景下的语义理解比OpenAI的text-embedding-3要稳特别是在处理财务、人事这类专业术语时。PolarDB Agent Express支持自定义Embedding模型的接入方式也可以直接用平台集成的默认模型我建议有条件的团队用bge-m3低成本且效果可复现。Rerank模型我选了bge-reranker-v2-m3。为什么要单独加一层Rerank因为向量召回本质上是近似搜索它找到的是看起来像的内容而不是语义上最相关的内容。Embedding召回Top 30Rerank精排后取Top 5准确率能明显提升。成本也就每次多一次模型推理对于企业内问答场景完全可以接受。3. RAG知识库链路的核心细节3.1 文档切分不要迷信固定窗口知识库的RAG链路我拆成了文档解析、切分、向量化、召回、重排、生成六个环节。其中最影响效果、也最容易翻车的是切分。直接按固定字符数切块是最省事但效果最差的做法。制度文档往往有第X章第X条这样的结构如果硬切会把一条完整的制度条款拦腰截断语义不完整召回率自然上不去。我的切分策略是结构优先、滑动补充先按Markdown标题或文档大纲结构切出语义块如果一个语义块太长再按段落切最后用带重叠的滑动窗口兜底。重叠长度我设置为100个token左右保证相邻切块之间不会丢掉上下文衔接。举一个实测数据在财务报销制度文档上固定300字符切分Rerank后Top 5命中率为62%改成结构优先切分后命中率升到84%。这个差距在最终问答效果上非常明显。3.2 召回与重排的工程细节知识库的召回链路我不是只做一次向量检索就收工而是采用多路召回加统一重排的结构。多路召回的意思是同一个query同时走多条召回通道把结果汇总后再去重、再重排。具体到我的实现里跑了三路召回向量检索用query的Embedding去向量库做相似度搜索取Top 30关键词检索对query做分词后用BM25做倒排召回同样取Top 30标题召回如果query里出现了知识库文档的标题关键词直接把对应文档的所有切块全部捞出来三路结果合并后去重交给Rerank模型统一打分最后取Top 5上下文送入生成模型。这个多路召回加统一重排的价值在于向量检索擅长语义相似关键词检索擅长精确匹配标题召回则能兜住用户直接问某份文档内容的场景。三路互补召回召回率能稳定提升。3.3 知识增强的Prompt模板RAG生成阶段的Prompt模板也需要打磨。我遇到过一个问题模型把检索到的文档原文直接复制粘贴到回答里语气生硬而且没有结合问题本身做归纳。后来我把Prompt结构改成问题加检索上下文加回答约束三段式。回答约束里我写死了几条规则优先引用检索到的文档原文但要用自己的话组织成通俗表达如果检索内容与问题不相关明确说知识库中没有找到相关内容禁止编造涉及金额、日期、人员等关键信息时必须保留原文表述格式回答控制在200字以内不要展开长篇大论这块的经验是约束不用写太多写多了模型会变得畏手畏脚反而影响回答质量。我试过写10条约束的版本结果模型每句话都带着根据文档我认为……这种防御性措辞用户反馈非常差。精简到4条核心约束后回答自然很多。3.4 知识库更新与版本管理知识库不是搭建完就一劳永逸的。制度文档会修订流程说明会变更如果知识库里的向量不更新模型会一直引用过期内容。我在PolarDB Agent Express里把知识库的文档按版本管理每次上传新文档会生成一个新的版本号同时记录文档的生效日期和失效日期。查询时Agent会优先检索当前日期在有效区间内的版本。这个设计在一次人事制度变更时立了大功——旧版报销标准被替换成新版后系统再也没有回答出过期的报销额度。4. NL2SQL链路的核心细节4.1 Schema Linking是SQL生成成败的关键NL2SQL最容易犯的错是让大模型裸写SQL。你给它一句自然语言它基于自己的常识生成SQL结果表名、字段名全是它自己编的。真实的业务表名往往是t_emp_info、dept_code这种大模型根本猜不到。我的做法是先做Schema Linking也就是把用户问题中的业务概念映射到具体的表和字段上。例如用户问各部门报销总额Agent先从schema信息里匹配出报销费用表t_expense、部门维表t_department、金额字段amount、部门名称字段dept_name再把这些映射关系显式地放进Prompt上下文里最后才让模型生成SQL。4.2 表结构元数据的组织方式要让Schema Linking生效需要维护一份高质量的元数据描述。这不是把建表语句直接扔给模型就行——字段名dept_code模型不认识但只要你描述成部门编码关联t_department表的dept_id模型就能正确使用。我在Agent Express中建立了一张schema元数据配置表记录了每个业务表的用途说明、每个字段的业务含义、字段间的关联关系、常用查询的枚举值。比如status字段有哪些值每种值代表什么状态这些信息必须写清楚。NL2SQL生成时Agent先根据用户问题找到相关表再把这几张表的元数据注入Prompt。这里特别提醒枚举值说明一定要写。没有枚举值说明时模型会自己猜比如把status1猜成待审核还是已通过全凭运气。写清楚之后SQL生成的准确率能提升一大截。4.3 Few-shot示例管理即使有了Schema Linking模型在面对一些复杂的统计口径时还是容易出错比如环比同比去重计数只算在职员工等等。这些业务口径光靠字段说明表达不清楚需要few-shot示例来兜底。我的做法是给每个业务主题准备5到10条典型的问答和SQL对。例如问题上个月各部门的报销总额按降序排列 SQLSELECT d.dept_name, SUM(e.amount) AS total_amount FROM t_expense e JOIN t_department d ON e.dept_id d.dept_id WHERE e.expense_date DATE_TRUNC(month, CURRENT_DATE - INTERVAL 1 month) AND e.apply_status approved GROUP BY d.dept_name ORDER BY total_amount DESC生成SQL时Agent会根据用户问题先从示例库中检索相似问题把最相近的几个示例连同schema元数据一起放入Prompt。这本质上也是一种RAG只不过检索对象是问答示例而不是文档片段。这个小技巧让我SQL生成的准确率从71%提高到了89%。4.4 SQL校验与兜底策略模型生成的SQL不可能百分之百正确所以一定要在执行前校验和执行后验证两层做兜底。执行前我先用解析器检查SQL语法再校验涉及的表名和字段是否真实存在。如果语法错误就带着错误信息退回去让模型重新生成最多重试两次。执行后如果SQL查询结果为空不能让Agent直接说没有数据——而应该把空结果作为一个信号触发二次检索重写一条更宽松的SQL再查一次。还有一类问题要特别注意只读控制。给Agent配置的数据库账号必须是只读权限并且禁止生成DELETE、UPDATE、DROP、INSERT这类写操作。我见过不止一个团队在演示时翻了车Agent自作聪明生成了DELETE语句差点把生产表清掉。这不是危言耸听NL2SQL在权限控制上必须从权限层封死防的不只是模型出错还有用户恶意输入。5. 在PolarDB Agent Express上落地实操5.1 环境准备与平台初始化实操部分就直接按我的落地过程一步步说。先准备一个PolarDB Agent Express实例按照控制台的引导完成创建。创建完成后第一件事不是写代码而是把数据库账号分开一个只读账号给NL2SQL链路用权限只有SELECT一个读写账号给知识库的向量索引维护用负责写入切块向量和文档元数据这个账号隔离的做法比在代码层加各种校验要可靠得多。就算某个环节出了问题数据库层面已经限制了破坏范围。5.2 创建知识库并导入文档在Agent Express控制台创建知识库选择向量索引类型和Embedding模型。我选的HNSW索引类型适合中小规模数据集的检索性能与召回平衡。导入文档时需要注意不要一股脑把PDF、Word、Markdown全部塞进去。我建议按照文档类型分批次导入每导入一批就做一次检索测试发现问题及时调整切分策略。批量导入后检查每条切块向量的来源文档ID和切分序号确保原始文档的行号、段落号信息都保留了——这是后面定位检索不准问题时的重要依据。5.3 配置数据库连接与Schema元数据在Agent Express里配置NL2SQL能力时关键步骤是把业务表的元数据录入系统。我的录入顺序是这样的先录入业务表的整体说明这是一张什么表记录什么业务主键是什么再录入每个字段说明字段类型、业务含义、是否可空、枚举值含义最后录入表间关系外键关联、维度表与事实表关系一个可以照抄的实践把元数据直接写在建表注释里。Agent Express能读取表、字段的COMMENT作为NL2SQL的上下文所以建表和改表时一定要写好COMMENT。很多人建表时COMMENT随便写甚至不写等到做NL2SQL时才发现模型根本看不懂表结构只好事后再补元数据配置。5.4 配置Agent技能与联动逻辑Agent Express支持以技能的方式把知识库检索和数据库查询注册成独立能力。我在Agent里配置了两个技能技能一知识库检索。输入是用户问题输出是Top 5相关文档片段加来源引用。技能二数据库查询。输入是用户问题加Schema元数据输出是SQL执行结果加自然语言描述。关键的联动逻辑在Agent的编排层。用户问题进来先过意图路由纯知识问题走技能一返回带引用的文本回答纯数据问题走技能二返回数据报表加解读混合问题先走技能一拿到相关背景再走技能二拿到数据最后把两者融合成完整回答混合问题的融合环节最容易做砸。我一开始是把两段结果简单拼接根据制度规定……背景。数据显示……数据。读起来非常生硬。后来改成让Agent基于背景资料对数据结果做解读比如背景资料说明了差旅报销标准分为三档那么查询结果就按三档分别汇总回答结构直接和背景逻辑对齐阅读体验明显提升。5.5 一个完整联动流程的实测样例用实际项目跑一遍完整的联动流程大家感受会更直观。用户问研发部今年上半年的差旅支出是多少符合公司的报销标准吗第一步意图路由判定为hybrid类型第二步知识库检索出《差旅报销管理制度》中关于报销标准和额度的条款第三步NL2SQL生成SQL查询t_expense表中部门为研发部、时间范围为今年1月到6月的报销金额汇总第四步SQL执行返回结果比如总金额为37.6万元第五步Agent把制度标准与执行结果结合输出回答研发部上半年差旅支出为37.6万元。根据《差旅报销管理制度》研发部属技术序列适用一类城市差旅标准人均每日住宿上限为500元单次出差总额需在预算范围内。建议进一步核对超预算部分。这个回答既有数据支撑又有制度依据完全不是纯RAG或纯NL2SQL能做到的。5.6 性能优化缓存与超时控制联动了知识库和数据库之后响应链路变长性能问题就躲不开了。一次混合查询要经过意图路由、向量检索、Rerank、schema拼接、SQL生成、SQL执行、结果融合完整链路延迟实测在6到9秒之间。我做了几层优化高频问题的问答缓存。同一个问题在24小时内重复提问直接返回缓存结果命中率大约25%Embedding结果缓存。相同或近似query的向量结果直接复用跳过向量检索和Rerank数据库查询超时控制。给SQL执行设置5秒超时超时后Agent主动向用户解释并建议缩小查询范围避免卡住不动的体验优化后缓存命中的场景响应时间降到1.5秒左右未命中的混合查询稳定在6秒上下对于企业内问答场景已经可接受。6. 常见问题与排查实录6.1 知识库检索答非所问这是RAG最常见的翻车点。排查顺序我建议从切分开始看而不是一上来就换Embedding模型。先用一个已知问题去检索看召回的Top 5片段到底是什么。如果片段内容本身是正确的但上下文被截断了那就是切分问题如果召回的东西完全跑偏比如用户问晋升条件召回的全是招聘要求那才是Embedding或检索策略问题。我这里遇到的典型案例是用户问年假怎么休召回了很多包含年假字样的片段但每段都是当年假与婚假、产假重叠时……这类无关内容。问题出在文档里年假主要作为条件短语出现真正定义年假规则的那一段反而用了带薪休假这个词。解决办法是在关键词召回通道里加入同义词扩展把年假和带薪休假关联起来同时把标题召回通道的权重提高。6.2 NL2SQL生成的SQL一直报错SQL报错的原因大部分不是模型笨而是给的上下文不够。第一次上线时生成SQL的报错率高达30%几乎都是表名不存在或字段名拼错。我把报错信息收集起来逐个和schema元数据分析对比发现相当一部分错误是因为模型在拼接JOIN条件时把维度表的主键id直接拿来关联但事实表里存的外键字段名完全不同。解决办法是在schema元数据中增加关联关系的明确描述而不是让模型自己推断。我把每张表的JOIN关系、关联字段、关联类型都写清楚比如t_expense.dept_id t_department.dept_id多对一关系这个配置加上之后SQL报错率降到了9%左右。6.3 混合问答时回答结构混乱知识库资料和数据结果融合之后回答经常出现前面说制度中间跳数据末尾又回到制度的混乱结构。我的处理方式是通过Prompt约束输出格式要求Agent按固定三段式组织混合回答先说结论再给数据依据最后补充制度或知识背景。另外回答中涉及具体数据时我会要求Agent用表格或列表呈现而不是写成大段散文。比如研发部37.6万市场部28.3万产品部21.8万这种信息一行一个部门加一个金额比堆在段落里清楚得多。这个排版细节用户反馈好评度非常高。6.4 数据库数据更新了回答还是老数据排查这个问题的思路要从整条链路去找缓存。我在实践中发现过三层原因第一层是对话级别的缓存同一问题的结果被缓存了第二层是SQL执行结果被Agent内部的中间状态缓存第三层是业务方以为数据已更新但数据库的数据同步延迟还没完成。我建议在Agent配置中关闭SQL结果的跨会话缓存只保留对话内的短期缓存。同时在数据库写入端触发一个数据变更事件当业务表有增量更新时自动清理相关的对话缓存。这块做得好不好直接决定用户对系统实时性的信任度。最后再分享一个我自己踩过的坑在整套系统上线前我一直把精力花在模型选型和Prompt调优上忽略了一个最基础的问题知识的源头质量。有一次制度文档里本身就存在新旧版本矛盾的内容无论我怎么调切分参数、怎么优化PromptRAG的回答永远在旧版本说可以新版本说不可以之间摇摆。后来逐一比对了文档源文件删除过期版本后问题立刻消失了。所以我的体会是AI Agent的能力上限永远取决于你喂给它的知识质量和数据质量。模型、框架、调参都是放大器而源头的信息准确性才是地基。这个项目做完之后我把知识库的文档准入审核流程建起来了所有上架文档必须经过业务负责人签字确认效果比任何技术优化都明显。如果你是刚开始做类似项目建议先把源头质量规范定好再谈RAG和NL2SQL的联动调优顺序反了的话后面全是无底洞。
返回列表