Files
Agent-System/.doc/system-design/2026-09-28-agent-process-streaming-and-subagent-view-design.md
T
weichen 6a461dec0c docs(system-design): 小队过程流式展示与 subagent 子页面设计
七断点打通方案(reasoning 解析→agentx 转发→Backend OnEvent 回调→squad 桥接→runner 流式→includeDetails 参数化→前端 detail 分支)+ subagent 子页面设计(hash 路由/成员卡/ThinkingBlock/ToolCallCard/过程流聚合);P0–P5 分期路线与风险边界。纯设计稿,无代码实施。

任务: subagent-process-view-design-2026-09-28
2026-09-28 06:04:14 +00:00

31 KiB
Raw Blame History

智能体工作过程流式展示与 subagent 子页面设计

版本:v1.0(设计稿,未实施) 日期:2026-09-28 任务:subagent-process-view-design-2026-09-28 作者:Cursor(owner),三路并行代码探索 + 主线调研交叉验证 交付物性质:系统设计文档,不含任何代码实施(用户明示"可以先不实现")


A. 目标与参照

A.1 用户需求

  1. 让助手的工作过程像豆包、kimi、codex 等工具一样对外输出:思考(thinking)、工具调用(tool call)、过程内容输出三类信息;
  2. 使用流式输出持续更新助手的工作过程,而非终态一次性呈现;
  3. 多智能体小队设计 subagent 子页面承载上述展示(先设计,不实现)。

A.2 参照产品的展示形态拆解

产品 思考展示 工具调用展示 增量输出 交互模式
豆包 "深度思考"折叠块:思考中呼吸态滚动,完成后折叠为一行摘要,可点击展开全文 工具卡片:图标+工具名+入参摘要+结果摘要+耗时 正文逐 token 流式 主流内嵌折叠,不打断阅读
kimi 推理过程折叠区,默认收起,进行中有进度感 搜索/工具步骤条 逐段流式 同上
codex(CLI/Web) 思考行内灰字流式 命令执行块(命令+输出+退出码) 流式 工程向,密度更高

共同设计语言:默认折叠(低认知负荷)→ 状态可感知(进行中动效)→ 按需展开(全文/详情)→ 归档后可回放。

A.3 本仓已有基座与约束

本设计不是从零发明,而是延伸既有三层信息架构(2026-09-08 streaming-events 设计,已归档但语义仍有效):

  • L1 phase/status("现在在干嘛"):已实现并持久化(task_run_events);
  • L2 summary/delta/briefing("结论是什么"):已实现(delta 为整段伪流式);
  • L3 detail("推演依据"):协议已定义(DetailPayload.category: agent_thought/tool_call/step_output/log),但无生产者、双重丢弃——本设计的主战场。

既有 UX 约束必须继承:

  1. 不打扰原则:detail 默认折叠,不默认展开思考链/工具日志(2026-09-09 chat-wait-error-retry 设计 P1 明确);
  2. 透明可追溯:详细思考与草稿不丢失,按需可查(工作区文件留存兜底);
  3. 治理红线:过程展示只是可视化,不改变验收证据边界——PI 叙事、模型输出不能冒充 PASS/测试证据(AGENTS.md 安全与证据)。

A.4 概念区分(重要)

本设计的 "subagent" 指产品内小队成员(internal/squad 的 AgentSpec:organizer/regulatory/writer/leader 等 8 标准专员 + ad_hoc 派生专员),不是本仓开发流程的工程工具 PI 子任务派发(scripts/pi_subagents.py + .doc/ops/2026-09-24-pi-subagent-executor.md)。两者名字相近但完全不同域,实施时不得混用数据源。


B. 过程事件协议扩展

B.1 现状协议回顾

internal/stream/types.go 的 StreamEvent 已定义 12 种事件类型;其中 DetailPayload:

