# Harness Tool 调用链:一次工具调用的完整旅程 **更新日期**:2026-08-03 **主题**:从「模型决定调用工具」到「模型收到观察」的运行时完整链路——拦截器 → invoke → Adapter → ToolBoundary → 返回 → 二次加工 → ToolCallResponse **结构篇**:[Harness tool 域代码学习笔记-工具的注册调用与执行链路](Harness%20tool%20域代码学习笔记-工具的注册调用与执行链路.md)(讲装配/注册/静态结构) **本文**:动态时序(一次调用怎么跑完) ## 1. 旅程全景(一张图) ```mermaid sequenceDiagram participant M as 模型 participant F as 框架 ReactAgent participant I as HarnessToolInterceptor(per-Run) participant ET as HarnessEvidenceTools(单例) participant AD as RagToolAdapter(单例) participant TB as ToolBoundary(单例) participant P as DiagnosisProgressTracker rect rgb(240, 248, 255) Note over M,I: 阶段 A:模型决定 → 拦截器(执行前) M->>F: 输出 tool_call(工具名 + 参数 JSON) F->>I: 回调 interceptToolCall(request, handler) I->>I: ① supports 注册检查 I->>ET: ② parse(typed 严格契约) ET-->>I: ParsedAgentToolCall(previous_observation + input) I->>P: ③ 协议校验(pending 评价)+ 判重 end rect rgb(255, 250, 240) Note over I,TB: 阶段 B:invoke → 执行(backend + 投影) I->>ET: ④ invoke(context, toolName, toolCallId, args) ET->>AD: bridge 闭包 → adapter.execute(context, envelope) AD->>TB: boundary.execute(context, envelope, executor, projector) TB->>TB: ⑤ 五阶段:preflight/预算/begin → executor 跑 backend → 校验 → projector 投影 → markReady TB-->>I: ToolBoundaryResult(READY/ERROR) end rect rgb(245, 255, 245) Note over I,M: 阶段 C:返回 → 模型(执行后) I->>I: ⑥ 双源校验(controlView 重读 evidence_status) I->>P: ⑦ recordCompleted(NO_EVIDENCE 立即 NO_GAIN / FOUND 挂 pending) I->>I: ⑧ modelObservation 加工(有界观察 + stop_required/reason) I-->>F: ToolCallResponse.of(toolCallId, toolName, observation) F-->>M: observation 作为本轮 tool 结果 end ``` **三个阶段**:A 执行前(模型决定→门禁)→ B 执行中(invoke→backend→投影)→ C 执行后(校验→记账→成型)。 --- ## 2. 阶段 A:模型决定 → 拦截器(执行前) ### 2.1 模型怎么知道有这个工具 ``` 模型 → callbacks 里看到工具(名字+描述+Schema)→ 决定调用 lookup_knowledge → 输出 tool_call JSON(工具名 + 参数) ``` 工具名是**模型决定的**——框架把模型输出包成 `ToolCallRequest`(含 toolName + arguments),回调拦截器。 ### 2.2 拦截器的三道执行前门 ```mermaid flowchart LR A["① supports(toolName)?"] -->|"否(非证据工具)"| X["handler.call 透传"] A -->|"是"| B["② parse:typed 严格契约
FAIL_ON_UNKNOWN_PROPERTIES + FAIL_ON_TRAILING_TOKENS"] B -->|"违规"| Y["协议处理(不执行)"] B --> C["③ 协议校验(pending 评价)+ 判重"] C -->|"重复"| Z["recordDuplicateScope(不执行)"] C -->|"通过"| D["进入阶段 B:invoke"] ``` 关键:**不是「拿到名字就执行」**——parse(模型输出必须精确匹配 `RagToolCall{previous_observation, input}`,多一个字段都炸 INVALID_ENVELOPE)、协议校验、判重,三道门不通过都不执行 backend。 --- ## 3. 阶段 B:invoke → 执行(backend + 投影) ### 3.1 invoke 的委托链 ``` I.invoke(context, "lookup_knowledge", "call-1", args) → ET.invokers.get("lookup_knowledge") ← 注册表取 bridge 闭包 → bridge lambda:adapter.execute(context, new ToolCallRequestEnvelope(runId, "call-1", "lookup_knowledge", args, true, true)) → ragAdapter.execute(context, envelope) → boundary.execute(context, envelope, executor, projector) ``` **envelope 是 bridge 里现造的**:`authorized=true, readOnly=true` 写死——每个进 ToolBoundary 的信封都声明「已授权 + 只读」。 ### 3.2 Adapter 组装两个函数(接线员) ```java return boundary.execute(context, envelope, // executor:跑 backend 拿 raw(LookupResult 序列化成 JSON 文本) ignored -> objectMapper.writeValueAsString(legacyExecutor.execute(request.query())), // projector:raw → 有界契约 + evidenceStatus raw -> projector.project(request, envelope.toolCallId(), raw)); ``` | 端口 | 干什么 | 产物 | |---|---|---| | `executor` | 调具体后端 | rawResponse(JSON 文本,执行链「货币」) | | `projector` | 净化定型 | ProjectedToolResult(agentResult, evidenceStatus) | **模型永远看不到 raw**——raw 只用于校验、落 canonical、投影。 ### 3.3 ToolBoundary 五阶段 ```mermaid flowchart TD A["① preflight + Tool 预算 + request bytes → begin(PROJECTING)"] B["② executor.execute(requestJson) → backend raw"] C["③ raw 大小校验 + Run bytes 预留"] D["④ projector.project(raw) → 有界 agent_result + evidenceStatus"] E["⑤ agent_result 校验 + bytes → markReady(READY) 或 markError(ERROR)"] A --> B --> C --> D --> E ``` 返回 `ToolBoundaryResult(READY/ERROR)`——PROJECTING 永不外泄。 --- ## 4. 阶段 C:返回 → 模型(执行后) ### 4.1 拦截器的二次加工(不是直接返回) ```mermaid flowchart LR A["ToolBoundaryResult"] --> B{"status == READY?"} B -->|"否"| E1["error observation
BUDGET_EXHAUSTED 额外 markBudgetLimitReached"] B -->|"是"| C["⑥ 双源校验:controlView 重读 evidence_status"] C -->|"不一致"| E2["OBSERVATION_CONTRACT_MISMATCH 拒绝"] C -->|"一致"| D["⑦ recordCompleted
NO_EVIDENCE → 立即 NO_GAIN
FOUND → 挂 pending"] D --> F["⑧ modelObservation 加工
(有界观察 + stop_required/reason)"] F --> G["ToolCallResponse.of(...) → 框架 → 模型"] ``` ### 4.2 双源校验(自洽性防线) ```text 源1:result.evidenceStatus() ← Projector 投影时计算的声明值 源2:controlView(agentResult).evidenceStatus() ← 从 agent_result 内容重读 一致 ? 通过 : OBSERVATION_CONTRACT_MISMATCH 拒绝 ``` 防止「声明有证据但内容空 / 声明无证据但内容有」的不一致状态进入 progress 记账。 ### 4.3 给模型的对象形态 ``` ToolCallResponse.of(toolCallId, toolName, observation) observation = 有界观察: 正常结果:脱敏后的契约内容(可能裁剪) 饱和时: 附加 stop_required:true + reason 协议错误:repair_required:true + violation_type/期望ID/指令 ``` 框架把 observation 作为本轮 tool 结果给模型——**模型下一轮读取它,决定继续调用(带评价)还是输出 Draft 收尾**。 --- ## 5. 旅程的衔接点(模型视角的闭环) ```mermaid flowchart LR A["模型调工具"] --> B["观察(有界契约)"] B --> C{"模型决定"} C -->|"继续"| D["下次 Tool Call + previous_observation 评价"] C -->|"收尾"| E["输出 Draft → Release 发布"] D --> B ``` **progress 协议的闭环**:模型每次继续调用,都要在 Envelope 里回带对上一轮的 GAINED/NO_GAIN 评价——这就是拦截器 ③ 校验的 pending 逻辑(可回看 progress 笔记)。 --- ## 6. 关键点总结 | 阶段 | 关键认知 | |---|---| | A 执行前 | 工具名是模型决定的;parse 是 typed 严格契约(输出必须匹配 Schema);三道门不通过不执行 | | B 执行中 | executor/projector 是 Adapter 组装进 boundary 的**参数**;执行链货币是 JSON 文本;模型永远看不到 raw | | C 执行后 | 拦截器不直接返回——双源校验 + progress 记账 + modelObservation 成型;ToolCallResponse 才是模型拿到的对象 | ## 7. 面试 30 秒说法 > "一次工具调用的完整旅程分三段:执行前,模型从 callbacks 看到工具并决定调用,拦截器做 supports 分流、typed 严格 parse、协议校验和判重——三道门不通过都不执行 backend;执行中,invoke 经 bridge 到 Adapter,Adapter 把 executor(跑 backend 拿 raw)和 projector(raw 投影成有界脱敏契约)组装进 ToolBoundary 的五阶段门禁,返回 ToolBoundaryResult;执行后,拦截器不直接返回——先双源校验 evidence_status,再 recordCompleted 记进度,再 modelObservation 加工成有界观察,最后包装成 ToolCallResponse 给模型。模型看到的永远是脱敏后有界的观察,raw 只进 canonical 供审计验真。" ## 8. 代码位置索引 | 环节 | 文件 | |---|---| | 拦截器(A/C 阶段) | `src/main/java/com/superbiz/agent/harness/agent/HarnessToolInterceptor.java` | | 注册表 + parse + invoke | `src/main/java/com/superbiz/agent/harness/agent/HarnessEvidenceTools.java` | | Adapter 组装(B 阶段) | `src/main/java/com/superbiz/agent/harness/tool/adapter/RagToolAdapter.java` | | 五阶段门禁 | `src/main/java/com/superbiz/agent/harness/tool/boundary/ToolBoundary.java` | | 双源校验 + 观察成型 | `src/main/java/com/superbiz/agent/harness/agent/ToolResultViewProjector.java` | | 模型观察形态 | `src/main/java/com/superbiz/agent/harness/agent/ToolControlView.java` |