从 Agent Harness 到 Scenario Loop

引言:为什么要谈 harness / loop / hook

当我们谈论一个 Agent 系统的设计,三个角度几乎绕不开:

  • Agent Harness(智能体骨架)
  • :宿主进程如何承载 Agent,从输入捕获、上下文封装到结果回写的"外壳"。
  • Agent Loop(智能体回路)
  • :Agent 在面对不确定输入时,如何一步步收敛到一个可交付结果的"主循环"。
  • Hook(钩子/切面)
  • :在 Loop 的关键节点上挂载可观测、可介入、可容错的"切面"。

主流 Agent 框架在这三个维度上常见的做法是:把 Loop 写成固定的 ReAct 模板,把 Harness 做成 CLI 或 Server 包装,把 Hook 做成 LangGraph Edge 或 Tool Pre/Post Callback。这套范式在做"通用对话+工具调用"时很顺,但当任务本身具有强结构性(比如"从一句话生成一个完整的部门管理模块"),就会暴露三个问题:

  1. Loop 的步骤数量和组合方式应该由意图驱动,而不是写死的模板。
  2. 异构步骤(NLP 推理 / DSL 生成 / Java 代码生成 / 编译集成)应该被统一抽象,而不是各跑各的轨道。
  3. 人类介入(确认设计、修改字段、重跑某一段)应该是一等公民的 Hook,而不是补丁。

Ooder 在最近一轮重构中,把原本的 NlpPipeline + NlpDesignOrchestrator + NlpProjectIntegrator 三轨硬编码流程,重新组织为基于 SceneGroup 的场景编排核。这篇文章会从 harness、loop、hook 三个角度逐层拆解这套设计。

第一视角 · Agent Harness:分层骨架

Ooder 的 Harness 不是单一的进程外壳,而是一个由三层结构组成的承载体系:

图 1 · Ooder Agent Harness 的三层结构:宿主层、编排核、能力面

① Host Layer · 宿主层

承担"接住外部请求、流式回写"的职责。Ooder 的宿主层有三种典型形态:

  • HTTP Controller
  • :NlpGenerateController 暴露 /api/nlp/generate 等 REST 端点;
  • 测试 Harness
  • :ooder-nlp-harness 跑闭环用例(llm-chat → four-separation → genCode → build);
  • Trae Hooks
  • :在 IDE/Agent 上下文里嵌入 PostToolUse / UserPromptSubmit 等切面,把用户的自然语言带回 harness。

宿主层的核心动作只有一个:把异构入参翻译成 ScenarioContext,然后调用编排核。

javascript 复制代码
// NlpGenerateController#generate
ScenarioContext context = new ScenarioContext(query, null, null);
context.putAccumulated("projectName", projectName);
context.putAccumulated("autoSave", autoSave);
context.putAccumulated("channelCode", channelCode);

ScenarioOrchestrationResult result = scenarioOrchestrator.orchestrate(context);

② Orchestration Core · 编排核

编排核 ScenarioOrchestrator 是整个 Harness 的"大脑",由四个协作组件构成:

组件 职责 关键方法
Pattern Resolver 把意图(systemType + buildLevel)映射到 6 种编排模式之一 resolvePattern()· OrchestrationPattern.fromIntent()
Group Activator 根据模式决定哪些 SceneGroup 激活/跳过 pattern.isGroupActive(groupId)
Step Loop 组内串行/并行执行 ScenarioStep,处理失败策略 AbstractScenarioGroup.execute()
Confirmation Hook 当组结果标记 needConfirm 时暂停等待用户决策 ConfirmationCallback.confirm()

③ Capability Surface · 能力面

能力面是 Ooder 编排核的精髓所在:将4 类 88+ 个异构步骤统一适配为同一个 ScenarioStep 接口:

  • 13 个 NlpPipelineStep
  • (intent_classification、entity_extraction、cls_audit...)→ PipelineStepAdapter
  • 5 个 DesignStage
  • (IntentUnderstanding → DesignAssembly)→ DesignStageAdapter
  • 30 个 @A2uiSkill
  • (基于注解的 UI Skill 注册表)→ A2uiSkillAdapter
  • 34 个 NlpComponentBuilder
  • (基于工厂的组件构建器)→ ComponentBuilderAdapter
  • 6 步 AggRootBuildSPI
  • (genSPI → genView → ... → getAggroot)→ BuildStepAdapter

