refactor(harness): remove legacy agent architecture

This commit is contained in:
zhuyongxin
2026-07-22 18:02:01 +08:00
parent bc36248cd8
commit 8ee7cc0b70
148 changed files with 3091 additions and 13889 deletions
@@ -1,119 +0,0 @@
# ISS-012 Executor Token 预算与上下文膨胀
**状态**:待规划
**严重程度**:高
**发现时间**:2026-07-20
**关联**:ISS-002、ISS-004、ISS-011
---
## 背景
ISS-011 完成 StateGraph 切换后,复杂 Chat 链路已经具备显式 Node、Gatekeeper、Verifier、Composer 和 Run 级 Trace。但最终 live E2E 暴露出 Executor 的 Token 成本和上下文增长问题:一次只返回安全 Fallback 的请求消耗了超过 11 万 Token。
## 现象
最终验收 Run:
- `runId`:`run-808ac38f-3ad0-4462-a6d0-ed50d8686473`
- 总耗时:`75964ms`
- 最终答案:109 字符,Run 为 `CHAT/SUCCESS`,`degraded=true`
- AgentStep:8 条,其中 Planner 1 次、Executor 7 次
- ToolInvocation:12 次
- Run `total_token_count`:`111802`
Executor 每次模型调用的 Token 逐步上升:
```text
6396 -> 11265 -> 12786 -> 16774 -> 17965 -> 19340 -> 24236
```
工具调用包括 4 次 `lookup_knowledge`、6 次 `query_logs`、1 次 `query_metrics` 和 1 次 `get_available_log_topics`。最终因模型输出缺少 `source_invocation_id`,Gatekeeper 将结果降为 `LOW_CONFID` 并进入安全 Fallback。
## 已确认事实
1. 数据库中 8 条 `agent_step` 均为不同记录,不存在重复插入;`111802` 等于各步骤 `token_count` 的求和。
2. `TokenTrackingChatModel` 当前只保存供应商返回的 `usage.totalTokens`,没有拆分输入、输出、缓存和推理 Token。
3. `ChatService.backfillRunMetrics` 直接累加每条 AgentStep 的 `token_count`。
4. `AgentLoggingHook` 只把模型输入截断为 500 字符写入审计表,无法从当前 Trace 还原模型实际发送的完整 Prompt。
5. Executor 当前没有独立的模型调用次数、工具调用次数或 Token 预算;Graph recursion limit 不能限制 ReactAgent 内部工具循环。
## 初步根因假设
- 每次 Executor 模型调用都会重新携带 Planner 结果、历史消息和之前的工具返回,导致输入上下文随工具循环增长。
- 工具返回内容包含较多日志、知识库结果和检索明细,完整结果被反复带入后续模型请求。
- 工具结果没有稳定返回 `source_invocation_id`,模型无法可靠生成精确证据引用,导致高成本检索后仍然进入 Fallback。
- 当前只能看到 `totalTokens`,尚未确认供应商 usage 中 input/output/cached/reasoning 的精确占比。
## 影响
- 单次诊断成本和延迟不可控,复杂问题可能继续超过模型上下文窗口。
- Token 消耗与最终答案质量不匹配,出现“高成本检索 + 安全降级”的低收益路径。
- 缺少 Token 分项指标,无法建立成本预算、P95 延迟和 degraded rate 门禁。
- Executor 可能重复查询相同或相近的知识域、日志主题和指标。
## 目标
1. 建立按 Run/AgentStep 的 input、output、cached、reasoning Token 可观测性。
2. 为 Executor 增加硬性模型轮数、工具调用和 Token 预算。
3. 将完整工具结果留在 Run Trace/数据库中,模型上下文只接收有界证据投影。
4. 让工具结果直接携带可引用的 `source_invocation_id` 和紧凑 `evidence_refs`。
5. 在预算耗尽时安全结束并明确记录原因,不绕过 Gatekeeper、Verifier 或 Run Trace。
## 建议方案
### 1. Token 统计拆分
- 从 ChatModel usage 中记录 `input_tokens`、`output_tokens`、`cached_tokens`、`reasoning_tokens`(供应商提供时)。
- 保留 `total_token_count` 作为汇总字段,但明确其计算口径。
- 在 `orchestration_trace` 中记录每个 Agent 的累计 Token 和预算命中情况。
### 2. Executor 硬预算
初版建议从以下上限开始,并通过固定 E2E 调整:
- Executor 模型调用最多 4 次。
- 工具调用最多 8 次。
- `lookup_knowledge` 最多 2 次。
- `query_logs` 默认最多返回 5 条日志,并限制单次输出长度。
- 达到预算后停止扩展检索,基于已验真证据输出,或进入带原因的安全 Fallback。
### 3. 有界证据上下文
- 工具完整原始结果继续写入 `tool_invocation`,不直接作为下一轮完整上下文。
- 返回模型的工具视图只保留 invocation ID、工具名、查询条件、有限 evidence refs、excerpt 和 no-evidence 状态。
- 同一工具数组项禁止重复绑定;相同知识域和日志主题不重复查询。
### 4. 证据引用闭环
- 每次 evidence tool 返回结果时直接包含 `source_invocation_id`。
- Executor 输出必须引用该 ID;Gatekeeper 不再依赖事后猜测或唯一候选补全。
- 由于引用失败进入 Fallback 时,Trace 必须记录具体缺失字段和预算消耗。
## 验收标准
- [ ] 每个 AgentStep 可查看 input/output/total Token,供应商支持时可查看 cached/reasoning Token。
- [ ] 固定 `payment-timeout` E2E 的 Token 上限、工具调用上限和最大延迟已定义并通过回归。
- [ ] 连续至少 10 次相同 fixture 运行,Token 和延迟 P95 不超过定义的预算。
- [ ] Executor 预算耗尽时只走安全 Fallback,不绕过 Gatekeeper、Verifier 或 Trace 持久化。
- [ ] 工具返回包含真实 `source_invocation_id`;正常证据链不再因缺少该字段而无谓降级。
- [ ] 12 个 diagnosis eval fixture、Graph workflow/node contract、Trace ownership 回归全部通过。
- [ ] E2E 日志和数据库能按 exact `sessionId + runId` 对齐 Token、工具调用、Fallback 原因和最终状态。
## 非目标
- 不删除 Gatekeeper、Verified Input 或 Verifier。
- 不以降低模型 `maxTokens` 代替上下文治理。
- 不恢复 Sequential/StateGraph 双轨或旧兼容协议。
- 不在本 Issue 中物理删除数据库中的历史 `diagnosis_session` 表。
## 相关文件
- `src/main/java/com/superbiz/agent/hook/TokenTrackingChatModel.java`
- `src/main/java/com/superbiz/agent/hook/AgentLoggingHook.java`
- `src/main/java/com/superbiz/agent/service/ChatService.java`
- `src/main/java/com/superbiz/agent/graph/diagnosis/ReactAgentDiagnosisInvoker.java`
- `src/main/java/com/superbiz/agent/service/ToolInvocationRecorder.java`
- `src/main/resources/prompts/chat-executor-prompt.md`
- `mvp/architecture/stategraph-runtime-architecture.md`
- `devflow/projects/2026-07-17-chat-diagnosis-stategraph-cleanup-docs/evidence.md`
@@ -1,81 +0,0 @@
# ISS-013 Chat 入口解耦与真正 SSE 收敛
**状态**:待规划
**严重程度**:高
**发现时间**:2026-07-20
**关联**:ISS-011、ISS-012
---
## 背景
当前 Chat 入口同时提供 `/api/chat` 和 `/api/chat_stream`。`ChatController` 不仅处理 HTTP/SSE 协议,还直接承担会话创建、历史读取与回写、模型和工具获取、执行策略调用以及异常响应组装,入口职责已经明显超出协议适配层。
现有 `/api/chat_stream` 会等待完整答案生成后再按固定长度切片发送,并不是真正的流式生成。同步和伪流式入口还复制了大部分业务流程,增加了维护成本和行为不一致风险。
## 已确认问题
1. Chat 的会话、历史、模型、工具、执行和结果回写功能耦合在 `ChatController` 中,HTTP 层与应用用例边界不清晰。
2. `/api/chat` 与 `/api/chat_stream` 重复编排同一套 Chat 流程。
3. `/api/chat_stream` 只是对完整答案做事后分块,不具备模型生成过程中的真实增量输出能力。
4. Controller 直接获取 `ChatModel` 和 `ToolCallbackProvider`,将模型基础设施细节暴露到入口层。
5. SSE 使用 Controller 自建的无界缓存线程池,缺少统一生命周期和容量治理。
6. 当前入口错误响应存在 HTTP 状态、外层 `ApiResponse` 与内层 `ChatResponse` 状态不一致的问题。
## 目标
1. 将会话生命周期、历史管理、执行调用和结果回写从 Controller 分离,形成单一 Chat 应用用例入口。
2. 只保留一个 `/api/chat` 接口,并将其协议改为真正的 SSE。
3. SSE 在模型或诊断链路产生内容时增量发送,而不是等待完整答案后再切片。
4. 保留 `sessionId + runId` 作为一次 Chat Run 的稳定关联契约。
5. 在满足入口职责分离的前提下使用最少组件,不引入没有实际职责的接口、工厂或适配层。
## 设计约束
- Controller 只负责请求校验、协议转换和 SSE 生命周期,不负责选择模型、组装工具、管理历史或编排诊断流程。
- 同一次请求只能进入一个应用用例入口,禁止同步和流式路径各自维护一套业务逻辑。
- 真正 SSE 至少需要区分元数据、内容增量、完成和错误事件。
- `sessionId`、`runId` 必须在内容事件之前可获得,并用于日志、数据库和 Trace 对齐。
- 客户端断开、超时和执行失败必须显式终止后台执行并完成 Run 状态记录。
- 不保留旧 `/api/chat_stream` 或同步 `/api/chat` 的兼容分支,直接以新协议为准。
- 优先使用 Spring 管理的执行设施和现有服务能力,不创建无界线程池。
## 建议的最小边界
```text
POST /api/chat (SSE)
-> ChatController:请求与 SSE 协议
-> Chat 应用用例:会话、Run、历史和执行生命周期
-> 现有 Chat 执行能力:简单回答或诊断编排
```
这里的“应用用例”是职责边界,不要求预先拆出多层接口。只有出现独立变化原因或明确复用需求时才增加新组件。
## 验收标准
- [ ] 对外只保留一个 `POST /api/chat`,响应类型为 `text/event-stream`。
- [ ] 删除 `/api/chat_stream` 及同步 Chat 兼容路径。
- [ ] 首个内容事件在完整答案生成完成前发送,禁止通过固定字符切片伪造流式输出。
- [ ] SSE 事件包含稳定的 metadata、content、error、done 契约。
- [ ] Controller 不再直接依赖 `ChatModel`、`ToolCallbackProvider`,也不管理会话历史和 Run 持久化。
- [ ] 同一请求的 `sessionId + runId` 在 SSE、应用日志、`diagnosis_run`、`agent_step` 和 `tool_invocation` 中一致。
- [ ] 客户端断开、超时、模型失败和工具失败都有明确的资源清理与 Run 终态。
- [ ] 不存在 Controller 自建的无界线程池。
- [ ] 单元测试覆盖入口校验和 SSE 事件契约;端到端测试验证真实增量输出、断开清理及 Trace 对齐。
## 非目标
- 不在本 Issue 中重新设计 StateGraph 节点、Gatekeeper、Verifier 或证据协议。
- 不为未来可能出现的其他传输协议预建通用框架。
- 不引入多套 Command、Handler、Adapter、Factory 只为形式上的分层。
- 不保留旧同步接口或 `/api/chat_stream` 的兼容逻辑。
- 不以“完整答案分块发送”作为 SSE 验收通过条件。
## 相关文件
- `src/main/java/com/superbiz/agent/controller/ChatController.java`
- `src/main/java/com/superbiz/agent/service/ChatService.java`
- `src/main/java/com/superbiz/agent/service/session/SessionManager.java`
- `src/test/java/com/superbiz/agent/controller/ChatControllerTest.java`
- `src/test/java/com/superbiz/agent/service/ChatServiceGraphIntegrationTest.java`
- `mvp/architecture/current-mvp-architecture.md`
@@ -1,6 +1,6 @@
# ISS-014 单体 ReAct Agent、Harness 与 ACI 工具瘦身
**状态**:实施中(阶段 0-6B 已归档,下一阶段 7)
**状态**:已完成(阶段 0-7 已验收并归档)
**严重程度**:高
**发现时间**:2026-07-20
**目标分支**:`refactor/chat-single-react-harness`
@@ -1307,27 +1307,27 @@ ISS-014 是总设计 Issue,不创建跨阶段共享的 OpenSpec change。以
## 14. 总体验收标准
- [ ] 复杂诊断只存在一个拥有工具循环的 Diagnosis ReAct Agent。
- [ ] 不存在业务 StateGraph 或 Planner/Executor/Composer 多 Agent 主链路。
- [ ] Harness 不承担业务推理,不演变为工作流引擎。
- [ ] EvidenceGuard 是 Harness 内的确定性能力,不是独立编排节点。
- [ ] SemanticGuard 使用完全隔离上下文,无工具、无记忆、无回调循环。
- [ ] SemanticGuard 不使用未校准数值置信度控制在线释放。
- [ ] 所有 Agent-facing evidence Tool 符合 ACI 状态和 Tool Call ID 契约。
- [ ] RAG 不再向 Agent 返回 ContextPack/Trace/Rerank 等审计数据。
- [ ] query_logs 不要求 Agent 先调用 Topic discovery,且返回聚合、抽样、脱敏结果。
- [ ] MySQL Tool 只读、安全解析、参数绑定、allowlist、超时和结果上限全部生效。
- [ ] Tool 原始结果不会未经有界投影进入 Agent 上下文;Redis canonical evidence 当前可暂不脱敏,但不得被 Agent 直接读取。
- [ ] Redis 每次 Tool Call 单 Key 保存,状态、TTL、容量、ACL 和日志禁泄漏规则均有测试。
- [ ] 每个 Tool Call ID 可以按 exact runId 和调用记录状态验真,并取得对应 `agent_result`。
- [ ] 只保留一个 `/api/chat` SSE 接口。
- [ ] SSE 不输出 Thought、Prompt、原始 Tool 载荷和未验证结论。
- [ ] 客户端断开、模型/Tool/SemanticGuard 超时均有明确取消和 Run 终态。
- [ ] 所有重试由 Harness 按类型化策略装配和记录,无 SDK/HTTP/数据库隐藏重试或整个 Diagnosis Agent 重跑。
- [ ] 最终 E2E 能按 sessionId/runId 对齐 SSE、日志、AgentStep、ToolInvocation 和最终答案。
- [ ] Token、工具调用、Tool 投影、Redis TTL 和总延迟预算均来自集中配置,并有可验证的强制上限和耗尽原因。
- [ ] 11 个 OpenSpec changes 均已独立 Archive,并分别对应一个范围清晰的 Git commit。
- [ ] 不保留旧兼容分支、注释代码、本地 refs 卸载和硬编码凭据。
- [x] 复杂诊断只存在一个拥有工具循环的 Diagnosis ReAct Agent。
- [x] 不存在业务 StateGraph 或 Planner/Executor/Composer 多 Agent 主链路。
- [x] Harness 不承担业务推理,不演变为工作流引擎。
- [x] EvidenceGuard 是 Harness 内的确定性能力,不是独立编排节点。
- [x] SemanticGuard 使用完全隔离上下文,无工具、无记忆、无回调循环。
- [x] SemanticGuard 不使用未校准数值置信度控制在线释放。
- [x] 所有 Agent-facing evidence Tool 符合 ACI 状态和 Tool Call ID 契约。
- [x] RAG 不再向 Agent 返回 ContextPack/Trace/Rerank 等审计数据。
- [x] query_logs 不要求 Agent 先调用 Topic discovery,且返回聚合、抽样、脱敏结果。
- [x] MySQL Tool 只读、安全解析、参数绑定、allowlist、超时和结果上限全部生效。
- [x] Tool 原始结果不会未经有界投影进入 Agent 上下文;Redis canonical evidence 当前可暂不脱敏,但不得被 Agent 直接读取。
- [x] Redis 每次 Tool Call 单 Key 保存,状态、TTL、容量、ACL 和日志禁泄漏规则均有测试。
- [x] 每个 Tool Call ID 可以按 exact runId 和调用记录状态验真,并取得对应 `agent_result`。
- [x] 只保留一个 `/api/chat` SSE 接口。
- [x] SSE 不输出 Thought、Prompt、原始 Tool 载荷和未验证结论。
- [x] 客户端断开、模型/Tool/SemanticGuard 超时均有明确取消和 Run 终态。
- [x] 所有重试由 Harness 按类型化策略装配和记录,无 SDK/HTTP/数据库隐藏重试或整个 Diagnosis Agent 重跑。
- [x] 最终 E2E 能按 sessionId/runId 对齐 SSE、日志、AgentStep、ToolInvocation 和最终答案。
- [x] Token、工具调用、Tool 投影、Redis TTL 和总延迟预算均来自集中配置,并有可验证的强制上限和耗尽原因。
- [x] 11 个 OpenSpec changes 均已独立 Archive,并分别对应一个范围清晰的 Git commit。
- [x] 不保留旧兼容分支、注释代码、本地 refs 卸载和硬编码凭据。
## 15. 非目标