Pi Agent Web 迭代任务

目标:在保留 Java v1 + React + SSE 主链路的前提下,把 web/ 演进为生产级 Agent 控制台。 原则:所有新增能力直接围绕 Java v1 + React + SSE 主链路演进,不引入独立的 Node/WebSocket 实验运行时。 状态说明(2026-08-14,按当前源码核对):[x] 已实现;[ ] 尚未完成。部分实现项拆为子项,避免将未完成的验收能力标为完成。

架构优先级(后续迭代遵循)

在引入多智能体、分布式状态、RAG/向量库、Docker/Kubernetes 沙箱或控制面之前, 优先把 单 Agent Core 契约与可观测性 做扎实。当前 pi-agent-v1 的目标是一个 可独立运行、可审计、可扩展的 ReAct runtime,而不是提前复刻完整 Harness/Service 平台。

近期的 Core 收敛边界:

迭代记录约定

每次有代码、协议或行为改动的迭代,都在 docs/iterations/ 新增一份独立 Markdown,命名为 YYYY-MM-DD-<slug>.md。记录必须包含:目标、变更文件、契约变化、验证证据、已知边界与下一步; 不以滚动更新本计划替代具体变更记录。纯讨论但未落地的方案不创建迭代记录。

当前基线

P0:主对话富渲染

验收:已在本地浏览器的 Mock 工具调用中验证 Markdown 表格、长代码块展开,以及工具引用定位并展开;流式自动滚动仅在距底部 72px 内触发。

P1:执行链路与工具体验

后端依赖:SSE 已透出 toolCallId、工具耗时 durationMs、输出行数 outputLines、超时标记 timedOutagentend 携带 status/turnCount/failureCount/durationMsturnend 携带 durationMs;历史 JSONL 已持久化 durationMs/lineCount/timedOut,刷新后可恢复展示(含超时状态)。模型失败自动重试 1 次并发出 model_retry 事件。

暂停契约:POST /api/chat/pause 接收 { sessionId } 并返回 202。它设置当前运行的协作式取消信号;AgentLoop 会在下一轮开始、模型返回后以及每个工具调用前检查该信号,追加 ABORTED 消息并以 agent_end.status=cancelled 收束运行。不会为“立即停止”强杀正在执行的模型 HTTP 请求或子进程。

P1:Token 与可观测性

验收:Token usage 已随 assistant 历史消息持久化并在刷新后返回;仍需以真实模型 API usage 对账验证。

待排期:模型推理流展示

目标:在回答正文之前展示模型实际返回的推理增量,默认折叠;它是模型流式输出的独立通道,不与最终回答文本混合。

边界:当前 deepseek-chat 通常不返回推理字段,不能把模型自行生成的 <thinking> 文本当作真实推理链;仅展示 provider 明确返回的 reasoning 数据。默认关闭展开,且不把 reasoning 纳入用户可见的最终答案或长期摘要。

P2:文件工件与结果预览

后端契约:GET /api/artifacts?sessionId=... 返回本会话成功 write/edit 或显式声明 bash.artifactsartifacts[],包含 id(即 toolCallId)、pathnamemimeTypesizecreatedAtmodifiedAtsourceToolpreviewableGET /api/artifacts/{id}/content?sessionId=... 按需返回不超过 1 MB 的内容。toolexecutionend 也会携带首个工件元数据供实时 UI 渲染。路径即使在关闭工具沙箱时也必须重新校验在 workspaceRoot 内。

P2:模型追问与选项卡片

模型需要用户补充信息时,暂停执行并发送结构化交互事件;用户选择或输入后,恢复同一次 Agent 任务。

建议 SSE 事件:

{
  "id": "ask_001",
  "type": "user_input_required",
  "title": "请选择生成方式",
  "description": "不同方式会影响输出格式。",
  "mode": "single",
  "options": [
    { "id": "web", "label": "网页报告", "description": "适合浏览和分享" },
    { "id": "markdown", "label": "Markdown 文件", "description": "适合纳入项目" },
    { "id": "both", "label": "两者都要", "recommended": true }
  ],
  "allowCustom": true,
  "submitLabel": "继续"
}

已实现接口:POST /api/chat/continue,提交 { sessionId, requestId, selections, customInput };待答表单持久化到 AgentState.pendingInteraction,提交后写入原 toolCallId 的 ToolResult,再启动下一段 AgentLoop。

后端边界:AgentLoop 通过 Suspend 结束当前 HTTP/SSE 段,/api/chat/continue 基于持久化会话启动新的执行段;因此服务重启后仍可提交待答表单,但并非恢复原 JVM 栈或同一 SSE 流。页面刷新可重新读取并展示 pending 卡片。

P3:响应式与生产化体验

P2:前端体验精修(参考 deepseek-harness)

来源:2026-08-14 对 deepseek-harness 前端代码的完整走读。以下各项均标注了参考路径和落地难度。

1. 运行统计条(StatsLine)

在输入框下方固定展示一行紧凑的运行统计,让用户随时了解 Agent 效率。

参考:packages/client/ui-conversation/src/client/chat/StatsLine.tsx 难度:小(纯前端聚合,SSE 数据已齐全)

2. 工具行 Shimmer 扫光动画

工具执行时,工具行出现一道从左到右的扫光效果,比 spinner 更优雅。

参考:packages/client/ui-tool/src/client/tool/components/ToolRow.module.css @keyframes dsh-tool-row-sweep 难度:极小(纯 CSS,零依赖)

3. 审批面板 Composer Takeover

高风险命令审批从弹窗升级为沉浸式——审批出现时替换输入框区域,用户注意力更集中。

参考:packages/client/ui-conversation/src/client/skeleton/ApprovalPanel.tsx + .module.css 难度:中(需要调整审批交互从弹窗到 composer 区域的布局切换)

4. 追问表单分页与 IME 兼容

多步追问支持分页浏览,并兼容中文输入法。

参考:packages/client/ui-user-questions/src/client/QuestionComposer.tsx 难度:中(前端交互逻辑较多,但后端 ask_user 协议无需改动)

5. 工具 IN/OUT 展开卡片

工具展开后用结构化卡片分别展示输入参数和输出结果。

参考:packages/client/ui-tool/src/client/tool/components/ToolRow.module.css .ioCard / .ioSection / .ioLabel 难度:小(纯 CSS + 少量 JSX 结构调整)

6. 模型重试实时倒计时

model_retry 事件渲染为可折叠的倒计时组件,而非静态文本。

参考:packages/client/ui-conversation/src/client/chat/MessageItem.tsx ModelRetryItem 难度:小(SSE model_retry 事件已携带 delayMs

7. 三栏布局自动折叠

窄屏自动折叠侧栏,宽屏恢复,拖拽手柄更流畅。

参考:packages/client/ui-layout/src/client/AppFrame.tsx 难度:中(需调整现有 ColumnResizeHandle 的实现)

推荐实施顺序

  1. P0 Markdown/代码渲染 + 工具卡增强。
  2. P1 执行时间轴 + 运行回执 + Token 面板。
  3. P2 模型追问选项卡片与 Agent 暂停/继续协议。
  4. P2 工件预览与详情抽屉。
  5. P3 长日志性能、迷你地图、移动端与状态恢复。
  6. P2 前端精修:统计条 → Shimmer 动画 → IN/OUT 卡片 → 重试倒计时 → 审批 Takeover → 追问分页 → 布局自动折叠。

每项任务完成标准