每个 Adapter 都不是简单的方法转发,而是承担两个关键职责

  1. 上下文转换
  2. :在 ScenarioContext 与原生上下文(NlpPipelineContext / DesignStageContext)之间双向同步;
  3. 元数据声明
  4. :通过 getRequiredCapabilities() / getProducedOutputs() / getRequiredInputs() 让编排器知道步骤间的依赖关系。
javascript 复制代码
public interface ScenarioStep {
    String   getStepId();           // 全局唯一标识
    String   getGroupId();          // 所属场景组(SG-UNDERSTAND...)
    int      getOrder();            // 组内执行顺序
    boolean  isEnabled(ScenarioContext ctx);   // 动态启用判断
    StepResult execute(ScenarioContext ctx, StepResult previousResult);
    FailureStrategy getFailureStrategy();      // FATAL / RECOVERABLE / SKIPABLE
    List<String> getRequiredCapabilities();
    List<String> getProducedOutputs();
    List<String> getRequiredInputs();
}

当能力面被压平为同一个接口,编排核才有可能用 同一套 Loop + Hook 去驱动数十种异构能力 ------ 这就是 SceneGroup 设计能跨越 NLP / DSL / 代码生成 / 编译四个维度的根本原因。

第二视角 · Agent Loop:场景驱动的运行回路

大多数 Agent 框架的 Loop 长这样:Plan → Tool → Observe → Plan' → ...。Ooder 的 Loop 长这样:Resolve Pattern → Activate Groups → Run Group Sequence → Quality Loop → Integrate。

图 2 · 5 大 SceneGroup 编排序列、质量回路与编排模式激活矩阵

2.1 五大场景组:把异构步骤纵向切片

场景组 语义 包含步骤 关键 Order
SG-UNDERSTAND 理解用户意图与实体 system_design / intent_classification / entity_extraction / entity_resolution 5 / 10 ‖ 20 / 22
SG-DESIGN 架构设计与四分离 module_decomposition / four_separation / design_assembly 25 / 4 / 5
SG-GENERATE 配置生成与组件构建 config_generation / A2uiSkill × 30 / Builder × 34 / design_bridge 30 / 10‖20 / 40
SG-QUALITY 四分离审计与场景校验 llm_fallback / cls_audit / cls_validation 35 / 45 / 48
SG-INTEGRATE 项目集成与构建 project_integration / module_integration / 6 步 BuildSPI 50 / 55 / 61~66

2.2 编排模式:Loop 的形状由意图决定

同样是"创建一个部门管理",根据 LLM 推断出的 systemType 与 buildLevel,Loop 的形状会发生显著变化。这是 OrchestrationPattern.fromIntent() 的真正威力:

javascript 复制代码
public static OrchestrationPattern fromIntent(String systemType, String buildLevel) {
    if (systemType == null) return STANDARD;
    switch (systemType) {
        case "NAV_BASED":     return NAV_BASED;     // 全场景组激活
        case "CHART_BASED":   return CHART_BASED;   // 跳过 DESIGN
        case "SINGLE_MODULE": return SVG_BASED;     // 跳过 DESIGN + QUALITY
        default: // CRUD_BASED
            if ("SIMPLE".equals(buildLevel))   return SIMPLE;
            if ("STANDARD".equals(buildLevel)) return STANDARD;
            return FULL;
    }
}

对照图 2 下半部分的激活矩阵:

  • "创建一个表单" → SIMPLE:只走 Understand → Generate → Integrate,跳过 Design 和 Quality;
  • "创建部门管理(含字段)" → STANDARD:加上 Design,但不开质量回路;
  • "创建复杂 CRM 系统(带导航)" → NAV_BASED:所有 5 组全开,含 3 轮质量循环;
  • "画一个流程拓扑图" → SVG_BASED:跳过 Design、Quality,只走 Understand → Generate(svg) → Integrate。

