Files

8.6 KiB
Raw Permalink Blame History

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

  1. Apply V012 additive columns/index; all new columns nullable, old writers remain compatible。
  2. Deploy stage 6A code while public Controller continues using old path;new fields remain unused except internal tests。
  3. Stage 6B wires new application use case and begins writing fields atomically per Run。
  4. 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 校准。