一、RAG概述

RAG(Retrieval-Augmented Generation,检索增强生成) 是一种结合检索系统和生成式大语言模型的问答方法。它能有效缓解大模型“遗忘”和“幻觉”问题,提升回答的准确性与可靠性。

当用户提出问题时,RAG 会:1、先从外部知识库中检索与问题相关的信息; 2、然后将这些信息与用户的问题合并成一个提示(Prompt); 3、最后将提示输入到大模型中生成答案。
在这里插入图片描述

相比传统的生成方式,RAG 方法具备以下优势:1、利用了外部知识库,能回答超出模型训练范围的问题; 2、提升了答案的可解释性; 3、更便于实现个性化或特定领域的问答系统。

RAG 的典型流程

RAG 通常分为两个阶段:

1.数据准备阶段

  • 数据收集:从网页、文档等来源获取原始文本;
  • 文本分割:将长文本切分为适合检索的段落;
  • 向量化:使用 embedding模型将文本转为向量;
  • 数据入库:将向量存储在向量数据库中(如 Chroma等)。

2.应用阶段

  • 用户提问;
  • 系统从向量库中检索相关段落;
  • 将上下文注入 Prompt 中;
  • 交由大语言模型生成最终答案。

在这里插入图片描述

二、项目结构说明

在本项目中,我们将使用以下组件来构建一个本地运行的 RAG 系统:

  • LangChain:用于编排数据流和调用组件;
  • HuggingFace Embedding 模型:用于文本向量化;
  • Chroma 向量数据库:用于存储和检索文本向量;
  • Ollama 本地大模型:用于调用大模型生成答案;
  • 本地知识库文件夹:作为信息源。

目录结构如下:

├── naiveRAG.py         # 主程序代码
├── chroma_naive_db/    # 向量数据库(自动生成)
└── my_knowledge/       # 存放知识库文本
    └── 1.txt     # 示例文档(如从百度百科复制图灵相关内容粘贴进去)

三、代码解析

3.1 模型初始化

llm = OllamaLLM(model="qwen3:0.6b")
embedding_model = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5")

OllamaLLM:调用的是本地部署的大语言模型,例如 qwen3:0.6b。需要先在本地部署并运行的大模型。
HuggingFaceEmbeddings:这是向量化模型,用于将文本转换为高维向量。这里我们选用了表现优异的中文嵌入模型 bge-small-zh-v1.5

3.2 构建知识库

file_dir = Path('my_knowledge') # 加载本地文档(如 .txt 文件)
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100) # 使用 RecursiveCharacterTextSplitter 分段
vector_store = Chroma(embedding_function=embedding_model,  persist_directory="./chroma_naive_db") # 利用中文 embedding 模型向量化,并存入本地向量数据库(Chroma)
retriever = vector_store.as_retriever(search_kwargs={"k": 5}) # 检索5条相关信息

file_dir:设置知识库所在目录。
text_splitter:将文本切成每段最多 500 字,重叠 100 字,避免因分段切断语义。
Chroma:向量数据库,存储嵌入后的文本;persist_directory 指定本地持久化的数据库路径。
retriever:封装检索器,设置为返回 top-5 最相似文本段落。

3.3 加载器与 Prompt 模板定义

text_loader_utf8 = partial(TextLoader, encoding='utf-8')
prompt_template = PromptTemplate.from_template("""
你是一个严谨的RAG助手。请根据以下提供的上下文信息来回答问题。
要求:先回答用户的问题,如果回答中使用了上下文信息,在回答后输出使用了哪些上下文。
-----------------------------------
上下文信息如下:{context}
问题:{question}
""")

text_loader_utf8:为了支持中文,我们显式指定编码格式为 UTF-8。
prompt_template:定义输入给大模型的提示词模板。模板中会将上下文和用户问题注入。

3.4 构建 LangChain 编排链