这是 SceneGroup 与传统 Step DAG 最关键的差异:**Loop 的形状不是预定义的 DAG,而是由意图推导出来的"模式"映射到固定的场景组序列。**这种设计使得新增一种系统类型只需要新增一个 OrchestrationPattern 枚举值,而不是重画 DAG。

2.3 质量回路:内嵌于 Loop 的反馈循环

真正能体现 SceneGroup 是"Agent Loop"而不是"管道"的地方,是 QualityGroup 的内部回路

javascript 复制代码
// QualityGroup#execute
do {
    result = super.execute(context);
    loopCount++;

    StepResult validation = context.getStepResult("cls_validation");
    if (validation == null || validation.isSuccess()) break;

    // D2 验证未通过 → 重置 D1/D2/D3 → 回到 GENERATE
    context.clearStepResults("llm_fallback", "cls_audit", "cls_validation");
} while (loopCount < MAX_VALIDATION_LOOPS);  // 最多 3 轮

关键设计点: ScenarioContext.clearStepResults() 在清理 stepResults 的同时,同步清理 accumulatedData 中由该步骤写入的 key。这保证了重试时不会读到上一轮的脏数据 ------ 这是审计阶段发现并修复的一个隐蔽 bug。

这个质量回路的语义和 ReAct 中的"Observe → Replan"是一致的:当观察结果不达标时,回退到生成阶段重做。但 Ooder 把它固化在 Group 内部 ,而不是暴露给 LLM 自由决策,从而让回路是有界、可验证、可观测的。

第三视角 · Hook:可观测、可控制、可介入

Hook 是 Ooder 编排核的"关节",决定了系统在 Loop 跑动时能被多深入地观察、多精细地控制。整套设计有四类 Hook,分布在 Harness 的不同层次。

图 3 · 四类 Hook 在编排核中的位置:宿主切面、内部观测、人机回路、步骤容错

3.1 OrchestrationListener · 内部观测切面

这是最直白的一类 Hook,任何观察者都可以注册到编排器,接收 7 种生命周期事件:

javascript 复制代码
public interface ScenarioOrchestrationListener {
    void onOrchestrationStarted(ScenarioContext ctx, OrchestrationPattern pattern);
    void onGroupStarted(String groupId, ScenarioContext ctx);
    void onGroupCompleted(String groupId, ScenarioGroupResult result);
    void onStepStarted(String stepId, String groupId, ScenarioContext ctx);
    void onStepCompleted(String stepId, String groupId, StepResult result);
    void onOrchestrationCompleted(ScenarioOrchestrationResult result);
    void onOrchestrationFailed(String error, ScenarioContext ctx);
}

典型用途包括:

  • 日志/链路追踪
  • :每个 step 入参出参写入分布式 trace;
  • WebSocket 流式推送
  • :实时把 step 进度推到前端,让用户看到"理解中...设计中...生成中...";
  • 性能埋点
  • :通过 StepResult.executionTimeMs 做 P95 监控。

3.2 ConfirmationCallback · 人机回路 Hook

这是 Ooder 区别于纯自动化 Agent 的关键设计:当某个 SceneGroup 的结果标记为 needConfirm=true(典型场景:设计阶段完成、需要用户确认架构图),编排器会暂停 Loop,调用 ConfirmationCallback,等待 4 种决策之一:

Decision 语义 行为
CONFIRM 用户确认通过 继续执行下一组
MODIFY 用户修改部分内容 修改写回 ScenarioContext,继续下一组
REJECT 用户拒绝 立即中止整个编排
RERUN 用户希望重做 重新执行当前组
javascript 复制代码
// ScenarioOrchestratorImpl
if (groupResult.isNeedConfirm() && confirmationCallback != null) {
    Decision decision = confirmationCallback.confirm(group.getGroupId(), context, groupResult);
    switch (decision) {
        case REJECT: return ScenarioOrchestrationResult.failure(...);
        case RERUN:  groupResult = group.execute(context); break;
        case MODIFY:
        case CONFIRM:
        default:     // 继续
    }
}

这种设计把"人类介入"提升为编排核的一等公民,而不是上层应用通过 polling 或 WebSocket 临时拼出来的能力。当一个 Agent 系统需要可信交付时(特别是会写文件、改数据库的场景),这种在 Loop 内置的确认 Hook 是必须的。

