Files

8.2 KiB
Raw Permalink Blame History

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 切换。