Day16 | RAG 基础——让模型用上“自己的资料“

📅 2026/7/8 7:22:01
Day16 | RAG 基础——让模型用上“自己的资料“
苦猿的大模型日记 · Day16 · RAG 基础——让模型用上自己的资料-帮普通人把AI学进简历系列前言一条问倒我的私信上周三晚上我在后台翻到一条私信——苦猿哥我们老板让我做个客服机器人模型用的 GPT-4结果它连我们自己产品的退换货政策都答不对。我让它回答7 天无理由它给我背一段消费者权益保护法问它保修期多久它说建议联系官方售后。这种该怎么搞是要微调吗我盯着这条消息看了挺久。不是因为它有多难——而是因为它代表了一类几乎所有人都会踩的坑模型不知道你家业务。GPT-4 也好、Claude 也好、DeepSeek 也好它们读过整个互联网但没读过你公司的产品手册。它们不知道你的退换货政策、不知道你的 SKU 编号、不知道你的工单流程。然后大多数人第一反应是——那我去微调一个等一下。别急。微调之前先回答我一个问题你的文档多久改一次如果答案是一个月改一次那微调就是地狱——文档一变整个模型重训。成本高、效果还不见得好。Anthropic 自己做过一组实验把同一份文档一份塞进 prompt也就是 RAG一份微调进权重。结果前者准确率比后者高 30% 以上。原因是——文档会变权重不会。今天我就把 RAG 这件事从头讲一遍。它不是玄学是一条可拆解的 5 步流水线。读完这篇你能搞懂为啥不该一上来微调、自己用 LangChain bge Milvus 跑通一个端到端 RAG、知道哪些坑会害你召回率惨不忍睹。PART 01为啥不直接塞 prompt——三种方案的边界很多人想到的第一方案是——模型上下文都 128K 了我把产品手册全塞 prompt 里不就行了能。但有三个死穴。第一贵。每条请求你都要重发那 100K token按 token 计费的话一条客服对话两块钱起。第二慢。模型处理 100K 输入的延迟是处理 2K 输入的几十倍。用户等不了。第三模型会迷失。哪怕上下文够长Lost in the Middle现象之前讲 Prompt 工程时详细聊过会让埋在中间的关键信息召回率掉一半。那微调呢微调教不了知识只能教行为。这句话希望你记死。微调擅长的是让模型学一种说话风格——比如你公司客服喜欢用亲开头、习惯先道歉再解决。这种东西适合微调。但事实性知识——退换货政策、保修条款、产品参数——这些东西绝对不能靠微调。文档一改全部重训模型还会幻觉记得硬编内容把过时的政策当真理讲。所以正确的方案是——行为靠微调知识靠 RAG少量资料靠塞 prompt。三种方案的边界我画了一张图你存下来遇到需求先对照判断这个判断点比后面所有技术细节都重要。大多数项目失败不是技术做错了是一开始方案选错了。PART 02RAG 最小流水线——5 步拆解RAG 不是黑盒是一条 5 步流水线——加载文档PDF / Word / Markdown / HTML 都行切块Chunking把长文档切成小块Embedding把每块文本转成向量入向量库存起来能搜召回 拼 prompt用户问题来了找最相关的几块塞进 prompt 给 LLM整体长这样——文档 → 切块 → embedding → 向量库 ↑相似度匹配 用户问题 → embedding ↓ 召回 top-k → 拼 prompt → LLM 生成答案这 5 步里90% 的坑在第 2 步切块和第 5 步召回。很多人一上来就纠结 embedding 模型选型——该用 bge 还是 OpenAI是不是越大越好——其实那个反而是最不容易出错的环节。开源 bge-m3 在中文场景已经接近天花板你换别的基本看不到差别。真正的差距在怎么切、怎么召回、怎么拼 prompt。PART 03切块与 Embedding——两个最容易被忽略的细节先说切块。大多数人切块的方式是错的。他们写chunk_size500一个chunk_overlap0跑起来觉得效果还行——直到上线发现召回的全是半句话模型答得磕磕绊绊。切块有三种策略——1. 固定窗口切每 500 字咔嚓一刀。简单粗暴但会切断句子、切断上下文。7 天无理由可能被切成7 天无和理由。2. 递归切按段落 → 句子 → 字符逐层降级。LangChain 默认方案多数场景够用。3. 语义切用模型判断哪里是语义边界。效果最好但成本高生产场景才值得。三个工程坑你必须知道chunk_size 太小召回的是半句话模型答不全chunk_size 太大召回的块里夹了大量无关内容干扰模型还会触发 Lost in the Middle不加 overlap边界处的关键信息被切飞召回不到我的经验值中文场景 chunk_size500、overlap50是一个比较稳的起点。再说 Embedding。它是什么三句话讲透——把文本转成一个高维向量比如 1024 维。语义相近的文本向量距离就近。所以找相关文档就等于找距离用户问题最近的几个向量。不背公式不推数学。你只要记住一件事embedding 模型跟 LLM 不是一回事不需要配套。你可以 bge 做 embedding GPT-4 做 LLM也可以 bge DeepSeek。这是常规组合。模型选型讲逻辑——bge-m3智源开源中文 SOTA免费默认推荐OpenAI text-embedding-3-small闭源稳定但要钱、要走代理国内闭源通义 / 智谱中文也不错但要 API Key最小可运行代码这一段请存下来——from langchain_text_splitters import RecursiveCharacterTextSplitter from FlagEmbedding import BGEM3FlagModel # 切块 splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap50, separators[\n\n, \n, 。, , , , ] ) chunks splitter.split_text(doc_text) # Embedding model BGEM3FlagModel(BAAI/bge-m3, use_fp16True) embeddings model.encode(chunks)[dense_vecs]10 行代码搞定切块和 embedding。剩下的事是把向量存进库。PART 04向量库与召回——Milvus 实操 top-k 的玄机向量库选型三档按规模分——FAISSMeta 开源本地库单机够用开发期玩Chroma轻量级自带存储原型期Milvus / Qdrant生产级分布式上线用一个判断点别一上来就上 Milvus。100 篇文档用 Chroma 一秒搞定没必要起个分布式服务。但真要上生产、文档量上万Milvus 是国产事实标准。Milvus 最小可用代码LangChain 包装——from langchain_milvus import Milvus vectorstore Milvus.from_documents( documentschunked_docs, embeddingembed_fn, # 上一节定义的 bge wrapper connection_args{uri: http://localhost:19530}, collection_namemy_docs ) # 召回 results vectorstore.similarity_search(query退换货政策, k5)接下来是全文最反直觉的一个点——top-k 的玄机。k是召回几条。大多数人随手写k5但其实——k1召回太少模型信息不够经常答不全k10召回太多Lost in the Middle 让中间几条等于白召回之前讲 Prompt 工程时聊过这个坑k3~5多数场景的甜点区别贪多。召回 10 条不如召回 5 条 后面接个 rerank重排这篇后面会专门讲。召回完怎么拼 prompt结构化不是糊一坨。context \n\n.join([ f[资料{i1}] {doc.page_content} for i, doc in enumerate(results) ]) prompt f你是一个客服助手根据以下资料回答用户问题。 如果资料里没有答案明确说我不知道不要编造。 {context} 用户问题{user_question} 最后那句找不到就说不知道是命根子。不加这句模型会拿召回的边角料硬编答案——这是 RAG 幻觉的最大来源。资料里只提到了7 天退货它会顺着编出15 天换货。一旦上线客服事故就来了。PART 05跑通一个能用的 RAG——对接 DeepSeek API把前面的零件串起来。整体架构长这样——用户问题 ↓ bge embedding → Milvus 召回 top-5 ↓ 拼 prompt指令 资料 问题 ↓ 调 DeepSeek API → 返回答案为啥选 DeepSeek一句话——便宜百万 token 几块钱、中文好、OpenAI 兼容。本地不用起服务注册个 Key 就能跑。比 vLLM 门槛低比 OpenAI 便宜。完整可运行代码——from langchain_openai import ChatOpenAI # DeepSeek APIOpenAI 兼容base_url 换一下就行 llm ChatOpenAI( modeldeepseek-chat, base_urlhttps://api.deepseek.com/v1, api_key你的 DeepSeek API Key ) # RAG 完整链路 def rag_answer(question, vectorstore, llm, k5): docs vectorstore.similarity_search(question, kk) context \n\n.join([d.page_content for d in docs]) prompt f根据以下资料回答问题。资料里没有就说我不知道。 资料{context} 问题{question} return llm.invoke(prompt).content到这里你已经有一个能跑的 RAG 了。但别高兴太早——上线第一天你就会撞上这三个崩盘点症状 1模型答非所问→ 召回准确率低→ 检查切块粒度或者换 embedding 模型重 embed症状 2模型说我不知道但资料里明明有→ 资料被切成了两块关键信息没一起召回→ 加 overlap或换切块策略症状 3模型瞎编资料里没有的东西→ prompt 没加找不到就说不知道约束→ 加约束 把温度降到 0最后给你一个心法也是 RAG 调优的总纲——RAG 的 90% 问题不在模型在召回。模型换 GPT-4 还是 DeepSeek效果差不了多少。但召回准确率从 50% 拉到 90%整个 RAG 体验是质变。而召回怎么拉到 90%——重排、查询改写、混合检索——这是明天的全部内容。结尾RAG 不是魔法是工程回到开头那位读者的问题——他的客服机器人为什么答不对退换货政策因为模型根本没看过他们公司的政策文档。微调也救不了——文档改一次重训一次。正确的做法是 RAG把退换货政策切块、embed、入向量库用户问的时候实时召回喂给 LLM。文档改了改一份文档整个 RAG 立刻生效。这就是 RAG 的核心价值——知识可更新模型不动。微调教模型怎么说话RAG 教模型说什么话——大多数项目后者更值钱。但 RAG 不是装上就完事的——它会召回率惨不忍睹、会模型瞎编、会切块切碎关键信息。这些都是工程问题而工程的问题永远有可量化的答案。明天我把召回准确率从 50% 拉到 90% 的几个实战技巧讲透——重排、查询改写、混合检索。今天的基础版只是入场券明天的才是分水岭。互动时间你做 RAG 踩过最深的坑是什么是切块、召回、还是模型瞎编评论区聊聊明天的内容我会挑高频问题重点展开。下一篇预告Day17 | RAG 调优——为什么你的 RAG 总是答非所问。重排 查询改写 混合检索把召回准确率从 50% 拉到 90%。— END —苦猿 · 帮普通人把 AI 学进简历