docs(harness): annotate entry orchestration, enums, and exception recovery paths
This commit is contained in:
@@ -23,16 +23,34 @@ import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Chat 应用编排(Harness Application 层):一次请求从创建到公开结果的负责人。
|
||||
*
|
||||
* <p>调用链:
|
||||
* <pre>
|
||||
* Controller → execute()
|
||||
* → core.startRun() // 建 RunContext 边界
|
||||
* → router.route() // Intent Router(Spring AI 单次模型调用)
|
||||
* → executePath(intent) // 按意图分叉
|
||||
* DIAGNOSIS → DiagnosisChatExecutor(Agent → Release)
|
||||
* → completePath / persistFinish
|
||||
* </pre>
|
||||
*
|
||||
* <p>它编排路径,但不做业务根因判断;也不把 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<String> sessionIdSupplier;
|
||||
private final ChatRunStore runStore;
|
||||
/** 意图路由:只产出 IntentType,不执行诊断。 */
|
||||
private final IntentRouting router;
|
||||
private final SystemChatOperation systemChat;
|
||||
private final KnowledgeQueryOperation knowledgeQuery;
|
||||
/** 诊断子路径:Agent 收集证据写草稿 + Release 门控发布。 */
|
||||
private final DiagnosisOperation diagnosis;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final DiagnosisTraceRecorder traceRecorder;
|
||||
@@ -74,12 +92,18 @@ public final class ChatApplicationUseCase {
|
||||
return execute(request, ChatApplicationObserver.noop());
|
||||
}
|
||||
|
||||
/**
|
||||
* 一次 Chat 请求的主编排。
|
||||
*
|
||||
* <p>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<RoutingHistory> history;
|
||||
Optional<PreviousTurn> previousTurn;
|
||||
try {
|
||||
@@ -90,14 +114,19 @@ public final class ChatApplicationUseCase {
|
||||
ChatFailureCode.RUN_PERSISTENCE_FAILED,
|
||||
"无法读取会话上下文,请稍后重试", exception);
|
||||
}
|
||||
|
||||
// ★ 步骤1:创建 Run 边界(runId/deadline/budget/cancel/lifecycle),显式向下传递
|
||||
RunContext context = core.startRun(sessionId);
|
||||
long startedNanos = System.nanoTime();
|
||||
IntentType intent = null;
|
||||
try {
|
||||
// ★ 步骤2:落库 RUN 开始 + 对外/对内可观测
|
||||
persistStart(context, request.query());
|
||||
traceRecorder.record(TraceAuditEvents.runStarted(context));
|
||||
observer.onStarted(new CoreRunControl(core, context));
|
||||
observer.onStarted(new CoreRunControl(core, context)); // SSE metadata + 取消句柄
|
||||
observer.onStatus(ChatApplicationStatus.ROUTING);
|
||||
|
||||
// ★ 步骤3:意图路由——只回答「走哪条应用分支」,不调业务工具
|
||||
intent = router.route(context, new IntentRouterInput(
|
||||
request.query(),
|
||||
history.map(RoutingHistory::intent).orElse(null),
|
||||
@@ -105,8 +134,11 @@ public final class ChatApplicationUseCase {
|
||||
traceRecorder.record(TraceAuditEvents.routingDecision(context, intent));
|
||||
persistIntent(context.runId(), intent);
|
||||
|
||||
// ★ 步骤4:按 intent 分叉执行(编排决策点)
|
||||
PathResult path = executePath(
|
||||
intent, context, request.query(), previousTurn.orElse(null), observer);
|
||||
|
||||
// ★ 步骤5:写入 Run 终态(成功)并持久化公开结果
|
||||
completePath(context, intent, path);
|
||||
String safeJson = write(path.content());
|
||||
persistFinish(context, intent, path.outcome(), safeJson,
|
||||
@@ -117,6 +149,7 @@ public final class ChatApplicationUseCase {
|
||||
context.sessionId(), context.runId(), intent, path.outcome(),
|
||||
path.content().contentType(), path.content());
|
||||
} catch (RuntimeException exception) {
|
||||
// 统一失败出口:尽量落终态,再映射成安全的对外失败码
|
||||
ReleaseOutcome terminal = terminalOutcome(context);
|
||||
try {
|
||||
runStore.finish(context, intent, terminal, null, null,
|
||||
@@ -130,6 +163,12 @@ public final class ChatApplicationUseCase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 路径执行完成后的 Run 终态处理。
|
||||
*
|
||||
* <p>诊断预算耗尽且已由子路径产出 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 选择执行分支。
|
||||
*
|
||||
* <p>这是 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(),
|
||||
|
||||
Reference in New Issue
Block a user