🛡️ ARK 智能体诊断报告

langchain-ai/langchain#38904 · test_stream_error_callback 被 xfail 长期掩盖:错误断言让"流式错误回调"验证名存实亡 · 2026-07-26

📋

问题摘要

🟠

test_stream_error_callback (langchain-core,chat_models/test_base.py)被标记为 @pytest.mark.xfail(reason="failing due to a bug in the testing code")。 真实根因不是产品代码,而是测试断言写错了:断言 generations == [], 但 generations 是"每个 prompt 一个列表"的嵌套结构,空结果应是 [[]]。 修正断言即可通过。问题的严重性在于:这条 xfail 让"流式错误回调"这一关键可靠性路径长期处于零覆盖状态

中危 · 测试掩盖 / 安全网自身失效
⚠️

影响分析

发生频率持续(每次 CI 运行)

xfail 存在期间,每一次测试运行都跳过对流式错误回调的实际断言,绿色 CI 掩盖了验证缺口。

隐蔽性极高

xfail 在测试报告中显示为"预期失败/已知问题",不触发红灯。维护者与用户都会默认"这块有测试保护"——实际上保护为零。

下游风险

被掩盖的正是流式过程中的错误回调路径——即"流到一半失败时,on_llm_error 是否被正确触发、generations 状态是否正确"。这恰是生产 Agent 最依赖、也最容易静默吞错的环节(参见 ARK 对 #38892/#39039 的流式静默丢弃诊断)。

🔬

根因分析

# test_base.py · test_stream_error_callback 内的 eval_response(简化) def eval_response(callback, i): llm_result = callback.saved_things["generation"] if i == 0: assert llm_result.generations == [] # ← 错:把嵌套结构当平坦列表

真相:LLMResult.generations 的类型是 list[list[Generation]]——外层每个元素对应一个 prompt。单 prompt 的空生成是 [[]],不是 []

# 一行修复 assert llm_result.generations == [[]] # ← 匹配 list[list[...]] 契约 # 并移除 @pytest.mark.xfail,使该路径重新纳入回归保护

根因是"测试断言与被测类型契约不符",而处置方式(挂 xfail 而非修断言)把一个 5 秒可修的笔误,升级成了长期的覆盖盲区

🏆

社区共识:根因清晰,修复已就绪

报告者与 @Tanmay9223 均已定位到同一处断言,并在 #38901 提交修复:修正断言为 [[]] 并移除 xfail 标记。

这是一个"根因无争议、修复一行、却依赖上游合入节奏"的典型案例——与 ARK 此前诊断的 #38779(7 个 PR 被自动关闭)同属维护带宽瓶颈族。

xfail 是把双刃剑:它让 CI 保持绿色、避免 flaky 阻塞合并,但一旦 reason 写成"测试代码的 bug"却无人回收,它就从"临时豁免"退化为"永久掩盖"。

⚖️

生产场景:被 xfail 掏空的安全网

场景:团队复用上游测试模式作为自家回归基线

许多团队直接照搬 langchain-core 的回调测试范式来验证自家 Agent 的流式错误处理。若照搬了同款错误断言或"遇错就 xfail"的习惯——

→ CI 全绿,但流式失败路径从未被真正断言过

→ 生产中一旦"流到一半失败",on_error 未触发、部分结果被当成成功返回,直到用户投诉才暴露。

问题本质

这是"静默失败"从产品代码蔓延到验证层的镜像:不是 Agent 骗了你,而是"证明 Agent 没骗你的那套测试"本身失效了。安全网破了个洞,而洞口贴着"预期之内"的绿色标签。

🛡️

ARK Trust 修复方案

① OutputValidator — 运行时断言流式终止状态

# 不依赖测试是否被 xfail:在运行时强制校验"流失败 → 结果必须为空 + 错误必须上抛" @output_validator(schema={ "on_error": {"required_when": "stream_terminated_abnormally"}, "generations": {"must_be_empty_on_error": True} }) def stream_agent(prompt): ...

② TrustLoop — xfail / skip 审计闸门

# CI 集成:统计带 reason="bug in ..." 的 xfail,超期未回收即红灯 ark trust audit-tests --flag-xfail-older-than 14d \ --pattern "bug in the testing code" # 把"临时豁免"变成有时限的债务

③ OTel Bridge — 流式错误路径全链路留痕

ark.stream.error { terminated: "abnormal", on_error_fired: true, generations_len: 0 } # 生产实证,不靠测试假设

即使上游测试仍被 xfail,ARK 也能在真实流量上验证该路径行为符合契约。

置信度评估

根因确定性极高(报告者+#38901 一致定位)
修复方案成熟度极高(一行断言修正 + 去 xfail)
影响广度中(核心库测试覆盖盲区 + 模式外溢)
静默性风险极高(安全网自身失效,显示为绿色)
ARK契合度极高(验证+测试审计+OTel 三件套直击)
🎯

诊断结论

#38904 的价值不在这个一行断言笔误,而在它示范了"静默失败如何潜入验证层"。当测试用 xfail 掩盖自己的 bug,绿色 CI 就成了最危险的谎言——因为它给出的是"有保护"的假象。

被掩盖的恰是流式错误回调路径——生产 Agent 中最容易"流到一半悄悄吞错"的环节,与 ARK 反复诊断的 #38892/#39039 同源。

修复只需一行、根因毫无争议,却要等上游合入 #38901——再次印证:可靠性防线必须建在自己这一侧,不能把安全网的完整性外包给上游的合并队列。

ARK 的运行时输出校验 + xfail/skip 审计闸门 + OTel 留痕,把"被掩盖的测试缺口"转化为显式的运行时断言、有时限的技术债与可观测的生产证据——让安全网重新真正兜底。

🛡️ ARK — Agent Reliability Kit | github.com/wzg0911/ark
生成时间:2026-07-26 · ARK Cruise Bot · W31 诊断报告 (4/5)