为什么我把AI助手记忆精排换成本地4B小模型:延迟、成本与合规的三重权衡

📅 发布时间:2026/10/16 13:26:00
为什么我把AI助手记忆精排换成本地4B小模型:延迟、成本与合规的三重权衡
做 AI 助手落地这一年多我最大的感悟是用户不会因为你的模型聪明而记住你但会因为你的助手“忘了上周说的事”而流失。所谓记忆精排就是在用户每次提问时从几千条历史记忆里挑出最该被模型看到的那几条。这个环节做不好后面再强的推理模型也等于在失忆状态下答题。我先后试了三套方案——JEV、clef、Nox分别代表了轻量重排、交互式打分、大模型评分三条路线。折腾了两个月最后团队所有人都以为我会选效果最好的云端大模型方案我却把生产环境的记忆精排换成了一个跑在本地显卡上的 4B 小模型。这篇文章把完整的对比过程、决策逻辑和落地细节写出来给同样在做 agent 记忆系统、RAG 精排的同学一个参考。1. 生产记忆精排到底在排什么1.1 记忆系统里的召回与精排先聊聊背景。我们的业务是一个偏私域场景的 AI 助手用户会和它进行多轮对话助手需要记住用户的偏好、之前的约定、项目进展、家庭成员信息等等。这些记忆以片段形式存在向量库里每条长度大概在 50 到 200 个 token 之间。用户发起一个新问题时系统要做两件事召回和精排。召回阶段用 embedding 相似度、关键词匹配、时间衰减等粗粒度方法从全量记忆里捞出一批候选通常取 top 50 到 100 条。这个阶段追求的是“别漏掉”所以召回率要高哪怕混进来一堆不相关的也没关系。精排阶段则完全不同它的任务是在这 50 到 100 条候选里结合当前对话上下文、用户最近的行为、记忆的时间权重最终筛出 3 到 5 条真正该放进 prompt 的记忆。粗排和精排的关系很像你从书架上找资料召回是从整个书架里抱下来二十本可能相关的书精排是翻开每本书的目录和前言最后挑出真正能回答问题的三本。召回阶段错了可以靠精排救回来一部分但精排一旦放松模型就可能在无关记忆的干扰下编造答案。1.2 为什么精排决定了“它到底记没记住”用户判断一个 AI 助手有没有记忆力标准非常朴素我上周说过的话今天它能不能接住。而“能不能接住”的关键恰恰不在模型本身而在精排器有没有把那条记忆送到模型的上下文里。举个真实场景。用户周一和助手说“下周三要去上海出差帮我订一间带办公桌的酒店”周三早上用户问“今天的行程有什么安排”。这个查询在字面上和“上海出差”“订酒店”并没有太高的向量相似度因为“今天的行程”是一个泛指如果没有精排器把“下周三上海出差”这条记忆排到前面模型很可能回复“我没有查到您的行程安排”。反过来如果精排器太激进把所有和“上海”沾边的记忆都塞进去模型可能会把上个月去上海的记忆误当成这次的行程导致完全错误的信息输出。这种错误比“没记住”更严重因为用户会认为助手产生了幻觉信任损失是双倍的。所以精排在记忆系统里的定位本质上是“上下文准入控制”——它决定了哪些信息有资格进入模型的视野。这个关卡做不好召回做得再好都是白搭。1.3 生产成本的三重约束在选型之前我先梳理了精排环节必须满足的三重约束。第一是延迟用户提问后整个链路必须在 1.5 秒内返回首 token这意味着精排环节的预算只有 300 到 500 毫秒。第二是成本我们的日活不算夸张但每个用户每天会触发几十次记忆检索全天下来精排的调用量在百万次级别单次成本如果多出 1 分钱一个月就是几十万的额外支出。第三是数据合规记忆内容包含大量用户隐私比如联系方式、健康信息、财务状况这些数据原则上不允许出本地网络。这三重约束直接决定了选型方向延迟和成本排除了任何云端大模型 API 的常规用法数据合规又排除了把记忆内容明文发送给外部服务的可能性。但完全用传统规则和向量相似度做精排效果又确实不够。这就是我们开始找第三方解决方案的原因。2. 三方摸底JEV、clef、Nox 各走一条路市面上做记忆精排的方案大致可以分成三类。我在选型时分别试了 JEV、clef 和 Nox它们正好代表了这三条路线也各有各的擅长和短板。2.1 JEV向量相似度之上的轻量重排JEV 的核心思路是在向量召回的基础上做一次更精细的相似度计算。它不是简单的 cosine 距离而是会对记忆片段做加权——query 里的实体、动词、时间词会被赋予更高的权重然后重新计算每条候选记忆的得分。我当时的测试结果是JEV 的延迟最低单条记忆的评分耗时只有 2 到 3 毫秒50 条候选全量重排也就 100 多毫秒几乎不占链路预算。它的部署也非常简单一个 Python 包加 Redis 就能跑起来不需要专门的模型服务。但 JEV 的问题也很明显它本质上还是在做“字面相似度”的打分对语义理解很浅。比如“帮我订一间带办公桌的酒店”和“我下周的商务住宿”这两句话在字面上几乎没有重叠的词但语义上是高度相关的。JEV 对这种语义跨越的匹配无能为力测试集上它的精确率只有 63% 左右而我们的目标是至少 85%。2.2 clef交互式打分器的精细活clef 走的是交叉编码器路线。它不像 JEV 那样把 query 和记忆分别编码再算相似度而是把两者拼接在一起喂给一个深层模型做联合编码直接输出相关性分数。交叉编码器的优势是能看到 query 和记忆之间的交互细节。比如“下周三的出差行程”和“上海出差订酒店”clef 能捕捉到“出差”“行程”“上海”“酒店”之间的语义关联哪怕字面不匹配也能给出高分。在我 1200 条人工标注的测试集上clef 的精确率能到 81%明显优于 JEV。代价是速度。交叉编码器需要对每条候选都做一次前向计算50 条候选全量跑下来大概需要 400 到 600 毫秒已经逼近我们的延迟预算红线。而且 clef 的商业授权是按调用量收费的百万级日调用量算下来月成本在六位数人民币量级这个数字在项目立项评审时基本就被否了。2.3 Nox大模型评分的“天花板”Nox 是完全不一样的路数它直接用对话式大模型来当裁判把 query 和候选记忆做成一个列表让模型逐条评判相关性。因为大模型有真正的语义理解能力它能理解“今天有什么安排”和“周三的上海出差”之间的深层关联也能理解“订带办公桌的酒店”这样的隐含信息。效果确实是我测试的三个方案里最好的。在同样的测试集上Nox 的精确率达到了 92%而且它还能输出理由方便我们做 bad case 分析。更关键的是Nox 能做真正的“全局排序”——它能比较所有候选之间的相对优先级而不是孤立地给每条打分。但 Nox 的硬伤在于它是个云端服务延迟在 800 毫秒到 2 秒之间波动赶上高峰期甚至超过 3 秒。成本方面单次调用按输入输出 token 计费我们的精排场景每次要带上 50 条候选记忆输入 token 量巨大百万次调用一个月下来是天文数字。另外把用户记忆内容发送到云端这件事在数据合规评审那关就没过去。2.4 一张表看清三者的取舍我把三套方案的测试结果整理成了表格方案精排精确率单次延迟月成本百万级调用数据合规部署复杂度JEV63%100ms很低本地可部署低clef81%400-600ms六位数人民币本地可部署中Nox92%800ms-3s极高需出网不通过低托管本地 4B86-88%150-250ms仅电费和显卡折旧全本地中高光看表格Nox 的精度优势很明显clef 是那个“性能还行、价格不行”的尴尬存在JEV 则主要输在精度上。如果只比单一维度答案很清楚但把延迟、成本、合规三个约束摆在一起Nox 和 clef 其实已经被排除了。剩下的问题变成了本地 4B 小模型能不能达到 85% 以上的精确率以及怎么让它稳定地达到。3. 决策过程为什么云端大模型被踢出局这一节把决策逻辑讲透。不是 Nox 不好而是它在我们的场景里根本没法进生产环境。3.1 延迟的账每多 300ms用户就流失一点耐心我们的交互场景是对话式的用户说完一句话期望在 1.5 秒内看到回复。实测数据表明响应时间超过 2 秒用户发起下一轮的意愿会下降约 20%。精排只是整个链路的一环召回花 80ms精排花多少决定了我们还有多少余量留给生成环节。Nox 的 800ms 起步延迟意味着即使用户问题一到服务端就并行发起精排生成环节也只能剩 600ms 左右。对于 4B 模型的生成速度来说这点时间只够吐几十个 token体验会非常差。而且云端服务的延迟还有抖动高峰期 p99 能到 3 秒这已经超出了“可接受”的范畴。clef 的 400-600ms 虽然比 Nox 好但在我们的链路里依然紧张再加上它还需要把候选集逐条做前向计算并发上来之后延迟会更不稳定。唯一在延迟上完全达标的是 JEV可它的精度又不够。3.2 成本的账按次计费在精排场景下扛不住很多人对精排的调用量没有概念。用户每说一句话只要触发记忆检索就会产生一次精排调用。一个重度用户一天可能触发 200 次我们假设人均 50 次日活 2 万用户就是每天 100 万次调用。一个月下来3000 万次。Nox 按 token 计费每次精排要带上 50 条候选记忆和当前 query粗算每次输入 8000 token、输出 200 token成本约每千 token 0.03 元单次调用约 0.25 元。3000 万次就是 750 万元一个月。即使谈到企业折扣打个三折也超过 200 万元。这个数字在任何预算评审会上都是直接毙掉的。clef 按调用次数计费单价看起来不高但百万次日调用乘以月成本同样在六位数人民币级别。相比之下租一台带 24GB 显存显卡的服务器月租金几千块本地 4B 模型推理 3000 万次精排GPU 利用率撑到 60% 也就够了边际成本几乎为零。3.3 数据的账用户记忆不该离开本地这是最硬的约束。我们系统里的记忆包含用户的全名、手机号、家庭住址、健康状况、银行卡消费记录等敏感信息。数据合规评审明确要求用户记忆内容不得离开我们的私有网络。Nox 作为云端服务意味着用户记忆要明文或加密后发送到外部服务器这在合规上直接出局。或许有人会说可以脱敏后再发但记忆精排恰恰需要完整语义才能做出准确判断脱敏后的数据必然导致精度下降那还不如不用。clef 如果部署在本地则没有这个问题JEV 同样没有。这个约束看似限制了方案选择其实反而帮我们理清了思路能在本地跑的方案才是有资格进入决赛圈的方案。3.4 质量账4B 模型真的够用吗排除 Nox 和 clef 之后剩下的路就是我们自己做精排器。而精排器的核心是一个跑在本地 GPU 上的 4B 参数对话模型。很多人一听到 4B 就开始摇头觉得 7B、13B 都不一定行4B 能干什么但精排任务和开放域对话不一样它实际上是一个“相对比较”任务给定一条 query 和几条候选记忆判断哪条更相关、哪条更该排前面。这类任务对模型的语义理解能力有要求但要求的是“理解到位”而不是“生成惊艳”。我自己的测试感受是4B 模型在语义理解上的能力被严重低估了。类似规模的模型在自然语言推理、文本分类、相关性判断这类任务上的表现和 70B 级别的差距并没有想象中那么大往往只差 5 到 8 个百分点。而精排这个问题本身允许一定误差——我们不需要模型给出完美的分数只需要它把 top 3 到 5 条最相关的记忆排出来。只要候选集里确实存在正确答案4B 模型挑出来的概率在测试集上达到了 86% 到 88%这个数字对生产环境来说已经足够可用。另外一个反直觉的点是4B 模型因为参数量小推理速度快我们反而更敢给它分配复杂任务。比如让它同时输出相关性分数和一句简短的推理理由再比如让它对 50 条候选做“粗筛 精排”两级判断。这些操作如果放到云端大模型上延迟和成本都会被放大到不可接受但本地 4B 跑起来毫无压力。4. 落地细节把本地 4B 调教成合格的精排器说完了为什么选这一章讲怎么落地。这一部分的信息量最大也最可能帮大家少走弯路。4.1 候选集裁剪先让精排器轻装上阵精排的第一步不是直接让模型处理 50 条候选而是先做一轮快速裁剪。原因很简单4B 模型的推理能力和大模型相比还是有差距候选里塞太多无关内容会稀释它的注意力。我们先用一个轻量的计算规则把候选从 50 条压缩到 20 条左右规则是三者的加权综合向量相似度占 0.4、时间衰减占 0.3、关键词命中占 0.3。这个裁剪阶段不需要太聪明它的目标只是把明显不相关的记忆过滤掉。比如用户问的是“明天的会议”那些三个月前关于“美食推荐”的记忆即使向量得分不低也会因为关键词不匹配和时间衰减被压下去。裁剪之后真正进入模型的候选记忆质量已经比较高了4B 模型的判断压力小很多。实测数据表明加了这层裁剪之后精排精确率提升了 4 个百分点左右而且单次精排的推理时间从 300ms 降到 180ms。这就是我说的“先让精排器轻装上阵”的含义——一个好的精排系统80% 的功夫应该在让模型只处理值得处理的内容。4.2 Prompt 设计用评分卡替代自由发挥精排 prompt 的设计有一个关键原则不要让模型自由打分要给一张评分卡。自由打分会导致两个问题一是输出格式不稳定解析困难二是模型容易“幻想”出理由给不在候选里的记忆编造关联。我最终采用的 prompt 结构是这样的你是记忆精排系统。根据用户的当前问题从候选记忆中选出 最相关的3条并按相关性从高到低排列。 当前问题 {query} 候选记忆 {candidate_1} {candidate_2} ... 输出要求只输出JSON格式为 {ranking: [candidate_3, candidate_1, candidate_5], reason: 一句话说明排序理由}这个 prompt 看起来简单但里面有几个细节很关键。第一“选出最相关的3条”而不是“给每条打分”这避免了模型对所有候选都给出模糊的中等分数。第二强制 JSON 输出配合解码层的 json schema 校验解析失败率从最初的 8% 降到了 0.5% 以下。第三要求输出一条排序理由不是为了给用户看而是为了后续 bad case 分析——当排序出错时我们能从理由里看出模型是哪一步理解偏了。有一个容易踩的坑是prompt 里不要写“假设你是”这类角色设定在精排这类任务上角色设定容易让模型进入“扮演”模式输出更多废话。直接、简洁、目标明确的任务描述效果最好。4.3 推理侧优化批量、KV Cache 与并发控制4B 模型虽然有速度优势但生产环境里并发量上来之后还是需要做推理侧的工程优化。我用的推理框架是支持连续批处理的那种开源方案配合 4bit 量化后单卡大概能同时处理 32 路并发请求单次推理的 p99 延迟稳定在 200ms 左右。三个优化点值得单独说。第一个是批量打包。记忆精排的输入天然适合拼接同一用户的同一轮 query可以一次性打包 20 条候选记忆作为一条推理请求让它一次性输出 20 条的相关性分数。这和逐条调用相比吞吐能提升 4 到 5 倍。代价是单条请求的响应时间边长了但因为精排本身是异步的整体链路不受影响。第二个是 KV Cache 复用。在一次对话中用户连续多个问题的 query 往往有相似的上下文。我们把每个用户的会话级 KV Cache 保留在显存里后续请求直接增量计算能省掉重复的 prefill 开销实测整体延迟再降 15% 左右。第三个是并发控制。刚开始上线时我直接把所有精排请求都丢给推理服务结果在高峰时段 GPU 算力被打满p99 延迟飙升到 800ms。后来加了基于令牌桶的并发限制把同时处理的请求数控制在 24 路以内超出的请求排队等待。看似牺牲了一点吞吐但 p99 延迟重新回到 220ms 以内线上稳定性好了不止一个档次。4.4 降级策略精排挂了不能拖着主链路陪葬最后是降级策略。任何模型服务都有可能挂精排器也不例外。我们的原则是精排挂了宁可让系统退回到粗排结果也不能让整个对话链路不可用。具体做法是双保险。第一层是在推理服务前面加熔断器如果连续 10 个请求失败或 p99 延迟超过 1 秒熔断器打开后续精排请求直接走一个极简的规则降级路径——用向量相似度 时间衰减的加权分数代替模型评分。第二层是降级期间的监控告警一旦熔断被触发值班同学会收到告警决定是重启推理服务还是扩大副本。这个降级链路在线上实际触发过两次都是因为显存不足导致的推理服务 OOM。由于降级及时用户侧完全没有感知精排的精确率在降级期间掉到 70% 左右但至少对话功能没有中断。等推理服务恢复后流量自动切回精排精确率恢复到正常水平。5. 上线一个月的观察数据、收益与踩坑5.1 线上指标命中率、延迟与资源占用上线跑了一个月我把核心指标拉出来对比了一下。最重要的指标是“记忆命中率”——用户提问后系统给出的答案里引用了正确记忆的比例。上线前用 JEV 时命中率是 71%换成本地 4B 精排之后命中率稳定在 89%提升了 18 个百分点。延迟方面精排环节的 p99 延迟稳定在 230ms 左右加上召回和生成整条链路 p99 响应时间在 1.4 秒以内达成了我们最初定的 1.5 秒目标。资源方面一台 24GB 显存的 GPU 服务器GPU 利用率平均在 55% 到 65% 之间高峰时段偶尔到 80%目前没有扩容压力。成本方面这台 GPU 服务器月租金加电费满打满算不到 5000 元相比 clef 和 Nox 的按月计费方案基本可以忽略不计。当然这不是说模型本身免费——我们累积投入的标注和微调成本大概是一个人月的工作量这个账要算清楚但长期看仍然比按调用量付费划算得多。5.2 踩坑记录三个让我熬夜的问题第一个坑是量化精度带来的排序抖动。刚开始为了追求吞吐我把模型量化到 4bit结果发现同一个 query 在重复请求时给出的排名偶尔会发生变化。排查下来是量化误差在 temperature 不为 0 的设置下被放大了。解决办法是推理时把 temperature 设为 0并关闭采样让解码变成确定性过程。这个改动之后排序抖动基本消失。第二个坑是候选记忆的去重。我们的记忆库里同一信息往往有多条近似重复的片段比如用户在不同时间说了两次“我下周去上海出差”。精排器偶尔会把两条重复记忆同时排进 top 3浪费了宝贵的上下文窗口。解决办法是在精排前做一次基于 embedding 相似度的去重相似度超过 0.92 的候选只保留时间最近的一条。第三个坑是 prompt 里的候选顺序影响排序结果。我发现同一个候选集合换一种排列顺序喂给模型输出排名会发生微妙变化。这是模型的位置偏置导致的。解决办法是采用“最佳 20 条候选随机打乱排序 多次采样取最大公约数”的策略但测试下来成本和收益不匹配最终选择了更简单的方式按类别分组排列先放明确的时间类记忆再放偏好类记忆最后放一般事实。这个固定顺序本身成了一个先验反而让排序更稳定。这个方案是歪打正着但确实管用。5.3 下一步想做的事一个月跑下来我对本地 4B 做精排的信心比以前足了很多但也在几个方向上看到了继续优化的空间。一是准备针对我们的业务数据做一次领域适配微调把精排精确率从 88% 再往上推几个点。二是在考虑把粗排和精排合并成一级结构用一个小模型直接处理全量候选省掉中间那层规则裁剪进一步降低链路复杂度。三是尝试让精排器输出结构化的“记忆摘要”而不是原文这样放进 prompt 里的信息密度会更高。最后分享一点个人体会。很多人选型的时候只盯着效果最好的方案但生产环境从来不是单一指标的竞赛。延迟、成本、合规、稳定性每个约束都是一票否决项。Nox 的效果确实最好但它的延迟和成本注定它只能活在 demo 里clef 看着性价比不错但百万级调用量照样把预算打穿反而是最初不被看好的本地 4B 小模型在把工程细节做到位之后用 88% 的精确率撑起了整条生产链路。如果你也在做类似的产品我的建议是先把候选集裁剪做扎实再选一个开源小模型用最简单的评分卡 prompt 起步别一上来就追求复杂的微调方案。先让链路跑通再逐步优化。你在运行中发现的问题往往比任何评测集都更能告诉你下一步该往哪走。