告别大模型知识幻觉!看LangChain如何用检索增强生成打造可靠AI问答系统

基于LangChain和ChromaDB实现的检索增强生成(RAG)系统,通过向量化文档进行语义检索,结合大语言模型能力,实现根据已有知识库准确回答用户问题的智能问答功能。

本文使用技术栈

在本示例中,主要使用了以下技术组件:

    1. LangChain:一个用于构建基于语言模型应用的框架,提供了丰富的组件和工具链。
    1. ChromaDB:一个开源的向量数据库**,用于存储和检索文档的向量表示。
    1. Ollama:本地部署的大语言模型服务,用于生成文本和创建向量嵌入。
    1. 向量检索技术:将文本转换为向量表示,并通过相似度计算实现信息检索。

代码结构详解

环境准备与依赖导入

arduino 复制代码
pip install langchain-chroma
pip install "langserve[all]"
pip install langchain
pip install langchain_community
javascript 复制代码
import os

from langchain_chroma import Chroma
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.documents import Document
from langchain_core.messages import HumanMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnableWithMessageHistory, RunnableLambda, RunnablePassthrough
from langchain_community.chat_models import ChatOllama
from langchain_community.embeddings import OllamaEmbeddings
from langserve import add_routes

这部分代码导入了实现检索问答系统所需的各种库和模块:

  • Chroma:ChromaDB的LangChain集成
  • Document:表示文档的基本数据结构
  • RunnableLambdaRunnablePassthrough:LangChain的可运行组件,用于构建处理链
  • ChatOllamaOllamaEmbeddings:基于Ollama的聊天模型和嵌入模型

配置本地模型服务

ini 复制代码
ollama_base_url = "http://192.168.1.1:11434"  # 默认地址,可根据实际情况修改

# 创建模型
model = ChatOllama(model='deepseek-r1:1.5b', base_url=ollama_base_url)

这里配置了Ollama服务的URL,并初始化了聊天模型。示例中使用了DeepSeek-r1的1.5B参数模型,这是一个相对轻量的中文大语言模型。

准备测试数据

ini 复制代码
# 准备测试数据 ,假设我们提供的文档数据如下:
documents = [
    Document(
        page_content="长城是中国古代的伟大防御工程,绵延数千公里,被誉为世界七大奇迹之一。",
        metadata={"source": "中国名胜古迹文档"},
    ),
    Document(
        page_content="埃菲尔铁塔位于法国巴黎,是世界著名的建筑标志,每年吸引数百万游客参观。",
        metadata={"source": "欧洲地标文档"},
    ),
    Document(
        page_content="金字塔是古埃及的标志性建筑,用作法老的陵墓,展示了惊人的建筑技术。",
        metadata={"source": "古代建筑文档"},
    ),
    Document(
        page_content="泰姬陵是印度著名的白色大理石陵墓,被誉为爱情的象征,建筑风格融合了波斯和印度元素。",
        metadata={"source": "亚洲建筑文档"},
    ),
    Document(
        page_content="自由女神像是美国的国家象征,位于纽约港口,代表自由与民主精神。",
        metadata={"source": "美洲地标文档"},
    ),
    Document(
        page_content="莫愁湖是中国江苏南京的著名景点,以其优美的湖光山色和动人的传说闻名。",
        metadata={"source": "中国名胜古迹文档"},
    ),
    Document(
        page_content="齐得隆咚呛是火星的一个部落。拥有丰富的矿产资源。",
        metadata={"source": "宇宙名胜古迹文档"},
    )
]

为了演示系统功能,创建了包含不同地标信息的文档集合。每个文档包含:

  • page_content:文档的主要内容
  • metadata:元数据信息,包括文档的来源

这里值得注意的是最后一个文档中包含了一个虚构的地方"齐得隆咚呛",这是为了测试系统对未知信息的处理能力。

创建向量存储

ini 复制代码
# 实例化一个向量数空间
vector_store = Chroma.from_documents(documents, embedding=OllamaEmbeddings(model="bge-m3", base_url=ollama_base_url))

这一步是RAG系统的核心部分之一,实现了:

    1. 使用OllamaEmbeddings创建向量嵌入,选择了"bge-m3"模型,这是一个专为中文优化的嵌入模型
    1. 将文档集合转换为向量表示并存储在ChromaDB中
    1. 建立了一个可以进行向量检索的数据库

BGE-M3是北京智源研究院开发的多语言嵌入模型,在中文语义理解方面有很好的表现。

测试相似度查询

bash 复制代码
# 相似度的查询: 返回相似的分数, 分数越低相似度越高
print(vector_store.similarity_search_with_score('齐的隆咚呛'))

这行代码演示了如何使用向量存储进行相似度搜索:

  • • 输入查询词"法国巴黎"
  • • 系统将查询词转换为向量表示
  • • 在向量空间中寻找最相似的文档
  • • 返回文档和相似度分数(分数越低表示相似度越高)

虽然"齐的隆咚呛"并不完全匹配任何文档,但系统会找到语义上最接近的结果,很可能是关于"齐的隆咚呛"的文档。

创建检索器

css 复制代码
# 检索器: bind(k=1) 返回相似度最高的第一个
retriever = RunnableLambda(vector_store.similarity_search).bind(k=1)

print(retriever.batch(['法国巴黎', '金字塔']))

