使用 LangChain + Higress + Elasticsearch 构建 RAG 应用

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
可观测监控 Prometheus 版,每月50GB免费额度
应用实时监控服务-应用监控,每月50GB免费额度
简介: 本文介绍了如何利用LangChain、Higress和Elasticsearch快速构建RAG(检索增强生成)应用,实现企业知识的智能检索与问答。首先通过LangChain解析Markdown文档并写入Elasticsearch,接着部署Higress AI网关并配置ai-search插件以整合私有知识库与在线搜索功能。最后,通过实际案例展示了RAG查询流程及结果更新机制,确保内容准确性和时效性。文章还提供了相关参考资料以便进一步学习。

1.gif


推荐点击文章底部的阅读原文以获得更好的阅读体验,包括显示文章外链和查看高清插图。


RAG(Retrieval Augmented Generation,检索增强生成) 是一种结合了信息检索与生成式大语言模型(LLM)的技术。它的核心思想是:在生成模型输出内容之前,先从外部知识库或数据源中检索相关信息,然后将这些信息作为上下文输入给生成模型,从而提升生成内容的准确性、时效性和相关性。


在本文中,我们将使用 LangChain、Higress 和 Elasticsearch 来构建一个 RAG 应用。本文所使用的代码可以在 Github 上找到:https://github.com/cr7258/hands-on-lab/tree/main/gateway/higress/rag-langchain-es


什么是 Higress?


Higress 是一款云原生 API 网关,内核基于 Istio 和 Envoy,可以用 Go/Rust/JS 等编写 Wasm 插件,提供了数十个现成的通用插件。Higress 同时也能够作为 AI 网关,通过统一的协议对接国内外所有 LLM 模型厂商,同时具备丰富的 AI 可观测、多模型负载均衡/fallback、AI token 流控、AI 缓存等能力。



什么是 Elasticsearch?


Elasticsearch 是一个分布式搜索与分析引擎,广泛用于全文检索、日志分析和实时数据处理。Elasticsearch 在 8.x 版本中原生引入了向量检索功能,支持基于稠密向量和稀疏向量的相似度搜索。


什么是 LangChain?


LangChain 是一个开源框架,旨在构建基于大语言模型(LLM)的应用程序。其核心理念是通过将多个功能组件“链”式组合,形成完整的业务流程。例如,可以灵活组合数据加载、检索、提示模板与模型调用等模块,从而实现智能问答、文档分析、对话机器人等复杂应用。


在本文中,我们将仅使用 LangChain 的数据加载功能,RAG 检索能力由 Higress 提供的开箱即用的 ai-search 插件实现。ai-search 插件不仅支持基于 Elasticsearch 的私有知识库搜索,还支持 Google、Bing、Quark 等主流搜索引擎的在线检索,以及 Arxiv 等学术文献的搜索。


RAG 流程分析


数据预处理阶段


在进行 RAG 查询之前,我们首先需要将原始文档进行向量化处理,并将其写入 Elasticsearch。在本文中,我们的文档是一份 Markdown 格式的员工手册,我们使用 LangChain 的 MarkdownHeaderTextSplitter 对文档进行处理。MarkdownHeaderTextSplitter 能够解析 Markdown 文档的结构,并根据标题将文档拆分。


Elasticsearch 支持内置的 Embedding 模型,本文将使用 Elasticsearch 自带的 ELSER v2 模型(Elastic Learned Sparse EncodeR),该模型会将文本转换为稀疏向量。建议将 ELSER v2 模型用于英语文档的查询,如果想对非英语文档执行语义搜索,请使用 E5 模型。


查询阶段


检索增强生成(RAG)是一个多步骤的过程,首先进行信息检索,然后进入生成阶段。其工作流程如下:


输入查询首先,从用户的输入查询开始,例如用户提出的问题。

信息检索然后,Higress 的 ai-search 插件会从 Elasticsearch 中检索相关信息。ai-search 插件结合语义搜索和全文搜索,使用 RRF(Reciprocal Rank Fusion)进行混合搜索,从而提高搜索的准确性和相关性。

提示词生成Higress 将检索到的文档与用户的问题一起,作为提示词输入给 LLM。

文本生成LLM 根据检索到的信息生成文本回答,这些回答通常更加准确,因为它们已经通过检索模型提供的补充信息进行了优化。



稀疏向量和稠密向量


