diff --git a/src/main/java/com/superbiz/agent/controller/ChatController.java b/src/main/java/com/superbiz/agent/controller/ChatController.java index 07b6fef..e990c50 100644 --- a/src/main/java/com/superbiz/agent/controller/ChatController.java +++ b/src/main/java/com/superbiz/agent/controller/ChatController.java @@ -24,12 +24,24 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadPoolExecutor; -/** HTTP and SSE protocol adapter for Chat. */ +/** + * HTTP / SSE 协议适配层(Harness 入口的最外层)。 + * + *
职责边界: + *
真正的一次请求编排在 {@link ChatApplicationUseCase}。 + */ @RestController @RequestMapping("/api") public class ChatController { + /** 应用编排入口:建 Run → 路由 → 分叉执行 → 收尾。 */ private final ChatApplicationUseCase chatApplication; + /** Chat 专用工作线程池;避免在 HTTP 线程上阻塞跑 LLM。 */ private final ThreadPoolExecutor chatWorkerExecutor; private final ChatHarnessProperties harnessProperties; @@ -41,6 +53,12 @@ public class ChatController { this.harnessProperties = harnessProperties; } + /** + * POST /api/chat:立即返回 SSE 流;业务在 worker 线程执行。 + * + *
SSE 事件由 {@link ChatSseSession} 发出(metadata / status / content / done)。
+ * 客户端断开时 session.disconnect,后续可经 RunControl 取消 Run。
+ */
@PostMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public ResponseEntity 这里是「框架能力」与「Harness 控制面」的粘合点:
+ * 由 {@code DiagnosisAgentUseCase} 抛出;{@code DiagnosisChatExecutor} 仅对
+ * {@link #isDraftContractFailure()} 为 true 的 kind 尝试 {@code recoverInvalidDraft}。
+ */
public final class DiagnosisAgentOutputException extends RuntimeException {
+ /**
+ * Agent 失败细分。
+ *
+ * 前三种(空/JSON/schema)算 Draft 契约失败,有 observed facts 时可 FALLBACK;
+ * {@link #EXECUTION_FAILED} 是 loop/框架执行失败,不走非法 draft 恢复。
+ */
public enum Kind {
+ /** agent.call 抛错且非受控停止,或未归类执行失败。 */
EXECUTION_FAILED,
+ /** 模型返回空文本,没有 draft。 */
EMPTY_DRAFT,
+ /** 输出不是合法 JSON。 */
INVALID_JSON,
+ /** JSON 可解析但不符合 DiagnosisDraft schema。 */
SCHEMA_INVALID
}
diff --git a/src/main/java/com/superbiz/agent/harness/agent/DiagnosisAgentUseCase.java b/src/main/java/com/superbiz/agent/harness/agent/DiagnosisAgentUseCase.java
index b41eecb..c63be39 100644
--- a/src/main/java/com/superbiz/agent/harness/agent/DiagnosisAgentUseCase.java
+++ b/src/main/java/com/superbiz/agent/harness/agent/DiagnosisAgentUseCase.java
@@ -21,8 +21,21 @@ import org.springframework.ai.chat.messages.AssistantMessage;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
+/**
+ * Diagnosis Agent 用例:在 Harness 边界内跑一次 Spring AI Alibaba {@link ReactAgent}。
+ *
+ * 框架负责 ReAct 多轮(模型 ↔ tool_call);Harness 负责:
+ * 由 {@code DiagnosisChatExecutor} 调用;成功产出 {@link DiagnosisDraft} 草稿(尚未对外发布)。
+ */
public final class DiagnosisAgentUseCase {
+ /** 写入 RunnableConfig metadata,供 hook/interceptor 取回同一 RunContext。 */
public static final String RUN_CONTEXT_METADATA = "runContext";
private final DiagnosisHarnessCore core;
@@ -55,6 +68,11 @@ public final class DiagnosisAgentUseCase {
progressProjection, "progressProjection must not be null");
}
+ /**
+ * 在当前 Run 内执行 Diagnosis Agent。
+ *
+ * 返回 completed(draft) 或 stopped(stopReason);草稿仍需经 Release 才能对外 SUCCESS。
+ */
public DiagnosisAgentExecution execute(RunContext context, DiagnosisAgentInput input) {
Objects.requireNonNull(context, "context must not be null");
Objects.requireNonNull(input, "input must not be null");
@@ -71,6 +89,7 @@ public final class DiagnosisAgentUseCase {
checkLimit("input", inputBytes, limits.maxInputBytes());
core.reserveRunBytes(context, inputBytes);
+ // 框架线程/配置:把 RunContext 显式挂进 metadata,避免隐式 ThreadLocal
RunnableConfig config = RunnableConfig.builder()
.threadId(context.runId())
.addMetadata("sessionId", context.sessionId())
@@ -78,12 +97,15 @@ public final class DiagnosisAgentUseCase {
.addMetadata(RUN_CONTEXT_METADATA, context)
.addMetadata("_stream_", false)
.build();
+ // 每次 run 新建 Agent,绑定本 run 的 interceptor(预算/投影/审计)
ReactAgent agent = agentFactory.create(context);
AssistantMessage response;
try {
+ // ★ Spring AI Alibaba:内部多轮 model + tool,直到产出最终文本或被 interceptor 打断
response = agent.call(inputJson, config);
core.checkActive(context);
} catch (Exception e) {
+ // 预算/收敛等可控停止 → stopped;其它异常包装为 Agent 输出失败
DiagnosisAgentExecution controlled = controlledExecution(context, e);
if (controlled != null) {
return controlled;
@@ -127,13 +149,37 @@ public final class DiagnosisAgentUseCase {
}
}
+ /**
+ * 把 ReactAgent / interceptor 抛出的「受控停止」从异常栈里捞出来,转成正常返回值。
+ *
+ * 不是笼统的「业务异常 → 正常」;只识别 Harness 约定的可控信号(沿 cause 链查找,
+ * 因为框架可能再包一层):
+ * 与 {@code DiagnosisChatExecutor#recoverInvalidDraft} 的分工:
+ * 本方法处理「loop 被预算/收敛打断、往往还没有合法 draft」;
+ * recoverInvalidDraft 处理「loop 跑完了,但输出不是合法 DiagnosisDraft」。
+ *
+ * @return 可交给 Release 的 stopped 执行结果;无法识别时 null
+ */
private DiagnosisAgentExecution controlledExecution(RunContext context, Throwable failure) {
+ // 1) 收集侧受控停止(ToolInterceptor 在 SATURATED 后仍收到 tool 请求)
DiagnosisCollectionStoppedException stopped = findCause(
failure, DiagnosisCollectionStoppedException.class);
if (stopped != null) {
return DiagnosisAgentExecution.stopped(
progressProjection.project(context), stopped.stopReason());
}
+ // 2) Run 已终态:仅预算耗尽可降为 stopped;取消/超时必须继续上抛
RunAbortedException aborted = findCause(failure, RunAbortedException.class);
if (aborted != null) {
if (aborted.termination().state() == RunState.BUDGET_EXHAUSTED) {
@@ -143,15 +189,18 @@ public final class DiagnosisAgentUseCase {
}
throw aborted;
}
+ // 3) 预算异常(可能尚未被包成 RunAborted,或 lifecycle 已先置 BUDGET_EXHAUSTED)
BudgetExceededException budget = findCause(failure, BudgetExceededException.class);
if (budget != null || context.lifecycle().state() == RunState.BUDGET_EXHAUSTED) {
context.progress().markBudgetLimitReached();
return DiagnosisAgentExecution.stopped(
progressProjection.project(context), DiagnosisStopReason.BUDGET_LIMIT_REACHED);
}
+ // 4) 未知失败:交给外层当 Agent 执行失败
return null;
}
+ /** 沿 cause 链查找目标异常类型(框架包装后根因仍在链上)。 */
private static 只表示「当前走到哪一阶段」,成功/失败结局看 ReleaseOutcome / ChatFailureCode。
+ */
public enum ChatApplicationStatus {
+
+ /** Intent Router 识别请求类型。 */
ROUTING("正在识别请求类型"),
+
+ /** 系统闲聊路径生成回答。 */
SYSTEM_RESPONDING("正在生成回答"),
+
+ /** 知识库检索中。 */
KNOWLEDGE_SEARCHING("正在查询知识库"),
+
+ /** 知识答案整理中。 */
KNOWLEDGE_ANSWERING("正在整理知识答案"),
+
+ /** 诊断 Agent 收集证据 / 写草稿(ReAct 循环中)。 */
DIAGNOSIS_RUNNING("正在收集诊断证据"),
+
+ /** Evidence / Semantic 门控或无效 draft 的安全发布阶段。 */
SAFETY_VALIDATING("正在进行安全校验");
private final String message;
@@ -14,6 +31,7 @@ public enum ChatApplicationStatus {
this.message = message;
}
+ /** 面向用户的简短进度文案。 */
public String message() {
return message;
}
diff --git a/src/main/java/com/superbiz/agent/harness/application/ChatApplicationUseCase.java b/src/main/java/com/superbiz/agent/harness/application/ChatApplicationUseCase.java
index 59e6224..178b850 100644
--- a/src/main/java/com/superbiz/agent/harness/application/ChatApplicationUseCase.java
+++ b/src/main/java/com/superbiz/agent/harness/application/ChatApplicationUseCase.java
@@ -23,16 +23,34 @@ import java.util.Optional;
import java.util.function.Supplier;
import java.util.regex.Pattern;
+/**
+ * Chat 应用编排(Harness Application 层):一次请求从创建到公开结果的负责人。
+ *
+ * 调用链:
+ * 它编排路径,但不做业务根因判断;也不把 HTTP/SSE 细节塞进 Core。
+ */
public final class ChatApplicationUseCase {
private static final Pattern SAFE_ID = Pattern.compile("[A-Za-z0-9][A-Za-z0-9._-]{0,63}");
+ /** 执行规则:预算、deadline、取消、终态(first-terminal-wins)。 */
private final DiagnosisHarnessCore core;
private final Supplier observer 通常是 SSE session:onStarted 推 metadata,onStatus 推进度。
+ */
public ChatApplicationResult execute(ChatApplicationRequest request,
ChatApplicationObserver observer) {
Objects.requireNonNull(request, "request must not be null");
Objects.requireNonNull(observer, "observer must not be null");
String sessionId = resolveSessionId(request.sessionId());
+ // 会话级上下文:上一路由结果 + 上一轮诊断摘要(给 Router / Diagnosis 用)
Optional 诊断预算耗尽且已由子路径产出 FALLBACK 时,不二次 completeSuccess
+ *(lifecycle 已是 BUDGET_EXHAUSTED)。
+ */
private void completePath(RunContext context, IntentType intent, PathResult path) {
if (path.handledBudgetTermination()) {
if (intent != IntentType.DIAGNOSIS
@@ -144,6 +183,12 @@ public final class ChatApplicationUseCase {
core.completeSuccess(context);
}
+ /**
+ * 根据 Router 产出的 intent 选择执行分支。
+ *
+ * 这是 Application 的核心编排决策:Router 只给枚举,分支执行权在这里。
+ * DIAGNOSIS 继续进入 {@link com.superbiz.agent.harness.application.executor.DiagnosisChatExecutor}。
+ */
private PathResult executePath(IntentType intent,
RunContext context,
String query,
@@ -162,6 +207,7 @@ public final class ChatApplicationUseCase {
ReleaseOutcome.SUCCESS, knowledgeQuery.execute(context, query), null, false);
}
case DIAGNOSIS -> {
+ // 诊断子编排:Agent(ReAct) → Guard/Release,不在本类展开
DiagnosisExecutionResult result = diagnosis.execute(
context, query, previousTurn, observer::onStatus);
yield new PathResult(result.outcome(), result.content(), result.publishedResult(),
diff --git a/src/main/java/com/superbiz/agent/harness/application/ChatFailureCode.java b/src/main/java/com/superbiz/agent/harness/application/ChatFailureCode.java
index 0162aaa..441c26b 100644
--- a/src/main/java/com/superbiz/agent/harness/application/ChatFailureCode.java
+++ b/src/main/java/com/superbiz/agent/harness/application/ChatFailureCode.java
@@ -1,11 +1,38 @@
package com.superbiz.agent.harness.application;
+/**
+ * Chat 应用层对外失败码(给 SSE failure / 客户端看的粗粒度原因)。
+ *
+ * 层级:Application 出口。不要和下列内部码混淆:
+ * 由 {@link ChatApplicationUseCase} 在 catch 中映射,文案对用户安全,不暴露内部堆栈。
+ */
public enum ChatFailureCode {
+
+ /** Intent Router 不可用或输出无法解析,无法决定走哪条路径。 */
ROUTING_UNAVAILABLE,
+
+ /** 系统闲聊分支暂时无法回答。 */
SYSTEM_CHAT_UNAVAILABLE,
+
+ /** 知识问答分支暂时无法完成检索/作答。 */
KNOWLEDGE_UNAVAILABLE,
+
+ /** 诊断分支整体不可用(非具体 FALLBACK 细分)。 */
DIAGNOSIS_UNAVAILABLE,
+
+ /** session/run 读写落库失败(start/intent/finish 等)。 */
RUN_PERSISTENCE_FAILED,
+
+ /** Run 被取消(客户端断开、用户取消等),对应 RunState.CANCELLED。 */
RUN_CANCELLED,
+
+ /** 未归类的内部失败;兜底码,应尽量少用、并靠 Trace 排查。 */
INTERNAL_FAILURE
}
diff --git a/src/main/java/com/superbiz/agent/harness/application/executor/DiagnosisChatExecutor.java b/src/main/java/com/superbiz/agent/harness/application/executor/DiagnosisChatExecutor.java
index 0ce8e2b..0dae689 100644
--- a/src/main/java/com/superbiz/agent/harness/application/executor/DiagnosisChatExecutor.java
+++ b/src/main/java/com/superbiz/agent/harness/application/executor/DiagnosisChatExecutor.java
@@ -23,9 +23,22 @@ import com.superbiz.agent.harness.release.DiagnosisReleaseUseCase;
import java.util.Objects;
import java.util.function.Consumer;
+/**
+ * 诊断路径子编排(Application 在 intent=DIAGNOSIS 时的执行器)。
+ *
+ * 两段式流水线,本身不实现 ReAct 循环:
+ * 由 {@code ChatApplicationUseCase.executePath} 在路由完成后调用。
+ */
public final class DiagnosisChatExecutor implements DiagnosisOperation {
+ /** Agent 接入:内部创建 ReactAgent 并 agent.call。 */
private final DiagnosisAgentUseCase diagnosisAgent;
+ /** 验证与发布:未证明的结论不能 SUCCESS 出口。 */
private final DiagnosisReleaseUseCase releaseUseCase;
private final PublishedResultPolicy publishedPolicy;
private final DiagnosisTraceRecorder traceRecorder;
@@ -46,27 +59,38 @@ public final class DiagnosisChatExecutor implements DiagnosisOperation {
this.traceRecorder = Objects.requireNonNull(traceRecorder, "traceRecorder must not be null");
}
+ /**
+ * 诊断子路径:Agent 出草稿 → Release 裁决对外形态。
+ *
+ * @param statusSink 回写 SSE 进度(DIAGNOSIS_RUNNING / SAFETY_VALIDATING)
+ */
@Override
public DiagnosisExecutionResult execute(RunContext context,
String query,
PreviousTurn previousTurn,
Consumer 触发条件(见 {@link DiagnosisAgentOutputException#isDraftContractFailure()}):
+ * 空输出 / 非法 JSON / schema 不符。真正的执行崩溃({@code EXECUTION_FAILED})不进恢复,直接再抛。
+ *
+ * 除「记审计 + 包装 FALLBACK 返回」外,还承担:
+ * 与 {@code DiagnosisAgentUseCase#controlledExecution} 的分工:
+ * controlledExecution 处理 loop 中途被预算/收集收敛打断(常无 draft,转 stopped);
+ * 本方法处理 loop 结束后输出契约失败(有/无 progress 决定 FALLBACK 还是失败)。
+ */
private DiagnosisExecutionResult recoverInvalidDraft(
RunContext context,
DiagnosisAgentOutputException exception,
Consumer 在 Harness 中的位置:Application 主编排里的「路由节点」,不是业务执行器。
+ * 只返回 {@link IntentType};由 {@code ChatApplicationUseCase.executePath} 决定后续分支。
+ */
public final class IntentRouter implements IntentRouting {
+ /** 输出契约:JSON 只能有 intent 一个字段。 */
private static final Set 与 {@link InvocationStatus} 分工:
+ * 层级:Release 内容契约(SafeFallback.type)。
+ * 出现在对外 Fallback 载荷与 Trace 的 RELEASE_DECISION 中。
+ *
+ * 注意:
+ * READY 的记录才可作为 Evidence Guard 引用目标;
+ * PROJECTING 表示尚未完成投影落库;ERROR 表示本调用失败收尾。
+ */
public enum InvocationStatus {
+
+ /** 已 begin,正在执行/投影,尚不可作为最终引用。 */
PROJECTING,
+
+ /** 投影完成且可引用(agentResult + evidenceStatus 已固化)。 */
READY,
+
+ /** 本调用以错误结束。 */
ERROR
}
diff --git a/src/main/java/com/superbiz/agent/harness/contract/ReleaseOutcome.java b/src/main/java/com/superbiz/agent/harness/contract/ReleaseOutcome.java
index 73ec17a..ba788cc 100644
--- a/src/main/java/com/superbiz/agent/harness/contract/ReleaseOutcome.java
+++ b/src/main/java/com/superbiz/agent/harness/contract/ReleaseOutcome.java
@@ -1,8 +1,28 @@
package com.superbiz.agent.harness.contract;
+/**
+ * 诊断发布裁决结果(Release 层):这一次诊断「对外给什么结局」。
+ *
+ * 层级:Release / Application 结果。注意与 {@link com.superbiz.agent.harness.core.RunState} 不同:
+ * 常见组合:RunState=BUDGET_EXHAUSTED 且有安全进展 → ReleaseOutcome=FALLBACK;
+ * 无进展 → 往往落到 FAILED。
+ */
public enum ReleaseOutcome {
+
+ /** 通过门控,可发布 DIAGNOSIS_REPORT。 */
SUCCESS,
+
+ /** 不发布根因报告,发布 SafeFallback(见 {@link FallbackType})。 */
FALLBACK,
+
+ /** 无法形成可发布的安全内容(含无 observed facts 的预算停等)。 */
FAILED,
+
+ /** 运行被取消,通常不再保证客户端能收到完整 done。 */
CANCELLED
}
diff --git a/src/main/java/com/superbiz/agent/harness/contract/SemanticVerdict.java b/src/main/java/com/superbiz/agent/harness/contract/SemanticVerdict.java
index da8f0d0..5f67f0f 100644
--- a/src/main/java/com/superbiz/agent/harness/contract/SemanticVerdict.java
+++ b/src/main/java/com/superbiz/agent/harness/contract/SemanticVerdict.java
@@ -1,6 +1,17 @@
package com.superbiz.agent.harness.contract;
+/**
+ * Semantic Guard 对「结论是否被证据支撑」的裁决。
+ *
+ * SUPPORTED → 可走向 Release SUCCESS;
+ * UNSUPPORTED → 通常 FallbackType.SEMANTIC_UNSUPPORTED。
+ * (Guard 技术失败走 SEMANTIC_UNAVAILABLE,不一定落本枚举。)
+ */
public enum SemanticVerdict {
+
+ /** 语义上认为草稿结论被已验证证据支持。 */
SUPPORTED,
+
+ /** 证据不足以支持当前根因/结论表述。 */
UNSUPPORTED
}
diff --git a/src/main/java/com/superbiz/agent/harness/contract/SseOutcome.java b/src/main/java/com/superbiz/agent/harness/contract/SseOutcome.java
index e61f888..5b99879 100644
--- a/src/main/java/com/superbiz/agent/harness/contract/SseOutcome.java
+++ b/src/main/java/com/superbiz/agent/harness/contract/SseOutcome.java
@@ -1,7 +1,19 @@
package com.superbiz.agent.harness.contract;
+/**
+ * SSE {@code done} 事件上的粗粒度结局(协议层)。
+ *
+ * 与 {@link ReleaseOutcome} 对齐的对外三态;取消场景连接可能已断,
+ * 不一定能发出 done(CANCELLED),故此处通常只有 SUCCESS / FALLBACK / FAILED。
+ */
public enum SseOutcome {
+
+ /** 对应成功报告 content。 */
SUCCESS,
+
+ /** 对应安全降级 content。 */
FALLBACK,
+
+ /** 对应 failure 事件或失败 done(实现以 ChatSseSession 为准)。 */
FAILED
}
diff --git a/src/main/java/com/superbiz/agent/harness/core/BudgetKind.java b/src/main/java/com/superbiz/agent/harness/core/BudgetKind.java
index f5edb56..c6f96c7 100644
--- a/src/main/java/com/superbiz/agent/harness/core/BudgetKind.java
+++ b/src/main/java/com/superbiz/agent/harness/core/BudgetKind.java
@@ -1,11 +1,31 @@
package com.superbiz.agent.harness.core;
+/**
+ * 硬预算维度:哪一类资源超限(Core / RunBudget)。
+ *
+ * 超限时抛 {@link BudgetExceededException},并常将 RunState 置为 {@code BUDGET_EXHAUSTED}。
+ * 这是资源账,不是「信息是否还有增益」(后者看 progress / DiagnosisStopReason)。
+ */
public enum BudgetKind {
+
+ /** 整次 Run 允许的模型调用次数。 */
MODEL_CALLS,
+
+ /** 整次 Run 允许的工具调用总次数。 */
TOOL_CALLS,
+
+ /** 单个工具名的调用次数上限。 */
TOOL_CALLS_PER_TOOL,
+
+ /** 累计 input tokens。 */
INPUT_TOKENS,
+
+ /** 累计 output tokens。 */
OUTPUT_TOKENS,
+
+ /** 累计 total tokens。 */
TOTAL_TOKENS,
+
+ /** Run 级 UTF-8 字节预算(输入、工具结果、draft 等占用)。 */
RUN_BYTES
}
diff --git a/src/main/java/com/superbiz/agent/harness/core/DiagnosisHarnessCore.java b/src/main/java/com/superbiz/agent/harness/core/DiagnosisHarnessCore.java
index 9db5c86..a58d63f 100644
--- a/src/main/java/com/superbiz/agent/harness/core/DiagnosisHarnessCore.java
+++ b/src/main/java/com/superbiz/agent/harness/core/DiagnosisHarnessCore.java
@@ -10,6 +10,17 @@ import java.time.Instant;
import java.util.Objects;
import java.util.function.Supplier;
+/**
+ * Harness Core:一次 Run 的执行规则(不是业务编排器)。
+ *
+ * 与 Application 的分工:
+ * 不依赖 Spring AI;Router/Agent/Tool 在每次消耗前调用本类 API。
+ */
public final class DiagnosisHarnessCore {
private final Clock clock;
@@ -66,6 +77,12 @@ public final class DiagnosisHarnessCore {
return startRun(sessionId, runIdSupplier.get());
}
+ /**
+ * 创建本次请求的 RunContext(显式上下文,不用 ThreadLocal)。
+ *
+ * 携带:身份(session/run)、deadline、预算、取消、生命周期、模型账本、进度收敛器。
+ * 后续所有模型与 Tool 调用必须传入同一个 context。
+ */
public RunContext startRun(String sessionId, String runId) {
Instant deadline = clock.instant().plus(maxRunDuration);
RunCancellation cancellation = new RunCancellation();
@@ -81,10 +98,15 @@ public final class DiagnosisHarnessCore {
lifecycle,
new DiagnosisProgressTracker(stopAfterConsecutiveNoGain,
stopAfterConsecutiveProgressProtocolViolations));
+ // 取消信号与终态联动:第一个终态获胜,迟到结果不能覆盖
cancellation.onCancel(reason -> lifecycle.finish(terminalState(reason), reason.name()));
return context;
}
+ /**
+ * 执行前闸门:已终态 / 超时 / 已取消 → 抛 RunAbortedException。
+ * Router、Agent、Tool、Guard 在关键步骤前都会走到这里。
+ */
public void checkActive(RunContext context) {
Objects.requireNonNull(context, "context must not be null");
context.lifecycle().termination().ifPresent(termination -> {
@@ -102,11 +124,13 @@ public final class DiagnosisHarnessCore {
}
}
+ /** 模型调用前:检查 active + 预留模型调用预算。 */
public void beforeModelCall(RunContext context) {
checkActive(context);
applyBudget(context, context.budget()::reserveModelCall);
}
+ /** 工具调用前:检查 active + 按工具名预留工具预算。 */
public void beforeToolCall(RunContext context, String toolName) {
checkActive(context);
applyBudget(context, () -> context.budget().reserveToolCall(toolName));
diff --git a/src/main/java/com/superbiz/agent/harness/core/RunCancellationReason.java b/src/main/java/com/superbiz/agent/harness/core/RunCancellationReason.java
index 1f5cb31..91a233b 100644
--- a/src/main/java/com/superbiz/agent/harness/core/RunCancellationReason.java
+++ b/src/main/java/com/superbiz/agent/harness/core/RunCancellationReason.java
@@ -1,9 +1,30 @@
package com.superbiz.agent.harness.core;
+/**
+ * 触发 Run 取消 / 与取消联动写终态的原因(Core)。
+ *
+ * 由 {@link RunCancellation#cancel} 携带;Core 会映射到对应 {@link RunState}:
+ * 层级:执行控制面。first-terminal-wins:第一个写入的终态不可被迟到结果覆盖。
+ *
+ * 不要直接当成用户看到的结果:
+ * 层级:Guard。校验 DiagnosisDraft 是否引用真实 tool_call、字段是否齐全等。
+ * 失败时可尝试 EvidenceRepair;仍失败则 FallbackType.EVIDENCE_VALIDATION_FAILED。
+ *
+ * 与 SemanticVerdict 不同:这里只问「引用是否真实、结构是否合法」,
+ * 不问「结论语义是否夸大」。
+ */
public enum EvidenceViolationCode {
+
+ /** 缺少整份 draft。 */
DRAFT_MISSING,
+
+ /** 缺少 analysis 列表或为空。 */
ANALYSIS_MISSING,
+
+ /** analysis 条目缺少 id。 */
ANALYSIS_ID_MISSING,
+
+ /** analysis id 重复。 */
ANALYSIS_ID_DUPLICATE,
+
+ /** analysis 缺少 kind。 */
ANALYSIS_KIND_MISSING,
+
+ /** analysis 缺少正文。 */
ANALYSIS_TEXT_MISSING,
+
+ /** 需要工具引用但未提供。 */
TOOL_REFERENCE_MISSING,
+
+ /** 报告级必填文本缺失。 */
REPORT_TEXT_MISSING,
+
+ /** 结论等引用了 analysis,但引用列表缺失。 */
ANALYSIS_REFERENCE_MISSING,
+
+ /** 引用了不存在的 analysis id。 */
ANALYSIS_REFERENCE_UNKNOWN,
+
+ /** 缺少 limitations(诊断契约要求声明范围/缺口)。 */
LIMITATIONS_MISSING,
+
+ /** tool 引用字段非法。 */
TOOL_REFERENCE_INVALID,
+
+ /** 引用的 tool_call 在本 Run 账本中不存在。 */
INVOCATION_MISSING,
+
+ /** tool_call_id 与账本记录不匹配。 */
INVOCATION_ID_MISMATCH,
+
+ /** 该 invocation 状态不可被引用(非 READY 等)。 */
INVOCATION_NOT_REFERENCABLE,
+
+ /** 证据 kind 与工具/投影约定不符。 */
EVIDENCE_KIND_MISMATCH,
+
+ /** 引用了不支持的工具名。 */
TOOL_UNSUPPORTED,
+
+ /** 投影结果不可用于校验。 */
PROJECTION_INVALID,
+
+ /** 投影 id 与引用不一致。 */
PROJECTION_ID_MISMATCH,
+
+ /** 投影状态与引用期望不一致。 */
PROJECTION_STATUS_MISMATCH,
+
+ /** 从 canonical store 查找调用记录失败。 */
CANONICAL_LOOKUP_FAILED
}
diff --git a/src/main/java/com/superbiz/agent/harness/progress/DiagnosisCollectionState.java b/src/main/java/com/superbiz/agent/harness/progress/DiagnosisCollectionState.java
index 9a903b9..9977062 100644
--- a/src/main/java/com/superbiz/agent/harness/progress/DiagnosisCollectionState.java
+++ b/src/main/java/com/superbiz/agent/harness/progress/DiagnosisCollectionState.java
@@ -1,6 +1,16 @@
package com.superbiz.agent.harness.progress;
+/**
+ * 证据收集阶段状态机(Progress 层,与 RunState 独立)。
+ *
+ * SATURATED 表示「再查也难有信息增益」,会触发 stop_required;
+ * 不等于 Run 已经 BUDGET_EXHAUSTED 或对外已经 FALLBACK。
+ */
public enum DiagnosisCollectionState {
+
+ /** 仍允许在预算内发起工具调用(需遵守进度协议)。 */
COLLECTING,
+
+ /** 已饱和:交付一次 STOP 指示后,再要工具可抛 DiagnosisCollectionStoppedException。 */
SATURATED
}
diff --git a/src/main/java/com/superbiz/agent/harness/progress/DiagnosisStopReason.java b/src/main/java/com/superbiz/agent/harness/progress/DiagnosisStopReason.java
index c9e3d34..aee0537 100644
--- a/src/main/java/com/superbiz/agent/harness/progress/DiagnosisStopReason.java
+++ b/src/main/java/com/superbiz/agent/harness/progress/DiagnosisStopReason.java
@@ -1,7 +1,26 @@
package com.superbiz.agent.harness.progress;
+/**
+ * 诊断「证据收集」为何受控停止(Progress 层)。
+ *
+ * 层级:Agent 收集收敛,不是对外 FallbackType。
+ * 出现在 {@code DiagnosisAgentExecution.stopped(...)} 与 Tool 侧 stop_required 观察里。
+ *
+ * 与预算的关系:
+ * 模型在后续 tool envelope 的 previous_observation 中声明;
+ * 连续 NO_GAIN 可推动 CollectionState → SATURATED。
+ */
public enum InformationGain {
+
+ /** 相对已完成查询有新的可用信息。 */
GAINED,
+
+ /** 无新增信息(含重复 scope、空证据等由 Harness 判定的情况)。 */
NO_GAIN
}
diff --git a/src/main/java/com/superbiz/agent/harness/progress/ProgressProtocolViolationType.java b/src/main/java/com/superbiz/agent/harness/progress/ProgressProtocolViolationType.java
index 71cd0e0..e23a7b5 100644
--- a/src/main/java/com/superbiz/agent/harness/progress/ProgressProtocolViolationType.java
+++ b/src/main/java/com/superbiz/agent/harness/progress/ProgressProtocolViolationType.java
@@ -1,9 +1,26 @@
package com.superbiz.agent.harness.progress;
+/**
+ * 工具调用「进度协议」违规类型(Progress / ToolInterceptor)。
+ *
+ * Agent 每次调证据工具应带合法 Envelope(business input + 可选 previous_observation)。
+ * 违规通常先返回可修复的 error observation;连续违规可导致
+ * {@link DiagnosisStopReason#PROGRESS_PROTOCOL_VIOLATED}。
+ */
public enum ProgressProtocolViolationType {
+
+ /** 非首次工具调用缺少对上一观察的 previous_observation。 */
MISSING_PREVIOUS_OBSERVATION,
+
+ /** previous_observation 指向的 tool_call_id 与账本顺序不符。 */
OUT_OF_ORDER_PREVIOUS_OBSERVATION,
+
+ /** 出现了协议不允许的 previous_observation(例如首次就带、或指向未知 id)。 */
UNEXPECTED_PREVIOUS_OBSERVATION,
+
+ /** 缺少必填 business input。 */
MISSING_INPUT,
+
+ /** 整体 Envelope 结构非法(JSON/字段形态不对)。 */
INVALID_ENVELOPE
}
diff --git a/src/main/java/com/superbiz/agent/harness/retry/RetryFailure.java b/src/main/java/com/superbiz/agent/harness/retry/RetryFailure.java
index 62ca7a2..1cd7128 100644
--- a/src/main/java/com/superbiz/agent/harness/retry/RetryFailure.java
+++ b/src/main/java/com/superbiz/agent/harness/retry/RetryFailure.java
@@ -1,14 +1,43 @@
package com.superbiz.agent.harness.retry;
+/**
+ * 重试框架对失败的分类(Retry 层)。
+ *
+ * 决定某次 attempt 是否允许再试:仅 TIMEOUT/TRANSPORT 等技术类通常可重试;
+ * CANCELLED / BUDGET_EXHAUSTED / 业务拒绝等必须透出,不能被重试吞掉。
+ *
+ * 用于 Intent Router、Semantic Guard 等包在 {@code HarnessRetryExecutor} 里的模型调用,
+ * 不是 Diagnosis Agent 主 ReAct 的自动重试分类(Agent 默认不做隐藏重试)。
+ */
public enum RetryFailure {
+
+ /** 单次 attempt 或总超时。 */
TIMEOUT,
+
+ /** 网络/传输层失败。 */
TRANSPORT,
+
+ /** 模型输出内容不符合约定(字段/枚举等)。 */
INVALID_OUTPUT,
+
+ /** JSON 等解析失败。 */
PARSE_ERROR,
+
+ /** 结构符合 JSON 但 schema/字段约束失败。 */
SCHEMA_INVALID,
+
+ /** 业务上判定无可用证据(若某组件使用该分类)。 */
NO_EVIDENCE,
+
+ /** 业务策略拒绝,不可靠重试改变结果。 */
BUSINESS_REJECTION,
+
+ /** Run 已取消,禁止重试。 */
CANCELLED,
+
+ /** 预算耗尽,禁止重试。 */
BUDGET_EXHAUSTED,
+
+ /** 未归类。 */
UNKNOWN
}
diff --git a/src/main/java/com/superbiz/agent/harness/tool/boundary/ToolBoundaryErrorCode.java b/src/main/java/com/superbiz/agent/harness/tool/boundary/ToolBoundaryErrorCode.java
index 96fcde0..f2d935d 100644
--- a/src/main/java/com/superbiz/agent/harness/tool/boundary/ToolBoundaryErrorCode.java
+++ b/src/main/java/com/superbiz/agent/harness/tool/boundary/ToolBoundaryErrorCode.java
@@ -1,17 +1,52 @@
package com.superbiz.agent.harness.tool.boundary;
+/**
+ * 工具边界(ToolBoundary)单次调用错误码。
+ *
+ * 层级:Tool 执行边界。出现在 ToolBoundaryResult.error / 投影给 Agent 的 error observation。
+ * 多数情况下 不抛异常打断 ReAct,而是把错误变成安全 observation 回注模型;
+ * 预算类还会 markBudgetLimitReached,后续模型轮次再由 ModelInterceptor 闸住。
+ *
+ * 与 {@code ChatFailureCode}、{@code FallbackType} 不同:这是单次 tool 级错误,不是整次 Chat 结局。
+ */
public enum ToolBoundaryErrorCode {
+
+ /** 请求 JSON / 参数不合法。 */
INVALID_REQUEST,
+
+ /** tool_call_id 缺失或格式非法。 */
INVALID_TOOL_CALL_ID,
+
+ /** 请求中的 runId 与当前 RunContext 不一致。 */
RUN_MISMATCH,
+
+ /** 未授权调用该工具(策略拒绝)。 */
UNAUTHORIZED,
+
+ /** 工具被要求只读,但请求带有写副作用语义。 */
NOT_READ_ONLY,
+
+ /** Run 已终态/取消/超时,不再执行工具(对应 RunAborted)。 */
RUN_INACTIVE,
+
+ /** 本工具调用触达硬预算(次数/bytes 等)。 */
BUDGET_EXHAUSTED,
+
+ /** 相同 tool_call_id 重复执行(幂等/防重)。 */
DUPLICATE_TOOL_CALL,
+
+ /** 原始结果或投影结果超过大小上限。 */
RESULT_TOO_LARGE,
+
+ /** 底层 executor 执行失败或返回 null。 */
TOOL_EXECUTION_ERROR,
+
+ /** raw → agent 投影失败。 */
PROJECTION_ERROR,
+
+ /** 投影后的 evidence_status 与契约不符。 */
INVALID_EVIDENCE_STATUS,
+
+ /** canonical store 读写失败。 */
STORE_ERROR
}
+ *
+ */
public final class DiagnosisAgentFactory {
public static final String AGENT_NAME = "diagnosis_agent";
@@ -62,22 +71,27 @@ public final class DiagnosisAgentFactory {
this.prompt = DiagnosisAgentPrompt.load();
}
+ /**
+ * 为当前 Run 构建 ReactAgent 实例(与 RunContext 绑定,不可跨 run 复用)。
+ */
public ReactAgent create(RunContext context) {
Objects.requireNonNull(context, "context must not be null");
return ReactAgent.builder()
.name(AGENT_NAME)
.description("Collects bounded evidence and authors one diagnosis draft")
- .model(chatModel)
+ .model(chatModel) // Spring AI ChatModel
.systemPrompt(prompt)
- .tools(evidenceTools.callbacks())
+ .tools(evidenceTools.callbacks()) // 证据工具(如 lookup_knowledge)
.interceptors(
+ // 每次模型调用前:checkActive + 预算 + token 审计
new HarnessModelInterceptor(core, context, modelCallAuditor),
+ // 每次工具调用:边界投影给 Agent + 完整轨迹进审计
new HarnessToolInterceptor(
context, evidenceTools, objectMapper, traceRecorder))
- .hooks(auditHooks)
+ .hooks(auditHooks) // 如 agent_step 落库
.outputSchema(new DiagnosisDraftOutputSchema(objectMapper).getFormat())
.returnReasoningContents(true)
- .parallelToolExecution(false)
+ .parallelToolExecution(false) // 串行工具:预算与 step 绑定可解释
.releaseThread(true)
.build();
}
diff --git a/src/main/java/com/superbiz/agent/harness/agent/DiagnosisAgentOutputException.java b/src/main/java/com/superbiz/agent/harness/agent/DiagnosisAgentOutputException.java
index 9d01e60..1f21f71 100644
--- a/src/main/java/com/superbiz/agent/harness/agent/DiagnosisAgentOutputException.java
+++ b/src/main/java/com/superbiz/agent/harness/agent/DiagnosisAgentOutputException.java
@@ -4,12 +4,28 @@ import com.superbiz.agent.harness.progress.DiagnosisProgressSnapshot;
import java.util.Objects;
+/**
+ * Diagnosis Agent 输出/执行失败异常。
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * Controller → execute()
+ * → core.startRun() // 建 RunContext 边界
+ * → router.route() // Intent Router(Spring AI 单次模型调用)
+ * → executePath(intent) // 按意图分叉
+ * DIAGNOSIS → DiagnosisChatExecutor(Agent → Release)
+ * → completePath / persistFinish
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * NO_EVIDENCE 仍可能是 success 的工具执行(查了但空),常记为信息无增益。
+ */
public enum EvidenceStatus {
+
+ /** 返回了可被引用的证据块。 */
EVIDENCE_FOUND,
+
+ /** 执行完成但范围内无证据(空结果,不是必然系统故障)。 */
NO_EVIDENCE,
+
+ /** 工具侧错误或无法形成合法证据观察。 */
ERROR
}
diff --git a/src/main/java/com/superbiz/agent/harness/contract/FallbackType.java b/src/main/java/com/superbiz/agent/harness/contract/FallbackType.java
index 79d29d8..f0b5df1 100644
--- a/src/main/java/com/superbiz/agent/harness/contract/FallbackType.java
+++ b/src/main/java/com/superbiz/agent/harness/contract/FallbackType.java
@@ -1,10 +1,43 @@
package com.superbiz.agent.harness.contract;
+/**
+ * FALLBACK 细分类型:在 {@link ReleaseOutcome#FALLBACK} 时说明「为什么降级」。
+ *
+ *
+ *
+ */
public enum FallbackType {
+
+ /** Evidence Guard:引用/结构校验未通过(含 repair 后仍失败)。 */
EVIDENCE_VALIDATION_FAILED,
+
+ /** Semantic Guard:结论不被证据支撑(verdict=UNSUPPORTED)。 */
SEMANTIC_UNSUPPORTED,
+
+ /** Semantic Guard:技术上无法完成语义评审(超时/不可用等),不发布根因。 */
SEMANTIC_UNAVAILABLE,
+
+ /**
+ * 历史/枚举保留:预算耗尽类降级。
+ * 当前主路径预算受控停止有安全进展时,通常映射为 {@link #INSUFFICIENT_EVIDENCE}。
+ */
BUDGET_EXHAUSTED,
+
+ /**
+ * 已做有限检查,但证据不足以确认根因。
+ * 典型来源:信息饱和 stopped、预算 stopped 有 facts、非法 draft 有 progress 的
+ * {@code releaseInvalidDraft} / {@code releaseControlledStop}。
+ */
INSUFFICIENT_EVIDENCE,
+
+ /** 缺少定向诊断所需上下文(对象/时间窗等),尚未形成有效查询进展。 */
MISSING_REQUIRED_CONTEXT
}
diff --git a/src/main/java/com/superbiz/agent/harness/contract/InvocationStatus.java b/src/main/java/com/superbiz/agent/harness/contract/InvocationStatus.java
index 43cff22..446cf54 100644
--- a/src/main/java/com/superbiz/agent/harness/contract/InvocationStatus.java
+++ b/src/main/java/com/superbiz/agent/harness/contract/InvocationStatus.java
@@ -1,7 +1,19 @@
package com.superbiz.agent.harness.contract;
+/**
+ * 工具调用在 canonical store 中的生命周期状态。
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ */
public enum RunCancellationReason {
+
+ /** SSE/HTTP 客户端断开。 */
CLIENT_DISCONNECTED,
+
+ /** 显式用户取消(若产品支持)。 */
USER_REQUESTED,
+
+ /** 墙钟超过 Run deadline。 */
DEADLINE_EXCEEDED,
+
+ /** 硬预算耗尽(与 BudgetExceededException 联动)。 */
BUDGET_EXHAUSTED,
+
+ /** 应用判定内部失败并收尾。 */
INTERNAL_FAILURE
}
diff --git a/src/main/java/com/superbiz/agent/harness/core/RunState.java b/src/main/java/com/superbiz/agent/harness/core/RunState.java
index 5f0f515..da20fa6 100644
--- a/src/main/java/com/superbiz/agent/harness/core/RunState.java
+++ b/src/main/java/com/superbiz/agent/harness/core/RunState.java
@@ -1,11 +1,34 @@
package com.superbiz.agent.harness.core;
+/**
+ * 一次 Run 的内存生命周期状态(Core / RunLifecycle)。
+ *
+ *
+ *
+ */
public enum RunState {
+
+ /** 仍可执行(未终态)。 */
RUNNING(false),
+
+ /** 正常完成(Application completeSuccess)。 */
SUCCESS(true),
+
+ /** 内部失败终态(completeFailure 等)。 */
FAILED(true),
+
+ /** 取消终态(客户端断开、用户请求等)。 */
CANCELLED(true),
+
+ /** 超过 Run deadline。 */
TIMED_OUT(true),
+
+ /** 模型/工具/Token/字节等硬预算耗尽。可与 ReleaseOutcome.FALLBACK 并存。 */
BUDGET_EXHAUSTED(true);
private final boolean terminal;
@@ -14,6 +37,7 @@ public enum RunState {
this.terminal = terminal;
}
+ /** 是否已是终态(终态后 checkActive 会 abort)。 */
public boolean isTerminal() {
return terminal;
}
diff --git a/src/main/java/com/superbiz/agent/harness/guard/evidence/EvidenceViolationCode.java b/src/main/java/com/superbiz/agent/harness/guard/evidence/EvidenceViolationCode.java
index 4894627..6e73e38 100644
--- a/src/main/java/com/superbiz/agent/harness/guard/evidence/EvidenceViolationCode.java
+++ b/src/main/java/com/superbiz/agent/harness/guard/evidence/EvidenceViolationCode.java
@@ -1,25 +1,76 @@
package com.superbiz.agent.harness.guard.evidence;
+/**
+ * Evidence Guard 结构/引用违规码(规则门控,通常不调大模型)。
+ *
+ *
+ *
+ * 有安全 observed facts 时,Release 常映射为 FallbackType.INSUFFICIENT_EVIDENCE。
+ */
public enum DiagnosisStopReason {
+
+ /** 连续无信息增益(或等价饱和策略),收集状态进入 SATURATED。 */
INFORMATION_SATURATED,
+
+ /** 模型次数/工具次数/Token/bytes 等硬预算触顶。 */
BUDGET_LIMIT_REACHED,
+
+ /** 进度协议连续违规达到阈值(envelope / previous_observation 等)。 */
PROGRESS_PROTOCOL_VIOLATED
}
diff --git a/src/main/java/com/superbiz/agent/harness/progress/InformationGain.java b/src/main/java/com/superbiz/agent/harness/progress/InformationGain.java
index 6a8a934..9eb7a1d 100644
--- a/src/main/java/com/superbiz/agent/harness/progress/InformationGain.java
+++ b/src/main/java/com/superbiz/agent/harness/progress/InformationGain.java
@@ -1,6 +1,16 @@
package com.superbiz.agent.harness.progress;
+/**
+ * 单次工具观察相对已有收集是否带来新信息(Progress 协议字段)。
+ *
+ *