Files
SuperBizAgent-java/mvp/engineering/harness/Harness组件全景-职责-设计原因与边界.md

31 KiB
Raw Permalink Blame History

Harness 组件全景:职责、设计原因与边界

更新日期:2026-07-29 适用代码:src/main/java/com/superbiz/agent/harness 术语与状态:CONTEXT.md · Harness生命周期与状态.md 配套主文:Harness设计-非确定性Agent的确定性控制边界.md

本文是完整组件参考手册,不建议第一次接触 Harness 时顺序阅读。入门请先读 Harness 阅读入口,需要逐组理解组件时使用组件渐进式导读。

1. 这份文档怎样定义“全部组件”

当前 harness 目录包含 10 个一级职责域、189 个 Java 源文件。它们并不都是独立运行的“服务”:

  • 执行组件拥有行为,例如 Core、Interceptor、Boundary、Guard、Release、Executor;
  • 端口与适配器隔离框架、Redis、JPA、JDBC 和业务 Tool;
  • 状态与契约类型固定跨组件语言,防止字符串协议漂移;
  • Limits、Prompt 和异常类型把边界配置与失败语义显式化。

因此本篇先解释 10 个职责域为什么存在,再列出每个生产类型。判断某个类应该放在哪里时,只问三个问题:它拥有什么状态、它能作出什么决定、它绝不能决定什么。

2. 组件地图

职责域 文件数 解决的问题 核心组件
application 34 谁创建 Run、路由请求、持久化和映射公开结果 ChatApplicationUseCase、三个 Executor、ChatRunStore
core 14 deadline、取消、预算和唯一终态由谁拥有 DiagnosisHarnessCore、RunContext、RunBudget、RunLifecycle
agent 17 如何把框架 ReAct 接入 Harness,而不复制 ReAct DiagnosisAgentUseCase、Factory、Model/Tool Interceptor
progress 14 如何识别无增益、重复和协议空转 DiagnosisProgressTracker、Projector、scope normalizer
tool 49 Tool 如何安全执行、保存真相并只暴露必要内容 ToolBoundary、Adapters、Projectors、Canonical Store、MySQL sandbox
guard 15 如何分开验证引用真实性和结论支持度 EvidenceGuard、SemanticGuard、GuardModelCall
release 6 谁拥有最终 SUCCESS / FALLBACK 决策 DiagnosisReleaseUseCase、EvidenceRepair、SafeFallbackFactory
retry 8 哪些失败允许重试、attempt 如何可见 HarnessRetryExecutor、Policies、Failure taxonomy
audit 17 如何重放决策而不复制敏感正文 Trace、Tool audit、Model ledger、Agent hook
contract 15 跨层公开语言如何保持类型化 Draft、PublishedResult、Fallback、状态枚举
flowchart LR
    APP["application<br/>Run 与公开用例"] --> CORE["core<br/>执行不变量"]
    APP --> AGENT["agent<br/>ReAct 接入"]
    AGENT --> PROGRESS["progress<br/>收敛控制"]
    AGENT --> TOOL["tool<br/>证据边界"]
    APP --> RELEASE["release<br/>唯一发布"]
    RELEASE --> GUARD["guard<br/>真实性与支持度"]
    CORE --> RETRY["retry<br/>显式 attempt"]
    CORE -.-> AUDIT["audit<br/>可观测账本"]
    AGENT -.-> AUDIT
    TOOL -.-> AUDIT
    RELEASE -.-> AUDIT
    CONTRACT["contract<br/>类型化语言"] -.-> APP
    CONTRACT -.-> AGENT
    CONTRACT -.-> TOOL
    CONTRACT -.-> GUARD
    CONTRACT -.-> RELEASE

3. Application:Run 的应用所有者

为什么需要

Core 只知道一次 Run 是否活跃,并不知道 HTTP、SSE、意图路由、数据库持久化和上一轮上下文。若这些职责塞进 Core,Harness 会变成业务工作流引擎;若散落在 Controller,则每个入口都可能产生不同的终态和 Fallback。

