news 2026/10/2 2:29:35

从 0 跑通一个最小 RAG Demo:文档切分、向量检索与答案生成

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从 0 跑通一个最小 RAG Demo:文档切分、向量检索与答案生成

RAG 的本质是给大模型“开卷考试”:先把你的文档切成小块存进向量库,用户提问时检索出最相关的几块,连同问题一起交给模型生成答案。下面从零开始,用最少的依赖跑通完整流程。

第一步:理解 RAG 的两个阶段

在动手之前,先明确你在做什么:

阶段一(离线):把你的文档加载 → 切分 → 向量化 → 存入向量数据库。这步只需要跑一次。

阶段二(在线):用户提问 → 把问题向量化 → 在库里搜索最相似的 N 个文本块 → 拼成提示词 → 交给大模型生成答案-1。

第二步:安装依赖

只需要四个核心库。建议用清华镜像加速:

pip install langchain langchain-openai langchain-community langchain-text-splitters chromadb -i https://pypi.tuna.tsinghua.edu.cn/simple

这些库的分工:langchain负责串联流程,langchain-openai提供嵌入和对话模型接口,chromadb是向量数据库,langchain-text-splitters负责文档切分-7。

第三步:准备你的知识库文档

创建一个简单的文本文件作为知识库。比如knowledge.txt:

RAG 是检索增强生成的缩写。 它的核心思想是在回答问题前先从外部知识库检索相关信息。 向量数据库用于存储文本的向量表示。 ChromaDB 是一个轻量级的向量数据库,适合本地开发。 嵌入模型负责把文本转换成高维向量。

第四步:编写完整代码

创建一个rag_demo.py文件,把下面的代码复制进去。每一步我都加了注释,你可以直接运行。

import os from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_community.vectorstores import Chroma from langchain_openai import OpenAIEmbeddings, ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnablePassthrough from langchain_core.output_parsers import StrOutputParser # ========== 0. 配置 API Key ========== # 你需要一个支持 OpenAI 接口的 API Key # 如果使用阿里云百炼,把 base_url 改成 dashscope 的地址 os.environ["OPENAI_API_KEY"] = "你的 API Key" os.environ["OPENAI_BASE_URL"] = "https://api.openai.com/v1" # 或替换为你的代理地址 # ========== 1. 加载文档 ========== loader = TextLoader("knowledge.txt", encoding="utf-8") docs = loader.load() print(f"加载了 {len(docs)} 个文档") # ========== 2. 切分文档 ========== # chunk_size=200 表示每个文本块约 200 个字符 # chunk_overlap=50 表示相邻块之间有 50 字符重叠,避免语义断裂 text_splitter = RecursiveCharacterTextSplitter( chunk_size=200, chunk_overlap=50, separators=["\n\n", "\n", "。", "!", "?", " ", ""] ) splits = text_splitter.split_documents(docs) print(f"切分成了 {len(splits)} 个块") for i, split in enumerate(splits): print(f" 块 {i}: {split.page_content[:50]}...") # ========== 3. 向量化并存入向量库 ========== # 使用 OpenAI 的嵌入模型将文本块转为向量,存入 Chroma embeddings = OpenAIEmbeddings(model="text-embedding-3-small") vectorstore = Chroma.from_documents( documents=splits, embedding=embeddings, persist_directory="./chroma_db" # 持久化,下次不用重新嵌入 ) print(f"向量库构建完成,共 {vectorstore._collection.count()} 条向量") # ========== 4. 创建检索器 ========== # 搜索时返回最相似的 2 个文本块 retriever = vectorstore.as_retriever(search_kwargs={"k": 2}) # ========== 5. 构建 RAG 提示词 ========== # 核心规则:只允许基于提供的上下文回答,不能编造 prompt_template = """仅根据以下提供的上下文回答问题。如果上下文不包含答案,请直接说“提供的文档不包含足够的信息来回答此问题”。 上下文: {context} 问题:{question} 答案:""" prompt = ChatPromptTemplate.from_template(prompt_template) # ========== 6. 初始化大模型 ========== llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) # ========== 7. 串联成 RAG 链 ========== def format_docs(docs): return "\n\n".join(doc.page_content for doc in docs) rag_chain = ( {"context": retriever | format_docs, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser() ) # ========== 8. 测试 ========== questions = [ "RAG 是什么?", "ChromaDB 是什么?", "今天天气怎么样?" # 这个问题知识库里没有,测试模型的边界处理 ] for q in questions: print(f"\n问题:{q}") answer = rag_chain.invoke(q) print(f"答案:{answer}")

