Files

107 lines
8.2 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
阶段 1 已交付未接生产入口的 Diagnosis StateGraph 状态、拓扑、路由和 trace builder。当前复杂 Chat 仍由 `ChatService`、`SequentialAgent`、`VerifierInputHook` 和 `VerifierContextHolder` 编排;Executor 解析在 Hook 内,Verifier/Composer 解析与安全渲染在 ChatService 内。阶段 2 只把真实 ReactAgent 和确定性 Java 服务接入 Graph action ports,并抽出可复用协议组件,生产切换留到阶段 3。
本 change 涉及 `graph.diagnosis`、Hook 和 ChatService 内部协作,接口影响为 L2。`/api/chat`、数据库、Trace API、Prompt 业务协议和当前生产路由均不改变。
## Goals / Non-Goals
**Goals:**
- 提供显式透传 `RunnableConfig` 的 Planner、Executor、Verifier、Composer adapters。
- 将 Executor、Verifier、Composer 协议解析和安全渲染抽为单一共享实现,并让旧路径委托以保持行为。
- 将 Gatekeeper、可信输入投影、evidence retry prepare 和两类 Fallback 实现为确定性 Java Node。
- 保证 Verifier 只接收通过 binding 投影的材料,并将执行状态、模型 verdict 和 effective verdict 分离。
- 修正 LOW_CONFID evidence retry 仅接受 critical gap 的阶段 1 偏差。
**Non-Goals:**
- 不切换 `ChatService.executeChatComplex` 到 StateGraph。
- 不修改 `/api/chat`、数据库、Run/Trace DTO 或持久化。
- 不删除 SequentialAgent、VerifierInputHook 或 VerifierContextHolder。
- 不修改共享 Verifier Prompt;其输入说明在阶段 3 切换时同步。
- 不运行 Maven live E2E、日志或数据库验收。
## Decisions
### 1. Agent 调用经最小 port 隔离
`DiagnosisAgentInvoker` 只暴露 `invoke(String, RunnableConfig) -> String`;`ReactAgentDiagnosisInvoker` 包装 `ReactAgent.call(input, config)` 并返回消息文本。各 Agent adapter 自己负责白名单输入序列化、输出解析、状态映射和 orchestration event,不读取完整父 Graph State。
选择最小 port 而不是直接使用 `ReactAgent.asNode(...)`,因为显式字符串输入便于证明输入白名单、固定技术重试输入和 run config 透传,也避免父 Graph messages/private state 泄漏。ReactAgent/invoker 通过构造注入;本阶段不复制 ChatService 的 Prompt 或 Agent factory,生产实例装配属于阶段 3。
### 2. 共享协议组件替代复制
在中立的 `com.superbiz.agent.diagnosis.protocol` 包抽取无状态的 `JsonPayloadSupport`、Executor/Verifier/Composer parser、Composer safe input builder 和 fallback renderer。旧 `VerifierInputHook`/`ChatService` 委托这些组件,新 Nodes 使用同一实现;抽取本身不得改变旧可观察行为。共享组件不得依赖 `graph.diagnosis`、Hook、ChatService 或 ThreadLocal。
选择共享组件而不是在 Graph 包复制旧私有方法,避免旧 Sequential 与新 Graph 对同一 JSON 契约产生两个真理源。阶段 2 保留旧控制流只是迁移顺序,不形成长期双轨。
### 3. Node failure 分类 fail closed
Adapter 将合法结构映射为 COMPLETED,将可识别的临时调用失败映射为 RETRYABLE_FAILED,将非法 JSON/结构映射为 INVALID_OUTPUT;未知或明确不可重试异常映射为 NON_RETRYABLE_FAILED/FAILED。Executor 不论失败类型都不重试,合法 no-evidence 结构仍为 COMPLETED。
分类器允许注入以便测试。无法确定的异常不猜测为可重试,防止无限或扩大副作用。
### 4. Gatekeeper 是 Graph 唯一验证入口
Graph Verifier ReactAgent 不注册旧 `VerifierInputHook`。显式 Gatekeeper Node 从 `RunnableConfig.metadata.runId` 和 `executor_output` 调用 `ExecutorGatekeeperService.validateRun` 恰好一次,同时保存 raw result 与 normalized status:pass 为 PASS;fail/low_confid 为 LOW_CONFID;fail/reject 为 REJECT;缺失、unknown、异常或自相矛盾均为 REJECT。
旧 Sequential 路径在阶段 3 前仍由 Hook 调用 Gatekeeper。两个入口服务于互斥的控制流,不允许同一次 Graph run 双执行。
### 5. Verified Input 按 binding 精确投影
Builder 只接受 Gatekeeper `checked_bindings.status=pass`。它以 `claim_id + source_invocation_id + tool_name + raw_path` 精确关联 Executor claim binding,输出过滤后的 `verified_executor_output` 和只含 `claim_id/source_invocation_id/tool_name/raw_path/matched_text` 的 `verified_evidence`。
失败 binding、hypotheses、未引用工具结果、完整 `tool_trace_summary` 和 raw Executor 文本不得进入 Verifier。合法零 claim 输出保留空列表,但 LOW_CONFID 零可信 binding 已由 Router 在 Builder 前阻断。
### 6. Verifier 状态与 verdict 分离
Verifier parser 产出执行状态和模型 verdict;Adapter 再应用 Gatekeeper ceiling 得到 effective verdict。技术失败只写 `verifier_status`,不得伪造诊断 verdict。ceiling=LOW_CONFID 时模型 PASS 最高只能得到 LOW_CONFID。
Verifier technical retry 复用首次生成的完全相同输入字符串;Composer technical retry 同样复用首次 allowed-material 输入。重试不得读取变化后的前序 raw state。
### 7. Evidence retry 使用共享 critical-gap extractor
`EvidenceGapExtractor` 同时被 Router guard 与 Retry Prepare Node 使用,唯一资格为 `is_critical=true` 且 `verification` 为 `no_evidence` 或 `indirect_support`。这样修复阶段 1 Router 漏检 critical 标记,又避免路由判断与 retry payload 漂移。
Retry context 包含 prior verified output/evidence、结构化 gaps、从 verified evidence 去重得到的 completed query refs,以及固定约束:最多一次、不重复成功查询、只做增量查询、保留 prior verified claims。第二轮 Executor 被要求执行增量查询但输出完整 `executor_evidence_v2` 快照;Java 不合并 claim 文本,完整快照重新经过 Gatekeeper。
### 8. 两类 Fallback 使用不同材料边界
前置验证 Fallback 只使用 reason code、校验状态、工具概况和人工查看 Trace 建议,绝不输出 Executor claim。Composer 后置 Fallback 只使用 Verifier 已允许的 claims、missing info 和 recommendations,不读取 raw Executor/tool output。
Fallback 由确定性 renderer 生成;若连安全答案都无法生成,异常交给阶段 3 的外层 Run failure handling。
### 9. 阶段内装配与测试边界
新增真实 Node action set/factory 装配入口,但不让 ChatService 成为消费者。单元测试使用 fake invoker 验证 Node 契约,使用真实 `ExecutorGatekeeperService` mock 边界验证单次调用,并以 CompiledGraph 场景验证 adapters、critical evidence retry 和 Fallback 路由。旧 Sequential/Hook/Gatekeeper focused tests作为共享抽取回归门禁。
## Interface Impact
- 等级:L2 内部接口。
- 新内部消费者:阶段 3 的 Graph orchestrator/Agent factory。
- 旧内部消费者:VerifierInputHook 与 ChatService 改为委托共享 parser/renderer,公开方法与外部响应不变。
- 外部 API、DB、Trace、Prompt 输出协议:无变化。
- 回滚:revert 本阶段提交;生产仍走旧 Sequential 路径。
## Risks / Trade-offs
- [共享解析器抽取造成旧行为漂移] → 保留原输入/输出形态并运行旧 focused tests。
- [binding 关联不精确导致证据泄漏] → 使用四元键精确匹配并覆盖 mixed pass/fail binding。
- [Gatekeeper 双执行] → Graph Verifier 不注册旧 Hook,并以调用次数测试锁定。
- [异常误判为可重试] → 未知异常默认 fail closed;分类器用显式用例覆盖。
- [阶段 2 Prompt 与 Node payload 说明暂不一致] → 本阶段不接生产入口;阶段 3 切换时同一 change 更新 Prompt。
## Migration Plan
1. 先抽共享协议组件并让旧 Hook/ChatService 委托,运行旧 focused tests。
2. 接入 invoker、Agent adapters、Gatekeeper、Verified Input、Retry Prepare 和 Fallback Nodes。
3. 修正 Router critical-gap guard,装配未接生产入口的真实 Graph 并运行 Node/Graph tests。
4. 归档并提交本阶段;阶段 3 再切换 ChatService、Prompt、DB 和 Trace。
回滚为完整 revert 本阶段提交;不需要数据库回滚,不存在外部协议迁移。
## Open Questions
无。阶段 3 前不得扩大范围到生产入口或 Prompt 切换。