Files
SuperBizAgent-java/mvp/engineering/harness/Harness生命周期与状态.md
T

16 KiB
Raw Blame History

Harness 生命周期与状态

更新日期:2026-07-29 状态:当前实现口径 术语前置:CONTEXT.md

1. 先澄清:系统不存在一条包含所有状态的总状态机

当前 Harness 有多组正交状态:

  • RunState 控制内存执行终态;
  • ChatApplicationStatus 表示用户可见处理阶段;
  • InvocationStatus 表示单次 Tool invocation 生命周期;
  • EvidenceStatus 表示 Tool 客观结果;
  • CollectionState 表示能否继续收集证据;
  • StopReason 表示停止收集的内部原因;
  • SemanticVerdict 表示结论支持度;
  • ReleaseOutcome 表示最终发布结果;
  • SSE Session State 表示连接能否继续发送。

它们在一次请求中并行演进,只在少数边界发生映射。生命周期文档的目标不是把它们合并,而是说明谁先发生、由谁拥有、在哪里汇合。

2. 一次 Run 的主时间线

sequenceDiagram
    participant C as Client / SSE
    participant A as ChatApplicationUseCase
    participant H as Harness Core
    participant R as Intent Router
    participant D as Diagnosis Runtime
    participant L as Release Pipeline
    participant P as Persistence / Trace

    C->>A: query + optional sessionId
    A->>A: 读取 routing history / PreviousTurn
    A->>H: startRun(sessionId)
    H-->>A: RUNNING RunContext
    A->>P: diagnosis_run=RUNNING + RUN_STARTED
    A-->>C: metadata(sessionId, runId)

    A->>R: route(query, bounded history)
    R-->>A: IntentType

    alt SYSTEM_CHAT
        A->>A: 单轮受控模型回答
    else KNOWLEDGE_QUERY
        A->>A: 一次 RAG + 单轮答案
    else DIAGNOSIS
        A->>D: Agent ReAct / Tool / Progress
        D-->>A: Draft 或 Controlled Stop
        A->>L: release(Draft/Progress/StopReason)
        L-->>A: SUCCESS Draft 或 SafeFallback
    end

    alt 正常完成
        A->>H: completeSuccess(预算 Fallback 特例除外)
        A->>P: 持久化 release outcome / safe content / usage
        A->>P: RUN_FINISHED
        A-->>C: content + done
    else 失败
        A->>H: completeFailure 或读取既有终态
        A->>P: 持久化 FAILED/CANCELLED
        A->>P: RUN_FINISHED
        A-->>C: failure + done(FAILED)
    end

创建顺序很重要:Application 先读取会话上下文,再创建 RunContext、持久化 RUNNING、记录 RUN_STARTED,之后才进入路由和执行。SSE 的 metadata 在 onStarted 中发布 exact sessionId/runId,后续结果必须匹配这组身份。

3. Run 生命周期

3.1 状态

stateDiagram-v2
    [*] --> RUNNING
    RUNNING --> SUCCESS: 正常路径完成
    RUNNING --> FAILED: 不可恢复内部失败
    RUNNING --> CANCELLED: 客户端断开或用户取消
    RUNNING --> TIMED_OUT: deadline 到达
    RUNNING --> BUDGET_EXHAUSTED: 任一硬预算超限

    SUCCESS --> [*]
    FAILED --> [*]
    CANCELLED --> [*]
    TIMED_OUT --> [*]
    BUDGET_EXHAUSTED --> [*]

RunLifecycle.finish 使用原子 compare-and-set:只有从“尚无 termination”到某个终态的第一次转换成功。所有终态都不可再次转换。

3.2 状态含义

