8.6 KiB
Context
阶段 2-5 已形成内部 Harness 核心:RunContext、canonical evidence tools、唯一 Diagnosis Agent、EvidenceGuard/SemanticGuard 和 release policy。当前生产 Chat 仍由旧 ChatService/Controller 创建 Run、选择 Agent、维护历史并生成答案,新的组件没有统一入口,也没有 intent/release_outcome/published_result 持久化,阶段 6B 无法只做协议切换。
阶段 6A 建立同步的内部 application use case 和 typed output。阶段 6B 将在受控任务执行器中调用它并把 observer 转成 SSE;本阶段不触碰 HTTP、前端或旧公开行为。
Goals / Non-Goals
Goals:
- 应用用例拥有 session/run 创建、路由、固定 executor dispatch、最终终态和安全持久化。
- Router 只看最小输入并按 Harness policy 进行最多两次技术 attempt。
- SYSTEM_CHAT、KNOWLEDGE_QUERY、DIAGNOSIS 三条路径接收同一个未改写 Query,能力严格隔离。
- 从 MySQL
diagnosis_run确定性读取有界 PreviousTurn。 - 提供 protocol-neutral observer/run control,使阶段 6B 能发送 metadata/status 和处理断开取消。
- 新增可前向迁移、可回滚的 L3 数据库契约。
Non-Goals:
- 不修改 Controller、SSE endpoint、前端消费者和 HTTP DTO。
- 不删除旧 ChatService、多 Agent、ThreadLocal 或旧 session storage。
- 不实现 token streaming、事件 schema 或 Controller 线程池。
- 不执行 live E2E;阶段 7 统一启动项目验证。
Decisions
1. Application Use Case 是唯一业务入口
ChatApplicationUseCase.execute(request, observer) 固定流程:
resolve session
-> read prior safe routing/diagnosis context
-> start one RunContext and persist RUNNING
-> observer.onStarted(run control)
-> IntentRouter
-> persist intent
-> fixed executor
-> complete Harness lifecycle
-> persist release outcome/public content/published result
-> typed ChatApplicationResult
它通过构造器接收 Router、三个 executor、Run store、Core 和 session ID supplier,不接收 Controller 注入的 ChatModel/Tool。Controller 在阶段 6B 只调用这一接口。
读取 prior context 必须早于当前 Run 持久化,避免 latest query 命中当前 PENDING/RUNNING 行。替代在 Controller 中先查历史;拒绝,因为会拆散生命周期所有权并制造 session/run 竞态。
2. Protocol-neutral observer 与取消控制
Observer 只接收 ChatRunControl(sessionId, runId, cancelClientDisconnect) 和固定 ChatApplicationStatus,不依赖 Spring MVC/SSE 类型。应用用例先成功写入 Run,再发送 started;阶段 6B 将 started 映射为 metadata,将 status 映射为安全状态事件。
Run control 只暴露客户端断开取消,不暴露 Core/RunContext。Observer 异常按执行失败处理并进入 Run 终态,避免协议层静默丢失连接后继续运行。
3. Router 是直接单轮模型调用
IntentRouterInput 只含原始 query、可选 last intent 和 last user query。Router 使用新的 system prompt 和共享 GuardModelCall,无 Tool/记忆/ReactAgent;严格解析唯一字段 intent。timeout/transport/任何非法输出映射到 HarnessRetryPolicies.intentRouter() 支持的失败类型,最多两次使用同一 JSON 输入。
两次失败抛出稳定 ChatApplicationException(ROUTING_UNAVAILABLE);不得默认 DIAGNOSIS。取消/预算耗尽继续传播为 Run 终态。
4. 固定执行器与 typed public content
- SYSTEM_CHAT:一次
GuardModelCall,system prompt 只含受控产品能力说明;不接历史、Tool 或 evidence guard。 - KNOWLEDGE_QUERY:生成当前 direct invocation 的唯一 ID,调用
HarnessEvidenceTools.lookup_knowledge一次。NO_EVIDENCE返回固定安全说明;正证据只调用模型一次并严格解析KnowledgeAnswerDraft,校验 exact call ID 和 document ID subset,再发布移除 Tool ID 的KnowledgeContent。 - DIAGNOSIS:调用
DiagnosisAgentUseCase(query, previousTurn),再调用DiagnosisReleaseUseCase。SUCCESS 发布SemanticDraftView + verified sources,Fallback 只发布SafeFallback。
ChatApplicationResult 使用 ChatContentType 与 sealed typed content,不使用 Map/Object payload。阶段 6B 可直接序列化 content;Diagnosis public view 不含 Tool Call ID 或完整 evidence snapshot。
5. PreviousTurn policy 同时负责写入和读取边界
PublishedResultPolicy 从 SUCCESS Diagnosis 的 Query、Conclusion、Limitations/Scope 和 verified RAG metadata 构造有界 PublishedResult,并从持久化结果构造 PreviousTurn。每个字符串、list 和 source document 都按集中 limits 在字段边界截断;不调用 LLM。
只有 conclusion 非空才写 published_result。Repository 查询必须精确为同一 Session、intent=DIAGNOSIS、release_outcome=SUCCESS、JSON 非空,按 created_at,id 倒序一条。解析失败或字段无效时 fail closed 为 previousTurn=null。
6. Run store 和数据库契约
DiagnosisRun 新增:
intent VARCHAR(32),枚举SYSTEM_CHAT|KNOWLEDGE_QUERY|DIAGNOSIS;release_outcome VARCHAR(16),枚举SUCCESS|FALLBACK|FAILED|CANCELLED;published_result JSON,只存固定安全结构。
V012 添加三列及索引 session_id,intent,release_outcome,created_at,id。JpaChatRunStore 负责 ensure ChatSession、start、markIntent、finish、latest routing context 和 safe PreviousTurn;JSON 使用注入的 ObjectMapper,禁止 Java serialization。
Run status 保持既有字符串:安全 SUCCESS/FALLBACK 都标记 SUCCESS,技术失败 FAILED,客户端/用户取消 CANCELLED;release_outcome 保留精确发布语义。finish 同时写安全 public content、budget tokens/tool count 和 duration。
7. 异常与 first-terminal-wins
应用用例 catch 所有执行异常后先读取 Run lifecycle:CANCELLED 映射 release_outcome=CANCELLED,其余未完成异常调用 core.completeFailure 并写 FAILED。成功/Fallback 都调用 core.completeSuccess。持久化错误不伪造成功;异常只通过稳定 ChatFailureCode 向阶段 6B 暴露,不携带供应商/JPA/Prompt 详情。
Module and Ownership Audit
ChatApplicationRequest
-> ChatApplicationUseCase (session/run/dispatch/terminal owner)
-> ChatRunStore (MySQL truth)
-> IntentRouter (minimal direct ChatModel)
-> SystemChatExecutor | KnowledgeQueryExecutor | DiagnosisChatExecutor
-> GuardModelCall | HarnessEvidenceTools | DiagnosisAgent/Release
-> ChatApplicationResult + Observer statuses
- Core owns deadline/cancel/budget/lifecycle;Application owns business dispatch and persistence;Store owns MySQL mapping;executors own path-specific behavior;Controller remains protocol-only.
- 最大风险是应用终态和 DB 写入分离;用例使用单一 finish path 和 first-terminal-wins lifecycle,focused tests 覆盖 success/fallback/failure/cancel。
- 最大兼容风险是 V012;Entity/migration/repository query 必须同步,阶段 6B 前不读取新入口不会改变公开行为。
- 不复用旧
ChatService私有 routing/session helpers,避免新用例依赖旧多 Agent 结构。
Interface Impact
- Level: L3 collaboration/database contract。
- Consumers: 阶段 6B Controller/SSE adapter、DiagnosisRunRepository、MySQL schema。
- Public HTTP/frontend: 本阶段无变化。
- Independent contract: 本 design 的 Database Contract/Migration Plan 章节作为独立接口说明。
Risks / Trade-offs
- [Router hallucinated value] -> exact one-field JSON + enum parser + two-attempt policy + fail closed。
- [Knowledge model fabricates reference] -> exact invocation ID/document subset validation before public projection。
- [Current Run shadows previous context] -> prior reads before start/persist current Run。
- [PublishedResult leaks evidence] -> dedicated typed contract/policy and serialized-field negative tests。
- [Observer disconnect leaves work running] -> run control directly cancels shared Core context。
- [DB/entity drift] -> V012 migration + entity/repository/static schema tests + compile。
Migration Plan
- Apply V012 additive columns/index; all new columns nullable, old writers remain compatible。
- Deploy stage 6A code while public Controller continues using old path;new fields remain unused except internal tests。
- Stage 6B wires new application use case and begins writing fields atomically per Run。
- Rollback: switch Controller back to old path, then optionally drop V012 index and three columns;V011 data and old writers remain valid。
Open Questions
- None。具体模型/byte/PreviousTurn 数值由构造配置提供默认值,阶段 7 根据真实 Trace 校准。