🛡️ ARK 智能体诊断报告

langchain-ai/langchain#39052 · QdrantVectorStore MMR 检索 lambda_mult 语义完全反转 · 2026-07-28 · W32 诊断 3/5

📋

问题摘要

🔴

使用 QdrantVectorStore.as_retriever(search_type="mmr") 时, lambda_mult 的语义被完全反转: LangChain 全生态约定 1.0=纯相关性、0.0=纯多样性, 而 Qdrant 路径把它直接透传给 Qdrant 的 diversity 参数—— Qdrant 内部再做 lambda = 1.0 - diversity,两层约定叠加后 用户设 1.0 得到纯多样性、设 0.0 得到纯相关性。 不报错、不告警、每次都返回「看起来合理」的文档列表——只是排序策略与用户的意图恰好相反。

语义反转 · 参数契约跨库漂移 静默错误 · 结果永远"合法" ARK 实机双向确定性复现 ✅

影响范围

langchain-qdrant 1.1.0(Qdrant 原生 MMR 路径)· 所有 RAG-MMR 用户

框架现状

⚠️ open · 社区已定位同一根因 · 修复 PR 待受理

ARK 方案

✅ OutputValidator 行为不变式探针

🔍

根因定位(P2 Probe)

这是一次教科书级的「参数契约跨库漂移」——两个库各自都没写错,接缝处语义翻转:

  1. LangChain MMR 约定(langchain_qdrant/_utils.py:32): score = lambda_mult × relevance − (1−lambda_mult) × redundancy → 1.0 = 纯相关性
  2. langchain-qdrant 走 Qdrant 原生 MMR 时直接透传(qdrant.py:830): mmr=models.Mmr(diversity=lambda_mult, ...)
  3. Qdrant 对 diversity 的定义恰好相反(qdrant_client/local/local_collection.py:2109): lambda_ = 1.0 - diversity → diversity=1.0 = 纯多样性
  4. 同名概念、镜像定义、无单位换算 → 用户参数在管道里被静默取反

为何数值上永远不炸:lambda_mult 与 diversity 同为 [0,1] 区间浮点数, 类型校验、范围校验全部通过;MMR 无论 lambda 取何值都返回 k 篇合法文档。 错误只存在于语义层——这正是传统防御(类型系统/schema 校验)的盲区, 也是检索质量「莫名其妙变差」却极难归因的经典成因。用户跨 vectorstore 迁移(Chroma→Qdrant)时行为突变,代码却一行没改。

🧪

ARK 实机验证(2026-07-28 · langchain-qdrant 1.1.0 / qdrant-client 1.18.0 / Python 3.11)

ARK 构造了比原 issue 更强的复现:手工正交向量空间(3 个近重复"相关"文档 + 2 个正交"多样"文档), 无需嵌入模型、无需外部 Qdrant 服务(:memory: 本地模式),完全确定性。双向反转全部命中

lambda_mult=0.0(文档语义:纯多样性) 文档语义预期 : [相关A, 多样E, 多样D] Qdrant 路径实际: [相关A, 相关B, 相关C] ← 纯相关性! 与「取反后的 lambda=1.0」预测完全一致 ✅ lambda_mult=1.0(文档语义:纯相关性) 文档语义预期 : [相关A, 相关B, 相关C] Qdrant 路径实际: [相关A, 多样E, 多样D] ← 纯多样性! 与「取反后的 lambda=0.0」预测完全一致 ✅ VERDICT: INVERSION CONFIRMED — 反转不是近似偏差,是精确镜像

验证强度说明:不仅证明「实际 ≠ 文档语义」,还证明「实际 == 取反语义的逐位预测」——排除了"MMR 实现差异导致的排序噪音"这一替代解释,把结论钉死在语义反转上。三级源码链(_utils.py:32 → qdrant.py:830 → local_collection.py:2109)与运行时行为互相印证。

🛡️

ARK Trust Layer 如何拦截(P3 Prescribe)

① OutputValidator · 行为不变式探针(集成时)
对任何声称实现 MMR 的检索组件,ARK 用已知构造的探针数据集(近重复簇 + 正交簇)跑一次极值自检: lambda=1.0 结果必须 ⊆ 相关簇lambda=0.0 结果必须含跨簇文档。 语义反转在接入当天现形,而非在生产检索质量报表上潜伏数月。

② InputGuard · 跨库参数契约显式声明
跨库透传的参数必须声明语义方向(higher-is-relevance vs higher-is-diversity)。 同名/同域但方向未声明的透传(本例 lambda_mult→diversity)被标记为高危接缝,强制适配层换算。

③ OTel 留痕
检索 span 记录 lambda_mult 原值 + 底层引擎实际生效参数。用户排查「检索结果变差」时, 能直接看到 lambda_mult=1.0 → qdrant.diversity=1.0 → effective_lambda=0.0 的换算链。

📚

缺陷模式索引归档(P4 Publish)

设立新缺陷族 F8 · 语义反转 / 参数契约跨库漂移(首例)。 与 F3(静默失败)的区别:F3 是「失败被伪装成成功」,F8 是「从头到尾没有失败—— 每一步都是合法值、合法调用、合法返回,唯独整体语义与用户意图相反」。 这类缺陷不可能被异常监控捕获,只能被行为不变式探针捕获,恰好落在 ARK 的射程内、传统 APM 的射程外。

ARK — Agent Reliability Kit · github.com/wzg0911/ark

诊断方法论:4P Framework(Pinpoint → Probe → Prescribe → Publish)· 所有结论基于实机验证,复现与未复现均如实披露

生成时间:2026-07-28 11:40 CST · W32 诊断报告 3/5