从 Naive 到 Agentic RAG:引入 Elasticsearch 打造工业级混合检索(Hybrid Search)

在上一篇文章中,我们梳理了 RAG 架构从死板的单路召回,演进到具备路由分发(Router)、多跳推理(Multi-Hop)和网络兜底(Web Fallback)的 Agentic RAG。

但在实际业务中,我们遇到了一些向量检索(Milvus)无法解决的痛点。例如:当用户搜索"马铃薯"时,纯语义检索可能会丢失"土豆"或"tomato"的精确匹配;面对专业术语和精确实体(如特定型号、人名全称),纯语义匹配经常出现偏差。

在本篇中,我们将引入 Elasticsearch (ES) ,构建 混合检索(Hybrid Search) 架构。正如图片笔记中所说: "混合检索 = ES 关键词检索 + Milvus 相似度检索。由模型统一融合多路结果,提升专业场景准确率。"


一、 为什么需要 Elasticsearch?

在 RAG 系统中,Mysql 存放原始数据 ,Milvus 提供语义相似度 ,而 ES 则是专职特种兵(关键词检索) 。

我们可以看一个实际的例子:

  • 基于 Milvus 向量检索:用户搜"马铃薯",能召回"土豆"(语义相近)。
  • 纯语义检索的缺陷:搜不到"tomato"(跨语言)或需要精确匹配的专业术语。
  • MySQL LIKE 的问题:%内容% 全表扫描,数据量一大性能就捉急。

因此,我们需要 ES 来解决基于关键词、全文检索的需求。

yaml

bash 复制代码
# docker-compose.yml 编排配置说明
# docker-compose 是用于将多个容器编排在一起的工具
# 指令说明:
# - docker compose up -d : 启动容器,-d 表示后台运行(后台找目录下的 docker-compose.yml 文件并启动)
# - docker compose down  : 停止容器
# - docker compose ps    : 查看容器状态

为了让 ES 跑起来,我们通常会使用 Docker Compose。ES 的默认端口是 9200,而 kibana(类似于 MySQL 的 phpMyAdmin)则是它的可视化查看工具,默认端口 5601。


二、 核心原理:倒排索引 vs 正向索引

ES 相比 MySQL 最大的核心优势,在于其底层基于 倒排索引(Inverted Index) 。

  • MySQL(正向索引) :以行为单位存储完整数据。检索文本内容时,需要逐行遍历,逐字匹配。数据量庞大时,模糊/文本搜索越慢,性能极差。

  • ES(倒排索引) :

    1. 会自动对 text 类型字段进行分词(Tokenization) 。
    2. 拆分成独立词条(Term)。
    3. 以词条为核心,反向关联所有包含该词条的文档。
    4. 用户输入关键词时,ES 直接通过词条快速匹配对应的文档,无需全表遍历,实现海量文本下的毫秒级全文检索。

💡 举个例子 :

输入文本:"可以在浏览器中运行机器学习模型"。

ES 分词后建立索引:[浏览器, 机器学习, 模型, ...]。

用户搜索"机器学习",直接命中索引。


三、 ES 实战:Mapping、分词与 DSL 查询

在理解了原理后,我们直接进入实战。ES 提供了一套 RESTful API,其查询语言被称为 DSL (Domain Specific Language) 。这就如同 MySQL 的 SQL,只不过 ES 是通过 HTTP JSON 来查询的。

1. 创建索引与 Mapping

类似于 MySQL 的建表语句(Schema)。

json

json 复制代码
// PUT /article  (创建索引)
{
  "mappings": {
    "properties": {
      "title": { "type": "text" },    // text 类型:自动分词建倒排索引,用于全文检索
      "content": { "type": "text" },  // text 类型:自动分词建倒排索引,用于全文检索
      "author": { "type": "keyword" } // keyword 类型:不分词,整体精确匹配(常用于过滤、聚合)
    }
  }
}

注释 :text 和 keyword 是 ES 中最常用的数据类型。text 会把"AI开发"分成"AI"和"开发",而 keyword 会保留"AI开发"整体,只能精确匹配。

2. 分词器:IK 中文分词器

中文不像英文天然有空格,ES 默认的 standard 分词器对中文极不友好,通常我们需要安装 IK 分词器。IK 分词器有两种模式:

json

json 复制代码
// POST /_analyze
{
  "analyzer": "ik_max_word", 
  "text": "Elasticsearch RAG 混合检索知识库"
}

注释 :ik_smart 为智能分词,粒度较粗;ik_max_word 则会尽量多的拆分出词组,粒度更细(例如会拆出"知识"、"知识库"),保证更全面的命中率。

3. 检索 API

ES 提供了丰富的检索方式。我们来看最核心的三种:

json

json 复制代码
// 1. match: 全文检索查询
// 会对查询词进行分词,再去倒排索引中匹配
GET /article/_search
{
  "query": {
    "match": { 
      "field": "词" 
    }
  }
}

// 2. term: 精确匹配查询
// 不会分词,要求字段值完全相同(通常用于 keyword 字段)
GET /article/_search
{
  "query": {
    "term": { 
      "author": "AI开发" 
    }
  }
}

// 3. multi_match: 多字段版的 match 查询
// 同时在 title 和 content 中检索查询词
GET /article/_search
{
  "query": {
    "multi_match": {
      "query": "RAG 向量检索",
      "fields": ["title", "content"]
    }
  }
}

注释 :GET /_cat/indices 可以输出所有的索引并列表显示,类似于 SHOW TABLES;。


四、 架构落地:将 ES 整合入 Agentic RAG (LangGraph)

回到我们上篇文章的 rag-webfallback.mjs 等 Agentic RAG 架构。在 LangGraph 的 retrieveNode 节点中,我们不再仅仅调用 vectorStore.similaritySearchWithScore,而是并行发起 Milvus 和 ES 的检索。

以下是混合检索节点的伪代码实现逻辑:

javascript

php 复制代码
// 检索节点:混合检索(向量 + 关键词)
const retrieveNode = async (state) => {
  const { question, k } = state;

  // 1. 走向量数据库 (Milvus)
  const milvusPromise = vectorStore.similaritySearchWithScore(question, k);
  
  // 2. 走 Elasticsearch (关键词检索)
  const esPromise = esClient.search({
    index: 'ebook_collection',
    body: {
      query: {
        multi_match: { 
          query: question,
          fields: ["title", "content"] // 根据ES的Mapping字段匹配
        }
      }
    }
  });

  // 3. 并发执行,节省时间
  const [vectorResults, esResults] = await Promise.all([milvusPromise, esPromise]);

  // 4. 结果融合(融合策略可以是加权打分、RRF Reciprocal Rank Fusion 或交给 Rerank 模型)
  const mergedDocs = fuseResults(vectorResults, esResults);
  // 去重,保证上下文不冗余
  const deduplicatedDocs = mergeUnique(mergedDocs);

  return {
    documents: deduplicatedDocs,
    // ... 其他状态
  };
};

在图片笔记中总结得非常到位:"基于 Langgraph 实现闭环的 Agentic RAG,也就是 Agent 自主决策要不要检索,用什么检索 (web search + milvus + es),信息够不够,效果怎么样,要不要重新搜索"。

五、 总结与展望

从简单的向量匹配到引入 Elasticsearch,我们的 RAG 系统脱胎换骨:

  1. 底层存储:MySQL 存储原始关系数据,ES 基于倒排索引应对全文关键字查询,Milvus 负责语义召回。
  2. API 交互 :通过 DSL (match, term, multi_match) 精准控制关键词检索的粒度。
  3. 架构设计:将 ES 集成入 LangGraph 的节点中,实现混合检索的并发调用,结合评估节点(Evaluator)和网络搜索(Web Search)兜底。

具体业务场景必须设计具体的 Agentic RAG。理解了这个闭环的思路,你就能根据业务特点,像搭积木一样自由组合你的检索武器库,打造出真正能解决用户痛点的大模型应用!

相关推荐
姜楠聊AI转型1 小时前
团队AI编程落地第 8 篇:AI 编程 Rules 规则模板(可直接复制):分层写法 + 5 个原则 + 维护机制
人工智能·ai编程·研发管理·vibe coding·ai coding·coding agent
桃西西呀1 小时前
LangGraph4j之七:子图与组合
人工智能·llm·ai编程
张伽蓝ErosJane1 小时前
AI 客服 Agent 搭建实战:从提示词 Demo 到可治理产线
ai编程
半调子的社畜1 小时前
记账 App 我装了删、删了装,最后自己写了一个:本地语音识别 + R8 踩坑复盘
android·ai编程
桃西西呀1 小时前
LangGraph4j之六:消息状态与消息通道
人工智能·llm·ai编程
程序员鱼皮1 小时前
Personal Agent 是什么?为什么大厂都抢着做?连 DeepSeek Bot 都来了。。
前端·后端·ai编程
追光者492 小时前
同一台机器,从"一整天零产出"到"20 分钟发三篇":我给流水线装了个"等窗口"的闸门
ai编程
xn71332 小时前
Gemini 4 Argon API 迁移清单:百万上下文、Thinking 预算与 Agent 验收
aigc·ai编程
每天都是不一样的太阳2 小时前
AI成长系列-09-提示词资产库
ai编程