Files
PlantAI-rag-new/docs/acceptance-r3.md
2026-09-10 18:13:49 +08:00

36 KiB

验收用例:176 手工测试 13 项缺陷(r3)

  • 状态:修复前落库(测试设计),复测在修复完成后按 TL 通知执行。
  • 范围:176 手工测试 Excel 缺陷 #7、#27、#28、#31、#32、#37/38、#39、#40、#42、#47、#52、#55。
  • 修复分工:Debug-1 前端(ui/)、Debug-2 后端(sdk/ + agent/ + deploy/)。本文件是修复目标与复测依据。
  • 对应修复基准:仓库 feature/bioflow-conversation-orchestration @ f463194;修复落在 175 本地 debug worktree 分支。

0. 环境与执行约定

标识 环境 用途
S1 175 dev 实例(vite dev server + 独立 dev BFF 9090) 功能正确性复测。绝不可打生产 8090。浏览器实测用 vite dev server + Mac 隧道,端口由 TL 在复测时提供。
S2 176 终验(生产拓扑 80 端口,真实 QA/Neo4j/MinerU,CPU 无 GPU) 性能阈值、真实 mineru、真实图谱数据。

通用约定:

  • 浏览器证据:DevTools → Console(错误)、Network(请求/响应体)、Performance(计时);关键帧截图存 ~/acceptance-r3/缺陷号-步骤.png(175 或本机均可,缺陷单里写路径)。
  • API 证据:curl 响应全文粘贴进缺陷单,或 -o /tmp/acceptance-r3/xx.json 存档。
  • 时序测量:UI 动画用 performance.now() 差值(本地动画,隧道网络不影响);API 时延用 curl -w 'time_total: %{time_total}s'。
  • 所有「<Ns」阈值的测量起点/终点在各用例内写死;±10% 记 PASS(备注实测值),超阈值记 FAIL。
  • 会话/消息接口基础约定:创建会话 POST /api/sessions {"title":"r3-xx"} 返回 id 即 SID,下文引用。

文案事实源(修复前后均以源码为准,断言文案若与修复实现有出入,以修复后 i18n 键为准并在缺陷单注明):

  • ui/src/i18n/labels.js:TAB「检索过程 / 文献来源 / 提取任务 / 知识图谱」、sourceEmpty: '本次检索未命中文献'、retrievalEmpty: '发起提问后,这里会实时展示检索过程'、skipReveal: '跳过'、extractRunLog(运行日志按钮)。
  • ui/src/i18n/docgen.js:文档 TAB「文档」、docPaneExportPdf: '导出 PDF'、docPaneExportDocx: '导出 DOCX'、docPaneExporting: '导出中…'、docPaneExportReady: '下载'、内置模板键 official(通用公文(红头))/ report / application / contract / speech / plan / document、docPaneEmpty 空态文案。
  • ui/src/i18n/private.js:libraryTitle: '📁 我的资料'、libraryEmpty、libraryLoading。
  • 端点契约(修复目标):POST /api/docgen/generate → 202 {ok,task_id,status:"queued"};GET /api/docgen/tasks/{task_id} → 200 {ok,task:{id,session_id,status,progress[],artifact|null,error}};GET /api/docgen/tasks?session_id=;POST /api/qa/search 超时 → 200 降级体;GET /api/kg/entities* 超时 504 TIMEOUT / 实例不可用 503 UPSTREAM_DOWN。

1. 缺陷 #7 答案揭示动画

项 值
环境 S1(动画/序号/跳过);S2 复跑一次终验
用户原话 「标题章节没有序号,且超时」

前置数据

  1. 新建会话(或使用无历史的新会话),关闭「减少动态效果」类系统设置,确认 prefers-reduced-motion 未命中(否则揭示动画被跳过,用例失效)。
  2. 准备两个问题:
    • Q1(序号断言):「请列出水稻基因组分析的主要步骤,每个步骤给出详细说明,包括数据准备、质控、比对、变异检测、注释五个阶段」——期望答案含多级 markdown 标题。
    • Q2(超时断言):「请写一篇关于水稻基因组学研究的综述文章,内容不少于 3000 字,分章节论述」——期望答案 >2000 字符。

