Repository navigation
AMD 显卡本地重排 Hindsight 中文记忆(llama.cpp Vulkan) #5945
219LUOYE
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
AMD 显卡本地重排 Hindsight 中文记忆(llama.cpp Vulkan)
背景与问题
先交代下测试环境。机器是 Ryzen 9 7945HX + RX 7800 XT(16 GB),系统 Windows 11,推理用
llama.cppb11790 的 Vulkan 构建(8090 端口跑 1024 维的bge-m3做 embedding,8091 端口跑bge-rerankerQ8 做重排)。测试用的自建 Hindsight 记忆库(bankdsh)是从 228 个中文会话里抽出来的个人偏好笔记,包含 141 个文档、564 条事实(observation 263 / world 257 / experience 44)。之所以折腾重排,是因为 Hindsight 默认的 embedding(
BAAI/bge-small-en-v1.5,384 维)和重排器(cross-encoder/ms-marco-MiniLM-L-6-v2,22.7M)都是纯英文模型。这套默认配置遇到中文时,重排器不是简单的"不起作用",而是会把原本排对的结果硬生生改错。拿 issue #5519 里的实测来说(约 2.3 万条中文记忆,治理类查询):在 9 路检索阶段,正确的记忆其实已经被 7 路召回了(semantic 0.83–0.84,graph 0.957),1554 个候选经过 RRF 融合后稳稳排在第 1、2、3 名;结果进了 cross-encoder 重排(300 候选)之后,直接掉到了第 70、74、71 名,导致最终输出的 top-12 里一条相关的都没有。我在本地用 25 条中文查询复现也是同样的结果:开着重排跑一次要 10.77 秒,正确答案完全从结果里消失;把重排关掉只要 0.07 秒,正确答案排在第 2 名。
面对这个问题,目前的几种常规方案都有明显代价:
HINDSIGHT_API_RERANKER_PROVIDER=rrf)。我实测关掉后延迟是 0.04 秒,top-1 76% / top-3 84% / top-5 88%,能用,但等于彻底放弃了精排,排序质量上限被锁死在 RRF 融合阶段。bge-reranker-v2-m3后,宿主机 CPU 直接冲到 1554%,第二天整个记忆服务宕掉,最后只能把 embedding 和 reranker 全迁到 SiliconFlow 云端,并在文末专门提醒"本地大 reranker 别硬跑"。配置做法
其实
llama.cpp的/v1/rerank接口和 Cohere 的报文格式是完全一样的:而 Hindsight 的
CohereCrossEncoder会把传入的base_url原样当作重排地址发请求,所以不需要改任何代码。只要单独起一个llama-server实例跑重排(注意不要和 embedding 共用同一个实例),再改一下 Hindsight 的配置就能接上:这里有两个底层实现上的细节可以提一下。
第一,
bge-reranker输出的是原始 logits,而不是 0–1 之间的相关度。比如实测同一个 query 下三个文档的返回分数:不过 Hindsight 内部的处理逻辑会先检查
min(scores) >= 0 && max(scores) <= 1,一旦发现分数超出[0, 1]范围,就会自动对整批结果走一遍 sigmoid。所以直接连到 8091 端口就行,不需要在中间写脚本做预处理。第二,
HINDSIGHT_API_RERANKER_PROVIDER一定要选cohere,不要选tei。TEI 的机制是为单次重排请求里的每一个文档预留一个池位,池满就直接把整个请求拒掉。Hindsight 默认的RERANKER_TEI_BATCH_SIZE=128乘以RERANKER_TEI_MAX_CONCURRENT=8等于 1024 个池位,但 TEI 自身默认只有 512。走cohere兼容的 HTTP 路径是独立分批的,没有这个耦合问题。实测数据
把重排挪到显卡(Vulkan)之后,两个实例合计占约 5.1 GB 专用显存(embedding 约 4.6 GB,reranker 约 0.44 GB,用 GPU 性能计数器差分测的),16 GB 的卡余量充足。处理 300 个候选时,568M 的
bge-reranker除首次冷启动需 1.54 秒外,稳态延迟在 0.88–0.92 秒;对比 CPU 跑 22.7M 默认模型的 9.38 秒,模型体积大了 25 倍,速度反而快了 10.6 倍。为了找到最佳配置,我把
semantic_min_similarity和重排器放在一起做了联合扫描。因为抬高语义门槛会在重排器看到候选之前就砍掉低置信度的结果,两者互相影响,分开单调很容易错过最优值:semantic_min_similarity可以看到
0.72–0.75这个区间并不是单调变化的:0.74是峰值,到了0.75反而开始回落,如果只测单点很容易漏掉。把阈值定在0.74后,开启 GPU 重排的整体耗时在 0.23–0.29 秒之间。对比直接关掉重排走 RRF(耗时 0.04 秒,top-1 76% / top-3 84% / top-5 88%),延迟只增加了约 0.2 秒,但召回率提升到了 top-1 88% / top-3 96% / top-5 100%。这里顺便说明一下跟项目官方基准的差异。官方在 issue #5230 里跑过一套 BEIR SciFact 基准(500 篇英文学术摘要用
gemini-3.1-flash-lite抽取成 1812 条记忆,开启默认的本地 embedder 和 reranker,跑出来的成绩是:nDCG@1/3/5/10 为 0.800/0.870/0.884/0.887,MAP@1/10 为 0.749/0.860,Recall@1/3/5 为 0.749/0.903/0.952,MRR 0.866,R-precision 0.802)。就像官方自己声明的,这两套数字不能直接拿来横比——官方测的是英文学术摘要、带分级相关性标注、有几百条查询;而我这里只是 564 条中文个人偏好、靠关键词是否出现来判分,而且总共只有 25 条查询。在这种样本量下,错对差 1 条查询就能让 top-1 波动 4 个百分点,所以上面的数字只能当调参的方向性参考,不能当作严谨的基准测试。附:一个还在查的问题(暂不结论)
排查重排时顺手发现:走 OpenAI 兼容后端时,
retain调用返回的 usage 里cached_tokens一栏始终是 0。但这台机器上实测,同一段抽取提示词直接发给 DeepSeek API 时前缀缓存命中率约 90%,
而通过 Hindsight 的 provider 层直接调用也能正确拿到
cached_tokens≈ 2944(命中率 93.7%)。也就是说:缓存本身是工作的,provider 层的解析也是对的,但经过
retain+ HTTP API 这条路径绕一圈之后这个数字变成了 0。目前还没定位到具体是哪一层丢的,先记在这里,查清楚再补。
更正:本文早前版本写过「Hindsight 把抽取指令拼进一个大 user message,导致固定前缀被
动态内容切碎,缓存没机会生效」,并附了一组 retain 全 0 的数据。这个因果解释是错的——源码里
retain发的是独立的 system 消息,多次调用之间该消息 SHA256 完全一致(12522 字符)。那组 0 数据反映的是上面那个上报问题,不是缓存本身失效。原段落已删除。
All reactions