这里顺便介绍一下稀疏向量和稠密向量的区别。稀疏向量(Sparse Vectors)和稠密向量(Dense Vectors)是两种常见的向量表示形式,在机器学习、搜索和个性化推荐等场景中都广泛使用。


  • 稠密向量(Dense Vectors)是指在向量空间中,几乎所有的元素都有值(非零)。每个向量元素通常代表了某一特定特征或维度,稠密向量的维度通常较高(如 512 维或更高),并且每个维度的数值都有一定的实际意义,通常是连续的数值,反映了数据的相似度或特征的权重。


  • 稀疏向量(Sparse Vectors)稀疏向量则是指在向量空间中,大多数元素为零,只有少数元素为非零值。这些非零值通常代表了向量中某些重要特征的存在,尤其适用于文本或特定特征的表示。例如,在文本数据中,词袋模型(Bag of Words)就是一个稀疏向量的典型例子,因为在大多数情况下,文本中并不会出现所有可能的词汇,仅有一小部分词汇会出现在每个文档中,因此其他词汇对应的向量值为零。



在 Elasticsearch 中使用稀疏向量进行搜索感觉类似于传统的关键词搜索,但略有不同。稀疏向量查询不是直接匹配词项,而是使用加权词项和点积来根据文档与查询向量的对齐程度对文档进行评分。



部署 Elasticsearch


在代码目录中,我准备了 docker-compose.yaml 文件,用于部署 Elasticsearch。执行以下命令启动 Elasticsearch:


docker-compose up -d


在浏览器输入 http://localhost:5601 访问 Kibana 界面,用户名是 elastic,密码是 test123


部署 Embedding 模型


Elasticsearch 默认为机器学习(ML)进程分配最多 30% 的机器总内存。如果本地电脑内存较小,可以将 xpack.ml.use_auto_machine_memory_percent 参数设置为 true,允许自动计算 ML 进程可使用的内存占比,从而避免因内存不足而无法部署 Embedding 模型的问题。


KibanaDev Tools 中,执行以下命令进行设置:


PUT _cluster/settings
{
  "persistent": {
    "xpack.ml.use_auto_machine_memory_percent": "true"
  }
}


在 Kibana 上访问 Machine Learning -> Model Management -> Trained Models,点击 Download 下载模型,然后点击 Deploy 部署模型。



创建索引映射


在写入数据之前,需要先创建索引映射。其中,semantic_text 字段用于存储稀疏向量,以支持语义搜索;content 字段则用于存储原始文本内容,以支持全文搜索。


在写入数据时,只需写入原始文本。通过 copy_to 配置,content 字段中的文本会自动复制到 semantic_text 字段,并由推理端点进行处理。如果未显式指定推理端点,semantic_text 字段会默认使用 .elser-2-elasticsearch,这是 Elasticsearch 为 ELSER v2 模型预设的默认推理端点。



PUT employee_handbook
{
"mappings": {
    "properties": {
      "semantic_text": { 
        "type": "semantic_text"
      },
      "content": { 
        "type": "text",
        "copy_to": "semantic_text"
      }
    }
  }
}


解析文档并写入 Elasticsearch


安装 LangChain 相关依赖包:


pip3 install elasticsearch langchain langchain_elasticsearch langchain_text_splitters


以下是相关的 Python 代码:


  • MarkdownHeaderTextSplitter 是 LangChain 提供的用于解析 Markdown 文件的工具,它能够根据标题将 Markdown 文档进行拆分。
  • 在索引内容时,LangChain 会为每个文档计算哈希值,并记录在 RecordManager 中,以避免重复写入。在本文中,我们使用了 SQLRecordManager,它将记录存储在本地的 SQLite 数据库中。
  • 使用 ElasticsearchStore 将文档写入 Elasticsearch,只写入 content 字段(原始文本内容),并将 cleanup 模式设置为 full。该模式可以确保无论是删除还是更新,始终保持文档内容与向量数据库中的数据一致。关于文档去重的几种模式对比,可以参考:How to use the LangChain indexing API。



from langchain_text_splitters import MarkdownHeaderTextSplitter
from elasticsearch import Elasticsearch
from langchain_elasticsearch import ElasticsearchStore
from langchain_elasticsearch import SparseVectorStrategy
from langchain.indexes import SQLRecordManager, index

# 1. 加载 Markdown 文件并按标题拆分
with open("./employee_handbook.md") as f:
    employee_handbook = f.read()

headers_to_split_on = [
    ("#", "Header 1"),
    ("##", "Header 2"),
    ("###", "Header 3"),
]

markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on, strip_headers=False)
docs = markdown_splitter.split_text(employee_handbook)

index_name = "employee_handbook"

# 2. 使用 RecordManager 去重
namespace = f"elasticsearch/{index_name}"
record_manager = SQLRecordManager(
    namespace, db_url="sqlite:///record_manager_cache.sql"
)
record_manager.create_schema()

# 3. 写入 Elasticsearch,只写入 content 字段(原始文本)
es_connection = Elasticsearch(
    hosts="https://localhost:9200",
    basic_auth=("elastic", "test123"),
    verify_certs=False
)

vectorstore = ElasticsearchStore(
    es_connection=es_connection,
    index_name=index_name,
    query_field="content",
    strategy=SparseVectorStrategy(),
)

index_result = index(
    docs,
    record_manager,
    vectorstore,
    cleanup="full",
)

print(index_result)


执行以下内容解析 Markdown 文件并写入 Elasticsearch:


python3 load-markdown-into-es.py


输入如下,Markdown 文件被拆分成了 22 个文档写入了 Elasticsearch。


{'num_added': 22, 'num_updated': 0, 'num_skipped': 0, 'num_deleted': 0}


我们可以先使用 LangChain 的 similarity_search 来测试查询效果。由于其默认的查询语句没有使用我们想要的 RRF 混合搜索,因此需要自定义查询语句。后续在使用 Higress 的 ai-search 插件时,也会采用相同的 RRF 混合搜索方式。



def custom_query(query_body: dict, query: str):
    new_query_body = {
        "_source": {
            "excludes": "semantic_text"
        },
        "retriever": {
            "rrf": {
                "retrievers": [
                    {
                        "standard": {
                            "query": {
                                "match": {
                                    "content": query
                                }
                            }
                        }
                    },
                    {
                        "standard": {
                            "query": {
                                "semantic": {
                                    "field": "semantic_text",
                                    "query": query
                                }
                            }
                        }
                    }
                ]
            }
        }
    }
    return new_query_body


results = vectorstore.similarity_search("What are the working hours in the company?", custom_query=custom_query)
print(results[0])


返回内容如下,可以看到准确匹配到了工作时间的相关文档,公司的上午 9 点到下午 6 点。



page_content='## 4. Attendance Policy
### 4.1 Working Hours
- Core hours: **Monday to Friday, 9:00 AM – 6:00 PM**
- Lunch break: **12:00 PM – 1:30 PM**
- R&D and international teams may operate with flexible schedules upon approval' metadata={'Header 3': '4.1 Working Hours', 'Header 2': '4. Attendance Policy'}


部署 Higress AI 网关


仅需一行命令,即可快速在本地搭建好 Higress AI 网关。


curl -sS https://higress.cn/ai-gateway/install.sh | bash


在浏览器中输入 http://localhost:8001 即可访问 Higress 的控制台界面。配置好 Provider 的 ApiToken 后,就可以开始使用 Higress AI 网关了。这里以通义千问为例进行配置。




Higress AI 网关已经帮用户预先配置了 AI 路由,可以根据模型名称的前缀来路由到不同的 LLM。使用 curl 命令访问通义千问:



curl 'http://localhost:8080/v1/chat/completions' \
      -H 'Content-Type: application/json' \
      -d '{
        "model": "qwen-turbo",
        "messages": [
          {
            "role": "user",
            "content": "Who are you?"
          }
        ]
      }'


返回内容如下,可以看到成功收到了来自通义千问的响应。



{
  "id": "335b58a1-8b47-942c-aa9e-302239c6e652",
"choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "I am Qwen, a large language model developed by Alibaba Cloud. I can answer questions, create text such as stories, emails, scripts, and more. I can also perform logical reasoning, express opinions, and play games. My capabilities include understanding natural language and generating responses that are coherent and contextually appropriate. How can I assist you today?"
      },
      "finish_reason": "stop"
    }
  ],
"created": 1745154868,
"model": "qwen-turbo",
"object": "chat.completion",
"usage": {
    "prompt_tokens": 12,
    "completion_tokens": 70,
    "total_tokens": 82
  }
}


配置 ai-search 插件


接下来在 Higress 控制台上配置 ai-search 插件,首先需要将 Elasticsearch 添加到服务来源中,其中 192.168.2.153 是我本机的 IP 地址,请用户根据实际情况修改。



添加完服务来源后,可以在服务列表中找到服务名称(Service Name),在本例中是 elasticsearch.static



接下来在通义千问的这条 AI 路由中配置 ai-search 插件。



