随着业务线的拓展,我手头积累了海量的技术文档、Docker 部署配置、长篇视频的英文字幕草稿以及复杂的 OpenCC 繁体转换规则。传统的文件夹搜索已经无法满足我跨文档的知识检索需求。

与其每次都去问 ChatGPT 那些通用的废话,不如让大模型”阅读”我自己的笔记。今天,我们利用 LangChain 框架和 RAG(检索增强生成)技术,构建一个完全懂你的”第二大脑”。

一、什么是 RAG?

简单来说,RAG 就是让大模型在回答你的问题之前,先去你的私人文档库里检索相关资料,然后把这些资料和你的问题一起喂给模型。这样模型给出的就不是”通用”的答案,而是基于你自己的知识库的精准回复。

核心流程:

1
用户提问 → 向量检索相关文档片段 → 拼接成上下文 → 发送给 LLM → 生成答案

二、环境搭建:Ollama 本地大模型

对于个人使用,完全不需要调用付费 API。Ollama 让你在本地机器上运行大模型(如 Llama 3、Mistral、Qwen 等),并且完全离线、零成本。

1
2
3
4
5
6
7
8
9
# 安装 Ollama(支持 Windows / macOS / Linux)
# 官网下载: https://ollama.com/

# 拉取中文友好的模型
ollama pull qwen2.5:7b # 通义千问 7B,中文能力强
ollama pull nomic-embed-text # 文本转向量的嵌入模型

# 启动服务
ollama serve

三、文档加载与向量化

使用 LangChain 加载你的 Markdown 文档并转为向量存储:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import OllamaEmbeddings

# 1. 加载所有 Markdown 文档
loader = DirectoryLoader(
"./my_docs/",
glob="**/*.md",
loader_cls=TextLoader
)
documents = loader.load()

# 2. 文本切片(按语义边界,而非机械切割)
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", "。", "!", "?", ",", " ", ""]
)
chunks = text_splitter.split_documents(documents)

print(f"共切分为 {len(chunks)} 个文档片段")

# 3. 向量化并存入 ChromaDB
embedding = OllamaEmbeddings(model="nomic-embed-text")
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embedding,
persist_directory="./chroma_db"
)
vectorstore.persist()

四、构建检索链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from langchain_community.llms import Ollama
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate

# 初始化 LLM
llm = Ollama(model="qwen2.5:7b", temperature=0.1)

# 自定义提示词模板
prompt_template = """你是一个专业的技术助手。请仅根据以下参考文档回答问题。
如果文档中没有相关信息,请诚实地说"文档中没有找到相关信息",
不要编造任何内容。

参考文档:
{context}

问题:{question}

回答:"""

PROMPT = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)

# 创建 RAG 链
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=vectorstore.as_retriever(search_kwargs={"k": 4}),
chain_type_kwargs={"prompt": PROMPT}
)

# 提问
answer = qa_chain.run("我在 Docker 部署中遇到了什么坑?如何解决的?")
print(answer)

五、检索优化:提高命中率

默认的向量检索有时会遗漏关键信息。几个优化策略:

1. 增加检索数量:将 k 从默认的 4 改为 6-8,给模型更多上下文。

2. 使用 MMR(最大边际相关性)

1
2
3
4
retriever = vectorstore.as_retriever(
search_type="mmr",
search_kwargs={"k": 6, "fetch_k": 20}
)

MMR 不仅考虑相关性,还考虑结果的多样性,避免返回 4 条几乎一样的片段。

3. 混合检索(关键词 + 向量)
纯向量检索对精确匹配(如命令、文件名)支持不佳。可以在检索结果中加入基于 BM25 的关键词检索结果,两者取并集后重排序。

六、生产环境优化

对于个人长期使用的知识库:

  • 增量更新:文档变化时,不要重新向量化全部,只更新变化的文档
  • 元数据过滤:在存入向量库时附带元数据(日期、分类),检索时可预过滤
  • 定期重建:随着文档增多,索引质量会下降,建议每月全量重建一次

总结

RAG 不是遥不可及的企业级技术。用 Ollama + LangChain + ChromaDB,一个下午就能搭建起完全属于你自己的知识库。从此以后,你的技术笔记不再是躺平的 Markdown 文件,而是一个能与你对话的”第二大脑”。