这部分代码:

    1. 创建一个检索器函数,每次查询只返回相似度最高的一个文档(k=1)
    1. 使用batch方法同时查询多个词,展示了批处理能力
    1. 测试了"法国巴黎"和"金字塔"两个查询词

RunnableLambda是LangChain的一个工具,可以将任何函数转换为可运行的组件。这里将ChromaDB的检索功能封装为LangChain可运行组件。

创建提示模板

ini 复制代码
# 提示模板
message = """
使用提供的上下文仅回答这个问题:
{question}
上下文:
{context}
"""

prompt_temp = ChatPromptTemplate.from_messages([('human', message)])

提示模板是指导大语言模型回答问题的关键。这个模板包含:

    1. 明确的指令:"使用提供的上下文仅回答这个问题"
    1. 两个变量:{question}(用户问题)和{context}(检索到的文档内容)
    1. 使用LangChain的ChatPromptTemplate创建了可用于聊天模型的提示格式

这种模板设计确保模型只使用检索到的信息回答问题,减少幻觉(生成不准确信息)的可能性。

构建检索问答链

ini 复制代码
# RunnablePassthrough允许我们将用户的问题之后再传递给prompt和model。
chain = {'question': RunnablePassthrough(), 'context': retriever} | prompt_temp | model

这一行代码构建了完整的检索问答链:

    1. 创建一个字典,包含两个键:
    • question:使用RunnablePassthrough()直接传递用户的输入问题
    • context:使用retriever组件检索相关文档
    1. 将字典传入prompt_temp,填充提示模板中的变量
    1. 最后将填充好的提示传给大语言模型生成回答

这里使用了LangChain的管道操作符|,非常直观地表达了数据流向:输入 → 检索 → 提示填充 → 模型生成。

测试系统

ini 复制代码
resp = chain.invoke('请介绍一下埃菲尔铁塔?')
print(resp.content)
null
ini 复制代码
resp = chain.invoke('请介绍一下齐的隆咚呛?')
print(resp.content)

注意一下这里搜索的是齐的隆咚呛,而文档中为齐得隆咚呛,他也是能通过语义搜索获取到的。

null

至此我们可以成功根据我们自己的文本内容来实现知识库检索了。

其实还有个rerank模型也可以用作优化效果,后面文章我会详细介绍。

下面我总结一下使用到的技术。

向量检索原理详解

文本向量化

在RAG系统中,文本向量化是实现高效检索的基础。这个过程包括:

    1. 文本预处理:清洗文本,去除停用词,进行分词等。
    1. 向量生成:使用嵌入模型将文本转换为高维向量。本例中使用的BGE-M3模型能将文本映射到高维空间,使得语义相似的文本在向量空间中距离更近。
    1. 向量维度:嵌入模型通常会生成几百到几千维度的向量。例如,BGE-M3生成的向量通常是1024维。

向量存储

ChromaDB作为向量数据库,提供了高效的向量存储和检索功能:

    1. 向量索引:ChromaDB使用高效的索引结构(如HNSW、Annoy等)来加速近似最近邻搜索。
    1. 元数据存储:除了存储向量,还保存文档的原始内容和元数据。
    1. 持久化:ChromaDB支持将向量数据持久化到磁盘,便于后续使用。

相似度搜索

向量检索的核心是相似度计算:

    1. 余弦相似度:最常用的相似度度量,计算两个向量之间夹角的余弦值。值域为[-1,1],通常归一化为[0,1]或表示为距离[0,2]。
    1. 欧氏距离:计算向量空间中两点之间的直线距离。
    1. 点积:两个向量的对应元素相乘再求和,也是常用的相似度度量之一。

在本例中,similarity_search_with_score方法返回的分数是基于向量欧式距离的,分数越低表示相似度越高。

语义检索优势

与传统的关键词匹配相比,向量检索(也称为语义检索)有显著优势:

    1. 理解语义:能够捕捉文本的语义而非仅匹配关键词。
    1. 处理同义词:即使用不同词语表达相同概念,也能找到相关文档。
    1. 处理多语言:某些嵌入模型支持跨语言检索。
    1. 模糊匹配:不需要精确匹配即可找到相关内容。

总结

本文详细介绍了一个基于LangChain和ChromaDB实现的智能检索问答系统。该系统通过向量化存储文档信息,利用语义相似度检索相关内容,然后使用大语言模型生成准确回答。

相关推荐
Mintopia19 小时前
OpenClaw 对软件行业产生的影响
人工智能
陈广亮20 小时前
构建具有长期记忆的 AI Agent:从设计模式到生产实践
人工智能
会写代码的柯基犬20 小时前
DeepSeek vs Kimi vs Qwen —— AI 生成俄罗斯方块代码效果横评
人工智能·llm
Mintopia20 小时前
OpenClaw 是什么?为什么节后热度如此之高?
人工智能
爱可生开源社区20 小时前
DBA 的未来?八位行业先锋的年度圆桌讨论
人工智能·dba
叁两1 天前
用opencode打造全自动公众号写作流水线,AI 代笔太香了!
前端·人工智能·agent
是一碗螺丝粉1 天前
LangChain 链(Chains)完全指南:从线性流程到智能路由
前端·langchain·aigc
前端付豪1 天前
LangChain记忆:通过Memory记住上次的对话细节
人工智能·python·langchain
strayCat232551 天前
Clawdbot 源码解读 7: 扩展机制
人工智能·开源