核心组件

组件 为什么设计 作用 明确边界
ChatApplicationUseCase 为一次请求建立唯一应用事务边界 解析 session、读取历史、创建 Run、路由、执行分支、落终态和安全输出 不做诊断推理,不自行构造诊断 Fallback
IntentRouter 路由也会消耗模型、超时并返回非法 JSON 在有界输入、timeout 和显式 retry 下输出唯一 IntentType 不执行业务 Tool,不生成最终回答
SystemChatExecutor 系统问答不需要 ReAct,但仍必须受模型预算控制 单轮回答产品能力和闲聊 不声称查询了实时数据
KnowledgeQueryExecutor 知识问答需要一次 RAG 和一次受控生成,但不需要完整诊断链 调用知识 Tool、校验 Tool result、生成带来源答案 首版不进入 SemanticGuard,不执行多 Tool 诊断
DiagnosisChatExecutor Agent 执行和安全发布需要一个明确接合点 执行 Agent、处理合法停止/非法 Draft、调用 Release、映射公开内容 不重复 Guard 或 Release 决策
ChatRunStore / JpaChatRunStore 内存 Run 状态与长期数据库状态职责不同 保存 Run 开始、intent、结果、预算摘要;读取安全上一轮 不保存 canonical raw;只允许安全发布结果进入 PreviousTurn
PublishedResultPolicy 直接复用上一轮完整结果会让上下文无限增长并传播失败内容 生成可持久化 PublishedResult 和有界 PreviousTurn Fallback、失败、raw evidence 不进入下一轮

类型清单

类型 分类与功能
ChatApplicationRequest、ChatApplicationResult 应用入口和出口 DTO;固定 session、run、intent、outcome 和内容类型
ChatApplicationContent、ChatContentType 公开内容的 sealed/typed 边界,避免任意对象直接发给 SSE
DiagnosisContent、KnowledgeContent、SystemChatContent、FallbackContent 四种公开内容载体;分别包装安全诊断、知识答案、系统回答和 Fallback
ChatApplicationStatus、ChatApplicationObserver 向 SSE 报告有界阶段,不泄露模型内部步骤
ChatRunControl 只向入口暴露 exact session/run 和客户端断开取消能力
ChatApplicationException、ChatFailureCode 把内部异常映射为稳定、可公开的失败语义
IntentRouting、SystemChatOperation、KnowledgeQueryOperation、DiagnosisOperation 四个应用端口;使主用例不依赖具体模型或执行器
DiagnosisExecutionResult Diagnosis 分支的内部返回,携带 outcome、content、published result 和预算已处理标记
IntentRouterInput、IntentRouterLimits、IntentRouterPrompt、IntentRoutingException 路由输入、边界、Prompt 和失败类型
KnowledgeQueryLimits、SingleTurnExecutorLimits 知识与单轮模型路径的输入、输出、timeout 上限
ChatRunStore、RoutingHistory 持久化端口及最小路由历史
PreviousTurnLimits、PublishedResultPolicy 安全历史的字段/大小限制与投影策略

4. Core:每个 Run 的执行不变量

为什么需要

模型、Tool、Guard 和 Application 都要检查取消、deadline 和预算。如果每层各自维护计数或终态,会出现多个真相源;如果依赖 ThreadLocal,则异步线程无法可靠继承。

组件 为什么设计 作用 明确边界
DiagnosisHarnessCore 所有执行边界需要同一套 active / budget / terminal 规则 创建 RunContext,执行模型/Tool/Token/bytes 门禁,处理取消和终态 不持久化,不调用 Agent/Tool,不维护全局 Run Map
RunContext Run 身份和状态句柄必须一起显式传播 固定 sessionId、runId、deadline 及 per-run handles record 结构不可变,不代表内部计数不变化
RunLifecycle 成功、失败、取消可能竞态到达 原子 compareAndSet 实现 first-terminal-wins 不映射公开 ReleaseOutcome
RunCancellation 取消原因和回调只能被第一个请求确定 first-reason-wins,并通知 lifecycle/资源回调 不承诺强杀同步 Provider 请求
RunBudget 多维预算必须原子地先检查再计数 模型、Tool、单 Tool、输入/输出/总 Token 和 bytes 计量 不判断信息是否有价值
RunCapacityCounter bytes 可能由并发边界累计 CAS 方式维护 Run 总容量 不负责字段级截断策略