RunState 含义 常见来源
RUNNING 尚未产生 RunTermination startRun 后默认状态
SUCCESS Application 已形成正常可持久化结果 正常内容,或非预算类 SafeFallback
FAILED 不可恢复执行失败 路由不可用、模型失败、无安全进展的非法 Draft 等
CANCELLED 协作取消已成为第一个终态 客户端断开、用户请求
TIMED_OUT checkActive 发现 deadline 已到 模型/Tool/Guard 边界前后检查
BUDGET_EXHAUSTED 模型、Tool、Token 或 bytes 预算超限 DiagnosisHarnessCore.exhaustBudget

3.3 RunState 与 ReleaseOutcome 不一一对应

最重要的反例是 Fallback:

  • 证据不足、缺少上下文、语义不支持等正常降级,RunState 最终为 SUCCESS,ReleaseOutcome 为 FALLBACK;
  • 预算耗尽后已经有安全 ProgressSnapshot,RunState 保持 BUDGET_EXHAUSTED,但 ReleaseOutcome 可以为 FALLBACK;
  • 超时或预算耗尽且无法形成安全内容时,RunState 分别保持 TIMED_OUT / BUDGET_EXHAUSTED,ReleaseOutcome 映射为 FAILED。

所以不能从 ReleaseOutcome 反推出精确 RunState,也不能把 FALLBACK 当作 RunState。

4. 取消生命周期

RunCancellation 使用 first-reason-wins。取消原因包括:

Reason RunState 映射
CLIENT_DISCONNECTED CANCELLED
USER_REQUESTED CANCELLED
DEADLINE_EXCEEDED TIMED_OUT
BUDGET_EXHAUSTED BUDGET_EXHAUSTED
INTERNAL_FAILURE FAILED

生命周期和取消句柄互相配合:取消回调尝试完成 RunLifecycle;deadline 和预算路径也会先确定终态,再触发取消阻止后续工作。

取消语义是协作式的:

  1. 后续模型、Tool、Guard 边界调用 checkActive 时立即失败;
  2. SSE 断开后不再发送 content;
  3. first-terminal-wins 阻止迟到成功覆盖 CANCELLED/TIMED_OUT;
  4. 已经进入 Provider 的同步调用是否物理停止,取决于底层客户端,Harness 不作虚假保证。

5. Application 阶段不是生命周期状态

ChatApplicationStatus 用于 SSE status 事件:

ROUTING
SYSTEM_RESPONDING
KNOWLEDGE_SEARCHING
KNOWLEDGE_ANSWERING
DIAGNOSIS_RUNNING
SAFETY_VALIDATING

这些值只是用户可见的处理阶段:

  • 不是严格完备的状态机;
  • 不表示终态;
  • 不持有取消或预算;
  • 不保证每个 Run 都经过全部阶段。

例如 Diagnosis Run 通常经过 ROUTING -> DIAGNOSIS_RUNNING -> SAFETY_VALIDATING,System Chat 则经过 ROUTING -> SYSTEM_RESPONDING。

6. Diagnosis Agent 执行生命周期

DiagnosisAgentExecution 只有两种合法形态,不额外定义一套枚举:

形态 字段 含义
Completed draft != null, stopReason=null Agent 输出严格合法 DiagnosisDraft
Controlled Stop draft=null, stopReason != null Tool loop 因信息饱和、预算或协议错误受控停止

其他情况通过异常表达:

  • 空 Draft;
  • 非法 JSON;
  • Schema 不合法;
  • 模型调用失败;
  • RunAborted。

Draft 合同失败不是 DiagnosisStopReason。非法 Draft 会被丢弃;只有异常携带的 ProgressSnapshot 已包含可验真 facts,Application 才允许 Release 生成过程型 Fallback。

7. 单次 Tool Invocation 生命周期

7.1 状态转换

stateDiagram-v2
    [*] --> PREFLIGHT
    PREFLIGHT --> REJECTED: invalid run/id/auth/readonly/request/budget/store
    PREFLIGHT --> PROJECTING: canonical begin 成功
    PROJECTING --> READY: backend + projection + store 成功
    PROJECTING --> ERROR: execution/projection/size/budget/store error
    READY --> [*]
    ERROR --> [*]
    REJECTED --> [*]

