摘要

1) 一句话总结

本文介绍了一种无需更改现有数据库模式或迁移数据,通过ReAct代理智能调度SQL计算与向量语义搜索,在传统SQL数据库上构建高性价比Agentic RAG系统的架构方案。

2) 核心要点

  • 架构目标:在不牺牲性能、不改变Schema的前提下,使传统关系型数据库中的长文本字段(如TEXT/LONGTEXT)具备深度语义理解和结构化计算能力。
  • 数据处理策略:在向量数据库中镜像SQL表中的文本和分类元数据(不含完整内容),对长文档采用滑动窗口分块嵌入,并将元数据附加到每个数据块上。
  • 双工具设计:构建了两个专用工具:search_database(SQL工具,处理计算、聚合、范围筛选)和search_articles(向量工具,处理全局或带过滤条件的语义搜索)。
  • 智能路由机制:ReAct代理根据查询性质决定执行路径,支持纯SQL查询、纯向量查询以及混合查询。
  • 混合查询工作流:针对复杂需求,系统先调用SQL工具进行结构化过滤(如按日期/类别),获取主键(如标题)列表后,再将该列表传递给向量工具进行精准语义搜索。
  • 指令分离原则:严格区分“工具文档字符串”(定义工具功能、参数和类型)与“系统提示词”(指导代理的路由策略和混合工作流),避免指令重叠或冲突。
  • 成本与性能优化:为减少Token消耗和延迟,建议在向量库中保留文本和分类元数据(避免频繁触发两次工具调用的混合查询),而将日期等数字元数据保留在SQL中处理。

3) 风险与不足

  • 系统提示词的脆弱性:系统提示词是该架构中最复杂且最脆弱的组件,必须依赖测试用例库进行设计,并在运营中针对边缘情况不断进行维护和丰富。
  • 后置过滤导致查询失效:如FAISS等向量数据库采用“后置过滤”(先全局语义搜索取top_k,再按元数据过滤),在遇到轻微拼写错误时,目标文档可能因未进入初始检索结果而导致查询失败;相比之下,“前置过滤”更为可靠。
  • 移除向量元数据的成本风险:若不在向量数据库中保留元数据过滤器,简单的带条件语义查询将被迫转为混合查询(需调用两次工具),从而显著增加Token使用量和系统延迟。

正文

如何在不改变现有数据库模式(Schema)、不进行数据迁移且不牺牲性能的情况下,在存储长文本字段的SQL表上实现一个可靠、低延迟且高性价比的RAG(检索增强生成)系统?

这并非一个纯理论问题。在大多数企业中,关键的业务知识已经存在于传统的关系型数据库中。提案、报告、合同、文章——这些通常存储在 TEXT 或 LONGTEXT 列中,其设计初衷是为了关键字匹配和聚合,而非语义检索。

随着大语言模型(LLM)的出现,业务需求已经演变为以自然、对话的方式进行结构化计算、深度语义理解和上下文洞察。例如:

  • 2023年至2025年间,有多少个超过100万美元的项目获得批准?
  • 总结过去6个月内科技领域的主要趋势。
  • 2025年中标提案的差异化优势是什么?

这些问题需要一种智能的检索策略,能够决定何时进行计算、何时进行语义搜索,以及何时将两者结合。本文将展示一种直接在传统SQL数据库之上运行的 Agentic RAG 架构(无需更改 Schema),并探讨使其在生产环境中可靠运行所需的设计原则。

系统设置

为了演示,我们使用了带有NLP元数据的 Social Animal 1万篇文章数据集的子集,其中包含大量新闻文章、博客文章及其元数据。创建的SQL数据库包含以下列:urltitleauthorspublished_datearticle_categoryword_count 以及 full_content

其中,标题(title)可以被视为内容的唯一标识符(主键)。文章类别包括科技、商业、体育、旅游、健康、娱乐、政治和时尚,文章在各类别中分布大致均匀。

系统使用的LLM为 gemini-2.5-flash,并使用 FAISS 来索引和存储向量嵌入(Embeddings)。该设计适用于任何LLM或向量数据库的选择。

系统架构与工具设计

除了对原始文本进行嵌入外,我们在向量存储中镜像了SQL中存在的元数据字段(除了完整内容)。正如我们在结果中将看到的,这允许系统进行过滤。对于长文档,可以采用滑动窗口分块和嵌入策略,并将元数据附加到每个嵌入块上。

我们构建了两个专门的智能工具,ReAct 代理(Agent)可以通过以下架构调用它们。ReAct(路由)代理通过根据查询的性质智能决定调用哪个工具,来编排整个查询流水线。它利用元数据和查询上下文来确定是使用SQL工具、向量工具,还是混合方法最为合适。