chain = (
    {"question": RunnablePassthrough()}
    | RunnablePassthrough.assign(context=itemgetter("question") | retriever)
    | prompt_template
    | llm
    | StrOutputParser()
)

编排步骤详解:
用户输入问题 → {“question”: RunnablePassthrough()}
保持原样传递给下一环节。
检索上下文信息 → assign(context=itemgetter(“question”) | retriever)
根据输入的问题,从向量库中检索相似的文本段,作为上下文 context。
注入模板生成 Prompt → prompt_template
将 context 和 question 注入上文定义的 prompt 模板中。
调用大语言模型生成回答 → llm
将 prompt 输入到本地模型中获取输出。
解析结果 → StrOutputParser()
抽取并返回字符串形式的结果。

3.5 主函数

if __name__ == '__main__':
    if len(vector_store.get()["ids"]) == 0:
        print("首次运行,正在初始化知识库...")
        docs = DirectoryLoader(str(file_dir), loader_cls=text_loader_utf8).load()
        docs = text_splitter.split_documents(docs)
        vector_store.add_documents(docs)
        print("知识库构建完成。")
    else:
        print("已加载知识库,跳过初始化。")

    question = "图灵的论文标题是什么?"
    answer = chain.invoke(question)
    print(f"问题:{question}")
    print(f"回答:{answer}")

主逻辑解析:
若向量库中如无文档,为首次运行:则从 my_knowledge 文件夹加载文本,进行分段、向量化后存入 Chroma。
若非首次运行:跳过初始化,直接加载向量库。
问答流程测试:使用 chain.invoke() 提交问题,并打印生成的回答。

四、完整代码

以下是一个完整的 RAG 实现示例

from _operator import itemgetter
from pathlib import Path
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough, RunnableParallel
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter, CharacterTextSplitter
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_openai import ChatOpenAI
from langchain_chroma import Chroma
from langchain_ollama import OllamaLLM
from functools import partial

# 1. 设置模型
llm = OllamaLLM(model="qwen3:0.6b")
embedding_model = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5")

# 2. 设置数据处理(加载、分块、存储、检索)
file_dir = Path('my_knowledge')
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100)
# 创建向量存储(使用Chroma数据库)
vector_store = Chroma(embedding_function=embedding_model, persist_directory="./chroma_naive_db")
# 创建检索器(返回top5相似结果)
retriever = vector_store.as_retriever(search_kwargs={"k": 5})

text_loader_utf8 = partial(TextLoader, encoding='utf-8')

prompt_template = PromptTemplate.from_template("""
    你是一个严谨的RAG助手。请根据以下提供的上下文信息来回答问题。
    要求:先回答用户的问题,如果回答中使用了上下文信息,在回答后输出使用了哪些上下文。
    -----------------------------------
    上下文信息如下:{context}
    问题:{question}
""")

# 3. 编排链: 问题输入 -> 检索 -> 注入prompt -> 模型 -> 解析
chain = {"question": RunnablePassthrough()} | RunnablePassthrough.assign(
    context=itemgetter("question") | retriever) | prompt_template | llm | StrOutputParser()

if __name__ == '__main__':
    # 检查向量数据库是否已经有文档
    if len(vector_store.get()["ids"]) == 0:
        print("首次运行,正在初始化知识库...")
        docs = DirectoryLoader(str(file_dir), loader_cls=text_loader_utf8).load()  # 加载文档
        docs = text_splitter.split_documents(docs)  # 切分文档
        vector_store.add_documents(docs)  # 存储文档
    else:
        print("已加载知识库,跳过初始化。")

    print(chain.invoke("图灵的论文标题是什么"))

五、总结

本项目通过 LangChain 框架串联本地文档加载、文本切分、向量化存储与检索,再结合 Prompt 模板和本地大语言模型,实现了一个完整的 RAG 问答系统。RAG是增强LLM应用能力的关键技术,希望本文能为你探索更高级的AI应用打下坚实的基础。

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