feat(harness): add run context and retry core

This commit is contained in:
zhuyongxin
2026-07-21 18:36:19 +08:00
parent 4274f3350b
commit 6b74990f86
47 changed files with 1910 additions and 1 deletions
@@ -0,0 +1,46 @@
## Why
当前 Chat/AIOps 通过 `SessionContextHolder`、`VerifierContextHolder` 和 `TokenUsageHolder` 等 ThreadLocal 隐式传播 session/run/token/retry 状态,且 Run 终态、取消、预算和重试分散在业务循环与 SDK 默认行为中。阶段 3A 的 Tool boundary、后续 Agent/Guard 和最终入口需要先共同依赖一个显式、可跨同步/异步边界传递的 Harness RunContext,否则会继续耦合旧 ChatService 并产生重复状态机。
## What Changes
- 新增结构不可变的 `RunContext`,显式携带 `sessionId`、`runId`、deadline、取消信号、线程安全预算、类型化重试策略和 first-terminal-wins 生命周期。
- 新增 `DiagnosisHarnessCore` 负责 Run 创建、deadline 检查、模型/Tool/Token/容量预算门禁、取消传播和唯一终态,不承担业务推理或持久化。
- 新增 caller-supplied `RunBudgetLimits`、线程安全 `RunBudget` 与单 Run 字节容量计数器;Core 不硬编码尚未校准的预算默认值。
- 新增 `HarnessRetryPolicies` 与 `HarnessRetryExecutor`,只允许 Router/SemanticGuard 的类型化技术失败最多两次 attempt;Diagnosis Agent、Tool 和 Evidence repair 固定一次 attempt。
- 将 Spring AI 1.1.7 全局底层重试 `spring.ai.retry.max-attempts` 从默认 10 压为 1,避免与 Harness 形成隐藏嵌套重试。
- 新增 Redis Tool Call Key Factory,只负责安全构造固定前缀下的 `runId + toolCallId` Key,不访问 Redis、不生成 Tool Call ID。
- 使用 Fake Model/Tool 和可控 Clock 验证显式异步传播、deadline、客户端取消、预算耗尽、重试记录、Key/容量边界和唯一 Run 终态。
- 本阶段不改 Controller/HTTP/SSE,不接入或临时适配旧 ChatService,不修改 `diagnosis_run` 持久化,也不实现 Tool-specific 投影或 Redis store。
## Capabilities
### New Capabilities
- `diagnosis-harness-run-context`: 提供显式 RunContext、Harness Core、预算、取消、deadline、类型化重试、唯一终态和后续 Tool store 所需的 Key/容量基础。
### Modified Capabilities
- None. 本阶段不声明旧 Chat/AIOps 运行路径已迁移;公开运行和数据库状态映射在后续 change 接入。
## Context Constraints
- 新代码不得读取或写入任何 ThreadLocal;RunContext 只能通过参数或框架受控 context 显式传播。
- RunContext 的结构不可变,但其 cancellation、budget 和 lifecycle 是线程安全的单 Run 状态句柄。
- 同一 Run 只允许第一个终态生效;取消、deadline、预算和异常之间的竞态不得覆盖先到终态。
- 同步模型/Tool 调用的取消只保证阻止后续执行并触发已注册资源取消回调,不虚假承诺无法证明的立即线程中断。
- `NO_EVIDENCE`、业务拒绝、取消和预算耗尽不是可重试技术失败。
- Tool Call Key 使用框架 `tool_call_id`;Key Factory 不生成、替换或回退到其他 ID。
## Interface Impact
- 等级:L2(内部 Harness 基础接口)。新增 Core 类型将被后续 3A-7 阶段消费,但本 change 不修改现有调用方。
- `application.yml` 的 Spring AI retry 从默认 10 attempts 变为 1 attempt,属于有意的内部运行配置变化;当前旧调用若遇到瞬时模型失败将不再由 SDK 隐式重试,避免未记录 attempts。Harness 允许的 Router/SemanticGuard 重试要到后续接入后显式执行和记录。
- 不改变 Controller、SSE、DTO、数据库 Schema 或公开错误码。
## Risks
- 旧运行路径在切换前会失去 SDK 隐式重试但尚未使用 Harness retry;这是为保证“所有 attempt 可观测”接受的短期行为变化,focused tests 需证明启动配置正确。
- RunContext 内含线程安全可变句柄,若被误解为纯值对象可能错误复制;设计和测试必须明确同一 Run 共享同一状态句柄。
- Budget 在模型调用后才能取得实际 Token,可能在记录实际消耗时才发现超限;Core 必须保留实际 usage 并立即终止后续执行。
- 本阶段不写数据库,内存生命周期只服务一次调用链;持久化终态映射由 Chat application use case 阶段负责。