操作步骤与断言

  1. 短答案 30ms/字符节奏(Q1,若答案 ≤2000 字符):
    • 发送 Q1,答案开始揭示。在消息区观察:字符以逐块节奏出现(源码常量 REVEAL_STEP_MS=30),非一次整段闪出。
    • 断言:stream 块可见 .stream-cursor(尾部光标)+ .shimmer-line(shimmer 条),揭示中「跳过」按钮可见(T.skipReveal 文案)。
    • 断言:总揭示时长 ≈ 字符数 × 30ms(±20%),可用 performance.now() 从首字符到光标消失测量。
  2. 长答案总时长上限(Q2):
    • 发送 Q2,答案完成后进入揭示阶段。用 performance.now() 记录 stream 块首帧出现 → 光标消失(揭示完成)的时长 T。
    • 断言:T ≤ 8.5s(需求:>2000 字符自动加速,总时长上限 8s,留 0.5s 计时误差)。
    • 注意:若实际答案 <2000 字符,标注「条件未达成,按短答案节奏校验」,不计 FAIL,需换更强的 prompt 重试一次。
  3. 跳过:
    • 揭示进行中点击「跳过」按钮。断言:全文立即完整显示、.stream-cursor/.shimmer-line/「跳过」按钮消失;消息状态为 done;Console 无残留 setInterval 报错、无未捕获异常。
  4. 标题编号注入(Q1 答案):
    • 揭示完成后,检查消息区 markdown 渲染结果:一级标题文本以「一、」开头、二级以「1.1」开头、三级以「1.2.3」形式开头。
    • 断言:所有 h1/h2/h3 标题均有编号;若答案中已有编号(如「1. 概述」),不得重复编号(原文保持)。
  5. 图片注入预算(含图答案,依赖 Debug-2):
    • 提问要求答案含图(如「用图表说明水稻基因组分析流程」),若 QA 答案带图片,断言:SSE 最后一个 answer/progress 帧到 complete 帧间隔 ≤3s(修复目标:图片注入总预算 2.5s,留 0.5s 网络抖动;3~5s 记 WARN 备注)。
    • 测量方法:curl -N 直连 dev BFF /api/qa/chat/stream 打时间戳,或浏览器 Network 的 SSE 消息时间线。

通过标准:1~4 全过;5 若环境产生图片答案则必过,否则记 N/A。 证据:时间戳测量(console 日志或截图带时间)、答案渲染截图、Network SSE 帧时间线;失败时录 browser console 全量日志 + 答案长度(字符数)。


2. 缺陷 #27 会话级来源聚合

项 值
环境 S1(功能);S2 终验一次
用户原话 「聚合后,文献的 id 对应不上,会往后+1」

前置数据

  1. 新建会话 SID。
  2. 在同一会话内连续问两个相关检索问题(如「水稻抗病基因有哪些」「水稻抗病育种研究进展」),使两轮答案各产生若干文献来源(答案卡下方来源行、文献来源 TAB 均有条目)。
  3. 记录每轮答案中来源的 paper_id(Network 中 /api/qa/search 与 /api/qa/chat/stream evidence 帧的 sources[].paper_id / id)。

操作步骤与断言

  1. API 聚合正确:
    • curl "http://<devBFF>/api/sessions/<SID>/sources"(S1 打 9090,下同)。
    • 断言:200,{ok:true, sources:[...]};按 paper_id 去重(无 paper_id 按 id);每个条目 count 等于该 paper_id 在两轮中出现的总次数;条目 id/paper_id 与前端展示一致。
  2. 文献来源 TAB 徽标:
    • 右工作台切到「文献来源」TAB,筛选「本会话全部」(聚合视图)。
    • 断言:条目数 = API 去重后的条目数;每条徽标显示的文献 id 与 API 返回的 paper_id/id 完全一致,无「+1」偏移;count 徽标数字与 API count 一致。
  3. 两轮重复文献场景:
    • 断言:两轮都出现的同一篇文献只显示一条(不重复列出),count ≥ 2。

通过标准:1~3 全过。 证据:/api/sessions/<SID>/sources 响应体全文、文献来源 TAB 截图(徽标清晰)、两轮答案的来源截图对照。


3. 缺陷 #28 检索空态 / 超时降级

项 值
环境 S1(正常空结果 + 前端文案);S2(超时降级、正常空态终验)
用户原话 「检索显示超时,应该提示『没有检索到此问题的有效回答』」

前置数据

  1. 准备一个正常空结果问题:「请问 1234567890qwertyuiop 这种字符串与植物有什么关系」(无意义问题,期望 sources: [])。
  2. 准备一个可触发上游检索超时的问题(176 终验):针对大体量库的复杂检索问题,或由 TL 在复测时指定;S1 若 dev QA 实例可复现超时则顺带测,否则该项留 S2。
  3. 修复要求确认:BFF /api/qa/search 请求体注入 native_chain_enabled:false;超时返回 200 降级体 {"mode":"search","answer":"","sources":[],"degraded":true,"reason":"TIMEOUT"}。

操作步骤与断言

  1. BFF 注入断言(API 层):
    • 在浏览器 Network 面板观察发送检索问题时 POST /api/qa/search 的请求体。
    • 断言:请求体含 "native_chain_enabled": false(或 BFF 层等价禁用图链的字段)。
  2. 正常空结果(S1 可测):
    • 检索模式(输入框下方模式 pill 选「检索」,或直接发送使意图路由到 search)发送空结果问题。
    • 断言:消息区出现明确提示文案「没有检索到此问题的有效回答」(修复后文案,以源码 i18n 为准);不显示 pipeline 五相位卡或错误样式。
    • 切到「检索过程」TAB:显示「本次检索未命中文献」(T.sourceEmpty)。
    • Network:POST /api/qa/search 响应 200,sources:[]、degraded:false(正常空结果不得误标 degraded)。
  3. 超时降级(S2):
    • 发送触发超时的问题。
    • 断言:消息区出现超时专属文案(如「检索超时」+「没有检索到此问题的有效回答」区分展示,与空结果文案可区分);不显示错误信封样式。
    • Network:/api/qa/search 响应 status=200,body 含 degraded:true、reason:"TIMEOUT"、mode:"search"。
    • 时序断言:请求发起到收到 200 降级体的时长 ≤120s(修复目标:检索模式超时上限 120s;当前源码 SEARCH_READ_TIMEOUT=180,修复后应缩短)。用 curl -w time_total 复核一次。
  4. 降级与空结果可区分:
    • 断言:降级(TIMEOUT)与正常空结果在 UI 呈现不同文案/样式;两者都不会让「检索过程」TAB 显示检索进度卡死(如有 pipeline 卡,应终态化而非永远 loading)。