// internal/stream/types.go:68
type DetailPayload struct {
	Category    string `json:"category"`          // agent_thought, step_output, tool_call, log
	StepID      string `json:"step_id,omitempty"` // 步骤标识 (如 tasks/01_regulatory)
	Role        string `json:"role,omitempty"`    // 对应角色
	Title       string `json:"title"`
	Content     string `json:"content"`           // 完整推演正文或技术草稿
	Collapsible bool   `json:"collapsible"`
}

现状问题:

  1. 只有 Content 全文单帧,无增量模式(思考链逐 token 需要数百帧,全文帧会放大 N 倍流量);
  2. 无 RunID 冗余(事件重放时按 run 已隔离,可省;但 live 合并需要前端从通道上下文取);
  3. 并行步骤下 Role/StepID 已有,但前端折叠键没有用它们(见 B.4)。

B.2 DetailPayload 扩展(增量模式)

// 设计草案(不实施)
type DetailPayload struct {
	Category    string `json:"category"`          // agent_thought | tool_call | step_output | log(沿用四类)
	StepID      string `json:"step_id,omitempty"` // 步骤标识(如 tasks/03_regulatory)
	Role        string `json:"role,omitempty"`    // 成员角色 key(如 regulatory)
	RunID       string `json:"run_id,omitempty"`  // 冗余 run 标识(重放通道按 run 已隔离,可选)
	Seq         int64  `json:"seq,omitempty"`     // 同一 (category, step_id, role, item_id) 流内的序号
	ItemID      string `json:"item_id,omitempty"` // 聚合项 ID:一个思考块/一次工具调用/一份产出
	// 增量模式三选一:
	Delta      string `json:"delta,omitempty"`       // 增量帧:追加到 ItemID 项
	Content    string `json:"content,omitempty"`     // 终帧/快照帧:ItemID 项的完整内容(或无增量能力的发送方)
	Done       bool   `json:"done,omitempty"`        // 终帧标记(ItemID 项结束)
	Title      string `json:"title,omitempty"`
	Collapsible bool  `json:"collapsible"`
	// tool_call 特化字段(Category == "tool_call" 时):
	Tool       *ToolCallInfo `json:"tool,omitempty"`
}

type ToolCallInfo struct {
	Name      string `json:"name"`              // write_file / read_file / grep ...
	Status    string `json:"status"`            // calling | succeeded | failed
	Input     string `json:"input,omitempty"`   // 入参摘要(JSON,可能分帧)
	Output    string `json:"output,omitempty"`  // 结果摘要(可能分帧)
	DurationMs int64 `json:"duration_ms,omitempty"`
}

增量语义(关键设计决策):

  • 发送方优先逐帧 Delta;Content 用于两类场景:(a) 无流能力的发送方(如既有 step_output 整段产出),(b) 周期性快照对账帧(防止增量丢帧后内容漂移);
  • 客户端按 (StepID, Role, Category, ItemID) 聚合:Delta 追加、Content 覆盖、Done 定稿;
  • 节流(发送方义务,见 F.1):逐 token 事件合并为 ~100–300ms 批帧,单帧上限(如 8KB)超限分帧。

B.3 llm 层事件扩展(reasoning 链)

internal/agentx/llm 现有事件只有 text_delta/tool_call_delta/message_end/error。OpenAI 兼容国产模型(MiniMax/DeepSeek 系)在 delta 中返回 reasoning_content 字段(思考链)。扩展:

// internal/agentx/llm/types.go 设计草案
const (
	KindTextDelta       EventKind = "text_delta"
	KindReasoningDelta  EventKind = "reasoning_delta" // 新增
	KindToolCallDelta   EventKind = "tool_call_delta"
	...
)

type Event struct {
	Kind           EventKind
	Text           string
	ReasoningDelta string // 新增:reasoning_content 增量
	...
}

ParseSSE(internal/agentx/llm/sse.go)补充解析 delta.reasoning_content → KindReasoningDelta。

