From b68ebe6445ed409003c115c353058f6ac24e30c2 Mon Sep 17 00:00:00 2001 From: admin Date: Mon, 7 Sep 2026 23:38:52 +0800 Subject: [PATCH] =?UTF-8?q?test(priv):=20R2=20=E8=BD=AE=E6=AC=A1=E6=8A=A5?= =?UTF-8?q?=E5=91=8A=E2=80=94=E2=80=94R1=20=E4=B8=89=E7=BC=BA=E9=99=B7?= =?UTF-8?q?=E5=85=B3=E9=97=AD,=E5=8F=91=E7=8E=B0=E4=B8=A5=E9=87=8D?= =?UTF-8?q?=E7=BC=BA=E9=99=B7=20BUG-PRIV-R2-01(LLM=20=E6=9C=AA=E6=94=B6?= =?UTF-8?q?=E5=88=B0=20question=20=E8=87=B4=E7=9C=9F=E5=91=BD=E4=B8=AD?= =?UTF-8?q?=E8=A2=AB=E8=87=AA=E8=B0=A6=E6=94=B9=E5=88=A4=E8=AF=AF=E6=9D=80?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- docs/team/TEST_CASES_PRIVATE.md | 11 ++-- docs/team/TEST_REPORT_PRIV_R2.md | 94 ++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 docs/team/TEST_REPORT_PRIV_R2.md diff --git a/docs/team/TEST_CASES_PRIVATE.md b/docs/team/TEST_CASES_PRIVATE.md index 08f2e6b..81df737 100644 --- a/docs/team/TEST_CASES_PRIVATE.md +++ b/docs/team/TEST_CASES_PRIVATE.md @@ -5,6 +5,7 @@ > 实现契约:docs/team/brief_priv_a1.md(A1 任务书,含全部签名/常量/JSON 契约)。 > 基线:feature/bioflow-conversation-orchestration @ dea4640(编制时)。 > 修订记录:v1.1(TL 复查裁决)——假设 1 替换为实施方案 §1.2 语义(表格原位聚合、outline 不参与切块);假设 9 改行为级判据;TC-PRIV-L1-05/L1-07 修订并新增 L1-07b(outline 不影响输出)、L1-42(跨轮归属)。用例总数 90 → **92**,追溯矩阵已同步。 +> 修订记录:v1.2(R2 回写)——TC-PRIV-L2-10/L2-11 信封含 ok:true;TC-PRIV-L2-12 确认空 question 与缺 question 均 422;TC-PRIV-L3-01/L4-03 注明无关查询 → hit:false(自谦答语确定性改判);TC-PRIV-L2-10 注明 BUG-PRIV-R2-01(LLM 上下文不含 question)现状。 ## 0. 约定 @@ -116,9 +117,9 @@ | 编号 | 对应 FR | 测试对象 | 前置条件 | 步骤 | 输入 | 期望输出 | 判据 | |---|---|---|---|---|---|---|---| -| TC-PRIV-L2-10 | FR-PRIV-004 | POST /api/qa/private 命中 | vstore chunk 库已就绪;DeepSeek key 有效 | curl POST /api/qa/private | {"question":"…与资料相关的问题","top_k":8,"min_score":0.30} | HTTP 200;{ok:true,hit:true,mode:"vector",answer:非空 markdown,items:[{doc_id,name,seq,chunk_id,snippet,score,category,tags,created_at,ref}],elapsed_ms} | answer 中每个 [资料n] 编号与 items[].ref 一一对应;answer ≤300 字 | -| TC-PRIV-L2-11 | FR-PRIV-004 | /api/qa/private 未命中 | 库中无相关内容 | curl POST | {"question":"无关问题"} | HTTP 200;{ok:true,hit:false,mode:"empty",answer:"",items:[]} | 三态信封完整;无 error 字段 | -| TC-PRIV-L2-12 | FR-PRIV-004 | /api/qa/private 参数校验 | 无 | curl POST | {"question":""} / 缺 question | HTTP 422 {ok:false,error:{code:"VALIDATION"}} | 统一 VALIDATION 信封(BFF 全局处理) | +| TC-PRIV-L2-10 | FR-PRIV-004 | POST /api/qa/private 命中 | vstore chunk 库已就绪;DeepSeek key 有效 | curl POST /api/qa/private | {"question":"…与资料相关的问题","top_k":8,"min_score":0.30} | HTTP 200;{ok:true,hit:true,mode:"vector",answer:非空 markdown,items:[{doc_id,name,seq,chunk_id,snippet,score,category,tags,created_at,ref}],elapsed_ms}(R2 起信封含 ok:true) | answer 中每个 [资料n] 编号与 items[].ref 一一对应;answer ≤300 字;R2 注:当前 LLM 上下文不含 question(BUG-PRIV-R2-01),LLM 自谦答语会被确定性改判 hit:false | +| TC-PRIV-L2-11 | FR-PRIV-004 | /api/qa/private 未命中 | 库中无相关内容 | curl POST | {"question":"无关问题"} | HTTP 200;{ok:true,hit:false,mode:"empty",answer:"",items:[]}(R2 起信封含 ok:true;自谦答语确定性改判 hit:false) | 三态信封完整;无 error 字段 | +| TC-PRIV-L2-12 | FR-PRIV-004 | /api/qa/private 参数校验 | 无 | curl POST | {"question":""} / 缺 question | HTTP 422 {ok:false,error:{code:"VALIDATION"}}(R2 确认:空 question 与缺 question 均 422,question min_length=1) | 统一 VALIDATION 信封(BFF 全局处理) | | TC-PRIV-L2-13 | FR-PRIV-004 | /api/qa/private vstore 宕机 | 停 vstore(TL 配合) | curl POST | {"question":"…"} | HTTP 503 {ok:false,error:{code:"UPSTREAM_DOWN", degraded:true}} | 信封 code 与 degraded 断言 | | TC-PRIV-L2-14 | FR-PRIV-004 | /api/qa/private LLM 失败 | monkeypatch/断 key 使 complete_text 抛(TL 配合) | curl POST | 命中但 LLM 失败 | HTTP 502 {ok:false,error:{code:"UPSTREAM_ERROR"}} | 信封 code 断言 | | TC-PRIV-L2-15 | FR-PRIV-001 | POST /api/docgen/library 上传(回归) | vstore 正常 | curl -F file=@资料.pdf -F name=… | 小型 PDF | HTTP 201;{ok:true, items:[{library_id,name,size,as_template}]} | 上传成功返回条目;BFF doc_library 落行 | @@ -151,7 +152,7 @@ | 编号 | 对应 FR | 测试对象 | 前置条件 | 步骤 | 输入 | 期望输出 | 判据 | |---|---|---|---|---|---|---|---| -| TC-PRIV-L3-01 | FR-PRIV-005 | AssistantMessage private_doc 块渲染 | mount AssistantMessage(b2 components.test.js 风格) | message.blocks 含 private_doc 块 | {type:"private_doc", answer:"…", items:[{ref:1,name,score,snippet}]} | 卡体 .private-doc-block 存在;标题「📚 私有资料参考」;条目 [资料1]/名称/相关度/片段齐全 | DOM 断言各项存在 | +| TC-PRIV-L3-01 | FR-PRIV-005 | AssistantMessage private_doc 块渲染 | mount AssistantMessage(b2 components.test.js 风格) | message.blocks 含 private_doc 块 | {type:"private_doc", answer:"…", items:[{ref:1,name,score,snippet}]} | 卡体 .private-doc-block 存在;标题「📚 私有资料参考」;条目 [资料1]/名称/相关度/片段齐全 | DOM 断言各项存在(R2 注:hit:false 改判后前端不产生 private_doc 块,本用例针对命中路径的块渲染) | | TC-PRIV-L3-02 | FR-PRIV-005 | private_doc answer 空态 | 无 | answer:"" + items 2 条 | 块 | 仅条目列表,无空白答区 | 答区元素不存在 | | TC-PRIV-L3-03 | FR-PRIV-005 | private_doc markdown 消毒 | 无 | answer 含 `` 与 **加粗** | 块 | script 被移除(DOMPurify);加粗渲染为 | innerHTML 无 script;strong 存在 | | TC-PRIV-L3-04 | FR-PRIV-006 | PrivateLibraryPane 列表 | vi.mock api/docgen.js(listLibrary 返回 2 条) | mount PrivateLibraryPane | 无 | 标题「📁 我的资料」+ 计数 2;两条 name/size/time 渲染 | DOM 断言 | @@ -166,7 +167,7 @@ |---|---|---|---|---|---|---|---| | TC-PRIV-L4-01 | FR-PRIV-001/002 | 上传→检索闭环 | 浏览器打开生产 UI | 拖拽上传 PDF → 查看「我的资料」→ 提问命中内容 | PDF | 上传成功条目出现;提问出「私有资料参考」块 | 全链闭环 | | TC-PRIV-L4-02 | FR-PRIV-003/005 | ask/search 两模式并行卡片 | 私有资料命中环境 | 分别 ask 与 search 模式提问 | 提问 | 两模式均出私有卡片,与 QA 答案并行展示不混排 | 卡片独立于 QA 答案 | -| TC-PRIV-L4-03 | FR-PRIV-003 | 未命中无感 | 私有资料命中环境 | 提问无关问题 | 无关问题 | 无任何私有块/提示;QA 答案正常 | 零痕迹 | +| TC-PRIV-L4-03 | FR-PRIV-003 | 未命中无感 | 私有资料命中环境 | 提问无关问题 | 无关问题 | 无任何私有块/提示;QA 答案正常 | 零痕迹(R2 起:无关查询 → hit:false,自谦答语改判) | | TC-PRIV-L4-04 | FR-PRIV-005 | 刷新恢复 | 出块后刷新页面 | F5 → 重进会话 | 无 | private_doc 块完整恢复(终态持久化) | 块与 items 完整 | | TC-PRIV-L4-05 | FR-PRIV-006 | 删除联动 | 我的资料列表 | 删除一条 → 再提问该资料内容 | 无 | 列表刷新;再提问不再命中该资料 | 删除后检索不到 | | TC-PRIV-L4-06 | FR-PRIV-008 | 停 vstore 降级 | 停 vstore(TL 配合) | 提问 QA + 观察 | 提问 | QA 照常(doc 级不依赖 chunk);无私有块;无用户可见错误 | FR-PRIV-008 验收口径 | diff --git a/docs/team/TEST_REPORT_PRIV_R2.md b/docs/team/TEST_REPORT_PRIV_R2.md new file mode 100644 index 0000000..535c23f --- /dev/null +++ b/docs/team/TEST_REPORT_PRIV_R2.md @@ -0,0 +1,94 @@ +# TEST_REPORT_PRIV_R2 —— 私有资料问答轮次测试 R2(缺陷复测 + 全量回归) + +> docs/team/TEST_REPORT_PRIV_R2.md | T1 执行 2026-09-07 | 用例表 docs/team/TEST_CASES_PRIVATE.md(v1.2) +> 基线:feature/bioflow-conversation-orchestration @ 04c08e8(R1 三缺陷修复,BFF 已重启 0.0.0.0:8090) +> L4 浏览器冒烟本轮仍跳过(TL 终验执行)。 + +## 1. 执行摘要 + +- **R1 三缺陷复测:2 关闭、1 关闭但有连带观察**(BUG-01 关闭 / BUG-02 关闭 / BUG-03 关闭+真命中误伤观察)。 +- **发现 R2 新缺陷 1 个**:BUG-PRIV-R2-01(ask_private 的 LLM 调用未传 question——user 消息只有检索上下文,见 §6)。 +- **全量回归全绿**:vstore 101/101;sdk 225 passed + 38 errors(预存基线,无新增);前端 373 + 23 = 396。 +- **红线 diff 复核**:QA 8091 / 前端 / vstore / sdk 红线 / 新模块 yield——diff 全部为空。 + +## 2. 缺陷复测结论 + +### BUG-PRIV-R1-01(信封缺 ok 键)——【关闭】 + +| 场景 | 实际响应 | 判定 | +|---|---|---| +| 命中(question="DOCGEN_R1_E2E_SENTINEL_K7Q2 是什么") | keys: [answer, elapsed_ms, hit, items, mode, **ok**],ok:true,hit:true,items:8 | 关闭 | +| 未命中(min_score=0.95) | keys 同上,ok:true,hit:false,mode:"empty" | 关闭 | + +### BUG-PRIV-R1-02(空 question 未 422)——【关闭】 + +| 场景 | 实际响应 | 判定 | +|---|---|---| +| body={"question":""} | HTTP 422 `{ok:false,error:{code:"VALIDATION",message:"请求参数不合法",degraded:false}}` | 关闭 | +| body={} | HTTP 422 同上 | 关闭(基线即正确,复验通过) | + +### BUG-PRIV-R1-03(无关查询零痕迹)——【关闭(方案 c:自谦答语确定性改判)】 + +| 场景 | 实际响应 | 判定 | +|---|---|---| +| question="zqxwcrvtnmkjhgfdsp 完全无关的问题xyz"(R1 复现用例) | `{ok:true, hit:false, mode:"empty", answer:"", items:[]}` | 关闭(零痕迹) | +| question="DOCGEN_R1_E2E_SENTINEL_K7Q2 是什么"(真命中验证) | ok:true, **hit:true**, items:8, answer 含 [资料1][资料2] 引用与具体内容(1.58s,elapsed_ms:1579) | 真命中路径可用 | + +修复实现核对(git show 04c08e8):`SELF_DEPRECATING_ANSWERS` 7 条短语 + `_is_self_deprecating`(含空答案视同未命中);命中/未命中包 ok 键;`PrivateQARequest.question` 加 `min_length=1`。commit 文件范围 3 个(private_docs.py / schemas.py / privateqa.py),全部在清单内。 + +## 3. 真命中误伤观察(与 BUG-03 修复连带,定位后升格为新缺陷) + +TL 指定的真命中验证问题「批量导入压测样例 序号 38」实测 **hit:false**(两次重试均如此)。排查链: + +1. vstore 检索正常:search-chunks q="批量导入压测样例 序号 38" → mode:"vector",sample_038 两条各 score **0.766**(远高于 0.30)。 +2. 响应耗时 1.31s(对照组 hit:true 1.58s)→ 走了 LLM 路径,属「LLM 自谦答语被改判」,非检索短路。 +3. 代码定位:`ask_private` 调 `complete_text(SYSTEM_PROMPT, context)`——**user 消息是检索上下文([资料n]《name》snippet 列表),question 从头到尾没进 LLM**。LLM 只看到 8 条彼此相似(序号 37/38/48/58…)的压测材料、不知用户问的是「序号 38」,于是诚实自谦 → 被改判 hit:false。 +4. 该问题自 A1 原实现(e783a26)即存在(git show 对照确认),非 04c08e8 修复引入;04c08e8 的改判机制把「诚实自谦」放大成了「零痕迹」,暴露了它。 +5. 与基线(e783a26)行为对比:修复前同样问题会返回 hit:true + 自谦答语 + items(R1 报告「测试文档8」即此形态);修复后一律 hit:false。**修复没有引入新错误,但真命中命中率下降**。 + +## 4. 全量回归统计 + +| 套件 | 命令 | 结果 | 对比 R1 | +|---|---|---|---| +| vstore pytest | 仓库根 `venv/bin/python -m pytest vstore/tests/ -q` | **101 passed**,2 warnings(fastapi on_event DeprecationWarning 预存) | 一致 | +| sdk pytest | `cd sdk && venv/bin/python -m pytest -q` | **225 passed / 0 failed / 38 errors**(errors 全部为预存基线 test_docgen_api.py collection ERROR,§R1 报告 §5 已有 dea4640 对照证据) | 一致,无新增失败 | +| 前端 vitest 全量 | `cd ui && npx vitest run` | 27 files / **373 passed** | 一致 | +| 前端 private_docs | `npx vitest run --config tests/private_docs/vitest.config.js` | 4 files / **23 passed** | 一致 | +| 合计 | | **396 + 101 + 225** | 无回归 | + +## 5. 红线 diff 复核(dea4640..HEAD) + +| 项 | 命令 | 结果 | +|---|---|---| +| QA 金标树 8091 | `git diff dea4640 HEAD --stat -- agent/qa` | 空 | +| 前端红线(modes/labels/bench/api/qa/sse/parser/PipelineCard) | `git diff dea4640 HEAD --stat -- …` | 空 | +| vstore 红线(retrieval/parsing/backend/crypto) | 同上 | 空 | +| sdk 红线(qa.py/sse_relay.py/compile_sdk.sh) | 同上 | 空 | +| 新模块无 yield | grep -c yield privateqa.py private_docs.py | 0 / 0 | + +HEAD=04c08e8,修复 commit 仅 3 文件全部在清单内。 + +## 6. R2 缺陷清单 + +### BUG-PRIV-R2-01(一般/严重待裁)|ask_private 的 LLM 调用未传 question,真命中问题易被自谦改判成 hit:false + +- 对应 TC:TC-PRIV-L2-10、TC-PRIV-L4-02(ask 模式命中出卡)。 +- 复现步骤:`curl -X POST /api/qa/private -d '{"question":"批量导入压测样例 序号 38"}'`(vstore 检索确认 sample_038 score 0.766 命中)。 +- 预期:hit:true + answer 引 [资料1] 回答序号 38 的样例内容。 +- 实际:hit:false、items:[](两次重试一致,响应 1.31s 表明 LLM 已调用)。 +- 根因:`sdk/app/services/privateqa.py` `ask_private` 中 `complete_text(SYSTEM_PROMPT, context)`——user 消息只有检索上下文,question 未传入(system prompt 也无占位注入)。LLM 无法回答用户问题只能自谦,被 R1-03 改判机制转 hit:false。 +- 影响:所有「上下文不含答案线索、需理解问题指向」的命中提问会被误杀为零痕迹;R1 报告中的「测试文档8」(当时 hit:true)现同样 hit:false。客户真实资料(正文含实质内容)下该问题会显著降低命中率——**建议升为严重级**。 +- 修复建议(交 A1):user 消息改为 `f"用户问题:{question}\n\n参考资料:\n{context}"`(或 system 末尾追加「用户问题:{question}」);SYSTEM_PROMPT 保留约束。改后回归 L1-22(命中链路参数断言)需同步断言 user 含 question。 +- 说明:此缺陷在 e783a26 即存在(git show 对照),R1 因当时答语形态为「hit:true+自谦」未暴露为零痕迹问题;04c08e8 改判机制放大暴露。**修复面小、价值高,建议 R3 修复后重开该验证用例。** + +## 7. 用例表回写(TEST_CASES_PRIVATE.md v1.2) + +- TC-PRIV-L2-10:信封含 ok:true;注明 BUG-PRIV-R2-01 现状(LLM 上下文不含 question,自谦答语确定性改判 hit:false)。 +- TC-PRIV-L2-11:信封含 ok:true;自谦答语改判 hit:false。 +- TC-PRIV-L2-12:确认空 question 与缺 question 均 422(min_length=1)。 +- TC-PRIV-L3-01 / L4-03:注明「无关查询 → hit:false(自谦答语改判)」。 +- 追溯矩阵复核:defined 92 = used 92,无孤儿无缺漏。 + +## 8. 结论 + +R1 三缺陷全部关闭(BUG-03 关闭方案 c 与 TL 裁决一致),全量回归无新增失败,红线零 diff。新缺陷 BUG-PRIV-R2-01(LLM 未收到 question)为 A1 原实现预存问题、被 R1-03 修复放大暴露,修复面小(user 消息拼 question 一行级改动),建议交 A1 修复后开 R3 复测(重点:TL 指定真命中问题「批量导入压测样例 序号 38」应 hit:true)。