第五步:理解每个关键环节的“为什么”

文档切分:chunk_size 和 chunk_overlap 怎么定

切分是 RAG 中最容易被忽视但影响巨大的环节。块太小,检索到的片段缺乏上下文;块太大,嵌入向量会“稀释”关键信息,检索精度下降-3-13。

经验起点:chunk_size=512tokens,chunk_overlap=10%-15%(约 50-80 tokens)-8。中文场景下可以先用chunk_size=200-300字符,因为中文字符的信息密度更高。

RecursiveCharacterTextSplitter的聪明之处在于:它按优先级尝试分割符(先段落、再换行、再句号),尽量保证每个块在语义边界处断开-7。

嵌入模型:选哪个

中文场景首选bge-large-zh-v1.5,它在 MTEB 中文榜单上表现最好,开源可本地部署,1024 维的向量维度是性价比最优的选择-4-9。如果追求便捷,text-embedding-3-small的 1536 维英文效果够用,成本也低-4。

关键认知:Embedding 模型决定了检索的“天花板”。换一个更好的嵌入模型,往往是投入产出比最高的优化手段-9。

提示词:防止幻觉的核心规则

RAG 提示词必须包含基础约束:明确声明模型只能使用提供的上下文,并定义“不知道”时的回退行为-5。

上面的模板用了最直接的表达:“仅根据以下提供的上下文回答问题。如果上下文不包含答案,请直接说……”这种命令式语言比“可以考虑使用上下文”有效得多-10。

第六步:跑起来,然后观察输出

运行python rag_demo.py。你应该看到:

  1. 切分结果:5 句话被切成了 3-5 个块(取决于你的文本长度和分割符)

  2. 前两个问题:模型从知识库中检索到了相关片段并给出了正确答案

  3. 第三个问题(“今天天气怎么样”):模型应该返回“不包含足够的信息”,而不是编造天气

如果第三个问题模型还是编造了答案,说明提示词约束不够强。把“如果上下文不包含答案”改成“禁止使用你训练数据中的任何信息”再试-5。

下一步可以做什么

跑通这个 Demo 后,你可以按需扩展:

  • 支持 PDF/网页:把TextLoader换成PyPDFLoader或WebBaseLoader

  • 换向量库:Chroma 适合本地,FAISS 更快,Milvus/Weaviate 适合生产

  • 增加对话记忆:让模型能理解“他/她/它”指代的是谁-2

  • 加 Reranker:先用向量检索召回 10 条,再用交叉编码器精排,能显著提升第一屏答案的质量-9

但不要一开始就搞这些。先把最小闭环跑通,确认检索到的内容确实和问题相关,再逐步优化。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 2:28:28

数据闭环VLM 推理挖掘:多模态大模型如何补足长尾场景标签

上篇说过,规则引擎再强也有够不着的地方:「施工区锥桶摆放混乱」「行人撑着花伞」「逆光下的临时交通管制」——这类语义级场景,结构化条件根本写不出来。这正是多模态大模型(VLM)推理挖掘的领地:让模型「看…

作者头像 李华
网站建设 2026/10/2 2:26:34

小白也能入行!揭秘高薪AI大模型运维工程师的火爆秘密!

本文介绍了AI大模型运维工程师这一高薪岗位,分析了其火爆原因,即随着AI应用的增多,对算力、模型和平台稳定运行的需求激增。文章详细解释了该岗位的职责,包括算力管理、模型服务及平台维护,并澄清了常见的误解。此外&a…

作者头像 李华
网站建设 2026/10/2 2:26:13

240、【AI】【模型部署】基座模型研究:logits 与 softmax

【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除 标题 240、【AI】【模型部署】基座模型研究&a…

作者头像 李华
网站建设 2026/10/2 2:25:43

Android 离线录音 APP 推荐:断网录音工具有哪些

很多线下记录场景会遇到网络不稳定的情况,会场屏蔽信号、野外采访无蜂窝网络,常规录音转写工具依赖云端服务,网络中断后音频无法保存,直接造成记录内容丢失。Android 平台上的录音工具能力差异集中体现在离线录音引擎的实现方式&a…

作者头像 李华
网站建设 2026/10/2 2:24:10

案例 4.4 微信小程序 swiper 和 switch 组件(swiper 共 4 个滑动项)

前言本次案例《4.4 swiper 和 switch 组件》,学习小程序轮播swiper轮播组件与开关switch组件。在基础功能上,给 swiper 新增一项,一共 4 个滑动页面,实现轮播切换,使用 switch 控制轮播自动播放。一、知识点介绍swiper…

作者头像 李华