具体工具如下:

  • search_database(SQL工具):处理需要计算、聚合或复杂逻辑的问题。它负责执行SQL查询。
  • search_articles(向量工具):处理有关内容、主题或特定实体的问题。接受自然语言查询,并可选择性地接受元数据过滤器,以执行全局语义搜索(例如:“关于儿童的文章”)或在数据子集中搜索(例如:“filter_authors=‘XYZ’” 且 “query=‘文章’”)。

一个查询可以采用以下路径:

  1. 仅使用SQL工具:用于计算(如:有多少篇文章…)、不等式/范围(如:2023年1月至4月间发表的文章)或聚合(如:平均字数是多少…)。
  2. 仅使用向量工具:用于带有或不带有过滤器的语义搜索。
  3. 混合查询:当你同时需要结构化数据(如按日期过滤)和非结构化内容(如对文章进行语义搜索)时,混合查询至关重要。查询具有元数据过滤条件(通常是类别或日期范围),此时先使用SQL工具获取文章,然后将标题列表传递给向量工具,仅对这些文章进行语义搜索。例如:“2023年3月至5月期间,时尚界是否有关于母亲节的文章?”

查询结果与路径示例

以下是各类查询的结果示例,注意观察每种情况下的工具调用路径:

  1. “按年份和类别划分的文章有哪些?” —— 这是一个复杂的计算查询,因此仅使用 SQL工具
  2. “你们有哪些关于儿童的文章?” —— 这与Schema中包含的任何元数据类别都不匹配,因此代理决定使用 向量工具 进行全局语义搜索。
  3. “时尚界的趋势是什么?” —— 代理找到了“类别=时尚”的条件,并使用 向量工具 结合此过滤条件运行语义匹配。
  4. “告诉我2023年关于加密货币的科技文章” —— 这是一个混合查询。首先使用 SQL工具 获取2023年“类别=科技”的标题,然后调用 向量工具(查询词=加密货币,并附带标题列表)。结果将在该子集中被找到。

核心设计考量

与任何架构一样,构建健壮的应用程序需要考虑一些设计原则:

  • 工具文档字符串 (Tool docstrings) vs 系统提示词 (System prompt) 这两种指令以不同方式引导代理行为。必须将它们用于预期目的,避免重叠或冲突,以确保代理性能可靠。

    • 工具文档字符串(位于 @tool 装饰器内)描述了工具的作用及使用方法。除了工具名称外,它还定义了参数、类型和描述。
    • 系统提示词则智能地指导代理的路由策略,使其能够决定何时使用SQL工具、向量工具或两者结合。这也是应用程序中最复杂、最脆弱的组件。它定义了如何在混合工作流中组合工具,提供正确使用工具的示例,并指定强制性规则和约束。设计系统提示词时,必须从预期用户查询的测试用例库开始,在提示词中提供示例,并在运营期间针对边缘情况不断丰富它。
  • 向量数据库的前置过滤与后置过滤 (Pre and Post filtering) 这是一个微妙的问题,可能会对特定查询产生意想不到且难以解释的结果。考虑以下两个查询,唯一的区别是拼写错误:“总结2023年4月17日政治领域关于 Doo ley 的文章” 和 “总结2023年4月17日政治领域关于 Dooley 的文章”。 这两个查询遵循相同的路径:SQL工具成功选出了该类别和日期的标题(只有1篇文章提到了Dooley法官),然后向量工具带着查询词在这个标题列表上被调用。奇怪的是,对于第一个查询,尽管列表中只有1篇文章可供选择,向量工具却因为这个微小的拼写错误返回了“未找到符合条件的文档”;而对于第二个查询,它返回了正确的文章。 原因不仅仅是拼写错误导致嵌入匹配度较弱,而是因为 FAISS(以及 Chroma 等)执行的是后置过滤——先对查询进行全局搜索,然后再根据元数据(即标题列表)过滤结果。在这种情况下,正确的文章在语义搜索后的 top_k = 3 结果中并未出现。相反,前置过滤数据库只会对标题列表中的文章进行语义搜索,即使拼写错误也能找到正确的文章。

  • 是否可以从向量工具中移除所有元数据过滤器? 是的,这是可能的,但这是一个成本更高的选项。因为带有元数据过滤器(如类别或作者)的简单语义查询将变成混合查询,需要两次工具调用,从而增加Token使用量和延迟。一个务实的折中方案是:仅在SQL中保留日期(以及可能的其他数字元数据,如本例中的字数),并在向量数据库中镜像所有文本和分类元数据。

结论

在SQL之上构建RAG不仅仅是添加向量嵌入,而是关于设计正确的检索策略。

当结构化元数据和长篇内容存在于同一张表中时,真正的挑战在于编排——决定何时用SQL计算,何时进行语义搜索,以及何时将两者结合。诸如元数据过滤和工具路由等微妙细节,往往决定了一个系统是可靠运行还是默默失效。

借助精心设计的 Agentic RAG 层,传统的SQL数据库无需进行Schema更改、昂贵的数据迁移或牺牲性能,即可为语义应用程序提供强大的动力。

关联主题