容器侧对应:deploy/pi-agent/runner.mjs:24 的 stripThinkBlocks 目前把 <think>…</think> 块从产物中剥离(丢弃)。设计上不改变"正文不含 think"的既有约定,而是在剥离前把 think 块作为 reasoning 事件分流转发(见 C.5)。

B.4 并行步骤归属路由(修复既有缺陷)

现状 PhasePayload 折叠键为 phase_id#step_index(taskrun/phase_snapshot.go:225 与 web/src/engine/phaseEventMapper.ts:65),并行组成员(多个 role 同时 step_executing)若无 step_index 会互相覆盖/误折叠。

设计:折叠键升级为 phase_id#step_index@role(role 存在时必选参与键)。前端 applyPhaseEvent 与后端 FoldPhaseEvents 同步修改,语义保持对齐(两处折叠逻辑本就要求一致,见 phase_snapshot.go 注释)。detail 事件天然带 role/step_id,聚合不受影响。

B.5 通道选择:沿用 stream/StreamEvent,不新增域事件

理由:

  1. events.StaticContracts(internal/events/registry.go)是静态 allowlist(13 种业务事件 + YAML parity 测试),加 agent 过程事件需改 Go+YAML+测试三处,且域事件总线面向业务投影(通知/阶段推进),不面向用户实时流;
  2. task_run_events(JSONB payload)+ after_seq 重放已为过程事件准备好持久化与断线恢复;
  3. chat SSE 通道(链路 A)无持久化,但 B 轨参谋对话的过程展示可接受"live-only + 终态落库"(chat_turns 记录终态),与现状一致。

C. 后端链路打通设计(七断点逐条)

C.0 总览

【链路 B:长任务(本设计主通道,先做)】
worker goroutine (taskrun.Manager.runWorker)
  └ bizdev.ExecuteTask → squad.WorkflowDriver.runOneStep
      └ agent.Backend.Execute(OnEvent 回调)          ← 断点 4
          ├ pi-native: agentx.Run(OnEvent)           ← 断点 2/3(直通)
          │    └ llm.Stream: reasoning/text/tool delta ← 断点 1
          └ pi-agent: POST runner /v1/run/stream      ← 断点 5(NDJSON 流)
  └ Streamer.EmitDetail(category, stepID, role, ...)  ← 断点 6 之前:detail 已能出 Streamer
      └ Manager.appendEvent → task_run_events (seq)   ← 持久化(已就绪)
          └ SSE: GET /tasks/runs/{id}/events          ← 透传(已就绪)
              └ 前端 detail 渲染分支                   ← 断点 7

【链路 A:聊天 SSE(P4 再做)】
chat.Session → coordinator.generateDialogueReply
  └ dialogueBackend.Execute(OnEvent) → token 级 EmitDelta(替换一次性 EmitDelta 伪流式)

C.1 断点 1:reasoning_content 解析

  • 文件:internal/agentx/llm/sse.go(sseChoice.Delta 增 ReasoningContent string \json:"reasoning_content"`)、[internal/agentx/llm/types.go](../../../internal/agentx/llm/types.go)(Event增ReasoningDelta+KindReasoningDelta`)
  • 语义:reasoning 与 text 分开累计;message_end 的 Message.Content 仍只含 text(与 runner stripThinkBlocks 语义对齐:正文无 think)。
  • 兼容性:无 reasoning 的模型零行为变化(字段空即无事件)。

C.2 断点 2:agentx 循环转发 tool_call_delta

  • 文件:internal/agentx/loop.go:178-183 consume():case llm.KindToolCallDelta 增加转发(emit 一个 tool_call_delta Event,带 ToolName/ArgumentsDelta);KindReasoningDelta 同理转发 reasoning_delta。
  • agentx.Event(loop.go:19)增字段:Kind string(llm 层事件细分)或复用 Type 前缀约定(text_delta/reasoning_delta/tool_call_delta/tool_execution_start/tool_execution_end…已够用,推荐复用 Type)。

C.3 断点 3:Backend 接口过程回调