PREFLIGHT 和 REJECTED 是本文用于解释流程的阶段,不是 InvocationStatus 枚举值。canonical record 只有:

  • PROJECTING:已创建,尚未形成可引用结果;
  • READY:执行和投影完成,可以被当前 Run 引用;
  • ERROR:调用失败,不可引用。

部分 preflight 失败发生在 canonical begin 之前,因此可能只有 ToolBoundaryResult.ERROR 和审计事件,没有 canonical ERROR record。

7.2 EvidenceStatus 是另一条轴

InvocationStatus EvidenceStatus 语义
PROJECTING null 未完成
READY EVIDENCE_FOUND 技术完成,存在候选内容
READY NO_EVIDENCE 技术完成,当前 scope 为空
ERROR ERROR 技术失败

READY + EVIDENCE_FOUND 仍不表示证据足以支持结论;后续还要经过 InformationGain、EvidenceGuard 和 SemanticGuard。

8. 信息收集生命周期

8.1 Collection State

stateDiagram-v2
    [*] --> COLLECTING
    COLLECTING --> COLLECTING: GAINED / 清零 no-gain
    COLLECTING --> COLLECTING: NO_GAIN / 未达阈值
    COLLECTING --> SATURATED: 连续 NO_GAIN 达阈值
    COLLECTING --> SATURATED: 连续 progress protocol violation 达阈值
    SATURATED --> SATURATED: 终态,不再允许新 evidence Tool

SATURATED 只表示证据收集不再允许继续。它不是整个 Run 的终态,Agent 仍有一次机会输出 Draft,之后进入 Release。

预算超限不会把 CollectionState 改成 SATURATED。它将 RunState 置为 BUDGET_EXHAUSTED,并把内部 DiagnosisStopReason 标记为 BUDGET_LIMIT_REACHED。

8.2 InformationGain

  • NO_EVIDENCE 和完全重复的 tool_name + normalized_scope 由 Harness 产生 NO_GAIN;
  • 其他成功非空结果由 Diagnosis Agent 评价 GAINED / NO_GAIN;
  • GAINED 清零连续 NO_GAIN;
  • 技术失败和 progress 协议错误不产生 InformationGain。

8.3 StopReason

DiagnosisStopReason 触发条件 对 CollectionState 的影响
INFORMATION_SATURATED 连续 NO_GAIN 达阈值 进入 SATURATED
PROGRESS_PROTOCOL_VIOLATED 连续协议错误达阈值 进入 SATURATED
BUDGET_LIMIT_REACHED RunBudget 超限 不要求进入 SATURATED;Run 已终止

STOP_REQUIRED 是否已经交付由独立 boolean 记录,它不是第四种 CollectionState。一次指令交付后仍请求 Tool,会抛出受控停止异常。

9. Guard 与 Release 生命周期

flowchart TD
    I["DiagnosisAgentExecution"] --> D{"有合法 Draft?"}
    D -->|"否,受控停止"| PS["验证 ProgressSnapshot"]
    D -->|"是,无 conclusion"| NC["验证已有 Tool references"]
    D -->|"是,有 conclusion"| EG["EvidenceGuard initial"]

    EG --> EV{"evidence valid?"}
    EV -->|"否"| ER["EvidenceRepair once"]
    ER --> RE["EvidenceGuard recheck"]
    EV -->|"是"| SG["SemanticGuard"]
    RE -->|"valid"| SG
    RE -->|"invalid"| EF["EVIDENCE_VALIDATION_FAILED"]

    SG -->|"SUPPORTED"| SU["ReleaseOutcome.SUCCESS"]
    SG -->|"UNSUPPORTED"| SF["SEMANTIC_UNSUPPORTED"]
    SG -->|"unavailable"| UF["SEMANTIC_UNAVAILABLE"]

    NC -->|"有 progress"| IF["INSUFFICIENT_EVIDENCE"]
    NC -->|"无 progress,有 missing_info"| MF["MISSING_REQUIRED_CONTEXT"]
    NC -->|"两者都无"| FAIL["FAILED"]
    PS -->|"有 verified facts"| IF
    PS -->|"无 verified facts"| FAIL

    EF --> FB["ReleaseOutcome.FALLBACK"]
    SF --> FB
    UF --> FB
    IF --> FB
    MF --> FB

