每个人都希望有一个能从自己文档中回答问题的聊天机器人——公司手册、合同、研究论文。这种技术被称为RAG(检索增强生成)。
我以构建生产级 RAG 和 LLM 系统为生,每次项目都要重新搭建相同的基础设施。因此我将其整理成一个小型开源起步模板。本文将介绍 RAG 的实际工作原理、关键设计决策,以及如何免费运行整个系统——无需任何 API 密钥。
文末附有完整开源仓库链接,可在几分钟内克隆并运行。
为什么需要 RAG?
像 GPT 或 Claude 这样的 LLM 仅了解其训练数据。询问它关于你笔记本电脑上某份文档的问题时,它会猜测——或者产生幻觉。
RAG 通过一个简单思路解决了这个问题:先检索相关信息,再让 LLM 仅基于这些信息回答。 结果是答案基于你的实际文档,并提供可追溯到具体页面的引用。
两步流水线
路径 1 — 索引(文档输入时):
- 从 PDF 中提取文本
- 将其分割成重叠的块
- 将每个块转换为捕捉其含义的向量(嵌入)
- 将这些向量存储在向量数据库中
路径 2 — 查询(用户提问时):
- 使用同一模型对问题进行嵌入
- 在向量数据库中搜索最相似的块
- 将这些块与问题发送给 LLM
- LLM 基于该上下文回答,我们返回源页面
技术栈选择
经过多次构建,以下技术栈既易于上手,又能在生产环境中稳定运行:
- FastAPI — 编写快速,API 简洁且支持异步
- ChromaDB — 轻量级向量数据库,嵌入式运行,无需管理单独服务器
- SentenceTransformers — 免费嵌入,支持多语言模型
- 任意 LLM — OpenAI、Claude、Gemini,或本地 Ollama
常被忽略的一点是:你可以在无需任何 API 密钥的情况下测试整个流程——要么使用仅返回匹配块的检索模式,要么使用 Ollama 实现完全本地运行,数据不出本地机器。
检索步骤的核心
查询侧的大致实现如下——嵌入问题、搜索、将块传递给 LLM:
def answer_question(question: str, top_k: int = 3):
# 1. Embed the question with the same model used at indexing time
query_embedding = embed(question)
# 2. Retrieve the most similar chunks from the vector store
chunks = vector_store.search(query_embedding, top_k=top_k)
# 3. Build a prompt grounded in those chunks, then ask the LLM
context = "\n\n".join(c["text"] for c in chunks)
answer = llm.generate(question=question, context=context)
return answer, chunks # chunks double as citations
Enter fullscreen mode Exit fullscreen mode
关键约束:对问题和文档使用相同的嵌入模型。 不同模型生成的向量不可比较,这是一个非常常见的错误。
决定质量的关键细节
根据经验,一个 RAG 系统能否良好回答问题,取决于几个常被低估的因素:
- 块大小。 太大则无关内容混杂在一起;太小则每个块失去上下文。块之间的重叠有助于避免句子中途被截断。
- 扫描版 PDF。 如果文件是图像,普通文本提取将返回空内容。你需要 OCR 回退方案,否则这些页面会静默索引为空。
- 重排序。 纯向量搜索快速但粗糙。添加一个交叉编码器重排序器,同时读取问题和每个块,可显著改善实际喂给模型的块的质量。
免费试用(开源)
我将所有内容打包成一个 MIT 许可的起步模板。克隆仓库,运行一条 Docker 命令,即可获得一个带有来源引用的文档问答聊天机器人。它支持多语言,且免费运行,无需 API 密钥。
👉 GitHub: https://github.com/panutpl/rag-chatbot-template-starter
git clone https://github.com/panutpl/rag-chatbot-template-starter
cd rag-chatbot-template-starter
cp .env.example .env
docker compose up --build
Enter fullscreen mode Exit fullscreen mode
然后打开 http://localhost:8000/docs,上传 PDF 并开始提问。
如果你正在构建 RAG 或刚入门,我很乐意收到反馈——尤其是在分块和检索策略方面,我仍在不断优化。欢迎在评论中分享你的经验。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.