设计取舍:不改 Backend.Execute 签名(改签名会波及 stub/fallback/limiter 全部实现与调用方),改为 Request 增可选回调:

// internal/agent/backend.go 设计草案
type Request struct {
	...
	// OnEvent 过程事件回调(nil = 不需要过程,行为与现状完全一致)
	// 实现方必须保证:回调在 Execute 返回前完成(同步或等待收尾),无泄漏 goroutine。
	OnEvent func(ev ProcessEvent)
}

// ProcessEvent 对 agentx.Event 与 runner NDJSON 事件的统一投影
type ProcessEvent struct {
	Type    string // text_delta | reasoning_delta | tool_call_delta | tool_execution_start | tool_execution_end | message_end | ...
	Text    string // delta 文本(Type 为 *delta 时)
	Tool    string // 工具名
	Detail  string // 工具参数/结果摘要(tool_execution_* 时)
}
  • pi-native(internal/agent/pinative.go):agentx.Run 的 OnEvent 双写——既拼 execution.log(现状保留),又投影到 req.OnEvent;
  • WrapWithConcurrencyLimit 透传 Request 即透传回调,无需改;
  • fallback/stub:fallback 转发 primary 的回调事件即可;stub 无事件。

C.4 断点 4:squad 层桥接 Streamer

internal/squad/workflow.go runOneStep(构造 agent.Request 处,约 :543):

// 设计草案
if req.OnEvent == nil {
	req.OnEvent = func(ev agent.ProcessEvent) { /* 仅拼日志(现状) */ }
}
// 注入流式桥(streamer 可为 nil,安全)
req.OnEvent = chainEvents(req.OnEvent, squadDetailBridge(streamer, step))

squadDetailBridge(新函数,~40 行)把 ProcessEvent 翻译为 EmitDetail:

agentx/runner 事件 EmitDetail 映射
reasoning_delta category=agent_thought, delta 帧, ItemID=step.ID#thought<N>
text_delta category=agent_thought 的草稿子项(或 P2 可选:不改,任务正文走终态 step_output,避免与最终 deliverable 重复)→ 推荐 P0 不发 text_delta,仅 reasoning + tool
tool_call_delta category=tool_call, delta 帧(入参增量), ItemID=step.ID#tool<N>
tool_execution_start tool_call 项 status=calling 帧
tool_execution_end tool_call 项 status=succeeded/failed + Output 摘要 + duration 帧
message_end 对应 ItemID 的 done 帧

节流在 bridge 内做(100–300ms 合并、8KB 分帧、工具输出截断 N KB——工具结果可能巨大,只发首尾摘要)。

B 轨参谋(链路 A,P4):internal/coordinator/dialogue_llm.go generateDialogueReply 同样注入 OnEvent,把 text_delta 逐帧转 streamer.EmitDelta(替换 internal/chat/session.go:624 一次性 EmitDelta(NaturalReply)),reasoning_delta 转 EmitDetail(agent_thought)。注意 chat SSE 无持久化——断线后靠 chat_turns 终态查询(现状机制),过程帧不可恢复(可接受,与豆包/kimi 行为一致)。

C.5 断点 5:远程 runner 流式端点

deploy/pi-agent/runner.mjs 增加端点(不动 /v1/run 既有契约——ResultV1 typed 校验、wire 预算 8MB 等全部保留):

POST /v1/run/stream   Content-Type: application/x-ndjson(chunked)
请求体 = /v1/run 同构 + stream: true
响应 = 逐行 NDJSON:
  {"type":"proc","ev":{...pi RPC stdout 事件行...}}    // 过程事件(原样转发已解析的行)
  ...(执行中持续 flush)
  {"type":"result","body":{...与 /v1/run 完全相同的 typed 结果...}}  // 终帧
  • runner 侧改动集中在 handleRun:stdout on("data") 里既有的逐行 JSON 解析循环(runner.mjs:365-388)旁路一个转发器;stripThinkBlocks 之前先把 think 块作为 {"type":"reasoning_delta"} proc 帧发出;
  • Go 侧 internal/agent/piagent.go executeRemote:req.OnEvent != nil 时改走流式端点——http.Client 无总超时的流式变体(Timeout 由 ctx 控制),bufio 逐行读:proc 帧 → req.OnEvent 投影;result 帧 → 走既有 DecodeRemoteRun 校验路径(终态校验逻辑零改动,保证治理边界不放松);
  • 降级:流式端点 404(旧 runner)→ 回落 /v1/run 一次性调用 + 无过程事件(capabilities 探测亦可,但 404 回落更简单)。

