ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RAG 检索增强生成——让大模型只根据你的材料回答

RAG 检索增强生成——让大模型只根据你的材料回答 个人主页 for_ever_love__ 欢迎各位大佬莅临其他栏目: 我想学python了 其他栏目: iOS项目总结大全 其他栏目: iOS UI 文章目录RAG 检索增强生成——让大模型只根据你的材料回答一、为什么需要 RAG二、第一步把文本变成向量Embedding2.1 Embedding 是什么2.2 调用 Embedding 接口三、第二步文档切片Chunking3.1 为什么不能整篇丢进去3.2 三种常用切片方式四、第三步检索与重排4.1 向量库从暴力搜索到近似检索4.2 混合检索向量 关键词4.3 Rerank 重排把最相关的提到最前面五、第四步组装 Prompt 并生成答案六、RAG 效果怎么评测6.1 检索环节指标6.2 生成环节指标七、常见坑清单八、本节小结RAG 检索增强生成——让大模型只根据你的材料回答一、为什么需要 RAG大模型有两个绕不开的短板不知道你的私有数据公司文档、产品手册、内部 FAQ它训练时没见过。会一本正经地编造问到它不知道的东西它倾向于合理编一个也就是幻觉。微调能解决一部分问题但成本高、更新慢改一次文档就得重新训练。RAGRetrieval-Augmented Generation检索增强生成的思路更直接别让模型凭记忆回答先去你的资料库里检索相关内容把找到的片段塞进 Prompt让模型开卷考试。用户提问 ↓ ① 把问题转成向量Embedding ↓ ② 在向量库里找最相似的 K 个文档片段 ↓ ③ 把片段 问题一起塞进 Prompt ↓ ④ 大模型基于材料生成答案并标注引用来源这样做的好处优势说明无需训练改文档即刻生效不用重训模型可溯源答案能标出来自哪一段方便核验降低幻觉模型被材料约束编造空间被压缩成本低相比微调成本几乎可以忽略二、第一步把文本变成向量Embedding2.1 Embedding 是什么Embedding 模型把一段文本映射成一个固定长度的数值向量比如 768 维或 1536 维。语义相近的文本向量在空间中距离更近。如何申请退款 → [0.12, -0.34, 0.56, ...] 退款流程是怎样的 → [0.11, -0.31, 0.58, ...] ← 与上面很接近 今天天气不错 → [-0.62, 0.44, -0.09, ...] ← 距离很远于是找相关内容就变成了找最近的向量这就是语义检索的本质——它匹配的是意思不是关键词。对比关键词检索BM25向量检索匹配依据字面重合语义相似同义词❌ 搜退款搜不到退货✅ 能关联精确编号/专有名词✅ 强⚠️ 弱是否需要模型否是实际系统里两者结合混合检索效果最好这点后面会讲。2.2 调用 Embedding 接口importosimportnumpyasnpfromopenaiimportOpenAI clientOpenAI(api_keyos.getenv(OPENAI_API_KEY),base_urlos.getenv(OPENAI_BASE_URL))defembed(texts:list[str])-np.ndarray:把文本列表转成向量矩阵形状 (n, dim)respclient.embeddings.create(modeltext-embedding-3-small,inputtexts)vecsnp.array([d.embeddingfordinresp.data],dtypefloat32)# 归一化后点积就等于余弦相似度后续算起来更快vecs/np.linalg.norm(vecs,axis1,keepdimsTrue)returnvecs docs[退款政策收到商品 7 天内可申请无理由退款需保持商品完好。,配送说明全国大部分地区 48 小时内发货偏远地区 3-5 天。,会员权益年度会员享受全年包邮与专属客服通道。,]doc_vecsembed(docs)print(向量形状:,doc_vecs.shape)# (3, 1536)# 语义检索问东西不想要了怎么退q_vecembed([不想要了怎么退])scoresdoc_vecs q_vec[0]# 归一化后点积 余弦相似度topnp.argsort(-scores)[:2]foriintop:print(f相似度{scores[i]:.3f}|{docs[i]})输出向量形状: (3, 1536) 相似度 0.742 | 退款政策收到商品 7 天内可申请无理由退款需保持商品完好。 相似度 0.415 | 会员权益年度会员享受全年包邮与专属客服通道。注意问题里没有出现退款二字也没出现7天但系统仍然正确匹配到了退款政策——这就是语义检索的价值。三、第二步文档切片ChunkingRAG 效果好不好切片策略占一半因素。3.1 为什么不能整篇丢进去上下文窗口有限长文档塞不下整篇做 Embedding 会把多个主题混合成一个向量检索精度下降你只想给模型相关那几段不是整本书3.2 三种常用切片方式方式做法优点缺点固定长度每 500 字切一刀简单可能把句子切断破坏语义按结构按标题/段落/空行切语义完整长度不均有的太短重叠滑动每段 500 字相邻重叠 100 字减少切断损失有冗余存储变大推荐做法按结构优先 长度上限兜底 少量重叠。importredefchunk_text(text:str,max_len500,overlap80)-list[str]:先按段落切过长的段落再按句子细切并保留少量重叠# 1. 先按空行切成自然段落paragraphs[p.strip()forpinre.split(r\n\s*\n,text)ifp.strip()]chunks[]forpinparagraphs:iflen(p)max_len:chunks.append(p)continue# 2. 过长的段落按句号切分累积到 max_len 就成块sentencesre.split(r(?[。]),p)bufforsinsentences:iflen(buf)len(s)max_len:bufselse:ifbuf:chunks.append(buf)# 保留尾部 overlap 个字符避免切断上下文bufbuf[-overlap:]selse:bufsifbuf:chunks.append(buf)returnchunks# 试一下text退款政策。用户可在收到商品后 7 天内申请无理由退款商品需保持完好且配件齐全。*3fori,cinenumerate(chunk_text(text,max_len120,overlap20)):print(f[{i}] ({len(c)}字){c[:40]}...)⚠️坑 1切片太短会丢失上下文它指的是什么无从判断太长则引入噪声。一般 300-800 字是个不错的起点再根据评测结果调。小技巧给每个 chunk 加上它所属标题作为前缀如【退款政策】…),能显著提升检索准确率。四、第三步检索与重排4.1 向量库从暴力搜索到近似检索文档不多几千条以内时NumPy 暴力算就够快classSimpleVectorDB:def__init__(self):self.vectorsNoneself.texts[]defadd(self,texts,vectors):self.texts.extend(texts)self.vectorsvectorsifself.vectorsisNoneelsenp.vstack([self.vectors,vectors])defsearch(self,query_vec,top_k3):scoresself.vectors query_vec# (n,)idxnp.argsort(-scores)[:top_k]return[(self.texts[i],float(scores[i]))foriinidx]dbSimpleVectorDB()db.add(docs,doc_vecs)fort,sindb.search(q_vec[0]):print(f{s:.3f}|{t[:40]})文档上万条后需要专门的向量库FAISS、Milvus、Chroma、pgvector它们用HNSW / IVF 等近似最近邻算法牺牲一点点精度换来数量级的速度提升。方案适用场景NumPy 暴力 1 万条原型验证FAISS单机、百万级性能好Chroma轻量级开箱即用Milvus / pgvector生产级、分布式、需持久化4.2 混合检索向量 关键词纯向量检索在精确匹配产品型号、错误码、人名上表现一般。解决办法是两路召回再融合defhybrid_search(query,vec_scores,bm25_scores,alpha0.6):alpha 控制向量检索的权重0.6 表示偏重语义defnorm(x):xnp.asarray(x,dtypefloat)return(x-x.min())/(x.max()-x.min()1e-9)returnalpha*norm(vec_scores)(1-alpha)*norm(bm25_scores)4.3 Rerank 重排把最相关的提到最前面两阶段策略是工业界的标配粗排召回 50 条→ 精排Rerank 模型打分→ 取 Top 5 给大模型Rerank 模型如 bge-reranker、Cohere Rerank会把问题和每个候选片段拼在一起过一遍模型判断相关性精度远高于直接算向量距离代价是慢一些。# 伪代码示意两阶段流程candidatesdb.search(q_vec[0],top_k50)# 粗排便宜、快rerankedrerank_model.score(query,candidates)# 精排贵、准finalreranked[:5]# 只把最相关的 5 条给 LLM五、第四步组装 Prompt 并生成答案检索到片段后关键是让模型严格基于材料回答。RAG_PROMPT # 角色 你是企业知识库助手只能依据下方【参考资料】回答问题。 # 参考资料 {context} # 问题 {question} # 约束 1. 只能使用参考资料中的信息禁止使用你的先验知识 2. 若资料中没有答案直接回答资料中没有相关信息不要编造 3. 每个关键结论后用 [1] [2] 标注对应的资料编号 4. 回答简洁控制在 200 字以内 defrag_answer(question:str,db,top_k3)-str:# 1. 检索hitsdb.search(embed([question])[0],top_ktop_k)# 2. 组装带编号的上下文context\n.join(f[{i1}]{t}fori,(t,s)inenumerate(hits))# 3. 生成promptRAG_PROMPT.format(contextcontext,questionquestion)respclient.chat.completions.create(modelgpt-4o-mini,messages[{role:user,content:prompt}],temperature0)returnresp.choices[0].message.contentprint(rag_answer(买的东西不想要了多久之内能退,db))典型输出您可以在收到商品后 7 天内申请无理由退款 [1]。申请时商品需保持完好且配件齐全 [1]。⚠️坑 2不写资料中没有就直说模型几乎一定会根据常识编一个像模像样的答案。这是 RAG 失败最常见的原因。六、RAG 效果怎么评测不要只看感觉挺好要拆成两个环节分别测6.1 检索环节指标指标含义目标RecallK正确片段出现在前 K 条中的比例 0.9MRR首个正确结果的排名倒数越接近 1 越好Hit Rate至少命中一条的比例 0.956.2 生成环节指标指标检测什么忠实度 Faithfulness答案是否都能在资料中找到依据防幻觉答案相关性是否回答了问题引用准确率标注的 [1] 是否真的支持该结论# 构建最小评测集eval_set[{q:退款期限是多久,expect_chunk:退款政策},{q:偏远地区几天发货,expect_chunk:配送说明},{q:会员有什么好处,expect_chunk:会员权益},{q:公司CEO是谁,expect_chunk:None},# 负样本不该命中]defeval_recall(db,k3):ok0forcaseineval_set:hitsdb.search(embed([case[q]])[0],top_kk)texts[tfort,sinhits]expectcase[expect_chunk]ifexpectisNone:# 负样本期望模型回答没有相关信息ok1continueokany(expectintfortintexts)returnfRecall{k}:{ok}/{len(eval_set)}print(eval_recall(db))负样本很重要专门准备一批资料里确实没有的问题验证系统会不会老实说不知道。七、常见坑清单坑现象解决切片切断语义检索到的片段读不通按结构切 重叠 加标题前缀切片过长引入噪声答案跑偏控制 300-800 字配评测调Prompt 不设约束模型自己编答案明确资料中没有就直说只做向量检索型号、编号搜不准混合检索向量 BM25忘记归一化向量相似度算错Embedding 后做 L2 归一化中文分词影响 BM25关键词路召回差用 jieba 分词 中文 BM25 实现一次性塞 20 条片段上下文过长、中间遗忘粗排后 Rerank最终只给 3-5 条八、本节小结RAG 检索 生成让模型开卷考试解决私有数据不可见和幻觉两大问题且无需训练。Embedding 是语义检索的基础把文本变向量用余弦相似度找近邻匹配的是意思而非字面。切片策略决定一半效果按结构切、长度 300-800 字、少量重叠、带上标题前缀。两阶段检索是标配粗排召回快→ Rerank 精排准→ 只给模型 3-5 条。混合检索兼顾语义与精确向量路 关键词路加权融合。Prompt 必须写死约束禁止用先验知识、资料没有就说不知道、标注引用编号。评测要分环节检索看 RecallK生成看忠实度并准备负样本验证会不会瞎编。到这里AI 学习主线已经串起来了Python 数据处理 → 机器学习 → 深度学习 PyTorch → Transformer → Prompt 工程 → RAG。下一步可以往Agent让模型自己规划、调工具、多步执行或微调与部署两个方向深入。一句话记住RAG 的本质不是让模型知道更多而是让模型只被允许用它该用的材料——检索负责找对Prompt 负责管住。
返回列表