类型清单

类型 分类与功能
RunBudgetLimits、RunBudgetUsage 预算配置和值快照;区分限制与已使用量
BudgetKind、BudgetExceededException 明确指出耗尽的是模型、Tool、Token 还是 bytes
RunState、RunTermination 内存执行状态与不可变终止快照
RunCancellationReason 客户端断开、用户请求、deadline、预算和内部失败的取消分类
RunAbortedException 将已经确定的 RunTermination 穿过深层调用栈,不丢失终态

5. Agent:框架 ReAct 与 Harness 的接合层

为什么需要

业务需要框架原生 Tool Calling 和 ReAct loop,但框架默认并不知道项目的 RunContext、预算、审计、Tool 双视图和停止协议。接合层的目标是“拦截边界”,不是重新实现 Agent 循环。

组件 为什么设计 作用 明确边界
DiagnosisAgentFactory 每个 Run 的 interceptor 和 metadata 不同 为当前 Run 创建 ReactAgent,注册 Tool callback、Prompt、Hook 和 interceptor 不缓存跨 Run Agent 状态
DiagnosisAgentUseCase 框架输入输出是字符串,业务要求严格 Draft 和 bytes 边界 序列化输入、显式注入 Run metadata、调用 Agent、严格解析 Draft、映射受控停止 不执行证据和语义校验
HarnessModelInterceptor 每一轮 ReAct 模型调用都必须进入预算和 Token 账本 调用前 reserve,调用后记录 Provider usage 并再次检查 active 不重试模型
HarnessToolInterceptor 模型 Tool Call 中混有 Harness 进展协议和业务参数 校验 Envelope、上一轮增益、重复/饱和、调用 Tool、投影 observation、交付 STOP_REQUIRED 不执行 backend,不复制 ToolBoundary 预算
HarnessEvidenceTools Tool schema 必须由服务端原生注册,且业务 Tool 可选启用 注册 RAG/log/MySQL callback,严格解析通用 Envelope,桥接 Adapter Prompt 不写死 Tool schema;未配置 MySQL 时不暴露死 Tool
ToolResultViewProjector canonical agent_result 仍含 Harness 控制字段 生成 Control View 和白名单 Model Observation 不读取 raw response,不判断根因

类型清单

类型 分类与功能
DiagnosisAgentInput、DiagnosisAgentExecution Agent 用例输入,以及 Draft/ProgressSnapshot/stop reason 的执行结果
DiagnosisAgentLimits query、previous turn、总输入和 Draft 的 UTF-8 bytes 上限
DiagnosisAgentPrompt、DiagnosisDraftOutputSchema 最小职责 Prompt 与严格结构化输出 Schema
EvidenceToolInvoker Agent 层到具体 Adapter 的函数端口
ParsedAgentToolCall 解包后的 previous observation、typed business input 和 JSON 参数
ToolControlView Harness 消费的 evidence status、count、scope 等控制视图
DiagnosisAgentLimitException Agent 输入或输出越界
DiagnosisAgentOutputException 空、非法 JSON、Schema 不合格 Draft,并可携带安全 ProgressSnapshot
DiagnosisCollectionStoppedException STOP_REQUIRED 后仍请求 Tool 时,把受控停止穿出框架 loop

6. Progress:从资源上限到正常收敛

为什么需要

预算只能阻止无限消耗,不能识别“连续查询没有推进诊断”。Progress 子系统只保存 Run 内最小控制状态,不复制完整证据。