DiagnosisReleaseResult 只表达 SUCCESS 或 FALLBACK。真正不可恢复的 FAILED / CANCELLED 由 Chat Application 异常路径映射。

10. RunState、ReleaseOutcome 和 FallbackType 映射

场景 RunState ReleaseOutcome FallbackType / 内容
有结论且 Guards 通过 SUCCESS SUCCESS Diagnosis report
缺少必要上下文,零 Tool 合法结束 SUCCESS FALLBACK MISSING_REQUIRED_CONTEXT
有限检查后证据不足 SUCCESS FALLBACK INSUFFICIENT_EVIDENCE
信息饱和后有安全进展 SUCCESS FALLBACK INSUFFICIENT_EVIDENCE
协议停止后有安全进展 SUCCESS FALLBACK INSUFFICIENT_EVIDENCE
EvidenceGuard 最终失败 SUCCESS FALLBACK EVIDENCE_VALIDATION_FAILED
SemanticGuard 判定不支持 SUCCESS FALLBACK SEMANTIC_UNSUPPORTED
SemanticGuard 技术不可用 SUCCESS FALLBACK SEMANTIC_UNAVAILABLE
预算耗尽但有安全进展 BUDGET_EXHAUSTED FALLBACK 当前发布 INSUFFICIENT_EVIDENCE
超时,无法形成安全内容 TIMED_OUT FAILED failure
预算耗尽且无安全进展 BUDGET_EXHAUSTED FAILED failure
不可恢复内部失败 FAILED FAILED failure
客户端断开 CANCELLED CANCELLED 不公开 CANCELLED done

这张表解释了为什么不能只问“最后 status 是什么”。必须先确定是在问内存执行、发布结果还是 Fallback 原因。

11. SSE 连接生命周期

ChatSseSession 有自己的连接状态机:

stateDiagram-v2
    [*] --> NEW
    NEW --> OPEN: onStarted / metadata
    NEW --> DISCONNECTED: 客户端提前断开
    OPEN --> OPEN: status events
    OPEN --> TERMINAL: content + done
    OPEN --> TERMINAL: failure + done(FAILED)
    OPEN --> DISCONNECTED: send failure / disconnect
    TERMINAL --> [*]
    DISCONNECTED --> [*]

公开事件顺序是:

metadata -> status* -> content | failure -> done

边界规则:

  • content 最多发送一次;
  • result 的 sessionId/runId 必须匹配 metadata;
  • TERMINAL 或 DISCONNECTED 后拒绝迟到内容;
  • ReleaseOutcome.CANCELLED 不作为公开 done outcome;客户端已经断开时没有可靠发送目标;
  • 当前 SseOutcome 枚举未参与运行时协议,ChatSseEvent.Done 使用 ReleaseOutcome。

12. 持久化生命周期

12.1 创建

RunContext 创建后,Application 写入:

diagnosis_run.status = RUNNING
session_id / run_id / query

之后记录 intent。

12.2 完成映射

JpaChatRunStore 根据 ReleaseOutcome 映射数据库通用 status:

ReleaseOutcome diagnosis_run.status
SUCCESS SUCCESS
FALLBACK SUCCESS
FAILED FAILED
CANCELLED CANCELLED

这里的 status=SUCCESS 表示请求已被正常处理并形成安全内容,不表示一定有诊断 conclusion。

同时保存:

  • release_outcome;
  • safe answer JSON;
  • 从 safe content 提取的 conclusion;
  • duration、Token、Agent step 数和实际 Tool call 数;
  • 仅在 DIAGNOSIS + SUCCESS 时保存 published_result。