3.3 FailureStrategy · 步骤级容错切面

每个 ScenarioStep 都声明自己的失败策略,编排器据此决策"出错时往哪走":

策略 行为 典型用例
FATAL 立即中止本组并向上抛失败 system_design、llm_fallback、project_integration、stepGenSPI
RECOVERABLE 记录失败但继续执行后续步骤 intent_classification、config_generation、cls_validation
SKIPABLE 跳过本步骤,不影响下游 entity_resolution、design_bridge、cls_audit

失败策略是每个步骤自己声明的(在 Adapter 中通过 mapFailureStrategy() 静态映射),编排器只是按照声明执行,这让容错策略和业务逻辑解耦。

3.4 Trae Hooks · 宿主侧切面

位于 Harness 的最外层、Spring 应用之外的环境(如 Trae IDE)通过 .trae/hooks/*.json 注册四类钩子:

  • SessionStart
  • :会话启动时校验三服务(aiserver/studio/ooder-test)健康;
  • UserPromptSubmit
  • :用户提交 prompt 时把上下文转写到 ooder-nlp-harness;
  • PostToolUse
  • :工具调用后做闭环校验(NLP → 四分离 → genCode → build 通跑);
  • Notification
  • :异常通知和自动修复触发。

Trae Hooks 不直接进入编排核,而是在 Harness 之外把 Agent 的输入/输出引导回闭环测试管线(参见 .trae/skills/ooder-nlp-harness/SKILL.md)。这是把"编排核 + Skill 闭环测试"做成一个可持续演进系统的关键。

从硬编码双轨到 SceneGroup 编排:演进对比

图 4 · 重构前后的架构对比

重构前的痛点

  1. 双轨重叠
  2. :NlpPipeline(13 step)与 NlpDesignOrchestrator(5 stage)在意图理解和实体建模上有重复执行;
  3. 硬编码顺序
  4. :步骤顺序写在 Pipeline 内部,新增系统类型(NAV/CHART/SVG)需要改主循环代码;
  5. 异构无共主
  6. :30 个 Skill 用注解注册、34 个 Builder 用工厂注册、5 个 Stage 用 OrderHandler 注册,调用方需要 if-else 分发;
  7. 容错不统一
  8. :每个步骤自己 try-catch,没有统一的 FATAL/RECOVERABLE/SKIPABLE 语义;
  9. 质量回路靠手撕 Map
  10. :cls_validation 失败后手动 map.remove("cls_audit"),容易漏清 accumulatedData 导致脏读。

重构后的关键改进

  1. 单一入口
  2. :宿主层只调用 scenarioOrchestrator.orchestrate(ctx),模式由意图自动推导;
  3. 意图驱动激活
  4. :6 个 OrchestrationPattern × 5 个 SceneGroup 形成 fromIntent 矩阵,新增系统类型只需扩枚举;
  5. 异构压平
  6. :5 个 Adapter 把 88+ 个异构步骤适配成同一个 ScenarioStep,编排核不感知底层差异;
  7. 容错标准化
  8. :每个步骤声明 FailureStrategy,AbstractScenarioGroup 按策略统一处理;
  9. 质量回路 API 化
  10. :ScenarioContext.clearStepResults(stepIds) 同时清理 stepResults 和 accumulatedData;
  11. 渐进迁移
  12. :旧接口 @Deprecated 标注,NlpOrchestrator.orchestrateViaScenario() 在 Bean 不可用时降级到旧路径,业务零停机。

端到端示例:一句话到一份可编译模块

以"创建一个部门管理,包含部门名称、部门编码、负责人、上级部门、创建时间字段"为例,看完整 Loop 是怎么跑的。

Step 0 · Harness 接收

javascript 复制代码
POST /api/nlp/generate
{ "input": "创建一个部门管理,包含...", "projectName": "Demo", "autoSave": true,
  "channel": "channel1_module_independent" }

Step 1 · Pattern 推导(resolvePattern)

关键词"管理" → 系统级嫌疑 → 但只描述了单个模块 → 命中 STANDARD 模式:激活 UNDERSTAND + DESIGN + GENERATE + INTEGRATE,跳过 QUALITY。

Step 2 · SG-UNDERSTAND 组

  • system_design
  • (order=5):判定为 CRUD_BASED · STANDARD;
  • intent_classification
  • (10) + entity_extraction(20) 并行:抽取出 moduleName="DepartmentManagement", caption="部门管理", fields=name, code, leader, parent, createTime
  • entity_resolution
  • (22):moduleEnglishName 转拼音、字段类型推断(leader→User引用、parent→自引用、createTime→Datetime)。

Step 3 · SG-DESIGN 组

  • module_decomposition
  • :单模块,跳过分解;
  • four_separation
  • :将字段切分为 Properties / Styles / Events / Behaviors;
  • design_assembly
  • :拼装为 Designer 可识别的 JSON。

Step 4 · SG-GENERATE 组

  • config_generation
  • (30):生成 genJson;
  • A2uiSkillAdapter(skill_form)
  • (20):根据 componentType=FORM 命中 FormSkill,生成 UI 配置;
  • design_bridge
  • (40):把 genJson 转换成 Designer 预览 payload。

这一步如果有 ConfirmationCallback,编排器会暂停等待用户在 Designer 上点确认 → 用户点 CONFIRM → Loop 继续。

Step 5 · SG-INTEGRATE 组

  1. project_integration
  2. (50):保存模块到 VFS(Phase 0);
  3. module_integration
  4. (55):处理菜单、外键引用;
  5. BuildStepAdapter
  6. 6 步链式调用 AggRootBuildSPI:
  7. stepGenSPI(61) → 生成 Entity 接口和 DTO;
  8. stepGenView(62) → 生成 View 层;
  9. stepGetRepository(63) → 生成 VO/DO/RepositoryService;
  10. stepGetAgg(64) → 生成 AggregateRoot;
  11. stepGenAPI(65) → 绑定 ESB;
  12. stepGetAggroot(66) → 全量编译 + 保存 ViewMeta。

Step 6 · 结果回写

编排器返回 ScenarioOrchestrationResult,宿主层将其转换为 NlpDesignResult(含 className、modulePath、saved 等字段),HTTP 端点流式返回给前端。

注意: 整个 Loop 中,宿主层从未感知"4 类 88+ 异构步骤"的存在 ------ 它只看到一个 ScenarioOrchestrator + 5 个 SceneGroup。这就是分层抽象的红利。

设计哲学:为什么是 SceneGroup 而不是 Step DAG

读到这里,一个自然的问题是:为什么不用 LangGraph 那种 Step DAG,或者 Temporal 那种 Workflow Activity?

论点 1:DAG 优化的是依赖,SceneGroup 优化的是语义边界

当步骤数到达 80+ 时,画 DAG 反而是负担:节点和边的拓扑爆炸,重构代价高昂。SceneGroup 把节点按语义 而不是依赖分组(理解、设计、生成、质量、集成),让每一组内部维护自己的 DAG(PARALLEL_GROUPS),组与组之间是顺序的。这种"宏观线性、微观并行"的结构对人类工程师非常友好。

论点 2:意图驱动 > 完全自由的 LLM 调度

让 LLM 自由决策"下一步用哪个工具"是 ReAct 的精髓,但在有强结构约束的工程任务 中(如代码生成),完全自由的 LLM 调度会带来不可预期的执行路径。Ooder 的折中是:意图由 LLM 推断,但 Loop 结构由模式映射。这既保留了灵活性,又保证了可观测、可重放、可审计。

论点 3:人机回路是产品要求,不是补丁

真正交付到客户手上的 Agent 系统几乎都需要人机协作(确认设计、修改字段、回滚错误)。SceneGroup 把 ConfirmationCallback 内置到 Loop 关节点,让"用户介入"成为编排核的一等公民,而不是上层 UI 通过轮询拼出来的。

论点 4:渐进式迁移高于一切

Ooder 已经有 13 个 PipelineStep + 5 个 DesignStage + 6 步 BuildSPI 在生产环境跑了一年多。任何新架构必须能包住而不是替换 这些既有资产。SceneGroup 通过 5 个 Adapter 实现"包"而不是"重写",老路径标 @Deprecated 但仍然可用,新路径通过 Spring Boot 自动配置无侵入注册。这种迁移策略让重构能在不停机不破坏既有调用方的情况下落地。

展望:从编排核到 Agent OS

SceneGroup 当前的形态解决了 Ooder 自身的编排问题,但它的边界值得继续推:

  1. 跨进程 Loop
  2. :当 Step 涉及外部 LLM/工具调用,可以把 ScenarioStep 接入 RPC 或 MCP,让 SceneGroup 同时编排本地 Java 步骤和远程 Agent。
  3. Loop 持久化
  4. :在 ConfirmationCallback 等待用户决策时,把 ScenarioContext 序列化到存储;用户重新打开页面时反序列化继续,等同于 Temporal 的 Durable Workflow。
  5. SceneGroup as Skill
  6. :让一组 SceneGroup 自己变成一个更大编排的 ScenarioStep,形成组合式编排(一个"系统级生成"由 N 个"模块级生成"组合而成)。
  7. 反馈学习闭环
  8. :把 Listener 收集的 stepResults 喂回 LLM,自动调整 OrchestrationPattern.fromIntent() 的权重 ------ 让"模式选择"本身可学习。

从这个角度看,SceneGroup 编排核更像一个面向结构化任务的 Agent OS 内核

  • 进程
  • = ScenarioContext
  • 调度器
  • = ScenarioOrchestrator
  • 系统调用
  • = ScenarioStep(适配 88+ 异构能力)
  • 中断
  • = ConfirmationCallback
  • 信号
  • = OrchestrationListener
  • 用户态切面
  • = Trae Hooks

当我们用"操作系统"的视角看 Agent 框架,会发现现在大多数框架都还停留在"shell 脚本"阶段 ------ 每个任务都重新写一遍 if/else 调度。SceneGroup 这种把语义边界、意图驱动、人机回路、容错策略一次性烧进内核的设计,可能是面向"工程级 Agent 系统"必须迈出的一步。

这篇文章是从 harness、loop、hook 三个视角拆解 SceneGroup 的尝试。如果你正在设计自己的 Agent 编排核,希望这套结构能给你一点不同于"Step DAG / ReAct" 主流叙事的视角。

Ooder A2UI · 编排核架构系列 · agent-sdk-core 2.5.0

关键词:SceneGroup · ScenarioOrchestrator · OrchestrationPattern · Agent Loop · Hook

相关推荐
MatrixOrigin1 小时前
MatrixOne Git4Data 技术详解(三):MatrixOne 架构及 Git4Data 原理解析,快照、Diff、Merge 凭什么这么快
数据库·人工智能·数据平台·矩阵起源·数据底座
KaMeidebaby1 小时前
卡梅德生物技术快报 | Fab 合成文库构建与抗体筛选实验流程及数据解析
人工智能·python·tcp/ip·算法·机器学习
天风之翼1 小时前
AI 全栈开发实战(8):前端开发(二)——流式对话界面与 Markdown 渲染
人工智能
培培说证1 小时前
大数据、人工智能、计算机、软件工程,到底怎么选?
大数据·人工智能·软件工程
装不满的克莱因瓶1 小时前
掌握3D CNN模型结构——从时空特征建模到视频理解与医学影像核心架构
人工智能·pytorch·python·深度学习·神经网络·3d·cnn
金融小师妹1 小时前
基于AI事件驱动模型与验证溢价框架的市场分析:从预期交易到事实验证,原油与黄金面临关键定价重构
大数据·人工智能·算法·均值算法·线性回归
YOLO数据集集合1 小时前
无人机航拍RGBT双模态行人检测数据集 | 可见光红外对齐 低空小目标检测 多模态计算机视觉基准数据
人工智能·深度学习·目标检测·计算机视觉·无人机
古希腊掌管代码的神THU1 小时前
解析 MiniMax M3 多模态大模型的架构/源码?
人工智能·深度学习·自然语言处理·面试
卡卡罗特AI1 小时前
Codex复刻小米MiMoCode官网,丝滑融入项目,只需要3步!保姆级教程!
人工智能·ai编程