
简介这份《字节跳动RAG实践手册》面向希望系统掌握检索增强生成技术的算法工程师、AI应用开发者与相关方向学习者聚焦RAG从架构设计到业务落地的完整链路。资源为1个PDF文件压缩包约1.41MB内容以图文文档形式呈现便于通读与查阅。手册围绕字节跳动在RAG领域的真实实践展开涵盖技术背景与RAG概述、业务线应用现状、数据层到生成层的整体架构设计并深入数据处理与准备、索引构建与优化、检索策略与实现、生成层设计与优化等核心模块还包含抖音电商智能客服、飞书等业务线落地案例。读者可借此理解向量生成、向量数据库管理、索引质量评估、提示工程与生成结果质量控制等关键环节的工程思路获得一份兼具体系性与实战参考价值的技术资料。目前已有523人学习。1. 字节跳动RAG实践手册从知识库选型到检索链路调优的落地拆解很多团队第一次搭 RAG 知识库都会掉进同一个坑把一堆 PDF 丢进向量库接上大模型问三个问题就开始胡言乱语。字节跳动这套 RAG 实践手册之所以被反复翻出来讨论不是因为它用了多新的模型而是它把「知识库怎么分层、检索链路怎么拆、什么场景该上结构化知识」讲得足够工程化。RAG 知识库和结构化知识库到底怎么区分图片能不能存ontology rag 和普通向量检索差在哪这些问题在真实项目里都会撞上。这篇笔记按一线落地顺序拆先讲清知识库分层和选型理由再给可复现的检索链路代码最后把踩过的坑和调参经验摊开。适合正在做企业知识问答、客服助手、文档检索的工程师新手能跟着跑通最小链路熟手能对照边界做取舍。2. RAG知识库的分层设计向量库、结构化库和图片到底怎么放2.1 为什么单一向量库撑不住真实业务RAG 知识库最容易被误解成一个「向量数据库」。实际项目里用户的问题至少分三类一类是语义模糊的开放问题比如「这个功能怎么申请」一类是精确条件查询比如「2024 年 3 月之后的退款政策」还有一类是带图带表的复合问题比如「这个报错截图对应哪条配置」。单一向量库对第一类还行对第二类和第三类几乎必然翻车。字节跳动在 RAG 实践里强调的分层思路本质是把「知识」按结构强度拆开。非结构化文本走向量检索结构化字段走 SQL 或倒排图片走多模态向量或对象存储加描述索引。这样做的直接好处是精确条件不进语义空间避免「3 月」被编码成和「4 月」差不多的向量图片不硬塞进文本 embedding避免 OCR 噪声污染召回。我一般会把知识库分成三层原始层存文件对象和元数据索引层存向量和倒排服务层做路由和重排。原始层用对象存储加数据库记录索引层用向量库加全文索引服务层根据 query 类型决定走哪条路。这个结构不复杂但能挡住后面 80% 的「为什么搜不到」问题。2.2 向量库、结构化库和图片存储的选型对照选型时不要只看向量库的 benchmark要看你的查询模式。下面这张表是我在几个项目里实际用过的对照参数按常见量级给具体数值按你自己的数据调。存储类型适合的数据典型查询关键参数常见坑向量库段落文本、FAQ、文档切片语义相似、模糊匹配维度、距离度量、topK切片太大导致召回不准结构化库订单、政策、配置表精确条件、范围、聚合索引字段、分区键把结构化数据硬转文本对象存储描述索引图片、截图、扫描件图搜图、图搜文图片描述模型、向量维度只存图不存描述召回为零全文索引日志、代码、长文档关键词、短语分词器、同义词中文分词没配好图片能不能存 RAG 知识库能但不要只存图片。常见做法是图片存对象存储同时用多模态模型生成一段描述文本把描述文本和图片 ID 一起写进向量库。检索时先召回描述再按 ID 取原图。如果图片里有表格额外用 OCR 抽成结构化字段走结构化库。这样图片问题不会变成「搜不到」而是「先搜描述再取图」。2.3 最小可跑的分层知识库搭建步骤下面这段 Python 用最简方式演示三层写入文本切片进向量库结构化字段进 SQLite图片描述进向量库并关联对象路径。依赖用 sentence-transformers 和 chromadb本地就能跑。# 分层知识库最小示例文本向量 结构化字段 图片描述 import sqlite3 import chromadb from sentence_transformers import SentenceTransformer # 1. 初始化向量库和结构化库 client chromadb.Client() text_col client.get_or_create_collection(text_knowledge) img_col client.get_or_create_collection(image_knowledge) conn sqlite3.connect(structured.db) conn.execute( CREATE TABLE IF NOT EXISTS policy ( id TEXT PRIMARY KEY, title TEXT, effective_date TEXT, region TEXT, content TEXT ) ) # 2. 文本切片写入向量库 model SentenceTransformer(all-MiniLM-L6-v2) texts [ 退款申请需要在订单完成后 7 天内提交。, 企业版用户支持批量导出入口在设置页。, ] text_col.add( documentstexts, embeddingsmodel.encode(texts).tolist(), ids[t1, t2], ) # 3. 结构化字段写入 SQLite conn.execute( INSERT OR REPLACE INTO policy VALUES (?, ?, ?, ?, ?), (p1, 退款政策, 2024-03-01, CN, 7 天内可退), ) conn.commit() # 4. 图片描述写入向量库原图路径存元数据 img_desc [登录页报错截图提示 token 过期需要重新授权。] img_col.add( documentsimg_desc, embeddingsmodel.encode(img_desc).tolist(), ids[img1], metadatas[{path: /oss/images/login_error.png}], ) print(写入完成)逻辑说明文本和图片描述都走同一个 embedding 模型保证语义空间一致结构化字段单独走 SQLite查询时用 SQL 精确过滤。参数上all-MiniLM-L6-v2是 384 维适合本地快速验证生产环境换成你业务微调过的模型。topK在检索时再设写入阶段不用管。图片的metadatas里必须带原图路径否则召回后拿不到图。提示切片长度建议 200 到 500 字重叠 50 字。切片太大向量被平均掉召回精度下降切片太小上下文断裂大模型拼不出完整答案。3. 检索链路拆解从 query 路由到重排的工程实现3.1 query 路由先判断走哪条检索路检索链路的第一步不是 embedding是路由。用户问「退款政策 2024 年 3 月之后」和问「怎么申请退款」走的路完全不同。前者应该先走结构化库过滤日期再对结果做语义排序后者直接走向量库。路由可以用规则也可以用一个小分类模型。规则版最快落地检测 query 里有没有日期、编号、地区、金额等实体有就走结构化优先。我一般会写一个轻量路由函数命中结构化模式就查 SQL否则查向量库。下面这段代码演示路由和混合检索的基本骨架。# query 路由 混合检索骨架 import re import sqlite3 import chromadb from sentence_transformers import SentenceTransformer model SentenceTransformer(all-MiniLM-L6-v2) client chromadb.Client() text_col client.get_or_create_collection(text_knowledge) conn sqlite3.connect(structured.db) def route_query(query: str): # 命中日期、编号、地区等模式走结构化优先 if re.search(r\d{4}[-年]\d{1,2}, query) or re.search(r[A-Z]{2}\d, query): return structured return vector def structured_search(query: str): # 示例按日期范围过滤实际按实体解析结果拼 SQL cur conn.execute( SELECT id, title, content FROM policy WHERE effective_date ?, (2024-03-01,), ) return [{id: r[0], text: r[1] r[2]} for r in cur.fetchall()] def vector_search(query: str, topk: int 5): emb model.encode([query]).tolist() res text_col.query(query_embeddingsemb, n_resultstopk) return [ {id: i, text: d} for i, d in zip(res[ids][0], res[documents][0]) ] def retrieve(query: str): mode route_query(query) if mode structured: candidates structured_search(query) # 结构化结果再做语义重排 if candidates: return candidates[:5] return vector_search(query) print(retrieve(2024 年 3 月之后的退款政策)) print(retrieve(怎么申请退款))逻辑说明route_query用正则做第一层判断命中日期或编号就走结构化。structured_search里实际项目要把 query 解析成实体再拼 SQL这里用固定日期简化。vector_search做标准向量召回。retrieve先走结构化结果为空再回退向量。参数上topk先设 5 到 10后面加重排再收窄。3.2 重排为什么召回 20 条最后只留 3 条向量召回的问题是「相似但不相关」。你搜「退款」它可能召回「退货」「换货」「取消订单」因为语义空间里这些词很近。重排的作用是把召回集按真实相关性重新打分。常见做法是用 cross-encoder 或 bge-reranker 对 query 和每条候选做联合编码输出相关性分数取 top 3 到 5 给大模型。重排的代价是延迟。召回 20 条重排 20 次每次几十毫秒总延迟可能增加几百毫秒。所以重排的候选数不要太大一般 20 到 50 条足够。如果延迟敏感可以用轻量重排模型或者只对 top 10 做重排。# 用 cross-encoder 做重排取 top 3 from sentence_transformers import CrossEncoder reranker CrossEncoder(cross-encoder/ms-marco-MiniLM-L-6-v2) def rerank(query: str, candidates: list, topn: int 3): pairs [[query, c[text]] for c in candidates] scores reranker.predict(pairs) ranked sorted(zip(candidates, scores), keylambda x: x[1], reverseTrue) return [c for c, s in ranked[:topn]] # 接上面的 retrieve cands retrieve(怎么申请退款) final rerank(怎么申请退款, cands, topn3) for f in final: print(f[id], f[text][:50])逻辑说明CrossEncoder对每对 query 和候选打分分数越高越相关。topn控制最终给大模型的条数一般 3 到 5。参数上ms-marco-MiniLM-L-6-v2是英文重排模型中文场景换成 bge-reranker-base 或你业务微调的版本。重排后如果最高分低于阈值比如 0.3可以判定为「无相关知识」直接回复不知道避免大模型硬编。3.3 结构化知识库和向量库的融合排序当 query 同时命中结构化和向量时需要融合排序。简单做法是加权分数结构化结果给一个基础分向量相似度给一个分按权重相加。权重怎么设看业务。政策类问题结构化权重大开放问答向量权重大。我一般先用 0.5 比 0.5 跑一批 badcase再调。融合时要注意分数归一化。向量相似度通常在 0 到 1结构化过滤后的结果没有天然分数可以按匹配字段数给分。比如命中日期加 0.3命中地区加 0.2命中关键词加 0.1。这样融合时不会因为量纲不同导致某一方主导。注意不要把所有知识都塞进向量库再指望重排解决。结构化查询走 SQL 的延迟是毫秒级向量检索是几十毫秒重排是几百毫秒。能走结构化就走结构化这是 RAG 链路里最容易被忽略的提速点。4. RAG实战避坑切片、召回和图片处理的常见翻车记录4.1 切片太大导致召回「看起来对但答不对」现象用户问「企业版怎么导出数据」召回了一条 2000 字的文档里面确实有导出说明但大模型回答时把退款政策也混进去了。原因切片太大一条切片里包含多个主题向量被平均成「企业版相关」但具体到导出时精度不够。解决按语义段落切片每片 200 到 500 字标题和正文可以分开存检索时标题加权。如果文档有层级按最小标题单元切。4.2 中文分词没配好全文检索形同虚设现象结构化库和全文索引里搜「退款申请」搜不到搜「退款」能搜到。原因默认分词器把「退款申请」当成一个词但文档里写的是「申请退款」。解决换中文分词器加同义词词典把「申请退款」「退款申请」「退钱」映射到同一组。向量检索对这类词序不敏感但全文索引敏感两者要配合。4.3 图片只存原图不存描述多模态检索直接归零现象用户上传一张报错截图问「这个怎么解决」系统返回空。原因图片只存了对象存储路径向量库里没有对应描述检索时没有任何文本可匹配。解决入库时用多模态模型生成图片描述描述文本进向量库原图路径进元数据。如果图片是表格额外 OCR 成结构化字段。描述要包含关键实体比如「登录页」「token 过期」「重新授权」。4.4 重排阈值不设大模型被迫「硬答」现象用户问一个知识库里完全没有的问题大模型还是编了一段答案。原因检索返回了 topK重排后最高分只有 0.1但没有阈值拦截大模型拿到低质量上下文仍然生成。解决设重排分数阈值低于阈值直接返回「暂无相关信息」。阈值用验证集调一般 0.3 到 0.5 之间。同时给大模型加指令如果上下文不包含答案明确说不知道。4.5 结构化字段当文本存精确查询全表扫描现象查「2024 年 3 月之后的政策」越来越慢。原因日期字段存成文本SQL 里用 LIKE 匹配无法走索引。解决日期、金额、编号等字段用原生类型存建索引。查询时先解析实体再拼条件不要用文本模糊匹配代替结构化查询。这一步在数据量上万后差异非常明显。5. 进阶技巧用 ontology 和验证集把 RAG 知识库调稳5.1 ontology rag 到底解决什么问题ontology rag 的核心不是多一个花哨概念而是把领域里的实体和关系显式建出来。普通向量检索把「退款」和「退货」当成两个相近的词ontology 里可以定义「退款」是「售后」的子类「退货」是「物流」的子类检索时按关系扩展或收窄。适合领域术语多、关系复杂的场景比如医疗、金融、法律。落地时不用一上来就建大图谱先抽核心实体和三层关系和向量检索并行用。query 命中实体时走图谱扩展否则走向量。5.2 用验证集量化检索质量别靠感觉调参调 RAG 最怕「感觉好了」。我一般会标 100 到 200 条验证集每条包含 query、期望召回的文档 ID、期望答案要点。每次改切片、改模型、改重排跑一遍验证集看召回率、MRR 和答案命中率。召回率看 topK 里有没有正确文档MRR 看正确文档排多前答案命中率看最终生成对不对。这三个指标一起看才能判断是检索问题还是生成问题。指标含义调参方向RecallKtopK 里有没有正确文档低则加大 K 或改切片MRR正确文档的平均排名低则加重排或改 embedding答案命中率最终回答是否正确低则查生成 prompt 或上下文质量5.3 一个具体技巧给切片加「上下文头」切片单独看经常缺主语。比如一片写「需要在 7 天内提交」不知道是什么的 7 天。我习惯在切片前拼一个上下文头格式是「文档标题 章节标题正文」。这样向量里带上了层级信息召回时更容易命中正确主题。上下文头不要超过 50 字否则会稀释正文语义。这个改动很小但在几个项目里都把 MRR 提了一截。最后说个血泪经验RAG 的瓶颈往往不在模型在数据准备和检索链路。我早期花大量时间换 embedding 模型后来发现把切片和路由改对效果提升比换模型大得多。先保证知识库分层清楚、路由准确、重排有阈值再去调模型。希望帮到你。本文还有配套的精品资源点击获取