Fallback 不进入下一轮 PreviousTurn。

12.3 当前可观测缺口

内存 RunTermination.state/reason 当前没有独立字段直接持久化。RUN_FINISHED 主要记录 ReleaseOutcome 和预算对账,精确的 TIMED_OUT/BUDGET_EXHAUSTED 原因需要结合异常路径和其他 Trace 事件判断。

因此数据库 status、ReleaseOutcome 和 Trace 都是必要观察面,任何一个都不是完整替代品。

13. Trace Timeline 如何对应生命周期

典型 Diagnosis SUCCESS:

RUN_STARTED
ROUTING_ATTEMPT
ROUTING_DECISION
AGENT_MODEL_STEP
TOOL_INVOCATION / TOOL_PROGRESS ...
EVIDENCE_GUARD_INITIAL
SEMANTIC_GUARD_ATTEMPT
SEMANTIC_GUARD_DECISION
RELEASE_DECISION SUCCESS
RUN_FINISHED SUCCESS

典型信息不足 Fallback:

RUN_STARTED
ROUTING_ATTEMPT
ROUTING_DECISION
AGENT_MODEL_STEP
TOOL_INVOCATION / TOOL_PROGRESS ...
COLLECTION_STOP(可选)
EVIDENCE_GUARD_INITIAL(无结论引用检查)
RELEASE_DECISION FALLBACK
RUN_FINISHED FALLBACK

TracePhase 和 TraceEventStatus 用来组织 Timeline。它们描述事件发生在哪一阶段、该事件结果如何,不构成新的 Run 生命周期。

14. 并发和迟到结果规则

一次 Run 的终止安全依赖三层协作:

  1. RunLifecycle:first-terminal-wins,终态不可覆盖;
  2. DiagnosisHarnessCore.checkActive:每个关键边界阻止终止后的新工作;
  3. ChatSseSession:TERMINAL/DISCONNECTED 后拒绝迟到 content。

这能保证逻辑上的“取消后不再发布”。它不等于强制终止底层线程或 Provider 计算;底层调用返回后仍要经过 active 和 SSE state 检查,迟到结果才会被丢弃。

15. 排障时应该先看哪个状态

问题 首先看 然后看
用户为什么收到 Fallback release_outcome + FallbackType RELEASE/EVIDENCE/SEMANTIC Trace
Agent 为什么停止调用 Tool DiagnosisStopReason TOOL_PROGRESS、TOOL_REQUEST_REJECTED、COLLECTION_STOP
Tool 为什么没有证据 InvocationStatus + EvidenceStatus canonical record 和 Tool audit error code
Run 是超时还是预算耗尽 内存 termination(运行中)或相关 Trace/异常 budget usage、collection stop、失败码
数据库为什么 status=SUCCESS 但没有结论 release_outcome answer 的 content type / FallbackType
为什么没有 SSE done ChatSseSession.State client disconnect/send failure 和 Run cancellation
为什么下一轮没有上一轮上下文 是否 DIAGNOSIS + ReleaseOutcome.SUCCESS + published_result PublishedResultPolicy

16. 生命周期不变量

  1. 一个 Run 只能有一个 RunTermination。
  2. 一个公开 SSE 最多有一次 content/failure 和一次 done。
  3. Tool Invocation 只有 READY 才能被引用。
  4. READY 必须同时拥有 EVIDENCE_FOUND 或 NO_EVIDENCE。
  5. NO_EVIDENCE 不能被提升为全局否定。
  6. SATURATED 后不再执行新的 evidence Tool。
  7. StopReason 不直接决定公开内容,必须经过 Release。
  8. 有 conclusion 才执行完整 EvidenceGuard/Repair/SemanticGuard 链。
  9. FALLBACK 是正常发布结果,不等于 RunState.FAILED。
  10. 数据库 status、ReleaseOutcome 和 RunState 不能互相替代。