组件 为什么设计 作用 明确边界
DiagnosisProgressTracker 连续无增益、待评价调用和协议错误需要线程安全单一所有者 记录 completed scope、pending evaluation、NO_GAIN、协议错误、饱和和一次停止指令 不保存 raw/agent_result,不判断非空内容的业务价值
ToolScopeNormalizer 字段顺序或无关格式不应绕过重复检测 将各 Tool typed input 规范化为稳定 scope 首版不做自然语言语义去重
DiagnosisProgressProjector 受控停止或非法 Draft 后仍需安全说明已检查内容 按 Tracker identity 回读 canonical READY 记录,生成有界事实、来源和限制 无法验真的记录直接排除,不输出 raw
DiagnosisProgressProjection Agent 用例不应依赖具体 Redis projector 定义 Run 到安全快照的端口,并提供 empty 实现 不决定 Fallback 类型

类型清单

类型 分类与功能
InformationGain 仅 GAINED / NO_GAIN,避免引入含混质量等级
DiagnosisCollectionState COLLECTING / SATURATED,只描述信息收集状态
DiagnosisStopReason 区分信息饱和、预算限制和进展协议错误
PreviousObservation 模型在下一次 Tool Call 回传上一轮 tool_call_id + information_gain
CompletedToolCall、ToolScopeIdentity 保存已完成调用的 identity 和规范化 scope,不保存 payload
DiagnosisProgressSnapshotState Tracker 的内部控制快照,含计数、pending ID 和停止指令状态
DiagnosisProgressSnapshot Release 可消费的安全快照,含 verified sources、observed facts 和 limitations
ProgressProtocolViolationType、ProgressProtocolViolationException 缺字段、乱序评价、意外评价和非法 Envelope 的稳定分类

7. Tool:执行、真相、投影与后端安全

Tool 是文件最多的职责域,但可以按四层理解。

7.1 Boundary:所有 Tool 共用的确定性入口

组件 为什么设计 作用 明确边界
ToolBoundary 每个 Adapter 自己实现授权、预算、store 和 audit 会产生漂移 统一 preflight、active、read-only、Tool budget、bytes、canonical 状态迁移和 audit 不理解 Tool 业务内容;不做信息增益判断
ToolCallRequestEnvelope 调用必须同时证明 Run、ID、Tool、参数、授权和只读意图 Boundary 的内部调用信封 不等同于模型侧 progress Envelope
ToolExecutor Boundary 不依赖具体 backend raw 执行函数端口 不投影结果
ToolResultProjector raw 到 canonical agent_result 的逻辑因 Tool 而异 标准化、限制、计算客观 evidence status 不判断结论支持度
ToolBoundaryResult 只允许 READY 或 ERROR 离开 Boundary 向上返回 ID、状态、agent result、evidence status 或稳定错误码 PROJECTING 不对外暴露
ProjectedToolResult projector 同时返回有界 agent result 与客观状态 Boundary/store 的中间值 不是最终 Model Observation
ToolBoundaryErrorCode 不能把内部异常正文交给 Agent 固定非法 ID、Run mismatch、未授权、非只读、超限、执行/投影/store 等错误 不包含敏感原因

7.2 Canonical Store:TTL 内的完整 Tool 真相

组件 为什么设计 作用 明确边界
CanonicalInvocationStore Guard 需要独立于 Agent 上下文读取原始调用真相 定义 begin/find/markReady/markError 状态端口 不负责长期审计
RedisCanonicalInvocationStore 完整 request/raw 有敏感性和容量,适合短期 TTL 存储 原子创建,保持剩余 TTL 的状态更新,读取不续期 只有该 Adapter 访问 Redis
CanonicalToolInvocation request、raw、agent_result 和两个状态必须形成合法组合 封装 PROJECTING -> READY/ERROR 转换及 Run 可引用判断 ERROR/PROJECTING 不可被 EvidenceGuard 引用
CanonicalInvocationLimits Redis record、raw 候选和 agent result 需要独立硬限制 统一 UTF-8 bytes 校验 不执行截断
ToolCallKeyFactory Key 要隔离 Run 且保留框架 ID 校验安全 segment,生成 prefix/runId/toolCallId 不生成或改写 tool_call_id
DuplicateInvocationException、InvocationStateException、CanonicalStoreException、ResultTooLargeException Store 失败需要可分类而不是字符串猜测 区分重复、非法迁移、基础设施错误和容量错误 最终对 Agent 仍映射为安全错误码