C.6 断点 6:includeDetails 参数化

两处硬编码/丢弃:

  1. internal/taskrun/manager.go:364 stream.WithListener(ctx, false, listener)——worker 侧 Streamer 是否发 detail 由创建时决定。设计:CreateAndStart/StartClaimedRun 透传 IncludeDetails(RunRecord 或 CreateOptions 增字段;默认 false 保持现状)。注意:detail 是否持久化与是否推送解耦——建议 detail 一律持久化(task_run_events JSONB 足够),includeDetails 只控制 live 推送与 chat 通道;重放端点用 query include_details=0 过滤,历史 subagent 页面仍可全量回放。取舍理由:持久化膨胀可控(F.1 节流后估算),而"刷新后过程消失"违背透明可追溯原则;
  2. internal/server/sse.go:476 _ = includeDetails——StreamChatRequest.IncludeDetails 已存在(chat 通道)但 task 通道读取后丢弃;接线到 worker 创建。

C.7 断点 7:前端协议与分支

  • web/src/api/chatStream.ts StreamEvent 增 detail?: { category?: string; step_id?: string; role?: string; item_id?: string; seq?: number; delta?: string; content?: string; done?: boolean; title?: string; collapsible?: boolean; tool?: { name?: string; status?: string; input?: string; output?: string; duration_ms?: number } };
  • web/src/hooks/useTaskRunAttempts.ts onEvent switch 增 case 'detail':聚合进消息的 processItems(见 D.4),不进正文。

D. subagent 子页面设计(重点)

D.1 入口与路由

决策:扩展 hash 路由,不引入 react-router。

理由:

方案 优点 代价
A. hash 扩展(推荐) 复用 web/src/engine/anchorNavigator.ts 既有解析/监听先例(#/s/{sessionId}/m/{messageId});零新依赖;与"工作台为唯一应用视图"的产品形态匹配 顶层 view 状态机要加一态;深链参数解析手写
B. react-router 标准 新依赖 + 全仓路由心智迁移;App.tsx 1758 行重构风险;本需求只有一个子页面,收益不成比例

路由形态:

#/s/{sessionId}/run/{runId}/agents            → 子页面(默认选中当前 running/最近完成成员)
#/s/{sessionId}/run/{runId}/agents/{roleId}   → 子页面(选中指定成员)

实现骨架(设计草案):

  • App.tsx 顶层 view 状态机扩展:'workbench' | 'admin' | 'subagents';parseAnchorHash 扩展识别 /run/{runId}/agents 段;
  • handleSubagentsView 由 TaskRunCard 的"查看小队过程"入口触发(写 hash 触发 hashchange,与消息深链同一机制);
  • 子页面经 OnDemand(web/src/components/lazy/OnDemand.tsx)懒加载,props 最小化:仅 runId、sessionId、onExit(返回 workbench)——规避 App.tsx props 钻透:子页面自持 SSE 订阅与状态,不依赖父容器消息流。

D.2 页面信息架构

┌──────────────────────────────────────────────────────────────┐
│ 小队过程 · {任务标题} · run:{runId 截断}      [刷新] [返回对话] │
├──────────────┬───────────────────────────────────────────────┤
│ 成员卡列表    │  选中成员(role: regulatory 法规专员)           │
│              │  ┌ 状态条: ⚙️ 正在执行 · 已用 2m13s · 第2/5步 ┐  │
│ 👤 organizer │  └──────────────────────────────────────────┘  │
│   ✅ 完成 1m2s│  ┌ ThinkingBlock(默认折叠)──────────────┐     │
│ 👤 searcher  │  │ 💭 思考中…(流式增量,呼吸态)          │     │
│   ✅ 完成 3m4s│  └───────────────────────────────────────┘     │
│ 👤 regulatory│  ┌ ToolCallCard ─────────────────────────┐      │
│   ⚙️ 执行中   │  │ 🔧 read_file tasks/01.../output.md    │      │
│ 👤 writer    │  │   ✅ 213ms · 12KB                     │      │
│   ⏳ 等待     │  └──────────────────────────────────────┘      │
│ 👤 leader    │  ┌ ThinkingBlock(第二段)──────────────┐       │
│   ⏳ 等待     │  ...                                    │       │
│              │  ┌ 步骤产出 output.md(终态 step_output)┐      │
│ [成员详情]    │  │ 📄 阶段性产出(可展开全文)           │      │
│              │  └──────────────────────────────────────┘      │
├──────────────┴───────────────────────────────────────────────┤
│ 事件时间线(跨成员 phase 事件折叠,复用 applyPhaseEvent 语义)   │
└──────────────────────────────────────────────────────────────┘
  • 左栏数据源:squad.json(成员编制:role/Name/Responsibilities——工作区文件,经新只读端点或既有 workspace API 投影)+ phase/detail 事件聚合出状态与耗时(step_executing/step_completed/step_failed by role);
  • 右栏数据源:detail 事件流按 role == selected 过滤,聚合为 processItems(见 D.4);
  • 底部时间线:全量 phase 事件(复用 TaskRunCard 折叠逻辑与 PhaseEvent 类型)。

D.3 三类过程组件

组件 状态 内容来源 交互
ThinkingBlock thinking(呼吸态+增量滚动)/ done(折叠为一行摘要:前 40 字 + 字数)/ error agent_thought 增量帧聚合 默认折叠;点击展开全文(Markdown 渲染);进行中可"实时展开"
ToolCallCard calling(转轮)/ succeeded(绿勾+耗时)/ failed(红叹+错误摘要) tool_call 帧聚合(name/input/output/status/duration) 入参/结果默认摘要(N KB 截断),点击展开 JSON/代码(需补块级渲染,见 D.6)
ProcessStepOutput done 既有 step_output 整段 content 默认折叠,展开渲染 Markdown

组件均为纯展示组件(props 驱动),放置 web/src/components/subagents/,配 node:test 静态断言测试(与仓内组件测试形态一致)。

D.4 前端数据流与状态

// 设计草案:web/src/hooks/useRunProcessStream.ts
type ProcessItem = {
  key: string;          // `${stepId}/${itemId}`
  category: 'agent_thought' | 'tool_call' | 'step_output' | 'log';
  role: string;
  stepId: string;
  title?: string;
  text: string;         // delta 累计或 content 快照
  status: 'streaming' | 'done' | 'error';
  tool?: { name: string; status: string; input?: string; output?: string; durationMs?: number };
  updatedAt: number;
};

function applyDetailEvent(items: Record<string, ProcessItem>, ev: DetailEvent): Record<string, ProcessItem>
// 纯函数(与 applyPhaseEvent 同风格,node:test 可测):
//   delta → 追加 text、status=streaming
//   content → 覆盖 text(快照对账)
//   done → status=done
//   tool 帧 → 合并 tool 字段

订阅策略(复用既有骨架):

  • live:subscribeTaskRunEvents(runId, { onEvent, afterSeq })(web/src/api/taskRuns.ts:113),事件分发:phase → 左栏状态 + 底部时间线;detail → applyDetailEvent;
  • 刷新/进入已结束 run:先 readTaskRunSSEOnce(runId, 0, …, { replayOnly: true }) 全量重放(事件已持久化,见 C.6),再决定是否续订 live;
  • 超量回放保护:after_seq 分页重放(既有能力),detail 大 run 可先只重放 phase + 按需拉成员 detail(GET /runs/{id}/events?role=xxx&include_details=1 过滤参数为可选增强,P2.5)。

D.5 与主对话气泡的联动

  • 气泡内 TaskRunCard 保持现状(L1 时间线),新增两处:
    1. 运行中:卡片底部一行"查看小队过程 →"(写 hash 深链进子页面);
    2. 完成后:L3 摘要条(如"5 名专员 · 12 次工具调用 · 3 段推演"),点击同样进子页面;
  • 不把全量 detail 渲染进气泡——认知负荷原则:气泡只承载 L1/L2,L3 全文在子页面。MessageBubble 现有 message.phaseEvents 通道不动。

D.6 前端基础设施缺口补齐(实施前置)

  1. 块级 Markdown + 代码高亮:现状 renderInlineMarkdown 仅行内语法(web/src/engine/renderMarkdown.ts)。工具调用入参/结果需要 fenced code block + JSON 高亮。方案:renderMarkdown 增块级分支(heading/fence/list——解析器自研或引入 marked/react-markdown;倾向自研轻量扩展,避免新依赖与 XSS 面扩大,沿用现有 escape 纪律);
  2. 高频增量渲染性能:processItems 更新频率受后端节流(100–300ms 批帧)保护;子页面右栏不用虚拟列表(成员过程项数量级 ~10²),但聚合函数必须纯函数 + React.memo 行项;
  3. 通用折叠组件:自研 CollapsibleBlock(受控 open + Summary 插槽),三组件复用。

D.7 后端配套只读端点(成员编制)

子页面左栏需要 squad.json。设计(P2 内最小):

GET /api/v1/tasks/runs/{run_id}/squad
→ { members: [{ role, name, responsibilities, source }], task_key, title }

实现:taskrun Manager 读 record.WorkspaceID → 工作区 squad.json 反序列化(只读投影,权限沿用 run 事件的 RequireProjectAccess)。


E. 分期实施路线

期 内容 验证形态(按验证契约) 依赖
P0 协议+直通 B.2/B.3 协议扩展;C.1–C.4(pi-native 全链路);C.6 includeDetails 参数化 + detail 持久化;前端 chatStream 类型 + detail 分支(仅聚合不渲染) Go 单测(llm sse_test 增 reasoning case、squad bridge 节流测试);stub/pi-native E2E;真实 PI Runner(dev 环境)冒烟 无
P1 气泡过程块 D.5 气泡内 L3 摘要 + 折叠 ThinkingBlock(最小渲染,块级 markdown 只做 fence+JSON) 前端 node:test + 真实浏览器验收(交互变更) P0
P2 subagent 子页面 D.1–D.7 全量(hash 路由、成员卡、过程流、squad 端点) 前端单测 + Playwright spec(新增 e2e/subagents-view.spec.ts)+ 真实 PI 全链路验收 P1
P2.5(可选) 事件过滤参数(role/include_details query);大 run 分页回放 接口测试 P2
P3 runner 流式 C.5 /v1/run/stream + piagent 流式消费 + 404 回落 runner 单测(mjs 侧 settle-policy 风格)+ 真实容器验收(dev) P0(可并行)
P4 B 轨参谋 C.4 尾段:dialogue_llm OnEvent → token 级 EmitDelta + reasoning detail 真实 PI 对话链路验收(chat SSE) P0
P5(演进) 历史回放端点(transcript.jsonl/execution.log 投影 API);per-member 持久化表(如 task_run_step_events,仅当 JSONB 容量实测超限) 视实测 P2/P3

每期独立合入(Conventional Commits:feat(stream)/feat(web) 前后端分原子提交),P0 即可演示 pi-native 路径的"思考流+工具卡"。


F. 风险与边界

F.1 事件量与持久化膨胀

  • 节流义务在 bridge(C.4):reasoning/text delta 合并 100–300ms 批帧;单帧 ≤8KB;工具输出截断(如 4KB 首部 + 省略标记);step_output 保持现状单帧(终态产物已有 deliverable 通道与文档存储,不重复全文持久化时可评估只存摘要——P0 先全文,P2.5 实测再收紧);
  • 容量估算:单 run 5 成员 × (1 思考块 2–8KB + 6 工具调用 × 1KB 摘要) ≈ 50KB/run 量级(节流后),task_run_events JSONB 单行无硬限,可控;
  • 兜底:execution.log/transcript.jsonl 仍全量落盘工作区(现状不变),P5 投影 API 作历史回放。

F.2 认知负荷与安全

  • detail 默认折叠贯穿三处(气泡摘要、子页面 ThinkingBlock、ToolCallCard 入参/结果摘要)——继承 2026-09-09 设计 P1 约束;
  • 脱敏:bridge 层过滤——不透传 prompt/AGENTS.md 全文(这些是任务输入不是过程);工具入参只发 workspace 相对路径相关字段;配置/密钥字段(provider/key)永不进事件(ProcessEvent 设计上就不含这些来源);
  • 治理红线:过程事件不写 events 域总线、不参与验收判定;测试/审查结论仍以报告路径与 receipt 为准(pi-subagents 规则),本设计纯可视化。

F.3 兼容与降级

  • 旧 runner(无 /v1/run/stream)→ 404 回落一次性调用,功能退化为 L1 时间线(现状);
  • OnEvent == nil → 所有后端行为与现状完全一致(回调是纯增量);
  • 前端不识别 detail(旧前端 + 新后端)→ JSON 字段忽略,无破坏;
  • includeDetails 默认 false → chat 通道现状不变。

F.4 并行正确性

  • 并行组多成员同时发 detail:Streamer.Emit 已有 RWMutex;bridge 无共享状态(每 step 独立闭包);
  • 前端聚合键含 role/stepID(B.4),并行不串扰;
  • step_output 终态帧在并行完成回调内发出(现状 workflow.go:746 位置不变)。

F.5 明确不做

  • 不引入 WebSocket(SSE + after_seq 重放已覆盖需求);
  • 不做过程事件的域总线登记(B.5 理由);
  • 不做 B 轨 chat 过程帧的持久化与断线重放(P4 live-only,终态落库现状);
  • 本期不实现任何代码(用户授权边界)。

G. 附录:断点-文件-方案对照表

# 断点 文件:行 方案章节
1 reasoning_content 未解析 internal/agentx/llm/sse.go、types.go C.1 / B.3
2 agentx 丢弃 tool_call_delta internal/agentx/loop.go:178-183 C.2
3 Backend 黑盒无回调 internal/agent/backend.go:38、pinative.go:102 C.3
4 squad 层无成员过程流 internal/squad/workflow.go:543(runOneStep) C.4
5 runner /v1/run 一次性 deploy/pi-agent/runner.mjs:600 C.5
6 includeDetails 硬编码 false / 丢弃 internal/taskrun/manager.go:364、server/sse.go:476 C.6
7 前端无 detail 协议与分支 web/src/api/chatStream.ts、hooks/useTaskRunAttempts.ts:474 C.7

H. 参考文件

  • 三层信息架构基座:.doc/archive/design/superpowers/specs/2026-09-08-streaming-events-and-digestible-ux-design.md
  • 认知负荷约束:.doc/archive/design/superpowers/specs/2026-09-09-chat-wait-error-retry-ux-design.md
  • 小队编排(含 §18.5 演进方向):.doc/system-design/agent-squad/02-智能体小队编排-设计-思路-架构-实现-难点-解决方案-亮点与优势.md
  • 长任务 resume:.doc/archive/design/superpowers/specs/2026-09-09-longtask-phase-live-resume-ux-design.md
  • 任务记录:.doc/ops/short-term/2026-09-28-subagent-process-view-design.md