feat(harness): add run context and retry core
This commit is contained in:
@@ -175,6 +175,22 @@
|
||||
- 定义:证据 Tool 的结果语义,固定为 `EVIDENCE_FOUND`、`NO_EVIDENCE`、`ERROR`。
|
||||
- 边界:`NO_EVIDENCE` 只表示当前查询范围内没有匹配结果,不能解释为问题不存在、根因被排除或系统健康。
|
||||
|
||||
### RunContext
|
||||
- 定义:一次 Diagnosis Run 的显式执行上下文,结构不可变地携带 `sessionId`、`runId`、deadline,以及该 Run 独占的取消、预算、重试策略和生命周期状态句柄。
|
||||
- 边界:RunContext 通过方法参数或框架受控 context 显式传播,不依赖 ThreadLocal;结构不可变不等于内部计数和取消状态不能变化,这些变化由线程安全句柄管理。
|
||||
|
||||
### Run Lifecycle
|
||||
- 定义:Diagnosis Harness 对单次 Run 执行状态的内存控制,采用 first-terminal-wins 规则保证成功、失败、取消、超时和预算耗尽只能产生一个最终终态。
|
||||
- 边界:Run Lifecycle 不直接等同于数据库实体写入;应用用例负责把最终状态映射到 `diagnosis_run` 持久化。
|
||||
|
||||
### Run Budget
|
||||
- 定义:单次 Run 的模型调用、Tool 调用、单 Tool 调用、输入/输出/总 Token 和 canonical invocation 字节容量的线程安全消耗计数与门禁。
|
||||
- 边界:预算上限由 Harness 配置显式提供;实际 Token 在模型响应后记录,超限后保留真实消耗并阻止后续执行。
|
||||
|
||||
### Harness Retry Policy
|
||||
- 定义:Harness 对同一技术操作 attempt 数和可重试失败类型的显式策略。
|
||||
- 边界:Router 与 SemanticGuard 的技术失败最多两次 attempt;Diagnosis Agent、Tool 和 Evidence repair 只有一次 attempt。Agent 正常 ReAct 轮次不是 retry,`NO_EVIDENCE`、业务拒绝、取消和预算耗尽不可重试。
|
||||
|
||||
### Verifier Skill Isolation
|
||||
- 定义:Chat Verifier 与 skill 系统隔离,只校验 Executor 答案和 `tool_trace_summary`。
|
||||
- 使用场景:防止 Verifier 把 playbook 指令当作事实证据;Verifier 只判断已有证据是否支持结论。
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
| 日期 | slug | 说明 | 领域 | 关键词 | 关联 OpenSpec | 状态 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 2026-07-21 | single-react-harness-run-context | 建立显式 RunContext、Harness Core、预算、取消、类型化重试和 Tool Store 基础。 | Harness/Run lifecycle/Budget | ISS-014, RunContext, deadline, cancellation, budget, retry, ToolCallKey | openspec/changes/archive/2026-07-21-single-react-harness-run-context | archived |
|
||||
| 2026-07-21 | single-react-aci-tool-contracts | 冻结 RAG、日志和 MySQL evidence Tool 的 Agent-facing ACI Schema、状态、框架调用引用和描述边界。 | Harness/Agent Tool contract | ISS-014, ACI, tool_call_id, evidence_status, RAG, query_logs, query_mysql, MOCK | openspec/changes/archive/2026-07-21-single-react-aci-tool-contracts | archived |
|
||||
| 2026-07-21 | single-react-design-freeze | 冻结单体 Diagnosis Agent、Harness、Guard、工具证据与阶段门禁契约。 | Chat/Harness/Agent contract | ISS-014, single ReactAgent, Harness, EvidenceGuard, SemanticGuard, tool_call_id, evidence_status | openspec/changes/archive/2026-07-21-single-react-design-freeze | archived |
|
||||
| 2026-07-10 | session-run-trace-isolation | 拆分会话态和运行态,引入 runId 隔离 Trace、Feedback、AIOps 和 demo 链路。 | Trace/session/run isolation | chat_session, diagnosis_run, runId, trace exact run, feedback fallback, AIOps SSE metadata, baseline drift | openspec/changes/archive/2026-07-10-session-run-trace-isolation | archived |
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# Acceptance: single-react-harness-run-context
|
||||
|
||||
## 实现结果
|
||||
|
||||
- 新增显式 `RunContext`、取消信号、deadline 检查、Run Lifecycle 和 first-terminal-wins。
|
||||
- 新增 caller-supplied limits、线程安全 Model/Tool/Token/Run bytes 预算和容量 CAS。
|
||||
- 新增 strict typed retry policies/executor,记录每个实际 attempt 并阻止取消/预算异常误重试。
|
||||
- 新增无 Redis 依赖的 ToolCallKeyFactory,精确保留框架 Tool Call ID。
|
||||
- Spring AI `spring.ai.retry.max-attempts` 设置为 1;未修改 provider/model routing。
|
||||
- 未接入旧 Chat/AIOps/Controller、ThreadLocal、JPA、Redis、Agent 或公开协议。
|
||||
|
||||
## 静态验证
|
||||
|
||||
- `openspec validate single-react-harness-run-context --strict`:通过。
|
||||
- 新 Harness 包 `rg`:无 ThreadLocal/current-holder/Redis 引用。
|
||||
- 旧 Chat/AIOps/Controller/JPA 调用链 diff:为空。
|
||||
- 受保护配置检查:仅新增 `spring.ai.retry.max-attempts: 1`,model routing/provider 保持不变。
|
||||
|
||||
## 脚本验证
|
||||
|
||||
- `mvn -q -DskipTests compile`:通过。
|
||||
- Core focused suite:通过。
|
||||
- 综合回归 suite(阶段 0/1 契约 + 阶段 2 Core + ChatController):通过。
|
||||
|
||||
## 浏览器/人工验证
|
||||
|
||||
- 不适用。本阶段没有 UI、Controller、SSE 或公开协议变化。
|
||||
|
||||
## 未验证
|
||||
|
||||
- 未执行真实模型调用、Redis、CLS、MySQL 或客户端断开 live E2E;这些属于后续 Tool store/adapter/最终 E2E 门禁。
|
||||
- 未将 RunContext 接入旧 ChatService;这是本阶段明确非目标,阶段 6A 才接入。
|
||||
- `RunContext` 取消对已进入的同步第三方调用仍是协作式;实际 HTTP/JDBC future 取消留给后续 adapter。
|
||||
- Provider 侧凭据轮换状态不由仓库证明。
|
||||
|
||||
## 剩余风险与后续门禁
|
||||
|
||||
- 关闭 SDK 隐式 retry 会使旧路径瞬时错误不再自动重试,直到后续 Router/SemanticGuard 接入 Harness;该变化已记录并可通过恢复配置回滚。
|
||||
- 下一阶段 3A 必须在 ToolInterceptor 接收 RunContext 和框架 Tool Call ID,直接复用 Key/Capacity/Cancellation 门禁。
|
||||
|
||||
## 状态
|
||||
|
||||
- Stage acceptance: accepted
|
||||
- OpenSpec archive: archived at `openspec/changes/archive/2026-07-21-single-react-harness-run-context`
|
||||
- Main spec sync: `openspec/specs/diagnosis-harness-run-context/spec.md`(8 added requirements)
|
||||
@@ -0,0 +1,32 @@
|
||||
# Brief: single-react-harness-run-context
|
||||
|
||||
## 背景
|
||||
|
||||
旧 Chat/AIOps 通过多个 ThreadLocal 和业务方法内状态机传播 session/run/token/retry,无法成为后续 Tool、Agent、Guard 和新入口的稳定共同边界。Spring AI 默认 10 attempts 还会制造未被 Harness 记录的隐藏重试。
|
||||
|
||||
## 目标
|
||||
|
||||
- 建立显式、结构不可变、可异步传播的 RunContext。
|
||||
- 集中实现 deadline、协作式取消、线程安全预算和唯一 Run 终态。
|
||||
- 实现类型化、最多两次 attempt、逐 attempt 记录的 Harness retry。
|
||||
- 提供阶段 3A 可直接使用的 Tool Call Key Factory 和单 Run 容量计数器。
|
||||
- 将 Spring AI 底层 retry 压为一次。
|
||||
|
||||
## 范围
|
||||
|
||||
- 新增 Harness Core/Retry/Tool Store foundation 类型和 focused Fake tests。
|
||||
- 更新 `application.yml` 的 Spring AI retry 配置。
|
||||
- 更新 glossary 和 OpenSpec/devflow 档案。
|
||||
|
||||
## 非目标
|
||||
|
||||
- 不接入旧 ChatService/AiOpsService/Controller/SSE。
|
||||
- 不读写现有 ThreadLocal,不删除旧实现。
|
||||
- 不写 `diagnosis_run` 或 Redis,不实现 Tool projection。
|
||||
|
||||
## 元数据
|
||||
|
||||
- 分档:complex
|
||||
- 接口影响:L2;另有关闭旧 SDK 隐式 retry 的有意内部行为变化
|
||||
- 关联 Issue:ISS-014 阶段 2
|
||||
- 关联 OpenSpec:`openspec/changes/single-react-harness-run-context`
|
||||
@@ -0,0 +1,110 @@
|
||||
# Decisions: single-react-harness-run-context
|
||||
|
||||
## 规模与入口
|
||||
|
||||
- 分档:complex。
|
||||
- 入口:ISS-014 阶段 2;阶段 0/1 已分别由 Git commit `58c3910`、`4274f33` 固化并 Archive。
|
||||
- 目标:建立后续 Tool、Agent、Guard 和入口共同依赖的显式 Harness 运行边界,不接旧 ChatService。
|
||||
|
||||
## Context
|
||||
|
||||
- `devflow/index.md` 命中 design freeze、ACI contracts 和 session/run trace isolation。
|
||||
- 当前 `SessionContextHolder`、`VerifierContextHolder`、`TokenUsageHolder` 使用 ThreadLocal;新 Harness 禁止复用。
|
||||
- 当前 ChatService 自行生成 runId、写 `diagnosis_run`、执行两轮 retry loop 并在 finally 清理 ThreadLocal,职责与新 Harness 边界冲突。
|
||||
- `DiagnosisRun.status` 当前是字符串 `PENDING/RUNNING/SUCCESS/FAILED`;本阶段不修改实体或迁移,由后续应用用例映射。
|
||||
- Spring AI 1.1.7 `SpringAiRetryProperties` 的配置前缀是 `spring.ai.retry`,默认 `maxAttempts=10`。
|
||||
|
||||
## Question Pool
|
||||
|
||||
| 维度 | 问题 | 模式 | 证据与结论 | 状态 |
|
||||
|---|---|---|---|---|
|
||||
| 术语 | “不可变 RunContext”是否意味着预算/取消也不能变化? | evidence-driven | ISS 要求上下文不可变同时要求计数/取消/终态;采用结构不可变 record + 线程安全单 Run 状态句柄。 | 已解决并汇报 |
|
||||
| 术语 | Run 与 DiagnosisRun 是否在阶段 2 直接持久化绑定? | evidence-driven | 阶段 2 只建 Core,阶段 6A 才接应用用例;本阶段生命周期为内存执行真理,不改 JPA。 | 已解决并汇报 |
|
||||
| 边界 | 是否迁移旧 ThreadLocal/ChatService 调用? | evidence-driven | ISS 1109-1111 明确禁止 ThreadLocal 和旧 ChatService 临时适配;只新增零消费者 Core。 | 已解决并汇报 |
|
||||
| 边界 | 取消是否承诺立即中断同步模型调用? | evidence-driven | 阶段 0 已确认分层取消;阻止后续边界并执行资源回调,不承诺不可证明的硬中断。 | 已解决并汇报 |
|
||||
| 验收 | 唯一终态如何证明? | evidence-driven | atomic first-terminal-wins lifecycle,并用取消/预算/异常/成功竞态测试证明后续终态不能覆盖。 | 已解决并汇报 |
|
||||
| 验收 | 异步传播如何证明不依赖 ThreadLocal? | evidence-driven | Fake Tool 在 `CompletableFuture` 线程只接收显式 RunContext,并验证相同 session/run/state handles。 | 已解决并汇报 |
|
||||
| 技术 | 隐藏重试如何关闭? | evidence-driven | 本地依赖 `SpringAiRetryProperties` 证实 `spring.ai.retry.max-attempts` 默认 10;配置改为 1 并加配置测试。 | 已解决并汇报 |
|
||||
| 技术 | 尚未校准的预算默认值如何处理? | evidence-driven | ISS 要求集中配置且不伪造数值;Core 接受显式 limits,不内置默认预算,后续 Spring wiring 决定配置值。 | 已解决并汇报 |
|
||||
| 技术 | Tool Call Key 是否生成 Tool ID? | evidence-driven | 阶段 0/1 冻结框架 ID;Factory 仅验证安全 segment 并拼接,不生成或改写。 | 已解决并汇报 |
|
||||
|
||||
## Grill 结论
|
||||
|
||||
- 术语、边界、验收和技术问题均可由已确认 ISS、现有代码和本地依赖 API 证明。
|
||||
- 没有新的产品偏好、公开协议或风险接受度问题需要 `user-interview`;短期关闭旧 SDK retry 的影响已在 proposal 明示。
|
||||
- `grill-with-docs` 的代码可证问题已先查证并向用户汇报;所有结论均已回写 proposal。
|
||||
|
||||
## 能力与工具限制
|
||||
|
||||
- Discover 能力来源:`sm-flow` + `grill-with-docs`。
|
||||
- 当前工具集没有 `codebase-retrieval` 和 LSP;使用 `rg`、源码阅读、本地依赖 `jar/javap`、编译和 focused tests 补足调用链与 API 核对。
|
||||
|
||||
## Cross-artifact 对齐
|
||||
|
||||
| 链路 | 状态 | 结论 |
|
||||
|---|---|---|
|
||||
| brief 目标/范围/非目标 -> proposal | 已对齐 | 显式 context、Core、预算/取消/终态、retry、key/capacity、隐藏 retry 和不接旧 runtime 全部覆盖。 |
|
||||
| proposal 范围/约束/承诺 -> design | 已对齐 | 类职责、并发语义、first-wins、配置变化、迁移和回滚均有明确设计。 |
|
||||
| design 决策/接口影响/风险 -> specs/tasks | 已对齐 | 每个状态边界都有 scenario,配置行为变化和 zero-consumer 边界有独立任务与验证。 |
|
||||
| specs 可观察行为 -> tasks | 已对齐 | 8 条 requirements 分解为 primitives、budget、Core、retry、key、配置和三层验证。 |
|
||||
|
||||
## Architecture Audit
|
||||
|
||||
- 能力来源:`zoom-out`,使用 glossary 的 RunContext、Run Lifecycle、Run Budget、Harness Retry Policy 和 Diagnosis Run 术语。
|
||||
- 输入链路为未来 application use case 创建 RunContext,处理链路由 Core/Retry/Key/Capacity 通过显式参数消费,输出为唯一 RunTermination;当前 runtime 不接入。
|
||||
- RunContext 拥有单 Run 内存状态,应用用例拥有数据库映射,Tool store 拥有 Redis invocation;数据所有权没有重叠。
|
||||
- Core 不保存全局 Run map、不调用 Agent/数据库/Redis、不实现循环编排,因此不会演变为工作流引擎。
|
||||
- 最大风险是关闭 SDK retry 对旧路径的短期行为影响;已作为显式配置变更进入 proposal/spec/test 和回滚说明,无 ADR 冲突。
|
||||
|
||||
## Commit Gate Preflight
|
||||
|
||||
- proposal、design、specs、tasks 完整,OpenSpec status complete,strict validation 通过。
|
||||
- question pool 无未汇报 evidence-driven 结论、无未确认 user-interview 问题。
|
||||
- 接口影响 L2;`spring.ai.retry.max-attempts=1` 的有意内部行为变化已明确影响和回滚边界。
|
||||
- cross-artifact 四段对齐无 gap,架构审计约束已进入 design/spec/tasks。
|
||||
- Apply 持续授权已存在;执行范围严格限制为新 Harness foundation、配置 override 和 focused tests。
|
||||
|
||||
## Pre-apply Research
|
||||
|
||||
### 参考实现与反例
|
||||
|
||||
- `SessionContextHolder`:ThreadLocal session/run fallback,新 Harness 明确禁止复用。
|
||||
- `TokenUsageHolder` / `TokenTrackingChatModel`:当前只记录 total token 且依赖 ThreadLocal,后续 model boundary 应改为显式 RunBudget;本阶段不改旧类。
|
||||
- `ChatService.executeChatComplex`:当前业务方法内创建 Run、两轮 retry、写终态和清理 ThreadLocal,是后续替换对象,不是 Core 参考实现。
|
||||
- `DiagnosisRun`:现有持久化字段和字符串状态;本阶段只确认映射边界,不修改实体或 Repository。
|
||||
- Spring AI 1.1.7 `SpringAiRetryProperties`:`spring.ai.retry` 前缀、默认 `maxAttempts=10`,支持精确配置 override。
|
||||
|
||||
### 技术栈清单
|
||||
|
||||
- Java 17 record 表达结构不可变 context/limits/snapshot/policy/attempt。
|
||||
- `AtomicReference` 实现 first-reason/first-terminal-wins;`AtomicLong` 实现 capacity CAS;同步临界区维护复合预算一致性。
|
||||
- `Clock` 和 `Supplier<String>` 注入保证 deadline/ID 可测试,不引入 scheduler 或全局 registry。
|
||||
- SLF4J 只记录取消 callback 异常,不记录用户输入、Tool payload 或凭据。
|
||||
- SnakeYAML 直接解析 classpath `application.yml` 验证 retry override,不启动外部 MySQL/Redis/Milvus/模型。
|
||||
|
||||
### 新建基础设施
|
||||
|
||||
- `harness.core`:RunContext、Cancellation、Lifecycle、Budget、Capacity、Core 和类型化异常/状态。
|
||||
- `harness.retry`:RetryFailure/Policy/Policies/Attempt/Executor/Exception 与函数接口。
|
||||
- `harness.tool.store.ToolCallKeyFactory`:纯 Key 构造,不访问 Redis。
|
||||
- focused unit tests 与 Fake Model/Tool;无需新 Maven 依赖。
|
||||
|
||||
### 影响半径
|
||||
|
||||
- 新生产包在本阶段保持零消费者。
|
||||
- 唯一现有运行配置变化为 `spring.ai.retry.max-attempts=1`;Model 路由、provider、Controller、JPA 和 Redis 配置保持不变。
|
||||
|
||||
## Apply 结果
|
||||
|
||||
- 冲突分类:未发现 OpenSpec 遗漏、代码偏离或方向不确定项;一次自审发现 RetryExecutor 需要无条件拦截预算/取消异常,已回写代码并通过回归测试。
|
||||
- 新增 `RunContext`、Cancellation、Lifecycle、Budget、Capacity、DiagnosisHarnessCore、typed Retry 和 ToolCallKeyFactory;未接旧 Chat/AIOps/Controller/Redis/JPA。
|
||||
- Spring AI 全局 retry 已由默认 10 压为 1;Harness strict policies 只允许 Router/SemanticGuard 技术失败一次显式重试。
|
||||
- 首模块对齐:Run state/budget/Core/retry/key/config 与 design/tasks 全部完成;Tool interceptor/store/Agent/应用用例仍留给后续阶段。
|
||||
|
||||
## Apply 验证
|
||||
|
||||
- 编译:`mvn -q -DskipTests compile`:通过。
|
||||
- Core focused:`mvn -q '-Dtest=RunContextTest,RunBudgetTest,DiagnosisHarnessCoreTest,HarnessRetryExecutorTest,ToolCallKeyFactoryTest,SpringAiRetryConfigurationTest' test`:通过(加固后复跑通过)。
|
||||
- 综合回归:`mvn -q '-Dtest=HarnessContractTest,RagToolContractTest,QueryLogsToolContractTest,MysqlToolContractTest,RunContextTest,RunBudgetTest,DiagnosisHarnessCoreTest,HarnessRetryExecutorTest,ToolCallKeyFactoryTest,SpringAiRetryConfigurationTest,ChatControllerTest' test`:通过。
|
||||
- 静态 scope:新 Harness 包无 ThreadLocal/current-holder/Redis 引用;旧 Chat/AIOps/Controller/JPA 调用链 diff 为空;模型路由/provider 未改。
|
||||
- OpenSpec:`openspec validate single-react-harness-run-context --strict`:通过。
|
||||
@@ -0,0 +1,25 @@
|
||||
# Evidence: single-react-harness-run-context
|
||||
|
||||
## 文档与依赖证据
|
||||
|
||||
- ISS-014 4.2/阶段 2 要求显式 `RunContext`、deadline、取消、预算、retry、Key Factory 和 no-ThreadLocal 边界。
|
||||
- 阶段 0/1 OpenSpec 已冻结框架 `tool_call_id`、两套状态语义和后续阶段串行门禁。
|
||||
- 本地 Spring AI 1.1.7 `SpringAiRetryProperties` 的 `@ConfigurationProperties("spring.ai.retry")` 默认 `maxAttempts=10`;配置已覆盖为 1。
|
||||
|
||||
## 代码证据
|
||||
|
||||
- `SessionContextHolder`、`TokenUsageHolder`、`VerifierContextHolder` 当前是旧链路 ThreadLocal;新 `com.superbiz.agent.harness` 包无任何 holder/ThreadLocal/Redis 引用。
|
||||
- `ChatService.executeChatComplex` 当前自行创建 run、执行两轮 retry、写 `diagnosis_run` 和清理 ThreadLocal;新 Core 不接入该方法,后续应用用例负责迁移。
|
||||
- `DiagnosisRun` 仍保留现有字符串状态和 JPA Schema;阶段 2 未修改实体、Repository 或数据库。
|
||||
- `DiagnosisHarnessCore` 不保存全局 Run map;RunContext 结构不可变,Cancellation/Budget/Lifecycle 为同一 Run 的线程安全句柄。
|
||||
|
||||
## Evidence-driven 结论
|
||||
|
||||
- first-reason-wins cancellation + first-terminal-wins lifecycle 可以用 AtomicReference 实现并跨异步边界共享。
|
||||
- 复合 Tool/Token 预算需要同步一致性;Run bytes 用 CAS 预留避免并发超限或部分增长。
|
||||
- SDK 隐藏 retry 压为一次后,Harness 才能记录 Router/SemanticGuard 的显式 attempt;Tool/Diagnosis/Evidence repair 固定一次。
|
||||
- Key Factory 可直接复用阶段 3A,但不生成或改写框架 Tool Call ID,也不访问 Redis。
|
||||
|
||||
## 工具限制
|
||||
|
||||
- `codebase-retrieval` 和 LSP 不在当前工具集中;使用 `rg`、源码阅读、`jar/javap`、Maven 编译、YAML 解析和 focused tests 补足核对。
|
||||
Reference in New Issue
Block a user