7.3 Adapter 与 Projector:隔离业务后端

组件 为什么设计 作用 明确边界
RagToolAdapter 检索实现会演进,但 Agent schema 和 Boundary 不应随之变化 解析 RAG request,经 Boundary 调 backend 和 RagResultProjector 不把检索轨迹直接给模型
QueryLogsToolAdapter 现有日志 Tool 的返回和时间语义需要规范化 校验范围、桥接 backend、投影日志事件 0 命中是 NO_EVIDENCE,不是技术失败
MysqlToolAdapter LLM 生成 SQL 必须经过授权和只读沙箱 解析 request、先校验 SQL,再经 Boundary 执行和投影 未配置数据源时 Tool 不注册
RagResultProjector 上游 evidence、score、relevance 表达不稳定 输出有界证据,并保留粗粒度 relevance_level 非空/REFERENCE 不等于支持根因
QueryLogsResultProjector raw 日志不可直接进入上下文 生成有界 events、pattern、scope 和截断标记 不泄露无限日志正文
MysqlResultProjector JDBC rows 和元数据需要稳定 Agent contract 限制行列、单元格和 bytes,输出 rows/columns/scope 不执行 SQL 安全判断
ToolProjectionLimits、MysqlToolLimits 各投影边界必须集中、可测试 配置证据数、文本、行列和结果上限 不改变 Run 总 bytes 预算

7.4 MySQL 只读沙箱

组件 为什么设计 作用 明确边界
MysqlSqlValidator readOnly=true 声明不能证明 SQL 安全 解析并限制单条 SELECT、数据源、schema/table/column、limit 等 不执行查询
MysqlDataSourceDefinition 连接存在不代表 Agent 可访问所有表列 保存逻辑数据源和 allowlist 不携带密码
MysqlQueryPlan 校验后的内容不应在 Executor 再解析原始请求 固定已批准数据源、SQL 和限制 只能由 Validator 产生
MysqlReadOnlyExecutor / JdbcMysqlReadOnlyExecutor JDBC 细节与 Harness Boundary 解耦 在只读连接、timeout 和 row limit 下执行 plan 不接收未经验证的 request
MysqlRawResult JDBC 原始但结构化的执行结果 携带 columns、rows、truncated、duration 仍需 Projector 后才能进入 canonical agent_result
MysqlSecurityException 安全拒绝必须与基础设施错误区分 表达非法 SQL、未授权对象等 不向 Agent泄露详细策略

7.5 Tool contract 完整清单

类型 设计原因与功能
AgentToolContracts Tool 名、描述和服务端注册契约的唯一常量源,防止 Prompt/代码漂移
RagToolCall、QueryLogsToolCall、MysqlToolCall 模型侧统一 Envelope:previous_observation + input
RagToolRequest、QueryLogsRequest、MysqlToolRequest 业务 Tool 的 typed input;Interceptor 解包后仍保持原业务协议
RagToolResult、QueryLogsToolResult、MysqlToolResult canonical agent-facing 标准结果,不等同于 raw backend response
RagEvidence、SourceDocument 有界知识证据及文档身份
RagRelevanceLevel PRECISE / HIGHLY_RELEVANT / REFERENCE 等客观检索相关度,不是诊断置信度
LogQueryScope、LogEvent、LogPattern 日志查询实际范围、事件和聚合模式
LogSourceKind、LogTopic 限制日志来源和主题的可选集合
ToolContractCollections 对 contract 集合做 defensive copy 和空值规范化