点击 AI 搜索增强 插件:



填入以下配置:


searchFrom:
-type:"elasticsearch"
serviceName:"elasticsearch.static"
username:"elastic"
password:"test123"
index:"employee_handbook"
contentField:"content"
semanticTextField:"semantic_text"


RAG 查询


配置好 ai-search 插件后,就可以开始进行 RAG 查询了。让我们先询问一下公司的工作时间是怎么规定的。



curl 'http://localhost:8080/v1/chat/completions' \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "qwen-turbo",
    "messages": [
      {
        "role": "user",
        "content": "What are the working hours in the company?"
      }
    ]
  }'


返回内容如下,工作时间是上午 9 点到下午 6 点。



{
  "id": "c10b9d68-2291-955f-b17a-4d072cc89607",
"choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "The working hours in the company are as follows:\n\n- Core hours: Monday to Friday, 9:00 AM – 6:00 PM.\n- Lunch break: 12:00 PM – 1:30 PM.\n\nR\u0026D and international teams may have flexible schedules upon approval."
      },
      "finish_reason": "stop"
    }
  ],
"created": 1745228815,
"model": "qwen-turbo",
"object": "chat.completion",
"usage": {
    "prompt_tokens": 433,
    "completion_tokens": 63,
    "total_tokens": 496
  }
}


原始文档的内容可能会随着时间的推移而发生变化。接下来,让我们修改 employee_handbook.md 文件中的工作时间,改成上午 8 点到下午 5 点。


然后重新执行 load-markdown-into-es.py 脚本,这次可以看到有一个文档被更新了。


{'num_added': 1, 'num_updated': 0, 'num_skipped': 21, 'num_deleted': 1}


再次询问相同的问题,可以看到返回的答案也相应地更新了。


{
  "id": "39632a76-7432-92ab-ab86-99a04f211a0d",
"choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "The working hours in the company are as follows:\n\n- **Core hours**: Monday to Friday, 8:00 AM – 5:00 PM.\n- There is a lunch break from **12:00 PM – 1:30 PM**.\n- R\u0026D and international teams may have flexible schedules, but this requires approval.\n\nToday's date is April 21, 2025, so these working hours are still applicable."
      },
      "finish_reason": "stop"
    }
  ],
"created": 1745228667,
"model": "qwen-turbo",
"object": "chat.completion",
"usage": {
    "prompt_tokens": 433,
    "completion_tokens": 95,
    "total_tokens": 528
  }
}


总结


本文通过实际案例演示了如何利用 LangChain、Higress 和 Elasticsearch 快速搭建 RAG 应用,实现企业知识的智能检索与问答。通过 Higress 的 ai-search 插件,用户可以轻松集成在线搜索和私有知识库,从而打造高效、精准的 RAG 应用。


参考资料


