diff --git a/mvp/engineering/harness/Harness agent 域学习笔记-从框架 ReAct 接入到受控停止.md b/mvp/engineering/harness/Harness agent 域学习笔记-从框架 ReAct 接入到受控停止.md new file mode 100644 index 0000000..5045aa1 --- /dev/null +++ b/mvp/engineering/harness/Harness agent 域学习笔记-从框架 ReAct 接入到受控停止.md @@ -0,0 +1,186 @@ +# Harness agent 域学习笔记:从框架 ReAct 接入到受控停止 + +**更新日期**:2026-08-04 +**主题**:agent 域完整链路——装配(Factory)/ 双拦截器(Model/Tool)/ 循环外壳(UseCase)/ 受控停止 / 双视图投影 +**配套**:[tool 域代码学习笔记](Harness%20tool%20域代码学习笔记-工具的注册调用与执行链路.md)(工具链)、[progress 代码学习笔记](Harness%20progress%20代码学习笔记-从拦截器五道门到唯一发布点.md)(Tool 拦截器五道门) + +## 1. 定位:框架 ReAct 接入层(粘合点) + +```text +框架(spring-ai-alibaba ReactAgent):负责 ReAct 多轮(模型 ↔ tool_call) +Harness:不复制 loop,只通过 Interceptor 卡住【每次消耗】 + → Model Interceptor:每次模型调用(预算/审计/Token) + → Tool Interceptor:每次工具调用(五道门) + +原则:拦截器是挂点,不是 loop 实现——Harness 不需要知道框架内部怎么循环 +``` + +## 2. 装配图(DiagnosisAgentFactory——粘合点) + +```mermaid +flowchart LR + subgraph 框架能力 + M["ChatModel"] + T["tools
evidenceTools.callbacks()"] + L["ReactAgent 循环"] + end + subgraph Harness 控制面 + I1["HarnessModelInterceptor
预算+Token 审计"] + I2["HarnessToolInterceptor
五道门+投影"] + H["Hooks
agent_step 落库"] + O["outputSchema
DiagnosisDraft(conclusion 可 null)"] + end + M --> L + T --> L + L --> I1 + L --> I2 + L --> H + L --> O +``` + +**关键装配决策**: + +```text +.parallelToolExecution(false) ← 串行工具:预算与 step 绑定可解释 +.returnReasoningContents(true) ← 推理内容返回 +.releaseThread(true) +每次 run 新建 Agent(create(context))——拦截器持有 RunContext,不可跨 run 复用 +``` + +## 3. HarnessModelInterceptor(模型拦截器) + +```text +interceptModel(request, handler): + core.beforeModelCall(context) ← 预算门(模型调用前扣预算) + call = auditor.begin(...) ← 审计开始(Token 记账) + response = handler.call(request) ← 框架实际调用 + recordUsage(call, response) ← 记 prompt/completion tokens + core.checkActive(context) ← 终态检查(预算耗尽在此打断) + return response +异常:记 0 token + 上抛(不吞) +``` + +**三个动作**:预算(beforeModelCall)→ 记账(auditor.begin/recordUsage)→ 终态(checkActive)——每次模型调用都被 Harness 卡住一次。Usage 字段 null/负数安全兜底(nonNegative)。 + +## 4. HarnessToolInterceptor(工具拦截器,已深学) + +五道门(progress 会话已沉淀):证据工具必炸 handler → 拦截器唯一执行路径 → 边界投影 → 审计落库 → 返回。本会话只补装配视角:`interceptors` 列表里第二个,构造时注入 context + evidenceTools + objectMapper + traceRecorder。 + +## 5. DiagnosisAgentUseCase(循环外壳) + +### 5.1 执行流程 + +```text +execute(context, input): + checkActive → 输入限制(query/previous_turn/input 字节) + reserveRunBytes(input) ← 输入也占 Run 预算 + RunnableConfig.metadata 挂 RunContext ← 显式传递(避免隐式 ThreadLocal) + agent = factory.create(context) ← 每次 run 新建 + response = agent.call(inputJson, config) ← ★ 框架跑完整个 ReAct 循环 + output → 字节限制 → reserveRunBytes(draft) → parse DiagnosisDraft + → completed(draft) 或 受控停止 +``` + +### 5.2 受控停止(controlledExecution)——从异常栈捞回可控信号转正常返回值 + +```text +① DiagnosisCollectionStoppedException(信息饱和后仍强 tool) + → stopped(stopReason) ← 收集该停(draft=null) + +② RunAbortedException + BUDGET_EXHAUSTED + → markBudgetLimitReached + stopped(BUDGET_LIMIT_REACHED) + +③ 其他 RunAborted(取消/超时/内部失败终态)→ 原样再抛 + ← 留给 Application 写 CANCELLED/FAILED(不降级为正常停止) + +④ BudgetExceededException 或 lifecycle 已 BUDGET_EXHAUSTED → 预算 stopped + +⑤ 都识别不了 → 包装 DiagnosisAgentOutputException(Agent 执行失败) +``` + +**关键**:不是笼统「业务异常 → 正常」——**只识别 Harness 约定的可控信号**(沿 cause 链找,因框架可能再包一层);取消/超时必须上抛(诚实终态)。 + +### 5.3 与 recoverInvalidDraft 的分工 + +```text +controlledExecution:loop 被预算/收敛打断(往往还没有合法 draft)→ stopped +recoverInvalidDraft:loop 跑完了,但输出不是合法 DiagnosisDraft → 恢复/重试 +``` + +### 5.4 输出解析(严格) + +```text +draftReader = FAIL_ON_UNKNOWN_PROPERTIES + FAIL_ON_TRAILING_TOKENS(严格模式) +JsonParseException → INVALID_JSON / SchemaInvalid → SCHEMA_INVALID(分类错误码) +空输出 → EMPTY_DRAFT(分类错误码) +``` + +## 6. 双视图投影(ToolResultViewProjector) + +```text +modelObservation() → 模型观察:只含该工具的内容字段 + lookup_knowledge → scope.query + evidence + relevance_level + query_logs → source_kind + scope + patterns + events + query_mysql → scope + columns + rows + + stop_required/reason(需要停止时附加) + +controlView() → 控制视图:evidence_status / relevance_level / returned_count / truncated + (Harness 控制面读,模型看不到) + +分工:模型看到「内容」,Harness 看到「控制信息」(判级/截断/进度消费) +``` + +## 7. 定义层小件 + +| 类 | 作用 | +|---|---| +| `DiagnosisAgentInput` | query + previous_turn(query 必填) | +| `DiagnosisAgentLimits` | maxQuery/PreviousTurn/Input/DraftBytes 四类字节上限 | +| `DiagnosisAgentPrompt` | classpath 加载系统提示词(prompts/diagnosis-agent-prompt.md) | +| `DiagnosisAgentExecution` | completed(draft, progress) / stopped(progress, stopReason) 双形态 | +| `DiagnosisDraftOutputSchema` | BeanOutputConverter postProcess:conclusion 允许 object/null(无结论合法) | +| `EvidenceToolInvoker` | 函数式:RunContext + toolCallId + arguments → ToolBoundaryResult | +| `ParsedAgentToolCall` | 解析出的工具调用(previousObservation + businessInput + arguments) | + +## 8. 关键设计点(面试) + +| 设计 | 为什么 | +|---|---| +| **不复制 loop** | 框架 ReAct 是标准能力;Harness 用拦截器挂在每次消耗点,不需要知道框架内部怎么循环 | +| 每次 run 新建 Agent | 拦截器持有 RunContext——Agent 与 run 绑定,防跨 run 串状态 | +| RunContext 显式传(metadata) | 避免隐式 ThreadLocal(框架线程池/异步下 ThreadLocal 不可靠) | +| 串行工具(parallel=false) | 预算与 step 绑定可解释(并行会让「哪一步花多少钱」不可审计) | +| 受控停止只认约定信号 | 取消/超时绝不降级为正常停止(诚实终态) | +| conclusion 允许 null | 无结论也是合法 Draft(FALLBACK 路径) | +| 双视图 | 模型观察 vs 控制视图分离——控制信息(判级/截断)不进模型上下文 | +| 输出严格解析 | FAIL_ON_UNKNOWN/TRAILING——防止模型输出混入意外字段 | + +## 9. 易错点 + +| 易错 | 正确 | +|---|---| +| Harness 自己实现 Agent loop | 框架跑 loop,拦截器挂消耗点(不复制 loop) | +| 任何异常都转 stopped | 只认约定信号(CollectionStopped/预算);取消/超时原样上抛 | +| Agent 复用 | 每次 run 新建(拦截器绑定 RunContext) | +| ThreadLocal 传 context | RunnableConfig metadata 显式传 | +| 并行工具省时间 | 串行(预算与 step 绑定可解释) | +| 模型看到控制信息 | 双视图:模型看内容,Harness 看控制 | +| 输出宽容解析 | FAIL_ON_UNKNOWN + FAIL_ON_TRAILING(严格模式) | + +## 10. 面试话术(30 秒) + +> "agent 域是框架 ReAct 的接入层:不复制 loop——spring-ai-alibaba 的 ReactAgent 负责多轮循环,Harness 通过两个拦截器卡住每次消耗:Model Interceptor(每次模型调用前 checkActive + 预算,调用后记 Token 审计)、Tool Interceptor(工具调用五道门)。装配在 DiagnosisAgentFactory,每次 run 新建 Agent(拦截器绑定 RunContext,RunnableConfig metadata 显式传递避免 ThreadLocal)。循环外壳 DiagnosisAgentUseCase 做输入/输出字节限制 + Run 预算预留,并实现受控停止——只把 Harness 约定的可控信号(信息饱和、预算耗尽)从异常栈捞回转成 stopped,取消/超时原样上抛留给 Application 写 CANCELLED/FAILED。串行工具保证预算与 step 绑定可解释。" + +## 11. 代码位置索引 + +| 类 | 文件 | +|---|---| +| `DiagnosisAgentFactory` | `src/main/java/com/superbiz/agent/harness/agent/DiagnosisAgentFactory.java` | +| `HarnessModelInterceptor` | `src/main/java/com/superbiz/agent/harness/agent/HarnessModelInterceptor.java` | +| `HarnessToolInterceptor` | `src/main/java/com/superbiz/agent/harness/agent/HarnessToolInterceptor.java` | +| `DiagnosisAgentUseCase` | `src/main/java/com/superbiz/agent/harness/agent/DiagnosisAgentUseCase.java` | +| `ToolResultViewProjector` | `src/main/java/com/superbiz/agent/harness/agent/ToolResultViewProjector.java` | +| `HarnessEvidenceTools` | `src/main/java/com/superbiz/agent/harness/agent/HarnessEvidenceTools.java` | +| `DiagnosisAgentExecution` | `src/main/java/com/superbiz/agent/harness/agent/DiagnosisAgentExecution.java` | +| `DiagnosisAgentOutputException` | `src/main/java/com/superbiz/agent/harness/agent/DiagnosisAgentOutputException.java` | +| 契约(DiagnosisDraft/PreviousTurn) | `src/main/java/com/superbiz/agent/harness/contract/` | diff --git a/mvp/engineering/harness/Harness组件学习路线-进度追踪.md b/mvp/engineering/harness/Harness组件学习路线-进度追踪.md index 9153cf7..fcf8dc8 100644 --- a/mvp/engineering/harness/Harness组件学习路线-进度追踪.md +++ b/mvp/engineering/harness/Harness组件学习路线-进度追踪.md @@ -83,7 +83,7 @@ | `core` | **执行控制**:身份 / deadline / 预算 / 取消 / 唯一终态,checkActive 三道闸 | ✅ 深入 | RunContext、budget、cancel、lifecycle、checkActive、termination | [执行控制笔记](Harness执行控制笔记-终态检查与取消广播.md)、[RunBudget 时序图](RunBudget预算流程-一次Run的资源门禁时序图.md) | | `retry` | **显式可计量重试**:分类裁决(技术/业务)、次数/时间/成本三重封顶、attempt 可审计 | ✅ 深入 | 设计动机、分类裁决、剩余超时、幂等性、SDK 关闭 | [Retry 重试机制](Retry重试机制-显式可计量的attempt循环.md) | | `contract` | **跨层类型化语言**:Draft / PublishedResult / SafeFallback / 状态枚举,防字符串漂移 | ✅ 深入 | 11 个状态枚举五层全景、四个正交轴(RunState⊥ReleaseOutcome、InvocationStatus⊥EvidenceStatus)、纵向映射链、SseOutcome 未接线发现 | [状态流笔记](Harness%20contract%20状态流学习笔记-11个状态枚举的正交全景.md) | -| `agent` | **框架 ReAct 接入**:拦截器把预算/审计/停止协议挂到框架循环上,不复制 loop | ⬜ 部分 | HarnessModelInterceptor(预算/Token 记账) | — | +| `agent` | **框架 ReAct 接入**:拦截器把预算/审计/停止协议挂到框架循环上,不复制 loop | ✅ 深入 | 装配(Factory 粘合点)、双拦截器(Model:预算+Token 审计;Tool:五道门)、UseCase 循环外壳(字节/预算限制)、受控停止(异常栈捞回可控信号)、双视图投影(模型观察 vs 控制视图)、串行工具 | [agent 域学习笔记](Harness%20agent%20域学习笔记-从框架%20ReAct%20接入到受控停止.md) | | `audit` | **可观测账本**:Trace 事件回放、Token 对账、metadata-only(不存敏感正文) | ✅ 深入 | trace 时序线(15 帧真实数据)、Ledger 分账、模型步审计 hook、RunConclusionExtractor、DiagnosisTraceService 三级回放 | [application+audit 笔记](Harness%20application+audit%20学习笔记-从%20Run%20编排到可回放审计.md) | | `application` | **Run 应用所有者**:创建 Run / 路由意图 / 执行分支 / 持久化 / SSE 输出 | ✅ 深入 | 六步编排、取消句柄(CoreRunControl)、统一失败出口、多轮记忆有界化、PublishedResultPolicy 落库 | [application+audit 笔记](Harness%20application+audit%20学习笔记-从%20Run%20编排到可回放审计.md) | | `guard` | **验证分离**:EvidenceGuard 机械验引用真实性 + SemanticGuard 隔离判结论支持度 | ✅ 深入 | 20 个违规码、三层校验(结构/验真/重读投影)、语义不变性、守卫模型受控调用、全栈衔接 | [证据安全链笔记](Harness%20证据安全链学习笔记-从收敛控制到唯一发布点.md) | @@ -110,6 +110,7 @@ | [Harness application+audit 学习笔记-从 Run 编排到可回放审计](Harness%20application+audit%20学习笔记-从%20Run%20编排到可回放审计.md) | application 六步编排/取消句柄/持久化策略;audit 域层次(trace 子体系/Ledger/审计表);**audit vs trace 区别**(真实数据对照)/三级回放 | ✅ 已沉淀 | | [Harness contract 状态流学习笔记-11个状态枚举的正交全景](Harness%20contract%20状态流学习笔记-11个状态枚举的正交全景.md) | 五层状态/四正交轴/纵向映射链/真实数据案例/面试叙事模板与追问应对 | ✅ 已沉淀 | | [Harness 面试复习笔记-五步复习与白板图沉淀](Harness%20面试复习笔记-五步复习与白板图沉淀.md) | **详细版**:30 秒陈述展开/三张白板图/九域五段式讲法(动机→决策→实现→边界→话术)/六易错点带原因/追问应对大全/支付超时案例/纠正认知清单/面试 Checklist | ✅ 已沉淀 | +| [Harness agent 域学习笔记-从框架 ReAct 接入到受控停止](Harness%20agent%20域学习笔记-从框架%20ReAct%20接入到受控停止.md) | agent 域:装配(Factory 粘合点)/双拦截器(Model 预算+Token 审计、Tool 五道门)/UseCase 循环外壳/受控停止/双视图投影/串行工具 | ✅ 已沉淀 | ## 3. 一次请求的完整学习主线 @@ -127,7 +128,7 @@ flowchart LR ## 4. 下一步规划 ```text -主线九域全部 ✅ + 面试五步复习 ✅(共沉淀 13 篇笔记) +主线九域全部 ✅(含 agent 域收尾)+ 面试五步复习 ✅(共沉淀 14 篇笔记) 面试前一天建议: 1. 重读「面试速查」§1-2 + §8(30 秒陈述 / 一张图 / 六易错点) @@ -137,8 +138,8 @@ flowchart LR 5. 2 分钟支付超时案例(复习笔记 §7) 可选深化(不阻塞面试): - 1. agent 域收尾:HarnessModelInterceptor / HarnessToolInterceptor 装配细节 - 2. audit 域深化:RagLookupAuditEnricher 检索审计明细 + 1. audit 域深化:RagLookupAuditEnricher 检索审计明细 + 2. 按需深入:模型步 hook 细节 / DiagnosisChatExecutor 恢复路径 / SSE 序列化 ``` ## 5. 建议每次学完一个域后更新