Llama 3知识库实践:先检索后微调,打通切分与混合检索
发布时间:2026/10/26 5:33:48 锦皓数字建站

1. 先想清楚为什么这个场景下不该急着微调1.1 从“模型没学过”到“模型查得到”Llama 3 基于知识库应用实践这个题目听起来像是“把文档丢进去模型就能答”但真正动手做过一轮的人都知道第一步要解决的是认知问题然后才是技术问题。我接触过不少团队一上来就问“微调要多少条数据”结果聊两句才发现他们的需求其实是“让模型能回答我们内部那份三百页的产品手册”。这两件事完全不是一回事。大模型的知识是训练时固化在参数里的权重一旦冻结它就不知道你公司上周刚改的报销标准也不知道你手上这份项目验收文档里写了什么。想让它“知道”路径只有两条一条是把新知识写进参数继续预训练或微调另一条是把知识放在模型外面需要的时候现查现用检索增强生成。前者成本高、周期长、更新慢改一个字都得重训后者成本低、更新快改文档就是改文件。这就是我在这类项目里几乎总是先做知识库、后考虑微调的原因。这篇文章面向的是已经在本地或内网把 Llama 3 跑起来、想把它接到自己文档上的开发者也适合还没动手但正在评估方案的技术负责人。我会把从硬件估算、数据切分、向量检索到提示词组装这一整条链路拆开讲参数怎么算、坑在哪、为什么这么选都说清楚。第一期先把“检索侧”打牢因为这一层做不好后面换再大的模型也救不回来。1.2 Llama 3 在这个链路里到底承担什么角色我见过一种很典型的误用把 Llama 3 当成“搜索引擎”。用户问一句直接扔给模型指望它从记忆里翻出答案。这种用法在通用常识上还凑合一旦碰到专业文档就必然翻车而且翻车的方式很隐蔽——模型会非常自信地编一段听起来很像真的内容给你。在知识库应用里Llama 3 的正确定位是“阅读理解 表达生成”而不是“知识存储”。它的输入应该是一段已经从你的文档里精准捞出来的原文任务是理解这段原文、按用户的问题组织语言、必要时做归纳和对比。知识存储和精准召回这两件事交给向量检索和关键词检索去做。分工清楚了整个系统的责任边界也就清楚了答错了先看是没捞到召回问题还是捞到了但没读懂生成问题。这两种故障的排查路径完全不同混在一起就只能瞎猜。还有一个容易被忽略的点Llama 3 的指令遵循能力和长上下文能力决定了它能吃多少参考资料。8B 版本在中文长文本上的稳定性比前代好不少但并不意味着你可以无脑塞两万字进去。上下文越长模型对中间部分的注意力越容易衰减这是我实测下来非常明显的现象——把关键段落放在上下文的开头或结尾回答质量会明显好于放在正中间。所以后面的拼装环节我会特意做一个“位置排序”的处理而不是简单按相似度从高到低堆。1.3 边界划清什么情况才轮到微调这里给一个我自己用的判断标准比较粗糙但好用需求特征优先方案理由知识随时更新一天改好几版检索增强改文件即生效无需重训需要引用出处、可追溯检索增强原文在哪一段是明确的要求固定输出格式、特定口吻提示词 少量示例先试提示词成本几乎为零领域术语极多、表达方式特殊微调LoRA优先检索解决“不知道”微调解决“不会说”需要稳定的分类/抽取能力微调或小模型专用任务大模型做分类是浪费看得懂这张表基本就不会走弯路。真正需要微调的场景往往是“模型其实知道答案但表达方式不符合你的业务规范”比如必须输出固定字段的 JSON、必须用某种公文口吻。这种问题检索解决不了因为检索只能给它原料改变不了它的表达习惯。反过来如果模型压根不知道这件事微调也不是好选择——你得准备大量高质量样本还得承担知识过期的风险。我在第一个项目里就犯过这个错为了让模型学会内部术语硬是标注了两千条问答做微调效果确实上去了但两个月后业务规则一改两千条数据全废还得重来。后来老老实实改回检索方案改一次文档只要十分钟。2. 硬件与部署形态先把显存这笔账算明白2.1 显存估算的一个可复用公式很多人卡在这一步不知道自己手上的机器能不能跑或者该选多大的模型。其实显存占用可以拆成三块来算我把它写成一个可以直接套的公式总显存 ≈ 权重显存 KV Cache 运行时开销约 1~2GB权重显存 参数量 × 每参数字节数。FP16 是 2 字节INT8 是 1 字节INT4 大约 0.5~0.6 字节量化有额外的缩放参数所以不会是精确的 0.5。以 Llama 3 8B 为例FP168 × 2 16GBINT88 × 1 8GBINT48 × 0.55 ≈ 4.4GBKV Cache 是很多人漏算的部分它跟上下文长度和并发数成正比。计算公式是每 token 的 KV Cache 2 × 层数 × KV 头数 × 头维度 × 字节数Llama 3 8B 是 32 层、8 个 KV 头分组查询注意力、头维度 128FP16 下每 token 约 2 × 32 × 8 × 128 × 2 131072 字节也就是 128KB。听起来很小但 8K 上下文就是 1GB四路并发就是 4GB。如果你开 32K 上下文再配八路并发光 KV Cache 就要 32GB比模型本身还大。把账算完INT4 量化的 8B 模型在 8K 上下文、四路并发下总占用大约是 4.4 4 2 ≈ 10.4GB一张 12GB 显存的卡刚好够用还能留一点余量。这个数字我在多台机器上验证过基本吻合。至于 70B权重 INT4 就要 38GB 左右加上 KV Cache 和开销单卡 48GB 才比较从容。如果只是做内部工具、日活几十人8B 完全够如果要做对外服务、并发上百再考虑上大模型或者多卡。2.2 三种部署形态怎么选推理侧的方案我大致分成三类各有各的适用场景方案类型特点适合场景主要代价服务化推理引擎连续批处理 显存分页吞吐高支持多并发暴露标准 HTTP 接口团队共用、有一定并发部署复杂配置项多启动慢单机量化运行时GGUF 格式资源占用低CPU 也能跑启动快个人开发、笔记本、边缘设备并发能力弱长上下文性能衰减明显一站式本地运行工具开箱即用一条命令拉起快速验证、做 Demo可调参数少生产环境不好扩展我的建议是验证阶段用一站式工具联调阶段换成服务化引擎。原因很实际——前期你主要验证的是“模型能不能读懂我的文档”这时候不该把时间花在编译和调参上等到要接业务系统了你需要稳定的接口、可控的并发、可观测的指标这时候一站式工具就不够用了。有一点要提醒不同方案对同一个提示词的处理细节不一样比如特殊 token 的拼接、系统消息的注入位置。我在切换方案时踩过一次坑明明提示词一个字没改换引擎之后回答风格突变排查了半天才发现是新引擎自动加了一段默认的对话模板前缀。所以换引擎后一定要拿同一批问题重跑一遍回归测试。2.3 量化不是免费的午餐INT4 量化能省下三分之二的显存代价是什么我的实测感受是在事实性问答上损失很小在需要推理和长链对比的任务上损失明显。具体来说让它从文档里找出一条明确写着的规则INT4 和 FP16 的准确率差别在几个百分点以内肉眼几乎看不出来。但如果问题是“对比文档里 A 方案和 B 方案的三个差异并说明哪个更适合当前场景”INT4 更容易漏掉其中一条或者把两条差异合并成一条。这种错误很隐蔽因为它读起来依然通顺。所以我的做法是分场景配置做检索问答这种“以读为主”的任务放心用 INT4做摘要、对比、多步推理的任务尽量用 INT8 或 FP16至少把关键节点用高精度跑一遍做校验。另外量化方式也有讲究主流的几种 4bit 量化里带激活值量化的版本对精度更友好但速度稍慢。这个取舍看你更在意哪个指标我一般优先保精度因为返工成本远高于那点延迟。还有一个细节量化后的模型对温度参数更敏感。FP16 下 temperature 设 0.7 还挺稳量化后同样的值容易输出重复片段或者突然跑偏。我通常会把量化模型的 temperature 往下压到 0.3~0.5配合 top_p 0.9稳定很多。3. 知识库的原料处理切分方式决定效果上限3.1 文档解析这一步八成的问题都埋在这我得说句实话知识库应用效果不好绝大部分锅在数据侧不在模型侧。而数据侧最常见的问题是解析阶段就错了。PDF 是最麻烦的。双栏排版被识别成一行、表格被拆成乱序的文字块、页眉页脚混进正文、公式变成一堆乱码——这些都是家常便饭。我见过一份技术文档解析出来后每一页的页脚“内部资料 请勿外传”都被拼进了正文段落中间检索的时候这段话反复被召回因为“内部资料”这个词在用户提问里出现频率很高结果模型每次都被这段噪音干扰。我的处理顺序是这样的先做格式清洗去掉重复出现的页眉页脚判断方法很简单统计每页首尾几行的重复率超过阈值的直接删再做版式还原双栏文档要么按阅读顺序重排要么干脆逐栏切分绝不能混着来最后单独处理表格把表格转成 Markdown 或者“字段值”的键值对形式不要让表格内容以散字的形式流进正文。表格这一块值得多花点时间。用户在问“某型号的额定功率是多少”时答案往往就在一张表里。如果表格被拆成散字检索几乎不可能精准命中。我的做法是把每一行表格转成一句自然语言描述比如“型号 A 的额定功率为 1500 瓦工作温度为 -10 到 45 摄氏度”这样既保留了结构化信息又能被语义检索命中。这个转换看起来笨但实测召回率提升非常明显。3.2 分块大小的推算与取舍切分粒度是第二个关键决策。切太大检索出来的段落里有一半是无关内容浪费上下文还干扰模型切太小一句话被拆成两半语义不完整模型读不懂。我的经验值是这样的中文技术文档正文块 300~500 字重叠 50~80 字。为什么是这个数先说上限。Llama 3 的上下文窗口够大理论上可以塞很多但检索精度会随块变大而下降——一个 1000 字的块里可能只有两句话跟问题相关向量模型在做整体编码时这两句话的权重会被稀释相似度分数上不去反而排不到前面。再说下限。低于 200 字的块很容易把“因为……所以……”这种逻辑关系切断。我试过 150 字的粒度结果模型经常回答“根据资料该流程需要三个步骤”但实际资料里写的是“该流程通常需要三个步骤但在紧急情况下可以简化”后半句被切到下一个块里了导致回答失真。重叠部分的作用是保住边界信息。50~80 字大约是一到两句话能覆盖大部分跨块语义。重叠太多会带来冗余召回同一个内容被召回三次白占上下文重叠太少则起不到保护作用。这个区间是我反复调过几次之后稳定下来的。还要强调一点切分要按语义边界不要按固定字数硬切。优先在段落、句号、分号处断开实在找不到再退而求其次。我写了一个简单的规则从目标长度往后找最近的句号如果超出上限 30% 还没找到就往前找分号再找不到才硬切。这几十行代码带来的效果提升比换个更大的模型明显得多。3.3 元数据给后面的检索和引用留后路切块的时候顺手把元数据打上这个习惯能省掉后面无数次返工。我至少会保留这几项来源标识文档名 版本号 页码或章节号层级路径一级标题 / 二级标题 / 三级标题方便在回答里给出“详见某章某节”时间戳文档的生效日期用于处理新旧版本冲突权限标签哪些角色可以看这段内容权限标签这一项很多人前期不做后期想加就得重新跑一遍全量数据。我吃过这个亏一个内部知识库上线后才发现财务相关的文档不该对所有人生效但索引里没有权限字段只能在检索后做二次过滤性能很差而且容易漏。后来重建索引每个块都带上可见范围标签检索时先按标签过滤再算相似度干净利落。时间戳也很关键。制度类文档经常有新旧两版同时在库里如果两个块都被召回模型很可能把旧规定当新规定答出去而且答得理直气壮。有了时间戳检索阶段就可以加一条规则同一主题下只保留最新版本或者在提示词里明确标注“以下内容发布于某年某月请注意时效”。4. 向量化与检索链路召回率才是天花板4.1 向量模型的选择逻辑生成模型用 Llama 3不代表向量模型也要用它。这两件事的目标完全不同生成模型要会表达向量模型要会把语义相近的文本映射到相近的位置。用生成模型做嵌入效果通常不如专用的向量模型而且成本高得多。选向量模型我看三个指标中文语义匹配效果、维度大小、推理速度。维度不是越大越好1024 维和 768 维在实际检索中的差距远没有宣传的那么大但存储和计算成本是按维度线性涨的。十万个块、1024 维、FP32 存储光向量就占 400MB 左右如果规模上到百万级这个数字就不好看了。这时候可以考虑降维或者用半精度存储实测对召回的影响在可接受范围内。还有一点容易被忽略向量模型必须和你的业务语料风格匹配。通用语料训练出来的模型在口语化提问上表现好但在大量专业术语、缩写、型号编号的场景下会明显吃力。如果你的知识库全是设备手册、型号参数纯语义检索经常失灵——用户问“X200 的替代型号”语义模型觉得“X200”和“X300”很像直接召回错误的块。这种时候关键词检索反而更靠谱。4.2 索引参数与检索参数怎么定向量索引这块我一般不做过度优化。十万级以下的规模暴力检索全量比对就够了延迟在几十毫秒量级没必要上复杂的近似索引。上了近似索引反而引入新问题召回率不再是 100%有些本该命中的块就是找不出来而且这种漏召回很难排查你会以为是模型的问题。规模上到百万级再考虑分桶或者图结构的近似索引同时把“召回率”这个指标盯住。我通常会用一批固定的测试问题对比近似索引和暴力检索的结果差异如果 top10 的重合度低于 90%说明索引参数调得太激进了该把搜索范围放宽。检索参数方面top_k 我一般设 5~8配合一个相似度阈值比如 0.3~0.5具体看模型和语料的分布。阈值的意义在于过滤掉“勉强相关”的结果。如果不设阈值用户问一个知识库里根本没有的问题系统依然会硬凑五个块塞给模型模型就只能硬答这就是幻觉的主要来源之一。阈值的具体数值不能拍脑袋得看分布。我的方法是拿 50 条真实问题跑一遍把每次 top1 的相似度分数打印出来看看“能答的问题”和“答不了的问题”这两组的分数分布在哪里分开。通常能找到一条相对清晰的分界线取那条线往下浮 0.05 作为阈值兼顾召全和精度。4.3 混合检索把关键词这条腿接上纯向量检索在专业场景下最大的问题是“精确匹配失灵”。型号、编号、人名、专有名词这些词的语义区分度很低向量空间里挤在一起反倒是字面匹配最准。所以我基本都会做混合检索向量召回一路关键词召回一路BM25 这类算法就够用不需要多复杂两路结果做分数归一化之后加权融合。权重我一般设成向量 0.6、关键词 0.4如果你的知识库偏手册、参数类可以把关键词权重提到 0.5 甚至 0.6。融合之后还有一步重排。用一个交叉编码器对 top20 的结果重新打分只保留前 5 条。这一步的收益在专业问答上非常明显因为它能捕捉到查询和文档之间的细粒度匹配关系而双塔结构的向量模型做不到这一点。代价是延迟增加一次重排大概多几十到一百多毫秒。对内网工具来说完全可以接受对高并发服务就得权衡。还有一个实用技巧把用户的口语化提问先做一次改写再检索。用户问“报销怎么弄”直接拿去检索效果一般改写成“差旅费报销流程 所需材料 审批时限”再检索命中率立刻不一样。这个改写可以用 Llama 3 自己做给一段简短的指令就行成本很低。但要注意别让改写跑偏我一般要求它只扩展同义词和补全省略的主语不许自己加限定条件。曾经有一次改写把“年假”扩成了“年假和调休”结果召回了一堆调休相关的块反而干扰了回答。5. 提示词组装与生成控制让模型老实说话5.1 上下文的拼装顺序有讲究检索回来五条内容怎么塞给模型效果差别很大。我踩过的坑是这样一开始按相似度从高到低顺序拼接结果发现模型经常只用第一条后面几条视而不见后来把顺序打乱发现模型对最后一条的利用率明显高于中间几条。这跟模型的注意力分布有关中间位置的信息最容易被忽略。所以我现在固定用这个顺序最相关的放第一条次相关的放最后一条其余放中间。同时给每条内容加上明确的编号和来源标记比如“【资料 1来源某手册 3.2 节】”。编号的作用是让模型可以引用来源标记的作用是万一答错了能快速定位是哪个块惹的祸。上下文总长度也要做预算。我的经验是给参考资料的部分不要超过模型上下文窗口的一半剩下的留给系统指令、对话历史和输出。比如 8K 上下文参考资料控制在 3K 到 4K 字左右输出预留 1K其余给指令和历史。超过这个比例模型开始出现“读串行”的现象——把资料 A 的内容说成是资料 B 的。拼接的时候还要清理噪音。检索出来的块经常带着上一段的尾巴或者下一段的开头我会在切块时就记录好边界拼装时做一次裁剪。别小看这个细节一段无关的尾巴有时候足以把模型带偏。5.2 生成参数的实测配置参数这块没什么玄学但确实需要按场景调。我的常用配置场景temperaturetop_p说明事实性问答、条款查询0.1~0.30.9要稳定、可复现同一问题答案必须一致内容摘要、要点归纳0.3~0.50.9允许适度措辞变化方案对比、分析建议0.5~0.70.95需要一定的发散能力另外一定要设最大输出长度。不设的话遇到模型“刹不住车”的情况它会一直生成下去白白占着显存。我一般按场景设 512 到 1024 token够用且安全。还有停止符的设置。如果你的业务需要模型输出结构化内容一定要把停止条件写清楚否则它会在 JSON 后面再接一段“希望以上内容对您有帮助”之类的废话解析直接失败。5.3 拒答设计比答对更重要这一点我想单独强调。知识库应用上线后用户最不能接受的不是“我不知道”而是“一本正经地胡说”。所以必须在提示词层面明确拒答规则而且要写得非常具体。我的提示词里通常有这么几条硬约束只能依据提供的资料回答资料里没有的内容一律说“现有资料中未找到相关内容”如果资料之间互相矛盾要指出矛盾点而不是自己选一个如果问题涉及资料之外的时间、数字、人名不要推测。最后再加一句“回答末尾标注所依据的资料编号”。光靠提示词还不够我还会加一层后处理校验如果模型输出的引用编号不在本次提供的资料编号范围内直接判定为无效回答降级成“未找到”。这个检查很粗暴但能拦下相当一部分幻觉。拒答率这个指标要盯着。拒答率太高说明检索没做好该找到的没找到太低说明模型在硬答不该答的也答了。我一般把拒答率控制在 15%~25% 之间比较健康具体看业务。如果知识库覆盖面本来就窄拒答率高是正常的不该为了降低这个数字去放松约束。6. 联调阶段的典型问题与排查思路6.1 问题速查表下面这张表是我这几年攒下来的基本覆盖了八成以上的故障现象最可能的原因排查动作回答完全跑偏跟问题无关检索没命中塞进去的是噪音打印 top5 的块和分数看第一条是否相关回答正确但很笼统没有细节块切得太大关键细节被稀释检查命中块的字符数考虑缩小粒度明明文档里有却答“未找到”阈值太高或表述差异大降低阈值或换成混合检索同一问题两次答案不一样温度过高或未设种子把温度压到 0.2 以下回答里出现文档中没有的数字幻觉且上下文中有相似数字加引用校验检查是否越界引用长文档问答只答前半部分上下文超预算被截断统计实际 token 数压缩参考资料中文夹杂英文乱码特殊 token 或模板拼接错误检查对话模板对比不同引擎输出并发一高就超时KV Cache 撑爆触发换页降并发或缩短上下文看显存曲线6.2 几个印象深刻的排查过程案例一一个“看起来很对”的错误答案。用户问某设备的维护周期模型答“每 500 小时”斩钉截铁。我去查原文写的是“每 500 小时或每 6 个月以先到者为准”。问题出在切块——后半句被切到了下一个块而那个块因为相似度略低没被召回。修复方式很简单把切块规则改成“遇到‘或’‘但’‘除……外’这类转折连词时优先向后多看 50 字再切”。改完之后这类问题基本消失。这件事让我意识到很多所谓的“模型幻觉”本质是数据切分造成的断章取义。案例二检索分数虚高。有一批块的相似度分数普遍在 0.8 以上但内容跟问题八竿子打不着。查了半天发现这批块来自同一份文档而这份文档的页眉是一句很长的公司介绍被混进了每个块的开头。所有块都带着同一段高权重文本导致它们的向量高度相似。清理页眉之后分数分布立刻恢复正常。这个教训是先怀疑数据再怀疑模型。案例三换模型后效果倒退。从 8B 换到更大的模型按理说应该更好结果反而开始出现“答非所问”。排查发现是大模型对提示词里的指令更敏感我原来那段写得很随意的系统提示“尽量回答得详细一点”在小模型上只是让它多说两句在大模型上却让它开始自行发挥、脱离资料。把提示词改成更严格的约束式表述后问题解决。结论是换模型一定要重跑回归集提示词不是通用的。6.3 建一个自己的回归测试集这件事越早做越好我建议在项目一开始就建。方法很朴素从真实业务里挑 50 到 100 个问题人工标注出“标准答案应该来自哪个文档的哪一段”存成一个表格。每次改动——换切块参数、换向量模型、换提示词、换生成模型——都跑一遍看两件事召回的那一段有没有变检索指标答案对不对生成指标。这个集子不需要多精致二十条也能起作用。它的价值在于把“感觉好像变好了”变成“这 20 条里有 17 条答对了上次是 14 条”。没有这个东西所有的调参都是玄学而且很容易出现“修好一个坏了三个”的情况却浑然不觉。我现在的习惯是每接一个新领域的知识库前三天不写任何业务代码全部用来做数据清洗和建测试集。听起来慢但后面省下的返工时间远不止三天。这套流程跑顺之后接一个新库从清洗到上线大概两三天其中大部分时间还是花在文档解析上。后面第二期我会接着聊多轮对话状态管理、权限隔离的具体实现以及怎么把这套东西接到实际业务系统里去——那部分踩的坑比检索这一层还多尤其是会话历史跟检索结果的冲突处理属于典型的“不加没事加了全是问题”。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。