相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
相关文章
|
8月前
|
前端开发 机器人 API
前端大模型入门(一):用 js+langchain 构建基于 LLM 的应用
本文介绍了大语言模型(LLM)的HTTP API流式调用机制及其在前端的实现方法。通过流式调用,服务器可以逐步发送生成的文本内容,前端则实时处理并展示这些数据块,从而提升用户体验和实时性。文章详细讲解了如何使用`fetch`发起流式请求、处理响应流数据、逐步更新界面、处理中断和错误,以及优化用户交互。流式调用特别适用于聊天机器人、搜索建议等应用场景,能够显著减少用户的等待时间,增强交互性。
2143 2
|
27天前
|
SQL 机器学习/深度学习 监控
构建数据中枢:数据中台指标体系如何赋能企业运营
杭州奥零数据科技有限公司成立于2023年,专注于数据中台业务,维护开源项目AllData并提供商业版解决方案。AllData提供数据集成、存储、开发、治理及BI展示等一站式服务,支持AI大模型应用,助力企业高效利用数据价值。
|
2月前
|
存储 人工智能 自然语言处理
LangChain RAG入门教程:构建基于私有文档的智能问答助手
本文介绍如何利用检索增强生成(RAG)技术与LangChain框架构建基于特定文档集合的AI问答系统。通过结合检索系统和生成机制,RAG能有效降低传统语言模型的知识局限与幻觉问题,提升回答准确性。文章详细展示了从环境配置、知识库构建到系统集成的全流程,并提供优化策略以改进检索与响应质量。此技术适用于专业领域信息检索与生成,为定制化AI应用奠定了基础。
391 5
LangChain RAG入门教程:构建基于私有文档的智能问答助手
|
2月前
|
存储 人工智能 监控
通过Milvus和Langchain快速构建基于百炼大模型的LLM问答系统
阿里云向量检索服务Milvus版是一款全托管向量检索引擎,并确保与开源Milvus的完全兼容性,支持无缝迁移。它在开源版本的基础上增强了可扩展性,能提供大规模AI向量数据的相似性检索服务。凭借其开箱即用的特性、灵活的扩展能力和全链路监控告警,Milvus云服务成为多样化AI应用场景的理想选择,包括多模态搜索、检索增强生成(RAG)、搜索推荐、内容风险识别等。您还可以利用开源的Attu工具进行可视化操作,进一步促进应用的快速开发和部署。
|
8月前
|
存储 运维 监控
超越传统模型:从零开始构建高效的日志分析平台——基于Elasticsearch的实战指南
【10月更文挑战第8天】随着互联网应用和微服务架构的普及,系统产生的日志数据量日益增长。有效地收集、存储、检索和分析这些日志对于监控系统健康状态、快速定位问题以及优化性能至关重要。Elasticsearch 作为一种分布式的搜索和分析引擎,以其强大的全文检索能力和实时数据分析能力成为日志处理的理想选择。
548 6
|
6月前
|
机器学习/深度学习 人工智能 运维
阿里云技术公开课直播预告:基于阿里云 Elasticsearch 构建 AI 搜索和可观测 Chatbot
阿里云技术公开课预告:Elastic和阿里云搜索技术专家将深入解读阿里云Elasticsearch Enterprise版的AI功能及其在实际应用。
425 2
阿里云技术公开课直播预告:基于阿里云 Elasticsearch 构建 AI 搜索和可观测 Chatbot
|
5月前
|
人工智能 自然语言处理 搜索推荐
云端问道12期实操教学-构建基于Elasticsearch的企业级AI搜索应用
本文介绍了构建基于Elasticsearch的企业级AI搜索应用,涵盖了从传统关键词匹配到对话式问答的搜索形态演变。阿里云的AI搜索产品依托自研和开源(如Elasticsearch)引擎,提供高性能检索服务,支持千亿级数据毫秒响应。文章重点描述了AI搜索的三个核心关键点:精准结果、语义理解、高性能引擎,并展示了架构升级和典型应用场景,包括智能问答、电商导购、多模态图书及商品搜索等。通过实验部分,详细演示了如何使用阿里云ES搭建AI语义搜索Demo,涵盖模型创建、Pipeline配置、数据写入与检索测试等步骤,同时介绍了相关的计费模式。
138 3
|
5月前
|
人工智能 算法 API
构建基于 Elasticsearch 的企业级 AI 搜索应用
本文介绍了基于Elasticsearch构建企业级AI搜索应用的方案,重点讲解了RAG(检索增强生成)架构的实现。通过阿里云上的Elasticsearch AI搜索平台,简化了知识库文档抽取、文本切片等复杂流程,并结合稠密和稀疏向量的混合搜索技术,提升了召回和排序的准确性。此外,还探讨了Elastic的向量数据库优化措施及推理API的应用,展示了如何在云端高效实现精准的搜索与推理服务。未来将拓展至多模态数据和知识图谱,进一步提升RAG效果。
195 1
|
6月前
|
弹性计算 自然语言处理 数据库
通过阿里云Milvus和LangChain快速构建LLM问答系统
本文介绍如何通过整合阿里云Milvus、阿里云DashScope Embedding模型与阿里云PAI(EAS)模型服务,构建一个由LLM(大型语言模型)驱动的问题解答应用,并着重演示了如何搭建基于这些技术的RAG对话系统。
|
7月前
|
JSON 数据可视化 NoSQL
基于LLM Graph Transformer的知识图谱构建技术研究:LangChain框架下转换机制实践
本文介绍了LangChain的LLM Graph Transformer框架,探讨了文本到图谱转换的双模式实现机制。基于工具的模式利用结构化输出和函数调用,简化了提示工程并支持属性提取;基于提示的模式则为不支持工具调用的模型提供了备选方案。通过精确定义图谱模式(包括节点类型、关系类型及其约束),显著提升了提取结果的一致性和可靠性。LLM Graph Transformer为非结构化数据的结构化表示提供了可靠的技术方案,支持RAG应用和复杂查询处理。
403 2
基于LLM Graph Transformer的知识图谱构建技术研究:LangChain框架下转换机制实践