8. Guard:把两个验证命题分开

8.1 Evidence Guard

组件 为什么设计 作用 明确边界
EvidenceGuard 模型不能被信任去验证自己引用的 ID 和 Run 归属 严格验证 Draft、analysis ID、canonical READY、当前 Run 所有权、evidence status 和引用闭包 不调用模型,不判断结论语义是否成立
EvidenceGuardResult 校验只能是 verified snapshot 或 violations 阻止半有效结果继续发布 不包含原始 Draft 修复逻辑
VerifiedEvidenceSnapshot SemanticGuard 只能看到已验真的证据投影 汇总 verified analyses 和 sources 不包含 raw Tool payload
VerifiedAnalysisEvidence、VerifiedEvidence 保持 analysis 到证据的归属关系 提供来源、scope、excerpt 等安全证据 不提升为业务结论
EvidenceViolation、EvidenceViolationCode Repair 和 Fallback 需要稳定失败原因 标识缺 analysis、未知调用、Run mismatch、非 READY、引用不闭合等 不保存内部异常栈

8.2 Semantic Guard 与模型调用边界

组件 为什么设计 作用 明确边界
SemanticGuard 引用真实仍可能无法支持结论 用隔离单轮调用输出 SUPPORTED / UNSUPPORTED,严格解析并有限 retry 无 Tool、无记忆、不访问 Redis、不改写 Draft
GuardModelCall Router、单轮回答、Repair、Semantic 都需一致的模型预算、timeout 和 bytes 控制 在线程池中执行单轮 ChatModel,记录 usage,超时取消 future 不决定业务 retry policy
SemanticDraftView Guard/Repair 只应比较用户可见语义 从 Draft 提取稳定语义视图,并判断修复前后是否一致 不包含引用实现细节
SemanticGuardInput、SemanticGuardDecision 固定 Guard 输入和带 verdict 的输出 只传 query、Draft view 和 verified snapshot 不传 Prompt 历史或 raw Tool response
SemanticGuardLimits、SemanticGuardPrompt 输入、输出、单次/总 timeout 和判定职责可测试 限定一次语义审查的成本与 Prompt 不暴露给 Diagnosis Agent
GuardModelCallException 单轮模型失败要按 timeout、transport、parse、schema 分类 为 Harness retry 提供类型化信号 不直接映射用户内容

9. Release:唯一公开决策点

为什么需要

如果 Agent、Guard、Application 都可以各自构造结果,同一个失败会出现不同用户语义,迟到内容也可能绕过安全校验。Release 必须集中回答一个问题:当前 Run 有哪些内容可以公开?

组件 为什么设计 作用 明确边界
DiagnosisReleaseUseCase Draft、停止、Guard 和 Repair 的组合分支必须只有一个所有者 处理有结论、无结论、受控停止、非法 Draft;协调 Guard/Repair/Semantic;返回成功或 Fallback 不持久化、不发送 SSE、不写新结论
EvidenceRepair 引用或结构小错不应总是丢掉语义正确的 Draft 单轮修复引用,严格解析,并用 SemanticDraftView 保证用户语义不变 只 attempt 一次;不新增事实、不改结论
SafeFallbackFactory 失败文案若交给模型生成会再次引入幻觉 确定性构造 evidence failed、semantic unsupported/unavailable、insufficient evidence、missing context 只使用已验真事实和有界问题码
DiagnosisReleaseResult 下游不能同时收到 Draft 和 Fallback 类型化承载 outcome、Draft、verified evidence 或 SafeFallback 不等同于 RunState
EvidenceRepairLimits、EvidenceRepairPrompt Repair 的成本与职责必须比 Diagnosis Agent 更窄 限制输入/输出/timeout,固定只修引用的指令 不允许 Tool Calling

10. Retry:显式、类型化、可审计的 attempt

为什么需要

