在上一篇文章中,我们通过微调(Fine-tuning)教会了 AI 如何“说话”。今天,我们要教 AI 如何“读书”——让它能够阅读和理解我们提供的私有文档,并基于这些文档回答问题。
这项技术就是检索增强生成(Retrieval-Augmented Generation, RAG)。
与微调不同,RAG 并不改变模型本身的“性格”或“技能”,而是给模型外挂一个“知识库”。当用户提问时,系统会先从这个知识库中检索出最相关的信息,然后把这些信息连同问题一起交给 AI,让它基于给定的材料来组织答案。
这极大地扩展了 AI 的能力,让它能回答关于特定、私有或最新信息的问题。
目标:构建一个基于我博客文章的问答机器人
我们的目标是创建一个 Python 脚本,它可以:
- 读取我
content/posts/tech/
目录下的所有 Markdown 文章。 - 将这些文章处理成一个可供检索的知识库。
- 接收我的提问,比如“如何微调一个模型?”,并根据我之前写的《AI也能“私人订制”…》那篇文章,给出精准的回答。
第一步:环境准备与工具选择
我们将使用一些强大的开源库来简化开发过程:
- LangChain: 一个流行的 AI 应用开发框架,它极大地简化了构建 RAG 流程的复杂性。
- FAISS: 来自 Facebook AI 的高效向量检索引擎。它能帮助我们快速找到与问题最相关的文档片段。
- SentenceTransformers: 一个用于生成高质量文本嵌入(Embeddings)的库。
首先,安装所有必要的依赖:
pip install langchain langchain_community sentence-transformers faiss-cpu
faiss-cpu
是在CPU上运行的版本。如果你的机器有支持CUDA的NVIDIA显卡,可以安装faiss-gpu
以获得更好的性能。
第二步:加载和分割文档
首先,我们需要加载我们的知识(也就是所有的 .md
文件),并把它们切分成更小的、易于处理的块(Chunks)。因为一次性把整篇文章丢给模型,效果通常不好。
# rag_bot.py
import os
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
def load_documents(path):
"""加载指定路径下的所有 .md 文件"""
print(f"正在从 {path} 加载文档...")
# 我们只加载 .md 文件
loader = DirectoryLoader(path, glob="**/*.md", loader_cls=TextLoader, loader_kwargs={'encoding': 'utf-8'})
documents = loader.load()
print(f"成功加载 {len(documents)} 篇文档。")
return documents
def split_documents(documents):
"""将文档分割成小块"""
print("正在分割文档...")
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=800, # 每个块的最大字符数
chunk_overlap=100, # 块之间的重叠字符数
)
splitted_docs = text_splitter.split_documents(documents)
print(f"成功将文档分割成 {len(splitted_docs)} 个小块。")
return splitted_docs
RecursiveCharacterTextSplitter
是一个很智能的分割器,它会尝试按段落、句子等方式来切分文本,以保持语义的完整性。
第三步:创建向量存储(Vector Store)
这是 RAG 的核心。我们需要把分割好的文档块“向量化”——即将文本转换成一串数字(向量),这些数字能代表文本的语义。然后,我们将这些向量存入 FAISS 中,以便后续的检索。
# rag_bot.py (续)
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
def create_vector_store(splitted_docs, model_name="sentence-transformers/all-MiniLM-L6-v2"):
"""为文档块创建向量存储"""
print("正在创建向量嵌入并存入 FAISS...")
# 选择一个嵌入模型
embeddings = HuggingFaceEmbeddings(model_name=model_name)
# 使用 FAISS 作为向量数据库
vector_store = FAISS.from_documents(splitted_docs, embeddings)
print("向量存储创建成功!")
return vector_store
all-MiniLM-L6-v2
是一个非常流行的小型嵌入模型,它在性能和速度之间取得了很好的平衡。
第四步:构建问答链(QA Chain)
现在,知识库已经准备好了。我们需要构建一个“链”(Chain),它能把提问、检索、生成答案这几个步骤串联起来。
# rag_bot.py (续)
from langchain.chains import RetrievalQA
from langchain_community.llms import Ollama # 举例,使用本地Ollama模型
def create_qa_chain(vector_store):
"""创建并返回一个问答链"""
print("正在创建问答链...")
# 这里我们使用一个本地运行的Ollama模型作为例子
# 你也可以换成 OpenAI, Anthropic 等任何 LangChain 支持的模型
llm = Ollama(model="qwen:0.5b")
# 从向量存储中创建一个检索器
retriever = vector_store.as_retriever(search_kwargs={"k": 3}) # k=3表示每次检索返回3个最相关的文档块
# 创建问答链
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff", # "stuff" 模式会把所有检索到的文档块塞进一个Prompt里
retriever=retriever,
return_source_documents=True, # 同时返回源文档,方便溯源
)
print("问答链创建成功!")
return qa_chain
注意:上面的代码使用了
Ollama
和qwen:0.5b
模型作为示例,这需要你本地已经安装并运行了 Ollama。你可以很轻松地把它换成from langchain_openai import ChatOpenAI
等其他模型。
第五步:开始提问!
万事俱备,只欠提问。我们将所有步骤整合起来。
# rag_bot.py (完整脚本)
# ... (前面步骤的代码) ...
def main():
doc_path = "./content/posts/tech" # 指向你的博客文章目录
# 完整流程
documents = load_documents(doc_path)
splitted_docs = split_documents(documents)
vector_store = create_vector_store(splitted_docs)
qa_chain = create_qa_chain(vector_store)
# 开始交互式问答
print("\n\n你好!我是你的博客知识库机器人。输入 'exit' 退出。")
while True:
question = input("\n请输入你的问题: ")
if question.lower() == 'exit':
break
print("正在思考...")
result = qa_chain.invoke({"query": question})
print("\n答案:")
print(result["result"])
print("\n参考来源:")
for doc in result["source_documents"]:
# 打印源文件名
print(f"- {doc.metadata.get('source', 'N/A')}")
if __name__ == "__main__":
main()
现在,运行 python rag_bot.py
,然后向它提问:“如何微调一个模型?”。你会惊喜地发现,它会根据你写的那篇关于 Fine-tuning 的文章,给出一个条理清晰的回答,并且还会列出参考的文章来源。
结语
通过 RAG,我们成功地让 AI 成为了一个能够学习和利用我们私有知识的强大助手。这项技术的前景非常广阔,从企业内部知识库、智能客服到个人笔记的智能检索,无处不能看到它的身影。
相比于需要大量数据和计算资源的微调,RAG 的实现门槛更低,效果也更可控。快去用它为你自己的知识库赋能吧!
...