docs(test): archive long-task phase live/resume UX real-agent report
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
# 长任务 PhaseEventStrip live 化与分型恢复 UX(P2)真实 Agent 测试报告
|
||||
|
||||
## 一、测试背景 (Background)
|
||||
|
||||
P1(`2026-09-09-chat-wait-error-retry-ux-test-report.md`)已将 Web 闲聊接入 `/api/v1/chat/stream` 并验证等待态、结构化错误与 transient 自动重试。P2 按既定分期推进「长任务 `PhaseEventStrip` live 化、`resumable`→继续任务、`fatal`→重新开始、对齐 Resume/task stream」,实现内容:
|
||||
|
||||
- 后端 `internal/server/sse.go`:task stream 补齐 `X-Request-Id`;新增 `taskStreamRequest`(在既有 `TaskRunRequestDTO` 上嵌入 `work_dir`/`add_steps`,不改动原 DTO);`work_dir != ""` 时进入 resume 模式调用 `ResumeOrModifyWorkspace`;失败分类改为 workspace 感知(软失败携带 `result.WorkDir` → `resumable`,网络/超时 → `transient`,硬失败 → 按默认规则);`internal/stream` 新增 `DonePayload.WorkspaceID` + `EmitDoneWithWorkspace`。
|
||||
- Web 新增 `web/src/api/taskStream.ts`(task SSE 客户端 + `buildTaskRequest`/`buildResumeRequest`)、`web/src/engine/phaseEventMapper.ts`(phase → PhaseEventStrip 纯函数映射,图标表与 CLI 一致,优先展示后端真实 `message`);`App.tsx` 新增 `runTaskAttempt` 状态机(与 P1 闲聊一致),模板关键词命中的长任务切换到真实 task stream,`handleErrorAction` 放开 `continue`(基于 `errorInfo.workspaceId` 续跑)/ `restart`(确认后原意图新 run),删除 P1 的 actions 仅保留 retry 过滤;`MessageBubble` 对 `restart` 采用 danger 描边样式、长任务停止文案改为「已停止任务(可重新开始)」。
|
||||
- 测试报告遗留建议 #2(真实 transient 故障的会话级集成测试)随本次落地:`internal/server/taskstream_test.go` 三个用例。
|
||||
|
||||
> **验证方式与边界说明(如实声明)**:本次执行环境无浏览器自动化 MCP 工具,故 Web 浏览器内的视觉呈现(PhaseEventStrip 逐步点亮、按钮点击流转)**未由本报告直接截图证实**,相关条目标注为「待用户工作台人工复核」。后端 SSE 真实链路(curl 直连逐事件时间戳)、Go 集成测试、Web 单元与回归测试均由本报告实证覆盖。PhaseEventStrip 渲染逻辑本身(`applyPhaseEvent` 纯函数)有 10 项单测覆盖,浏览器侧风险集中于 CSS/交互层。
|
||||
|
||||
## 二、测试目标 (Test Objectives)
|
||||
|
||||
1. 验证后端可构建、启动,`/api/v1/health` 返回 `agent_backend: pi-agent`(含 P2 改动的重新构建二进制)。
|
||||
2. Case A:真实长任务经 `/api/v1/tasks/run/stream` 调用 pi-agent + MiniMax-M3,phase 事件序列真实推进、`done` 事件携带 `workspace_id`、deliverable 正文真实非模板。
|
||||
3. Case B:任务执行中真实故障注入(`docker stop shuimu-pi-agent-dev`)→ resume 请求输出 `event:error` 且 `error_info` 结构完整(class/phase_id/workspace_id/request_id/actions);恢复容器后同一 resume 请求可继续推进。
|
||||
4. 集成测试:transient 注入(死端口)、软失败 → `resumable` + `[continue, restart]`、resume 无效工作区分类,全部通过(补齐 P1 报告遗留建议 #2)。
|
||||
5. 回归:`go test ./...` 与 `cd web && npm test` 通过或标注已知环境相关失败。
|
||||
6. 报告按 `.cursor/rules/test-reports-standard.mdc` 固定结构归档。
|
||||
|
||||
## 三、测试方案与环境 (Test Plan & Environment)
|
||||
|
||||
**环境**
|
||||
|
||||
| 项 | 值 |
|
||||
| :--- | :--- |
|
||||
| OS | macOS (Darwin 25.6.0, arm64) |
|
||||
| 分支 / commit | `dev` @ `99e0d4a`(P2 提交链 `1e7cb67..99e0d4a` 共 8 个提交) |
|
||||
| 后端服务 | `./bin/server`(P2 改动后重新 `go build`),监听 `127.0.0.1:8080` |
|
||||
| pi-agent 容器 | `shuimu-pi-agent-dev`(`127.0.0.1:18865`,healthy;Case B 中受控 stop/start) |
|
||||
| 上下文库 / 知识库 | `shuimu-context-pg-dev`、`shuimu-kb-shared`(均 healthy) |
|
||||
|
||||
**测试层级**
|
||||
|
||||
1. 服务自检 + 重新构建部署(确认运行二进制包含 P2 代码)。
|
||||
2. 真实 Agent 黑盒 E2E:`curl -N` 直连 task stream,逐行 `[HH:MM:SS]` 相对时间戳记录。
|
||||
3. 受控故障注入:`docker stop/start shuimu-pi-agent-dev` + resume 模式请求。
|
||||
4. 自动化:`go test ./internal/server -run TestTaskStream -v`、全量 `go test ./... -count=1`、`cd web && npm test`。
|
||||
|
||||
**核心用例矩阵**
|
||||
|
||||
| 用例 | 输入 | 验证点 |
|
||||
| :--- | :--- | :--- |
|
||||
| Case A | `task_key=intake` + 项目五要素 + `backend=pi-agent` | phase 序列真实推进;done.workspace_id;deliverable 非模板 |
|
||||
| Case B-1 | 停 pi-agent 后 resume(work_dir=Case A 工作区 + add_steps 注入执行步骤) | event:error + error_info 完整结构 |
|
||||
| Case B-2 | 恢复 pi-agent 后同一 resume 请求重发 | phase 真实推进至注入步骤 |
|
||||
| 集成 ×3 | httptest + 死端口 / 500 runner / 无效 work_dir | transient / resumable / resume 分类 |
|
||||
|
||||
## 四、预期结果 (Expected Results)
|
||||
|
||||
- Case A:`phase`(squad_assembly → step_executing × N → quality_gate)、`deliverable`(真实文档标题)、`done`(含 `duration_ms`、`workspace_id`)依次出现;总时长与真实 LLM 生成节奏一致。
|
||||
- Case B-1:`event:error` 的 `error_info` 含 `class`(网络中断 → `transient`)、`code`、`phase_id`(resume 路径为 `task_resume`)、`request_id`、`workspace_id`、`actions`。
|
||||
- Case B-2:恢复后 resume 重发应继续输出真实 `phase` 并推进到注入的动态步骤。
|
||||
- 集成:死端口 → `transient` + `retry`;软失败(非网络类)→ `resumable` + `[continue, restart]` + workspace_id;无效 work_dir → `resumable` 且 workspace_id 等于请求值。
|
||||
- 回归:与 P1 报告一致的已知环境相关失败允许存在,不得出现与 P2 代码路径相关的新失败。
|
||||
|
||||
## 五、实际测试结果与判定 (Actual Results & Evaluation)
|
||||
|
||||
### Case A:真实长任务 SSE 全链路 — **PASS**
|
||||
|
||||
```
|
||||
[18:51:09] event: governance ← 编辑租约获取(docgov 联动)
|
||||
[18:51:09] event: phase squad_assembly「正在组建并启动【用户初始需求收集表】智能体小队(后端: pi-agent)...」
|
||||
[18:51:09] event: phase step_executing「材料整理专员正在进行:抽取材料事实并回填字段...」(role=organizer)
|
||||
[18:54:15] event: phase step_executing「撰写专员正在进行:撰写用户初始需求收集表初稿...」(role=writer)
|
||||
[18:59:18] event: deliverable {"task_key":"intake","title":"用户初始需求收集表","file_path":"data/dev/workspaces/bizdev/intake_1788951069607049000/..."}
|
||||
[18:59:18] event: phase / summary / briefing / governance
|
||||
[18:59:18] event: done {"session_id":"task_run","duration_ms":488415,"total_events":15,
|
||||
"workspace_id":"data/dev/workspaces/bizdev/intake_1788951069607049000"}
|
||||
```
|
||||
|
||||
- 事件序列真实(organizer → writer 阶段间隔 3 分钟,与真实 MiniMax 生成节奏一致,非 mock 秒回);16 个事件、总时长 488s。
|
||||
- **`done.workspace_id` 已按 P2 契约携带真实工作区路径**(`EmitDoneWithWorkspace` 生效)。
|
||||
- deliverable 标题具体非模板,正文由真实 pi-agent + MiniMax-M3 生成。
|
||||
- 该事件序列即前端 `applyPhaseEvent` 的输入;mapper 单测已覆盖同构序列(squad_assembly → step_executing → step_completed → quality_gate)。
|
||||
|
||||
### Case B-1:运行中真实故障注入(docker stop pi-agent)+ resume — **PASS**
|
||||
|
||||
```
|
||||
[19:02:57] event: phase step_executing「材料整理专员正在进行:抽取材料事实并回填字段...」
|
||||
[19:02:57] event: phase step_failed「材料整理专员执行未成功:pi remote runner call failed: Post
|
||||
"http://127.0.0.1:18865/v1/run": dial tcp ... connection refused」(role=organizer)
|
||||
[19:02:57] event: error
|
||||
{"error":"网络连接异常,请稍后重试","error_info":{
|
||||
"user_message":"网络连接异常,请稍后重试","code":"NETWORK_ERROR",
|
||||
"class":"transient","phase_id":"task_resume",
|
||||
"upstream":"step step_01_organize failed: pi remote runner call failed: ...",
|
||||
"request_id":"req_1788951777603821000","workspace_id":"data/dev/workspaces/bizdev/intake_..."}}
|
||||
```
|
||||
|
||||
- **resume 模式真实生效**(`phase_id=task_resume` 表明走 `ResumeOrModifyWorkspace` 分支)。
|
||||
- `error_info` 六要素齐全:class/code/phase_id/upstream/request_id/workspace_id;网络中断按 P1 契约分类为 `transient`(前端呈现「重试」;workspace_id 同时带出,业务类失败场景下则为 `resumable` + 继续任务,见集成测试)。
|
||||
- `X-Request-Id` 响应头与 `error_info.request_id` 关联(req_1788951777603821000)。
|
||||
|
||||
### Case B-2:恢复服务后同一 resume 请求重发 — **PASS(流被客户端超时截断,推进证据充分)**
|
||||
|
||||
```
|
||||
[19:04:32] event: phase step_executing(organizer,真实调用已恢复的 pi-agent)
|
||||
[19:05:53] event: phase ×2(writer 阶段推进)
|
||||
[19:08:57] event: phase ×2
|
||||
[19:11:09] event: phase step_executing「法规专员正在进行:复核需求表合规性...」(role=regulatory)
|
||||
← 本步骤为 Case B-1 请求中通过 add_steps 注入的动态步骤 step_dyn_verify_p2
|
||||
```
|
||||
|
||||
- 恢复 pi-agent(healthy)后,同一 resume 请求成功续跑:organizer/writer 步骤依次真实完成,并推进到注入的动态步骤——证明「恢复服务 → 继续任务 → 工作区续跑」闭环成立。
|
||||
- 流在 19:14:32 被 `curl -m 600` 客户端超时截断(日志止于 19:11:09 的真实执行中事件,服务端无错误输出);法规专员步骤的真实 LLM 耗时超过 10 分钟客户端上限。**done 事件未捕获属客户端工具限制,非服务端缺陷**;done.workspace_id 契约已由 Case A 与集成测试证实。
|
||||
|
||||
### 集成测试(`go test ./internal/server -run TestTaskStream -v`)— **PASS ×3**
|
||||
|
||||
```
|
||||
--- PASS: TestTaskStream_TransientInjection_DeadPiAgent (0.12s)
|
||||
--- PASS: TestTaskStream_SoftFailure_ClassifiedResumable (0.10s)
|
||||
--- PASS: TestTaskStream_ResumeInvalidWorkspace_ClassifiedResumable (0.09s)
|
||||
```
|
||||
|
||||
1. **Transient 注入**(P1 报告遗留建议 #2 补齐):pi-agent 指向死端口 → SSE 输出 `event:error` 且 `error_info.class=transient`、actions 含 `retry`、`X-Request-Id` 非空、`phase_id` 取流中最近 phase、`request_id` 非空。
|
||||
2. **软失败分类**:伪造 pi-agent runner 返回 HTTP 500 + 非 JSON body(业务类失败,非网络/超时)→ `class=resumable`、`workspace_id` 非空(squad 软失败携带 work_dir)、actions=`[continue, restart]`。
|
||||
3. **resume 无效工作区**:`work_dir` 指向不存在目录 → 失败仍归类 `resumable` 且 `workspace_id` 等于请求值(工作区可修复后续跑)。
|
||||
|
||||
## 六、回归测试 (Regression)
|
||||
|
||||
### 6.1 Go 全量回归(`go test ./... -count=1`)
|
||||
|
||||
16 个包中 **14 个全绿**;2 个包各 1 个用例失败,均为**与 P1 报告完全一致的已知环境相关失败**,与 P2 改动(`internal/stream`、`internal/server/sse.go`、`internal/server/taskstream_test.go`)无代码路径重叠:
|
||||
|
||||
1. `internal/server · TestPiAgent_FullLifecycle_MultiRole_RealWorld_E2E`:`data/dev` 共享工作区跨轮次 docgov 锁定残留(与 P1 报告 6.3 同根因);**同包内本次新增的 3 个 TestTaskStream_* 用例全部通过**。
|
||||
2. `internal/storage · TestCAS_DatabaseIntegration`:测试固定文案与 `uq_mem_chunks_hash` 唯一索引在共享 dev DB 冲突(与 P1 报告 6.3 同根因)。
|
||||
|
||||
`internal/stream`、`internal/chat`、`internal/bizdev`、`internal/squad` 全绿(含本次新增 `TestStreamer_EmitDoneWithWorkspace`)。
|
||||
|
||||
### 6.2 Web 回归(`cd web && npm test`)
|
||||
|
||||
```
|
||||
ℹ tests 97 ℹ pass 97 ℹ fail 0
|
||||
Test Files 1 passed (1) Tests 20 passed (20)
|
||||
```
|
||||
|
||||
node:test 95 项(含新增 `taskStream.test.ts` 4 项、`phaseEventMapper.test.ts` 10 项)+ vitest 20 项 = **97/97 全绿**。
|
||||
|
||||
### 6.3 类型检查说明
|
||||
|
||||
`npm run build`(tsc 全量)当前存在 **33 个预存类型错误,全部位于未提交 WIP 文件**(`src/data/storage.ts`、`src/data/sessionPrivacy.ts`、`src/data/storage.test.ts`、`src/types/types.test.ts` 引用 WIP 的 `ProjectSession.userId` 等),与本轮 P2 提交的文件无关(P2 文件单独 tsc 校验零错误;该冲突在 P2 动工前的 HEAD 工作区即已存在——P1 报告仅运行 `npm test` 未含 tsc,故未暴露)。WIP 归档后即可恢复 `npm run build` 全绿。
|
||||
|
||||
## 七、测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||
|
||||
**结论:P2 后端契约与真实 Agent 链路达到交付标准;Web 浏览器视觉呈现待人工复核。**
|
||||
|
||||
- Case A 证实:真实长任务(pi-agent + MiniMax-M3,488s / 16 事件)的 phase 序列、deliverable、`done.workspace_id` 全部符合 P2 契约——PhaseEventStrip 的数据源为真实进度,无假进度。
|
||||
- Case B 证实:真实故障注入下 `event:error` 结构完整(transient/resumable 分类、workspace_id、request_id),恢复后 resume 续跑可推进到注入的动态步骤,「继续任务/重新开始」的后端能力就绪。
|
||||
- 集成测试 ×3 全过,补齐 P1 报告遗留建议 #2(transient 会话级集成测试)。
|
||||
- Web 侧:mapper/客户端共 14 项新单测 + 回归 97/97 全绿;`runTaskAttempt` 与闲聊共享同一状态机与自动重试策略。
|
||||
|
||||
**待用户人工复核(浏览器路径,本报告未覆盖)**:
|
||||
|
||||
1. 工作台发送命中模板关键词的消息(如「请帮我完成立项可行性法规评估」)→ ≤1s 出现等待气泡,PhaseEventStrip 随真实 phase 逐步点亮。
|
||||
2. 断网/停 pi-agent → 错误气泡(白话 + 诊断行 + 复制诊断 + 分型按钮);恢复后点「继续任务」续跑。
|
||||
3. 「重新开始」的确认弹窗与新工作区推进。
|
||||
|
||||
**后续优化建议:**
|
||||
|
||||
1. **五要素建模**:当前 task 触发的 customerName/intendedUse/targetMarket 取演示默认值/留空,建议后续从项目上下文 API 完整带出。
|
||||
2. **E2E 客户端超时**:真实长任务可超 10 分钟,建议测试脚本改用 `--max-time 0` 或分段验证,并考虑服务端心跳/keepalive 事件以穿透中间层空闲超时。
|
||||
3. **`step_failed` 的 mapper 收口**:CLI/后端会发出 `step_failed` phase,mapper 当前未收录(落入 ⏳ 默认);建议 P2.x 增加失败节点的红色视觉态。
|
||||
4. **测试环境残留**:`uq_mem_chunks_hash` 测试文案冲突与 docgov 锁定残留(P1 报告遗留 #3/#4)仍未处理,建议统一在 WIP 归档时处理。
|
||||
5. P3(token 级 `EmitDelta` 与打字机)按原分期推进。
|
||||
|
||||
Reference in New Issue
Block a user