重试会改变成本、延迟和副作用,必须是调用者的有意识决策。统一 Executor 负责循环,但具体组件持有自己的 Policy。

类型 设计原因与功能
HarnessRetryExecutor 在每个 attempt 前检查 Run active,统一记录成功/失败,并绝不吞掉取消和预算耗尽
HarnessRetryPolicies 集中定义 Router/SemanticGuard 最多两次,其余一次的严格策略
RetryPolicy maxAttempts + retryable failures 的不可变值,避免布尔 retry=true
RetryFailure timeout、transport、parse、schema、invalid output、cancel、budget 等稳定分类
RetryAttempt 记录 attempt 序号、是否成功和失败类型,供 Trace 使用
RetryOperation、RetryFailureClassifier 将执行和异常分类作为端口注入,Executor 不依赖具体模型组件
RetryExecutionException 重试终止时保留最终 attempt 和失败分类

11. Audit:记录控制事实,而不是复制业务正文

为什么需要

诊断系统需要回答“为什么停止、调用了什么、Token 是否对账、发布为何降级”,但普通观察面不应长期保存 Prompt、SQL、日志 query、raw response 或 reasoning。

组件 为什么设计 作用 明确边界
DiagnosisTraceRecorder / JpaDiagnosisTraceRecorder 所有阶段需要同一 exact-run timeline 按 Run 分配 sequence,追加安全事件;失败 best-effort 不改变业务结果,不保存敏感正文
TraceAuditEvents 各组件手写 details 容易字段漂移或泄露 集中构造 run/routing/model/tool/progress/guard/release 事件 只接受有界、安全字段
DiagnosisTraceAuditEvent Recorder 与业务组件解耦 统一事件 identity、phase、type、status、details 不是领域事件总线
ToolInvocationAuditSink / JpaToolInvocationAuditSink canonical raw 不能长期保存,但调用元数据要留存 保存 exact Run、Tool ID、状态、耗时、bytes 和有界 enrichments 不保存完整 request/raw/agent result
RagLookupAuditEnricher RAG 需要有限的质量诊断字段 从受限输入提取 step/query/relevance 等安全摘要 不打印完整 rewritten/raw query
HarnessAgentAuditHook 框架每轮 Agent 模型步骤需关联数据库 step 和 Provider reasoning 可用性 写 AgentStep metadata,并把 reasoning/assistant text 放独立受限存储 reasoning 不进普通 Timeline、Guard 或下一轮上下文
AgentStepAuditTracker Tool audit 需要关联当前 Agent step,但不能使用 ThreadLocal 按 runId 显式绑定/查询/清理 stepId 不保存 Step 实体
ModelCallAuditor / ModelCallLedger 所有模型入口都要按组件、轮次对账 Token begin call、记录 Provider usage、写 MODEL_TOKEN_USAGE、生成 Run reconciliation Usage 缺失时标 unavailable,不估算
RunConclusionExtractor Trace/DB 常需直接读取安全发布结论 从 public JSON 提取 conclusion 不读取 Provider reasoning

Audit 类型清单

类型 分类与功能
ModelCallComponent Router、System、Knowledge、Diagnosis、Repair、Semantic 的统一组件枚举,并映射 Trace phase
ToolInvocationAuditEvent durable Tool metadata 事件
TracePhase、TraceEventType、TraceEventStatus Timeline 的阶段、事件和结果词汇表

12. Contract:跨组件唯一语言

为什么需要

Harness 横跨模型 JSON、Java 对象、Redis、JPA 和 SSE。若状态以自由字符串在各层重复定义,SUCCESS、READY、EVIDENCE_FOUND 很容易被混为一谈。Contract 包将不同维度保持正交。