通过标准:1、2、4 全过;3 在 S2 过。 证据:Network 请求/响应体截图或复制、消息区提示文案截图、curl -w 时延输出。


4. 缺陷 #31 图谱节点详情

项 值
环境 S1(交互竞态);S2(实体端点最坏 30s 串行观察,随 neo4j 适配消失)
用户原话 「点击图节点没办法显示出实体属性…重新点击搜索…才能显示,同时,弹出频率异常」

前置数据

  1. 「知识图谱」TAB 打开,KgPane 图谱已渲染(含节点/边)。
  2. 准备 2 个已知实体节点(如「水稻」「拟南芥」或图谱中实际存在的节点,先在搜索框确认)。

操作步骤与断言

  1. 点击节点弹详情:
    • 原地单击(位移 <4px)节点 A。
    • 断言:≤1s 内右侧/侧滑出现 KgEntityDetail 面板,展示实体属性(properties 键值,如名称/类型/别名);无需二次点击搜索。
    • Network:GET /api/kg/entities/<A> → 200 {ok:true, ...} 且仅 1 次请求(无重复触发)。
  2. 拖拽不触发点击(修复目标:位移阈值 4px 判定拖拽):
    • 按住节点 A 拖动 2~3px 释放(微拖):断言不弹出详情面板。
    • 按住空白处拖动平移画布(≥5px):断言不弹出详情面板。
    • 再原地点击节点 B:详情面板更新为 B 的属性,内容与 B 一致(无串台)。
  3. 快速连点竞态(旧缺陷「弹出频率异常」):
    • 快速依次点击节点 A → 节点 B(间隔 <500ms)。
    • 断言:始终只有一个详情面板;最终显示的内容 = 最后点击的节点 B 的实体属性;面板内容与节点一一对应,无闪烁/无「点了没反应、再点一次才出」现象。
    • 检查 Console:无「response 与请求不匹配」类竞态日志(修复目标 :key 重建+序号守卫后无竞态)。
  4. 详情面板交互:
    • 面板内关闭按钮 → 面板关闭;再次点击同一节点可重新打开。
    • 邻域展开(若面板提供):点击邻域实体 → GET /api/kg/entities/<id>/neighborhood 200,渲染邻域图。
  5. 超时/不可用分流(S2 或 dev 模拟):
    • 查询超时 → 504 + {error:{code:"TIMEOUT"}};Neo4j 实例不可用 → 503 + {error:{code:"UPSTREAM_DOWN"}}。两者 UI 均显示可读错误提示,不白屏。
    • 模拟方法(仅 dev):临时将 dev BFF 的 NEO4J_URI 指向空端口复现 503;验后还原配置并重启 BFF。
    • 实体端点最坏 30s 串行:连续请求多个实体详情,观察在适配修复后不再出现「最坏 30s 串行」排队(S2 观察项,若仍存在记 WARN 并留证据)。

通过标准:1~4 全过;5 中 504/503 分流必过,30s 串行观察为 S2 记录项。 证据:节点点击/拖拽过程录屏或连拍截图、Network 面板 /api/kg/entities* 请求列表与状态码、Console 日志。


5. 缺陷 #32 实体搜索(「水稻」)

项 值
环境 S1(功能:搜索/命中/渲染);S2(时延 <1s、真实数据、索引脚本执行后)
用户原话 「输入水稻 响应超时」

前置数据

  1. S2 前置:176 上执行修复提供的 neo4j normalized schema Cypher 适配 + 索引脚本(部署 agent 负责,复测前由 TL 确认已执行)。
  2. 175 dev Neo4j 数据与生产同源,可用于 S1 功能验证。

