14 KiB
14 KiB
Decisions: single-react-diagnosis-agent
Discover status
- Checkpoint: Discover
- Capability source:
sm-flow,使用grill-with-docs进行代码可证问题澄清,并使用 GitNexus、源码、依赖 sources jar 与javap核对调用链和框架 API。 - Scale: complex。变更新增内部 Agent/use case,跨越模型、Tool、预算、结构化输出和审计边界,但不切换公开协议。
devflow/index.md命中 design freeze、RunContext、ACI contracts、canonical store、RAG/log projection 和 MySQL Tool;没有与 OpenSpec 冲突的 ADR。
Question Pool
| # | 维度 | 问题 | 模式 | 状态 |
|---|---|---|---|---|
| Q1 | 术语 | 单体 Diagnosis Agent 是否包含外层 Graph 或多个报告作者? | evidence-driven | 已解决 |
| Q2 | 边界 | 阶段 4 是否切换公开 Chat 或删除旧多 Agent 链路? | evidence-driven | 已解决 |
| Q3 | 验收 | 如何证明 ReAct 正常轮次不是 Harness retry? | evidence-driven | 已解决 |
| Q4 | 技术 | 如何取得并保留框架原始 tool_call_id? | evidence-driven | 已解决 |
| Q5 | 技术 | 如何在每个模型和 Tool 边界强制 Run 预算? | evidence-driven | 已解决 |
| Q6 | 输出 | 框架生成的 DiagnosisDraft schema 是否与可空 conclusion 契约一致,并会直接返回 DiagnosisDraft? | evidence-driven | 已解决 |
| Q7 | 上下文 | previous_turn 如何进入模型且保持固定 Schema 和有界? | evidence-driven | 已解决 |
| Q8 | 审计 | 如何保留 Run/AgentStep/ToolInvocation 而不引入 ThreadLocal? | evidence-driven | 已解决 |
| Q9 | 验收 | 无证据结果如何停止而不补造根因? | evidence-driven | 已解决 |
Evidence-driven
| 结论 | 证据来源 | 是否已汇报用户 |
|---|---|---|
只新增一个拥有 tool loop 的 ReactAgent,无 SequentialAgent、SupervisorAgent 或业务 StateGraph。 |
ISS-014 阶段 4;ChatService 旧链路反例;Spring AI Alibaba ReactAgent API |
已汇报 |
| 阶段 4 只提供内部用例,公开入口保持旧实现,接口影响 L2。 | ISS-014 1174-1197、阶段 0 design freeze、GitNexus createReactAgent 引用 |
已汇报 |
ToolCallRequest.getToolCallId() 精确暴露 AssistantMessage.ToolCall.id();ToolInterceptor 可在回调执行前取得该 ID。 |
framework sources ToolCallRequest、AgentToolNode |
已汇报 |
ModelInterceptor 包围每次真实模型调用,适合调用 beforeModelCall 并记录响应 Usage;ToolBoundary 已负责 Tool 预算,不能重复计数。 |
framework AgentLlmNode/InterceptorChain;ToolBoundary |
已汇报 |
BeanOutputConverter 可从 DiagnosisDraft 生成格式说明,但默认 schema 不允许冻结契约的 conclusion=null;必须 post-process nullable conclusion,且 ReactAgent.call 仍返回 AssistantMessage,需要 Harness 严格解析 JSON。 |
framework DefaultBuilder、ReactAgent、本地生成 schema |
已汇报 |
RunContext 可放入 RunnableConfig metadata,由框架控制地传播到 ToolInterceptor;不需要 ThreadLocal。 |
RunnableConfig.addMetadata(String,Object)、AgentToolNode |
已汇报 |
现有 AgentLoggingHook 优先读取 config metadata 的 sessionId/runId,可作为可选审计 Hook 复用;ToolBoundary 保持 canonical Tool invocation。 |
AgentLoggingHook、ToolBoundary |
已汇报 |
| 原始 Query 不截断;超限 fail closed。PreviousTurn 先按固定 record 序列化并受独立/总上下文字节限制。 | ISS-014 极简上下文与预算规则 | 已汇报 |
NO_EVIDENCE 不是错误,只能形成限定范围的 NEGATIVE_OBSERVATION;Prompt 必须要求 conclusion=null、记录 missing_info 并停止。 |
glossary、ACI spec、DiagnosisDraft contract | 已汇报 |
User-interview
- 本阶段没有新增 user-interview 问题。方向、范围、阶段串行规则、框架 ID、状态语义以及 routine Apply/Archive/Commit 持续授权均已由用户在 ISS-014 评审与前序阶段确认。
关键取舍
- 决策:使用框架
ToolInterceptor直接桥接已注册的 Tool 定义与 Harness adapter。- 原因:Spring AI
ToolCallback的调用参数不包含 Tool Call ID,而 Alibaba interceptor 明确提供原始 ID 和运行 metadata。 - 影响:ToolCallback 负责模型可见定义,interceptor 负责受控执行;未知 Tool 仍交给框架 handler 并最终失败,不伪造结果。
- 原因:Spring AI
- 决策:模型预算由
ModelInterceptor执行,Tool 预算继续由ToolBoundary执行。- 原因:避免在 Agent 层和 Tool boundary 双重 reserve。
- 决策:阶段 4 只严格反序列化
DiagnosisDraft,不提前实现 EvidenceGuard。- 原因:字段引用真实性、唯一性和语义支持属于阶段 5;本阶段只验证 Agent 能生成冻结结构并携带框架 IDs。
- 决策:复用现有 Agent Hook 注入点,不复制 AgentStep 持久化实现。
- 原因:阶段 4 不接公开运行态,阶段 6A 再装配真实 Repository 和 Run 持久化。
OpenSpec 回写
- 需进入 proposal/design/spec/tasks:单 Agent、内部入口、ToolInterceptor 原始 ID、ModelInterceptor 模型预算、ToolBoundary 单点 Tool 预算、严格 JSON 解析、上下文字节限制、审计 Hook 注入、无证据停止、不切换公开入口。
- 不创建 ADR:这些是 ISS-014 已冻结方向和当前阶段可逆的内部装配,不满足新的难逆转架构决策条件。
Cross-artifact 对齐
| 上游 -> 下游 | 检查内容 | 状态 |
|---|---|---|
| brief/ISS-014 -> proposal | 单 Agent、内部入口、极简上下文、预算、审计、非目标和验收预期 | 已对齐 |
| proposal -> design | Tool/Model interceptor、严格 Draft、无重试、审计注入和公开隔离 | 已对齐 |
| design -> specs/tasks | ID 传播、预算单点、输入输出限制、生命周期所有权和风险缓解 | 已对齐 |
| specs -> tasks | 7 组可观察要求均有输入/Prompt、Tool bridge、Agent use case 和 focused test 切片 | 已对齐 |
Architecture Audit
- 能力来源:
zoom-out,使用 glossary 的 Diagnosis Agent、Diagnosis Harness、RunContext、Evidence Status 和 Invocation Status 术语。 - 链路为
DiagnosisAgentInput + RunContext -> internal use case -> one ReactAgent -> model/tool interceptors -> ChatModel/ToolBoundary -> strict DiagnosisDraft,没有外层业务 Graph。 - RunContext 拥有预算/取消/生命周期,ToolBoundary/store 拥有 canonical invocation,use case 拥有输入和 Draft 解析,Agent 只拥有诊断语义;数据所有权不重叠。
- 最大耦合风险是 Spring AI Alibaba interceptor/output schema API;设计通过本地 1.1.2.0 sources jar 和实际 schema 生成结果证实,并以 focused framework-loop/schema tests 固定。
- 最大阶段风险是 Draft 在 Guards 前被误用;类名、文档和零 Controller 消费者共同保持 internal/draft 边界,阶段 5 前不得发布。
接口影响
- 级别:L2 内部接口。
- 变更对象:新增内部 Java use case/factory/input/limits/interceptors/registry,不修改既有方法签名。
- 消费者:本阶段只有 focused tests;阶段 6A 将成为首个生产消费者。
- 兼容性:公开 HTTP/SSE、Controller DTO、数据库和旧 Chat/AiOps 路径不变,无迁移或回滚要求。
Pre-apply Research
参考实现与框架源码
src/main/java/com/superbiz/agent/service/ChatService.java:createReactAgent、buildChatExecutorAgent和旧多 Agent 反例;只复用 Builder 形态,不复用运行职责。src/test/java/com/superbiz/agent/service/ChatServiceSequentialAgentTest.java:scriptedChatModel测试模式。src/main/java/com/superbiz/agent/harness/core/DiagnosisHarnessCore.java:模型/Tool/Token/容量边界。src/main/java/com/superbiz/agent/harness/tool/boundary/ToolBoundary.java:Tool budget 和 canonical record 单点。src/main/java/com/superbiz/agent/harness/tool/adapter/RagToolAdapter.java、QueryLogsToolAdapter.java、MysqlToolAdapter.java:阶段 4 唯一允许的 evidence Tool 执行入口。src/main/java/com/superbiz/agent/hook/AgentLoggingHook.java:可注入 AgentStep audit,优先读取 RunnableConfig metadata。- Spring AI Alibaba 1.1.2.0 sources:
ReactAgent、DefaultBuilder、AgentLlmNode、AgentToolNode、ToolCallRequest、InterceptorChain。
技术栈清单
ReactAgent.builder()+ 从DiagnosisDraft生成并修正 nullable conclusion 的.outputSchema(...),不新建 Graph/SequentialAgent。RunnableConfig.addMetadata(String,Object)显式携带 sessionId/runId/RunContext,并设置_stream_=false。ModelInterceptor对每次模型调用执行 Core budget;读取ChatResponseMetadata.Usage。ToolInterceptor读取 exact framework ID,调用 adapter bridge;ToolBoundary保留唯一 Tool reserve/store 边界。- Spring
FunctionToolCallback仅定义模型可见 Tool Schema/description;直接 callback 执行 fail closed。 - Jackson
ObjectMapper负责固定输入 JSON 和严格 DiagnosisDraft 反序列化;UTF-8 字节按StandardCharsets.UTF_8计算。 - 框架 Hook 列表作为 AgentStep audit 扩展点;阶段 4 不新增 JPA/Redis/Flyway。
新建基础设施
harness.agent:Input、Limits、Tool registry/interceptor、Model interceptor、Factory、UseCase 和异常类型。prompts/diagnosis-agent-prompt.md:唯一 Diagnosis Agent Prompt。- focused scripted model/tool-loop tests;无需新 Maven 依赖。
ISS-014 PRD 复用
- 阶段 4 不新建独立
prd.md:ISS-014 已完整覆盖问题、用户价值、数据流、Draft Schema、预算、重试、阶段边界和验收;brief.md只索引本切片,不复制总 Issue。
Commit Gate Preflight
- proposal、design、specs、tasks 完整,
openspec status为 complete,openspec validate single-react-diagnosis-agent --strict通过。 - question pool 全部为已汇报的 evidence-driven 结论;无未确认 user-interview、接口等级或风险接受问题。
- Cross-artifact 四段对齐无 gap;架构审计的数据所有权、阶段边界和框架耦合缓解已进入 design/spec/tasks。
- 接口影响为 L2,仅新增内部 Java API;公开 Controller/SSE/JPA/旧 ChatService 保持不变。
- Apply、Archive 和阶段 Git commit 使用用户对 ISS-014 各阶段的持续授权;实现必须严格限制为 Committed OpenSpec。
.committed已创建,Committed OpenSpec 可进入 Apply。
Apply Progress
- 首模块对齐:Input/Limits/Prompt/Tool registry/Tool interceptor 已落地,任务 1.2、2.1、2.2 完成;1.1 等待 focused test 后完成。
- 编译首次发现
ToolInterceptor必须实现Interceptor.getName();分类为代码偏离,已补充稳定名称并复编译通过,无需修改 OpenSpec。 - 单 Agent use case、Model interceptor、strict Draft parser 和 audit Hook 注入已完成;scripted ChatModel 真实执行框架
model -> Tool -> modelloop。 - 首次测试的两处失败均为测试假设偏差:Spring 会将 callback 异常包装为
ToolExecutionException,Tool observation 位于Prompt.instructions的ToolResponseMessage而非Prompt.getContents();已按框架真实 API 修正测试,生产设计未变。 - 阶段 4 focused tests 共 14 个通过,任务 1.1-4.2 完成;剩余任务 4.3 为综合回归、静态范围和 OpenSpec 验证。
Apply Result
- 新增一个且仅一个
diagnosis_agentFactory 和内部DiagnosisAgentUseCase;没有外层业务 Graph、SequentialAgent、SupervisorAgent 或手写 ReAct loop。 - 新增单一 Prompt、固定
DiagnosisAgentInput(query, previous_turn)、可配置 UTF-8 限制和严格DiagnosisDraft解析;不接受完整历史,不执行结构修复或 Agent retry。 - 新增
HarnessModelInterceptor,对每个非流式模型轮次执行 Core model/Token budget,并在 late result 返回后复查 Run active 状态。 - 新增
HarnessEvidenceTools和HarnessToolInterceptor,注册三类冻结 Tool,精确传播框架 Tool Call ID,并通过阶段 3B/3C adapter/ToolBoundary 返回有界结果。 - AgentStep 使用可注入框架 Hook 保留,RunnableConfig 显式传播 sessionId/runId;Run 和 Tool canonical invocation 继续由既有 Harness 边界拥有。
- 公开 Controller、ChatService、AiOpsService 无 diff;旧多 Agent 主链路继续保留。
Apply Verification
- 编译:
mvn -q -DskipTests compile通过。 - 阶段 4 focused:
mvn -q '-Dtest=HarnessToolInterceptorTest,DiagnosisAgentUseCaseTest' test,14 tests 通过。 - 综合回归:
mvn -q '-Dtest=DiagnosisAgentUseCaseTest,HarnessToolInterceptorTest,DiagnosisHarnessCoreTest,ToolBoundaryTest,ToolAdapterTest,MysqlToolAdapterTest,CanonicalInvocationStoreTest,HarnessContractTest,RagToolContractTest,QueryLogsToolContractTest,MysqlToolContractTest' test通过。 - OpenSpec:
openspec validate single-react-diagnosis-agent --strict通过。 - 静态范围:新 Agent 包无 ThreadLocal、手写 while、外层 Graph、多 Agent 类型或 raw response;Controller/ChatService/AiOpsService diff 为空。
- 未执行 live E2E:按 ISS-014 阶段门禁统一留到阶段 7。
Archive Result
- 11/11 OpenSpec tasks 完成,
.archive-ready已创建。 - 主规格已同步至
openspec/specs/single-react-diagnosis-agent/spec.md。 - Change 已归档至
openspec/changes/archive/2026-07-21-single-react-diagnosis-agent。 devflow/index.md和 ISS-014 阶段表已更新为阶段 0-4 archived,下一阶段为 5。- 提交前 schema 自审发现框架默认 BeanOutputConverter 将
conclusion限为 object,与冻结的无证据null语义冲突;分类为实现设计遗漏,已回写 archived design,新增 nullable schema post-process 与回归测试,规格行为未改变。