类型 为什么存在与表达什么
DiagnosisDraft Diagnosis Agent 唯一结构化输出;analysis 绑定 Tool Call 引用,允许 conclusion=null
KnowledgeAnswerDraft Knowledge Query 单轮模型输出,答案项绑定文档来源
PublishedResult 只有成功、安全的诊断结果可持久化为下一轮候选
PreviousTurn PublishedResult 的有界历史投影,不是完整会话记录
SafeFallback 确定性公开降级结构,包含 verified sources、observed facts、limitations、next steps 和 validation issues
SourceDocument 知识回答中的文档身份与来源
IntentType SYSTEM_CHAT / KNOWLEDGE_QUERY / DIAGNOSIS 路由结果
InvocationStatus Tool invocation 生命周期:PROJECTING / READY / ERROR
EvidenceStatus Tool 客观结果:EVIDENCE_FOUND / NO_EVIDENCE / ERROR
SemanticVerdict 最终推论支持度:SUPPORTED / UNSUPPORTED
ReleaseOutcome Harness 发布结果:SUCCESS / FALLBACK / FAILED / CANCELLED
SseOutcome 当前未被运行时消费的遗留枚举;公开 done 实际使用 ReleaseOutcome,不要把它作为 SSE 协议真理源
FallbackType evidence validation、semantic、insufficient evidence、missing context 等降级原因
AnalysisKind 区分正向证据分析与限定范围的负向观察
ContractCollections 对公共 contract 集合做 defensive copy、去空和不可变处理

13. 配置装配:组件如何真正连起来

HarnessChatConfiguration 不在 harness 包内,但它是运行时组件图的 Composition Root:

  • 构造两个有界线程池:Chat worker 与 Harness model executor;
  • 从 ChatHarnessProperties 创建 Run、预算、停止阈值和各模型调用 limits;
  • 装配 Redis canonical store、ToolBoundary、三类 Adapter/Projector;
  • 仅在存在有效逻辑数据源时注册 query_mysql;
  • 为每个 Run 创建带 Model/Tool interceptor 和 Audit Hook 的 Agent;
  • 装配 Guard、Repair、Release、Router、三个执行分支和顶层 Application UseCase。

它存在的原因是让所有限制和替换点在一个地方可见。组件内部不应自行读取 Spring 配置或寻找全局 Bean,否则 focused test 很难证明其边界。

14. 用调用链快速定位组件

想回答的问题 首先阅读 接着阅读
一次请求如何创建并结束 Run ChatApplicationUseCase DiagnosisHarnessCore、JpaChatRunStore
ReAct 每轮如何计预算 DiagnosisAgentFactory HarnessModelInterceptor、ModelCallAuditor
Tool 为什么被拒绝 HarnessToolInterceptor DiagnosisProgressTracker、ToolBoundary
Tool 结果为何没有原样给模型 ToolBoundary 具体 ResultProjector、ToolResultViewProjector
一条 evidence_ref 如何验真 EvidenceGuard CanonicalToolInvocation、具体 ToolResult contract
为什么最终是 FALLBACK DiagnosisReleaseUseCase SafeFallbackFactory、Trace 的 RELEASE 事件
为什么诊断停止继续查 DiagnosisProgressTracker Tool progress / rejection / collection stop Trace
Token 为什么对不上 ModelCallAuditor ModelCallLedger、RUN_FINISHED reconciliation

15. 组件边界自检

未来新增能力时,可以用下面的问题判断放置位置:

  1. 它是在判断业务根因吗?应留在 Diagnosis Agent,而不是 Core/Tool/Guard。
  2. 它能由代码机械证明吗?应放在 Boundary、Tracker 或 EvidenceGuard。
  3. 它需要完整 Tool 真相吗?读取 canonical store,不要从 Agent observation 反推。
  4. 它要决定公开内容吗?只能进入 Release,不要在 Application 或 Guard 私自构造。
  5. 它是短期验真数据还是长期运营元数据?前者 canonical,后者 metadata audit。
  6. 它会再次调用模型吗?必须进入 Core budget、ModelCallAuditor、timeout 和显式 retry policy。
  7. 它引入了新的状态吗?先确认是否只是 error code、stop reason 或 fallback reason,避免再造全局生命周期。