Files

123 lines
8.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Context
公开 Chat 仍有同步 `/api/chat` 和伪流式 `/api/chat_stream` 两条旧 `ChatService` 链路;Controller 直接读取 Session 历史、获取模型/Tools、选择策略并维护 cached thread pool。阶段 6A 已完成 protocol-neutral `ChatApplicationUseCase`、observer 和 `ChatRunControl`,但阶段 2-6A 的 plain Java components 尚未形成生产 Bean graph。前端同时保留 quick JSON 与 stream SSE 两套消费者。
本 change 是 L4 破坏性协议迁移。ISS-014 已冻结 event schema、无兼容分支和阶段 7 清理边界;本阶段必须原子切换 Controller、生产装配与前端,不能出现旧路径绕过 release boundary。
## Goals / Non-Goals
**Goals:**
- 唯一 `POST /api/chat` 返回 named SSE events,固定顺序和互斥规则。
- Controller 只负责校验、SSE/HTTP 和连接生命周期,业务执行只调用 `ChatApplicationUseCase`。
- 使用同一个 Run control 处理断开/timeout/send failure,并禁止断开后的终态发送。
- 生产装配阶段 2-6A 的 Core、Tools、Agent、Guards、executors、store 和 use case。
- 使用集中配置与 Spring-managed bounded executors。
- 前端唯一 consumer 严格消费新契约并保存 exact session/run IDs。
**Non-Goals:**
- 不做 Token streaming 或最终内容切片。
- 不修改 `/api/ai_ops` 的 URL、请求或事件协议。
- 不物理删除旧多 Agent、ChatService、Hooks 或 ThreadLocal;阶段 7 处理。
- 不做断线续传、event replay、WebSocket、轮询或 live E2E。
## Decisions
### 1. Named SSE event 是唯一协议层类型
新增 immutable SSE payload:`metadata(session_id,run_id)`、`status(code,message)`、`content(content_type,payload)`、`failure(code,message)`、`done(outcome)`。`SseEmitter.event().name(type).data(payload)` 直接使用业务 event name,不再发送旧 `message + {type,data}` 包装。
替代继续用统一 `message` event;拒绝,因为它允许 type 漂移并让浏览器保留旧兼容分支。`content.payload` 直接引用阶段 6A sealed typed content,禁止 Map/Object 临时协议。
### 2. 每个连接使用显式 first-terminal-wins SSE session
新增 protocol adapter 持有 emitter、`AtomicReference<ChatRunControl>`、disconnect flag、metadata flag 和 terminal flag。`onStarted` 固定发送 metadata;若 disconnect 已先发生则立即取消新得到的 Run control。status 只能在 metadata 后、terminal 前发送。success 原子发送 content + done 后 complete;failure 原子发送 failure + done 后 complete。
`onTimeout`、`onError`、异常 send 和非正常 `onCompletion` 只标记 disconnected 并调用 `cancelClientDisconnect()`;不尝试补发 failure/done。正常 complete 在回调前标记 terminal,避免把成功误判为断开。替代仅在 IOException catch 取消;拒绝,因为 timeout 和 disconnect-before-onStarted 会漏掉。
### 3. 请求接纳失败不进入 SSE 事件序列
空 query 在创建 emitter/Run 前返回 HTTP 400;bounded worker rejection 在创建 Run 前返回 HTTP 503。五事件顺序只适用于已接纳并成功持久化 Run 的请求,因而 metadata 始终是第一个 SSE event。Session ID 格式错误由 use case 形成有 Run 的稳定 failure,除非它在 startRun 前失败;Controller 只做 query shape 校验。
### 4. Chat Controller 不拥有模型、Tools、Session 或业务执行
删除同步 Chat 方法、`/chat_stream`、ChatResponse、历史 helper、内容切片和 Chat 使用的 `ChatService/SessionManager/ChatModel/ToolCallbackProvider`。Controller 构造注入 `ChatApplicationUseCase` 和 qualifier Chat TaskExecutor。
同一类中的 `/api/ai_ops` 仍需旧模型/Tools。为保持其公开协议不变又清理 Controller 依赖,向 `AiOpsService` 增加内部 overload,由 service 注入 ChatModel/ToolCallbackProvider 并调用既有执行方法;Controller 只传 request/session/run。替代本阶段迁移 AiOps 到新 use case;拒绝,因为超出阶段 6B。
### 5. Production assembly 使用一个显式 Bean graph
新增 `HarnessChatConfiguration`,按依赖顺序装配:
```text
properties/Clock/executors
-> DiagnosisHarnessCore + HarnessRetryExecutor + GuardModelCall
-> RedisCanonicalInvocationStore + ToolBoundary
-> RAG/log/MySQL adapters + HarnessEvidenceTools
-> DiagnosisAgentFactory/UseCase
-> EvidenceGuard/Repair + SemanticGuard + ReleaseUseCase
-> Router + System/Knowledge/Diagnosis executors
-> ChatApplicationUseCase(JpaChatRunStore)
```
所有模型入口复用同一 Spring `ChatModel` 和 model executor。RAG/log adapters 复用现有 `LookupKnowledgeTool`/`QueryLogsTools` legacy executors,但所有 Agent-facing output 仍经过 ToolBoundary projection。MySQL 从 `MysqlToolProperties` 生成独立 datasource map;空配置时 Tool 仍注册但 fail closed 为 unknown datasource。
### 6. 并发与预算全部集中配置
新增 `harness.chat` configuration properties,覆盖 Chat worker/model pool、Run deadline/budget、canonical TTL/bytes、Agent/Router/Guard/single-turn/Knowledge limits 和 key prefix。worker 与 model executor 均使用 bounded `ThreadPoolExecutor + AbortPolicy`,由 Spring 负责 shutdown;Controller 不持有或创建 executor。
Retry attempt audit 首版写结构化 debug log,不新增持久化表。替代硬编码构造参数;拒绝,因为阶段 0 已要求上限集中可校准。
### 7. 前端删除 quick/stream 双轨
移除 mode selector DOM、`currentMode`、`sendQuickMessage` 和旧 `/chat_stream` parser。`sendMessage` 只调用 `sendChatMessage`,POST `/api/chat` 并按空行分隔完整 SSE frame,解析 named event 与 JSON payload。metadata 更新 trace target;status 更新等待说明;content 一次渲染 typed payload;failure 显示安全消息;done 校验 outcome 并结束。
typed payload 使用专用 renderer 转换 SYSTEM_CHAT、KNOWLEDGE、DIAGNOSIS_REPORT、FALLBACK,不把对象隐式拼接为字符串。unknown event、重复 metadata/content/failure/done、content/failure 同时出现或流结束前无 done 均 fail closed。
## Module and Ownership Audit
```text
Browser fetch POST /api/chat
-> ChatController (validation + connection admission)
-> ChatSseSession (event order + connection cancellation)
-> bounded chat worker
-> ChatApplicationUseCase (session/run/routing/dispatch/terminal persistence)
-> Harness Core / Agent / Tools / Guards
-> typed content or stable failure
-> ChatSseSession (single content|failure + done)
```
- Controller owns HTTP/SSE only;SSE session owns event state;Application owns business Run;Core owns cancellation/budget;Store owns MySQL;Harness configuration owns infrastructure graph。
- 数据真理源仍为 `diagnosis_run`;SSE metadata 只透出同一 use case 生成的 IDs。
- 最大生命周期风险是 disconnect 与 onStarted 竞态;atomic control + pending disconnect 解决。
- 最大部署风险是 L4 前后端不同步;Controller/前端必须同一 commit 原子部署,回滚也必须成对。
- 无 ADR 冲突;ISS-013 的 token-like “incremental content”被更新的 ISS-014 明确收敛为实时 status + 单次安全 content。
## Interface Impact
- Level: L4 breaking HTTP/frontend contract。
- Removed: synchronous JSON `POST /api/chat`、`POST /api/chat_stream`、旧 `ChatResponse` 和 message-wrapper Chat SSE。
- Added: SSE `POST /api/chat` with five named events and exact snake_case payload fields。
- Consumers: bundled `static/app.js`、外部 Chat API callers、Controller tests/MockMvc;Trace/feedback continue using metadata IDs。
## Risks / Trade-offs
- [客户端仍调用旧 endpoint] -> 同 commit 迁移 bundled frontend;外部消费者必须按 L4 文档迁移,不提供兼容层。
- [发送中断后 worker 继续] -> every send failure/timeout/error cancels exact Run control;late result blocked by Core。
- [executor 饱和] -> bounded queue + HTTP 503 before Run;不 caller-runs、不无界排队。
- [Spring graph 缺 Bean/歧义] -> focused configuration context test + Maven compile。
- [legacy Tool side effects] -> Agent-facing output 仍经新 boundary;legacy classes stage 7 physically removed。
- [typed diagnosis payload frontend rendering复杂] -> deterministic renderer and JS contract tests/static assertions。
## Migration Plan
1. 部署前确保 V012 已迁移,Redis 与模型配置可用。
2. 同一版本注册 Harness Bean graph、切换 `/api/chat` SSE、删除 `/chat_stream` 并更新 bundled frontend。
3. smoke/focused tests 验证 metadata/status/content|failure/done、same IDs 和 cancellation;live E2E 延后阶段 7。
4. 回滚必须同时回滚 Controller 与 frontend 到阶段 6A commit;数据库 V012 nullable additive 可保留。
## Open Questions
- None。初始数值为可配置默认值,阶段 7 根据 live trace 校准。