操作步骤与断言

  1. API 前缀/精确匹配:
    • curl -w 'time_total:%{time_total}s' "http://<BFF>/api/kg/entities?q=水稻&limit=20"。
    • 断言:200;results[] 含 name 以「水稻」开头或精确等于的实体(字段 id/name/type/support_count);时延 S1 ≤3s、S2 <1s(修复目标)。
    • 超时场景:504 + code:"TIMEOUT";Neo4j 不可用:503 + code:"UPSTREAM_DOWN"(与 #31-5 同分流)。
  2. UI 搜索:
    • 知识图谱 TAB 搜索框输入「水稻」→ 回车/点搜索。
    • 断言:结果列表 ≤1s(S2)渲染命中实体;无「响应超时」提示;无卡死(旧缺陷)。
  3. 邻域渲染:
    • 点击任一命中实体。
    • 断言:详情面板/邻域图渲染该实体及其邻域(nodes/edges 非空),/api/kg/entities/<id>/neighborhood 200;图谱画布无长时间白屏。

通过标准:1~3 全过(S1 过功能与 ≤3s,S2 过 <1s)。 证据:curl 输出(含 time_total)全文、UI 搜索截图、Network 响应时间。


6. 缺陷 #37/#38 PDF 上传提取(mineru)

项 值
环境 S1(错误可读性/上传链路);S2(176 MinerU CPU 裸部署后真实提取)
用户原话 「【Error2】No such file or dictionary:'/home/xzq/miniconda3/envs/minerupy312/bin/mineru'」

前置数据

  1. 样例 PDF(≥2 个,含文字+图片,5MB 左右),存放 175 ~/acceptance-r3/samples/ 与 176 相应目录。
  2. 修复要求:mineru 路径 X_OK 预检返回可读错误;MINERU_CMD env 可覆盖;GPU 预检开关(176 设 0);GF_PYTHON_ENV env 化;figures.py 常量 env 化。
  3. S2 前置:176 MinerU CPU 裸部署完成(部署 agent 并行进行),MINERU_CMD 指向 176 部署路径。

操作步骤与断言

  1. 上传链路(S1):
    • 「提取任务」TAB 点「上传」选 PDF。
    • 断言:上传进度可见(loading 文案/进度),上传成功提示(✓);任务列表出现新任务,状态 queued → running。
    • Network:POST /api/extraction/upload 200/201。
  2. mineru 缺失/不可用时的可读错误(S1,若 175 无可用 mineru):
    • 任务进入 mineru 阶段后失败。
    • 断言:任务详情或运行日志出现中文可读错误,明确包含:不可用的 mineru 路径 + 原因(文件不存在/不可执行)+ 修复提示(如「请设置 MINERU_CMD 环境变量」),不得再出现原始 No such file or directory:'...mineru' 裸英文错误。
    • 若 175 存在可用 mineru(minerupy312 可用),则任务应成功进入 llm 阶段,本项改为断言成功路径。
  3. S2 真实提取(176):
    • 上传含图 PDF,任务 queued → mineru(35%)→ llm → done。
    • 断言:运行日志无 No such file or directory、无 GPU 相关报错(176 无 GPU,GPU 预检开关已设 0,不得因无 GPU 拒绝任务);提取产物(结构化内容/图表)可查看;时延记录(CPU 时延仅记录,不设阈值)。
    • 断言:env 化生效——BFF 进程环境含 MINERU_CMD/GF_PYTHON_ENV(由 Debug-2 提供检查命令),且任务按 env 指定路径执行。

通过标准:1 过;2 或 3 按环境满足其一;S2 真实提取全链过。 证据:任务详情/运行日志截图、BFF 日志(/tmp/plantai-bff*.log 或 dev BFF 日志路径,复测时确认)、上传 Network 截图。


7. 缺陷 #39 运行日志

项 值
环境 S1
用户原话 「日志无法刷新加载」

前置数据

  1. 「提取任务」TAB 有历史任务(或新上传一个 PDF 制造 running 任务)。

操作步骤与断言

  1. 打开总是重拉:
    • 点「运行日志」展开按钮。
    • 断言:每次打开(收起→再展开)都会发起新的 GET /api/extraction/run-log 请求(Network 可见),内容为最新日志,而非仅首次加载后不刷新(旧缺陷)。
  2. running 时 5s 轮询:
    • 存在 running 任务时保持日志展开,观察 15s。
    • 断言:Network 中 /api/extraction/run-log 每约 5s 出现一次(15s 内 ≥2 次);日志内容随时间变化(出现新行)。
  3. 刷新按钮:
    • 若修复提供手动刷新按钮:点击 → 立即发起一次 run-log 请求并更新内容。
  4. 展示形态:
    • 断言:日志区为 mono 字体(元素 .run-log,font-family monospace 系);最大高度受限(样式 max-height 生效,超高滚动);自动贴底——新日志行到达后滚动条停在底部(可强制向上滚动后观察是否被拉回底部,若实现为「仅当已贴底才跟随」则验证该语义)。

通过标准:1、2、4 过;3 若存在按钮则必过。 证据:Network 面板 run-log 请求时间线截图、日志区滚动截图。


8. 缺陷 #40 提取任务轮询刷新

项 值
环境 S1
用户原话 「轮询存在…tab内容没有刷新」

前置数据

  1. 上传一个 PDF 制造 running 任务(或复用 #39 的任务)。

操作步骤与断言

  1. 每 5s 轮询且内容刷新:
    • 停在「提取任务」TAB,任务 running。观察 15s。
    • 断言:Network 中 /api/extraction/jobs(及任务详情端点)每约 5s 出现一次;任务列表卡片内容随状态/进度更新(旧缺陷:轮询存在但 TAB 内容不刷新)。
  2. 切走暂停:
    • running 期间切到「检索过程」TAB(任何离开 extract 的 TAB)。
    • 断言:切换后 Network 中 10s 内不再出现 /api/extraction/jobs 轮询请求(轮询暂停)。
  3. 切回恢复:
    • 切回「提取任务」TAB。
    • 断言:≤5s 内恢复轮询,列表刷新为最新状态。
  4. 全部终态停止:
    • 等任务全部进入终态(done/error),停留 15s。
    • 断言:终态后轮询停止(Network 无周期请求),列表不再刷新。
  5. 详情/日志展开态保留:
    • 展开任务 A 的详情与运行日志 → 切到「文献来源」→ 切回。
    • 断言:任务 A 详情仍展开、日志仍展开且内容保留(提升到 store,切回不丢;旧缺陷相反)。

通过标准:1~5 全过。 证据:Network 时间线截图(标注切换 TAB 的时刻)、前后列表内容对照截图。


9. 缺陷 #42 docgen 模板选择

项 值
环境 S1
用户原话 「UI中没有模板选择选项」

前置数据

  1. 上传 1 份资料(任意 PDF/TXT)并在「我的资料」将该资料勾选/标记为「设为模板」(as_template=true;如 UI 尚无标记入口,用 API:PATCH /api/docgen/library/<id> {"as_template":true},复测时以修复后 UI 为准)。
  2. 确认 GET /api/docgen/library?as_template=true 返回该资料。

操作步骤与断言

  1. 模板下拉出现:
    • Composer 输入写作需求(如「帮我写一份项目申报材料」),在 docgen 意图被识别/输入态(修复目标:docgen 模式)下观察 Composer 区域。
    • 断言:出现模板选择下拉(或等价控件),含「自动推荐」默认项 + 内置模板列表:通用公文(红头)/ 报告总结 / 申报材料 / 合同协议 / 讲话稿 / 方案计划 / 通用文档 + 用户 as_template 资料。
  2. payload 携带 template_id:
    • 下拉选择「通用公文(红头)」→ 发送。
    • 断言:Network 中 POST /api/docgen/generate 请求体含 template_id,值为该模板的 id(与 /api/docgen/library 或模板索引对应);返回 202(修复后契约)+ task_id。
  3. 产物模板生效:
    • 生成完成后(轮询 task 至 done,见 #52),文档 TAB / DocCard 显示「模版:通用公文(红头)」;产物 JSON artifact.template_name(或 template_key)为 official。
  4. 自动推荐:
    • 不手动选模板直接发送 → payload 无 template_id 或为自动推荐结果,生成正常,产物模板名与自动推荐一致(记录实际推荐结果)。

通过标准:1~4 全过。 证据:Composer 下拉截图、Network generate 请求体复制、产物信息截图/JSON。


10. 缺陷 #47 导出 DOCX(红头)与文档占用

项 值
环境 S1(功能与格式);S2 终验一次
用户原话 「导出 DOCX…格式不一致,删除红头文件对话内容,红头文件还占用在文档界面中(不刷新网页下)」

前置数据

  1. 生成一份「通用公文(红头)」文档,正文含表格(模板自带或让 QA 生成含表格内容)与图片(生成时附带含图资料,见 #52)。

操作步骤与断言

  1. 47a 导出 DOCX 格式一致:
    • 文档 TAB 打开该文档 → 点「导出 DOCX」按钮。
    • 断言:按钮变「导出中…」→ 完成后触发下载(文件名合理,.docx);Network:POST /api/docgen/artifacts/<id>/export {"format":"docx"} 成功。
    • 打开下载的 .docx(本地 Word / LibreOffice):断言红头文字为红色且居中、标题层级与预览一致、表格有边框且内容完整、图片存在。与「导出 PDF」或 HTML 预览对照,整体版式一致(修复目标:HTML 同源转换)。
    • 异常回退路径(HTML 转换失败回退 md2docx)由 Debug-1/2 单元测试覆盖,复测仅记录正常路径;若导出失败,按缺陷单登记。
  2. 47b 会话切换 resetSession:
    • 在会话 A 生成红头文档(文档 TAB 被其占用)→ 切换到会话 B(无文档)。
    • 断言:文档 TAB 显示空态文案「尚无文档。在对话中提出写作需求…」,不再显示会话 A 的红头文档。
    • 切回会话 A:文档 TAB 恢复显示红头文档(resetSession 只清不丢)。
  3. 删除对话不残留(旧缺陷场景):
    • 会话 A 中删除该 docgen 对话消息(或删除整个会话)。
    • 断言:不刷新网页的前提下,文档 TAB 立即回到空态/资料库视图,红头文件不再占用文档界面。

通过标准:1~3 全过。 证据:导出 DOCX 打开截图(红头/表格/图片)、文档 TAB 前后截图、删除消息操作截图。


11. 缺陷 #52 上传资料生成文档(异步任务化)

项 值
环境 S1(功能);S2 终验一次
用户原话 「上传成功了,但是没办法进行对话…删除对话文件,保留资料内容,可以进行总结」

前置数据

  1. 准备 1~2 份资料 PDF(内容与「水稻」相关,便于断言生成基于上传内容)。
  2. 修复契约:POST /api/docgen/generate → 202 {ok:true, task_id, status:"queued"};GET /api/docgen/tasks/{task_id} → 200 {ok, task:{id, session_id, status, progress:[{section,title,state}], artifact:{id,title,template_name,template_key,has_html,letterhead,file_name}|null, error:null|中文可读}}。

操作步骤与断言

  1. 上传进度与资料留存:
    • Composer 拖拽 PDF → 附件 chips 出现 + 上传进度可见(百分比/进度条,0→100%)。
    • 断言:上传成功后资料出现在「我的资料」列表;POST /api/docgen/library → 201。
  2. 异步生成 202+task_id:
    • 发送「根据上传资料生成一篇总结文档」。
    • 断言:Network POST /api/docgen/generate 返回 status=202,body ok:true、task_id 为 uuid、status:"queued"(修复前是同步 200,重点断言 202)。
    • 消息区出现生成卡片(流水线五步或 DocCard),状态「排队中/生成中」。
  3. task 轮询与进度:
    • curl "http://<BFF>/api/docgen/tasks/<task_id>" 每 2~3s 一次直到终态。
    • 断言:status 按 queued → running → done 演进;progress[] 含 section/title/state(done|running|pending),与前端五步卡片一致;done 后 artifact 非空:title 非空、template_name 非空、has_html 布尔、letterhead 布尔、file_name 非空;error 为 null 或(失败时)中文可读。
    • GET /api/docgen/tasks?session_id=<SID>:返回该会话 task 列表,含上述 task_id。
  4. 生成期间可继续对话(核心旧缺陷):
    • 生成任务 running 期间(5~15s 窗口),在对话中输入「你好,介绍一下你自己」发送。
    • 断言:该消息得到正常回答(消息区出现 assistant 回复),不被文档生成阻塞(旧缺陷:上传成功后无法进行对话)。
  5. 删除对话文件保留资料:
    • 生成完成后,删除该 docgen 对话消息。
    • 断言:上传的资料仍在「我的资料」列表,未被级联删除;资料仍可发起新对话/总结(用该资料再发一次总结请求,可正常生成)。
  6. 失败重试与重启中断标记:
    • 失败路径(可强制:生成时 BFF 侧制造一次失败,或依赖真实失败):断言失败展示中文错误 + 提供「重试」按钮;点击重试 → 重新发起 generate(Network 新 task_id),可最终成功。
    • 重启中断:生成 running 时重启 dev BFF(仅 dev 9090)→ 重新打开页面,该任务在 UI 显示中断/失败态(不永久挂「生成中」),可重试。

通过标准:1~5 全过;6 中重试必过,重启中断为 S1 可测项必过。 证据:Network 202 响应体、task 轮询 curl 输出全文、对话并发的两条消息截图、删除消息前后「我的资料」对照截图。


12. 缺陷 #55 上传资料入库与文档 TAB 占用

项 值
环境 S1;S2 终验一次
用户原话 「一旦进行过文档生成任务,文档TAB就会被生成的文档占用,导致上传的资料无法正常显示」

前置数据

  1. 完成一次文档生成(文档 TAB 被生成文档占用)。

操作步骤与断言

  1. 视图切换按钮:
    • 文档 TAB 被文档占用时,DocPane 顶部出现「资料库」/「关闭文档」视图切换按钮(修复新增,文案以实现为准)。
    • 断言:点「资料库」→ 文档区切换为「我的资料」面板(libraryTitle: '📁 我的资料'),列表显示已上传资料。
    • 点「关闭文档」(或等价返回)→ 回到文档视图/空态。
  2. 上传资料可见:
    • 生成文档占用期间上传新资料(拖拽)。
    • 断言:上传进度可见(同 #52-1);完成后提示「已入库」(修复目标文案,以 i18n 为准);通过「资料库」按钮即可在文档 TAB 查看该资料(旧缺陷:上传的资料无法正常显示)。
  3. API 佐证:
    • GET /api/docgen/library → 200,列表含新上传条目(id/名称/大小/时间);GET /api/docgen/library?as_template=true 与资料列表共存正常。

通过标准:1~3 全过。 证据:文档 TAB 两个视图切换截图、「我的资料」列表截图(含新上传条目)、上传进度截图。


13. 回归矩阵

修复波及面 → 逐条「做什么 + 看什么」。执行于 S1,任一条失败登记缺陷单并标注关联修复项。

A. 知识库 sheet #6-12(聊天流/时间线/动画,关联 #7)

回归项 操作 看什么
A1 检索时间线 提一个正常检索问题 检索过程 TAB 实时出现检索步骤时间线,顺序正确,不卡死
A2 流式五相位卡 问答模式提问 消息区 pipeline 五相位卡(规划/检索/证据/生成/完成)按序点亮
A3 答案揭示/跳过 任意答案 揭示动画正常、跳过生效(复测 #7 已覆盖,此处抽查短答案)
A4 停止按钮 流式回答中点「停止」 立即停止,消息状态 done,无残留定时器;再发消息正常
A5 错误重试 制造一次失败(或点重试) 重试重新发起请求,错误文案中文可读
A6 来源卡/证据帧 带来源的答案 答案卡来源行与文献来源 TAB 一致(关联 #27)
A7 多轮历史 同会话连续 2 轮问答 第二轮携带 history,回答连贯,无 422

B. docgen 全链 #41-53(关联 #42/#47/#52/#55)

回归项 操作 看什么
B1 上传无大小限制 上传 >10MB PDF(≤50MB) 正常上传(修复前曾移除 100MB 门槛,回归确认 50MB 校验仍在)
B2 模板 HTML 预览 生成文档后打开预览 白底、排版正确、图片显示
B3 修改文档(prompt 修改) 编辑模式输入修改指令 修改生效,自动保存,无「修改失败」
B4 导出按钮文字 点导出 按钮文字保持「导出 PDF/导出 DOCX」,不变成「下载」;导出中显示「导出中…」
B5 导出文件名 导出 文件名按 prompt 核心意思命名
B6 编辑还原 编辑模式改动 → 切预览 → 切回 手动修改内容不丢
B7 二次预览 开文档 A 预览后,再开文档 B B 正常渲染,无空白
B8 意图路由 输入「帮我写一份报告」与普通提问 写文档意图进 docgen,普通问答不进;生物问答不进生信流(收紧回归)

C. 生信 sheet #32-35 图谱(关联 #31/#32)

回归项 操作 看什么
C1 图谱概览 打开知识图谱 TAB GET /api/kg/overview 200,节点/边渲染
C2 实体搜索 搜索已知实体 命中渲染(关联 #32)
C3 实体详情 点节点 属性面板(关联 #31)
C4 邻域展开 详情内展开邻域 邻域图正确
C5 图谱状态 GET /api/kg/status 200,状态正确

D. 轮询与提取历史用例(关联 #39/#40)

回归项 操作 看什么
D1 离线降级 停 dev 上游(或模拟)后打开提取 TAB 显示离线降级块 + 「重试」按钮,点重试恢复
D2 上传校验 上传非 PDF / >50MB 中文错误提示,不上传
D3 任务详情 展开任务 详情加载正常,错误态有重试按钮
D4 多任务列表 连续上传 2 个文件 列表两条任务,状态互不干扰

E. 会话切换全家桶(关联 #47b/#40-5)

回归项 操作 看什么
E1 切会话消息隔离 A/B 两会话各有消息,来回切 消息列表各自正确,无串台
E2 切换时流不串 A 会话流式中切到 B A 的流式不影响 B;切回 A 状态正确(完成或已停止)
E3 删除会话 删除会话 A 列表移除,B 正常;文档 TAB 不残留 A 的文档
E4 新建会话 新建 空态正确,检索过程 TAB 显示「发起提问后,这里会实时展示检索过程」
E5 轮询会话隔离 A 有 running 提取任务,切 B 轮询随 TAB 暂停/恢复(关联 #40),B 无 A 的轮询

F. 能力集成抽查(仅抽查,不回归全量)

回归项 操作 看什么
F1 联网搜索开关 打开 web_search 开关提问 命中联网结果时展示 web 来源
F2 停止/中断 SSE 流中途停止 无「断流崩溃」(BFF 日志无异常栈)

14. 复测流程约定

  1. 完成门禁:13 项缺陷中全部 S1 项通过,缺陷单清零(S2 项见下),才算修复完成;S1 失败项回归修复后重测该项 + 其回归矩阵关联行。
  2. S2 标记「176 终验」的项:#7(时长终验)、#27、#28-3(超时降级)、#31-5(30s 串行观察)、#32(时延 <1s)、#37/38(真实 mineru)、#47(格式终验)、#52、#55。S1 全过后由 TL 组织 176 终验。
  3. 176 执行清单与数据准备:
    • 数据:含图样例 PDF、水稻相关资料 PDF(≥2 份)、触达图谱「水稻」实体的 neo4j 数据、超时问题样例、>3000 字答案问题。
    • 部署前置:索引脚本已执行(问 TL/部署 agent)、MinerU CPU 裸部署完成且 MINERU_CMD 已设、GPU 预检开关=0。
    • 执行顺序:先 API(curl 层)后 UI;#37/38 上传提取;#32「水稻」时延;#28 超时;其余终验项按本文用例跑一遍并记录实测值。
  4. 复测环境纪律:S1 只用 175 dev(vite dev + dev BFF 9090),绝不碰生产 8090;dev BFF 的模拟配置(如 NEO4J_URI 指向空端口)用后即还原并重启。

15. 缺陷单模板

复测发现缺陷按此登记(记入复测报告,由 TL 归档):

缺陷编号: r3-<原始缺陷号>-<序号>(如 r3-31-01)
关联修复项: #31(前端 KgGraphView/KgEntityDetail / 后端 entity 端点)
复现步骤: 1. … 2. …
实际结果: …
期望结果: …
证据: 截图路径 / API 响应全文 / 日志路径(BFF /tmp/plantai-bff*.log 或 dev 日志)
严重级: P0(阻断) / P1(功能错误) / P2(体验) / P3(建议)
复测环境: S1(175 dev) / S2(176)

附录 A:快速 curl 断言清单(S1,dev BFF)

BFF=http://127.0.0.1:9090   # dev BFF,复测时以 TL 给的地址为准

# #27 会话来源聚合
curl -s $BFF/api/sessions/<SID>/sources | jq '.sources[] | {id, paper_id, count, title}'

# #28 检索模式(观察 200 降级体/空结果体;测量时延)
curl -s -w '\ntime_total:%{time_total}s\n' -X POST $BFF/api/qa/search \
  -H 'Content-Type: application/json' \
  -d '{"question":"<问题>","session_id":"<SID>","use_llm":false}'

# #31/#32 图谱
curl -s -w '\ntime_total:%{time_total}s\n' "$BFF/api/kg/entities?q=水稻&limit=20"
curl -s "$BFF/api/kg/entities/<entity_id>"
curl -s "$BFF/api/kg/entities/<entity_id>/neighborhood?depth=1&limit=50"

# #52 docgen 异步任务
curl -s -X POST $BFF/api/docgen/generate -H 'Content-Type: application/json' \
  -d '{"description":"根据上传资料生成一篇总结","session_id":"<SID>","library_ids":["<lib_id>"],"template_id":"<template_id>"}'
curl -s $BFF/api/docgen/tasks/<task_id>
curl -s "$BFF/api/docgen/tasks?session_id=<SID>"

# #55 资料库
curl -s "$BFF/api/docgen/library"
curl -s "$BFF/api/docgen/library?as_template=true"

# #39/#40 提取
curl -s "$BFF/api/extraction/jobs"
curl -s "$BFF/api/extraction/run-log"

附录 B:修复目标契约速查(断言按此)

POST /api/docgen/generate(请求体同现状+可选 template_id)
  → 202 {"ok":true,"task_id":"<uuid>","status":"queued"}
GET /api/docgen/tasks/{task_id}
  → 200 {"ok":true,"task":{"id","session_id","status":"queued|running|done|error",
     "progress":[{"section":1,"title":"…","state":"done|running|pending"}],
     "artifact":{id,title,template_name,template_key,has_html,letterhead,file_name}|null,
     "error":null|中文可读}}
GET /api/docgen/tasks?session_id=<sid> → 列表
POST /api/qa/search 超时 → 200 {"mode":"search","answer":"","sources":[],
  "degraded":true,"reason":"TIMEOUT"};正常空结果 → degraded:false
GET /api/kg/entities* 查询超时 → 504 TIMEOUT;实例不可用 → 503 UPSTREAM_DOWN

附录 C:定点复测结论(2026-09-10 晚,debug/frontend @ 53069d4 + debug/backend @ ad248cf)

缺陷编号 修复 复测方式 结论
r3-07-01 跳过按钮无效 MessageList 补 skip-reveal 转发 + ChatPanel 落 store.skipReveal() 真实 vite 5199 + dev BFF 9090:揭示中(stream 210 字符)点「跳过」→ 光标/shimmer/按钮即刻消失、全文 2906 字符定格、h2 编号正常 过
r3-52-02 done 后 desc 残留「生成中 x/y 节」 done 分支还原 generation 步默认 desc 真实生成一轮后检查五步卡:第 4 步「逐节生成」state=done、desc=「按大纲逐节生成正文」(非「生成中 8/9 节」) 过
r3-47-01 DOCX 内容塌缩 html2docx 递归遍历重写(穿透 doc-body 包裹层) 真实走 9090 重导出含表格文档(HTML 源 60 p / 8 h2 / 32 h3 / 13 table):DOCX 330 段落、13 表格(7x4 带表头)、77 标题,结构与 HTML 一一对应;红头红字居中不受影响 过
r3-42-01 内置模板回落 _pick_template 内置 key 直接解析 + pipeline builtin 分支定稿 key/name + router 兜底 「撰写一份水稻病虫害防治方案」+template_id=official → 产物 template_key=official、template_name=通用公文(红头)、DOCX 红头「红头文件」红字居中;未指定 template_id 仍自动推荐(实测 report/sample_050) 过

配套验证:后端 pytest 286/286、前端 vitest 373/373(旧 bench.store.test.js 已随测试债清理修复)。新增测试:ui/tests/r3debug/skipReveal.test.js(151 行)、docgenPoll.test.js done 还原断言、sdk test_docgen_html2docx.py doc-body 包裹样例、test_docgen_api.py 内置模板回退断言。

原 S1 未通过 4 项(r3-07-01/r3-47-01/r3-52-02/r3-42-01)全部转通过;S2(176 终验)清单不变。