首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >基于 Workflow 的 Agent 开发:前后端交互设计实战

基于 Workflow 的 Agent 开发:前后端交互设计实战

原创
作者头像
OneCode
发布2026-07-23 23:02:29
发布2026-07-23 23:02:29
1610
举报
文章被收录于专栏:ooderAgentooderAgent

一篇从 OODER 框架实战中提炼的工程博文,聚焦「流程驱动 Agent」架构下,前后端如何通过 SSE 事件流、三态模型与一等公民操作设计,构建可观测、可介入、可回退的智能体交互闭环。

写作日期:2026-07-23 · 背景项目:OODERAI(流程驱动 + 多模式 Agent 开发平台)

一、为什么 Workflow-Based Agent 需要重新设计前后端交互?

传统的 LLM Agent 前后端交互通常是「一问一答 + 流式 token」:

代码语言:javascript
复制
User → POST /chat → Backend (LLM Stream) → SSE token → Frontend render

这种模式在简单问答场景工作良好,但当我们引入 Workflow 引擎 后,会立刻暴露三个核心问题:

  1. 生命周期复杂化:一次用户请求不再对应一次 LLM 调用,而是触发一个包含 N 个活动(START/TASK/LLM_AGENT/HUMAN/SUBPROCESS/...)的流程实例,每个活动又有 Def/Inst/History 三态。
  2. 介入时机多样化:Agent 不再独占执行权——HUMAN 活动会暂停流程等待确认、AGENT_EVENT 会并行触发、guard_escalation 会在异常时介入。前端需要在不同时机渲染不同的 UI。
  3. 观测维度爆炸:除了 token 流,还有流程计划(flow_plan)、活动路由(flow_route)、产出物变更(flow_artifact)、心跳(flow_heartbeat)、技能进度(step/progress)等十多种事件需要前端消费。

如果我们继续沿用「token-only」的前端架构,会很快陷入:事件流缺口(后端推了前端不监听)、降级路径失控(前端默默创建 fallback 实例掩盖后端错误)、死代码堆积(监听器永远不触发但不敢删除)。

OODER 框架在 2026 年 Q3 完成了一次系统性的前后端对齐重构,本文就是这次重构的方法论沉淀。


二、核心架构:三态模型 + SSE 事件驱动

2.1 三态模型(Def / Inst / History)

流程驱动架构的基础是「定义、实例、历史」三态分离:

状态

Java 类

VFS 路径

语义

Def

ProcessDefinition / ActivityDefinition

process-def/{flowId}/...

静态定义:活动节点、路由、eventDisplayConfigs

Inst

ProcessInstance / ActivityInstance

process-inst/{processInstId}/...

运行时实例:状态、上下文、子流程

History

(无独立 Java 类,Map 结构)

process-inst/{id}/activities/{actId}/history.json

历史快照:SkillExecutionSummary、产出物、子流程合并字段

关键设计原则:History 不建立独立 Java 类,而是以 Map 结构存储在 VFS 的 HISTORY 层。这样 LLM 生成的非结构化摘要、技能执行明细、产出物清单都可以无 schema 变更地追加。前端通过 historyVfsPath 字段动态加载,避免后端 DTO 频繁膨胀。

2.2 SSE 事件驱动

后端通过 SseEventPushListener(实现 FlowEventListener 接口)将流程事件统一推送到 SSE 通道:

代码语言:javascript
复制
@Component
public class SseEventPushListener implements FlowEventListener {
    @Override
    public void onActivityStarted(ProcessInstance instance, ActivityInstance actInstance,
                                   ActivityDefinition actDef, ProcessDefinition procDef) {
        Map<String, Object> eventData = sseEventAssembler.buildFlowStepEvent(
            instance.getInstanceId(), actDef.getActivityId(), "executing", null);
        // 注入 activityInstId、领域信息、展现注解、进度、displayMeta
        enrichWithDomainInfo(eventData, instance, actDef, procDef);
        enrichWithDisplayAnnotation(eventData, actDef, instance);
        injectProgressFromInstance(eventData, instance);
        injectDisplayMetaUniversal(actDef.getActivityId(), eventTypeStart, eventData);
        pushSseEvent("flow_step", eventData);
    }
}

前端通过 EventSource.addEventListener(eventName, handler) 精确订阅每个事件类型,而不是用一个 onmessage 处理所有事件。这带来两个好处:

  1. 事件类型显式化:62 处 addEventListener 清单本身就是前后端契约文档
  2. 变更可追溯:移除一个监听器等于显式声明「这个事件前端不再消费」,会暴露真实事件流缺口

三、SSE 事件分层设计:四层 23 事件

OODER 将 SSE 事件按生命周期阶段分为四层:

3.1 流程生命周期层(8 事件)

事件

触发时机

关键 payload

flow_start

onProcessStarted

processInstId, processDefId, flowId, parentInstId

flow_plan

IntentDispatchScene 预测路径后

steps[], predictedActivityIds, overallSummary, source

flow_route

onProcessStarted/onActivityRouted

source=process_started/activity_routed, routeAction

flow_step

onActivityStarted/Completed/Failed/Paused/Routed

phase, activityInstId, displayAnnotation, meta

flow_complete

onProcessCompleted

taskSummary, mdContent, stageResults, artifactSummary

flow_completed

onProcessCompleted(实例状态推进)

status=completed

flow_failed

onProcessFailed

error, success=false

flow_archived

onProcessArchived

status=archived

3.2 活动生命周期层(5 事件)

事件

触发时机

关键 payload

flow_step (phase=executing/completed/failed/paused/routing)

onActivity*

activityInstId, duration, historyVfsPath

flow_artifact

onArtifactChanged

artifact:{type,path,action}, toolName=page_ready(cls/page)

flow_heartbeat

startActivityHeartbeat(每5秒)

elapsedMs, estimatedMs

human_confirm

onActivityPaused (HUMAN)

confirmId, type=CHOICE, config.actions

guard_escalation

onGuardEscalation

failedActivityId, interventionActivityId, reasonCode, availableOperations

3.3 文件与工具层(4 事件)

事件

触发时机

关键 payload

file_progress

onFileReadProgress

filePath, totalLines, currentLine, percent

file_change

onFileWriteChange

operation, content(截断100)

flow_tool_call

FunctionCallingLoopExecutor

toolName, arguments, result

flow_thinking

FunctionCallingLoopExecutor

thinking(流式追加)

3.4 场景编排层(动态事件)

事件模式

触发时机

数量

flow_crud_*

onCrudEvent(CRUD 编排)

10 个独立监听

flow_dbfirst_*

DBFirst 动态循环

~26 个

flow_viewfirst_*

ViewFirst 动态循环

~26 个

flow_designerfirst_*

DesignerFirst 动态循环

~28 个

flow_db_*

业务动态循环

~18 个

关键设计:动态事件通过 _handleCrudEvent 统一处理器分发,按 crudEventType 字段路由到对应的 icon/label/status。这避免了为每个动态事件写独立监听器(会产生 100+ 个相似函数)。


四、前后端对齐的六大维度

经过实战踩坑,我们提炼出前后端对齐的六个关键维度。每个维度都需要专门的审计。下图展示了 OODER 采用的「定义层 / 编辑层 / 调度消费层」三层对齐模型——L2 编辑的字段必须 ⊆ L1 持久化字段 ⊆ L3 消费字段:

图 4-1:BPM 流程定义三层对齐模型(L1·L2·L3 同名同义同位置)

同时,所有活动类型通过统一的 ActivityDefinition + 类型字段 + 子模式字段建模,避免继承树爆炸:

图 4-2:统一活动模型(单一 ActivityDefinition + type + subMode)

五、HUMAN 一等公民操作设计

5.1 HUMAN 活动的"显示生命"

传统 BPM 中 HUMAN 节点只是「等待用户确认」,但在 Agent 开发中,HUMAN 是一等公民,拥有完整的"显示生命"。下图展示了这条"显示生命"的四阶段链路:

图 5-1:HUMAN 一等公民操作的"显示生命"闭环

10 个一等公民操作:

操作

默认可用

触发条件

SEND / PAUSE / RESUME / TERMINATE

默认可用

SPECIAL_SEND / WITHDRAW / RETURN / DELEGATE_H2A / H2H / H2T

需 DESIGNER config.operations 显式定义

5.2 一等公民操作路由闭环

RouteToEngine.resolveHumanRoute 必须在 _taskCompleted 检查前先调用 checkHumanOperationRoute,按优先级消费上下文标记:

代码语言:javascript
复制
SPECIAL_SEND  →  _specialSendFrom            (continueExecution)
RETURNED      →  _returnFrom, _returnToActivityId  (continueExecution 或 legacy BACKWARD)
WITHDRAWN     →  活动状态 WITHDRAWN          (仅终止当前分支)
DELEGATED     →  _delegatedFrom_{actId}      (continueExecution, 类似 AUTO_ADVANCE)

ActivityRouteResult 新增 4 种 RouteAction + 工厂方法 + isBackwardRequired() / isBranchTerminated() 辅助函数,让路由决策显式化。

5.3 SceneGroup 内 HUMAN 不阻断

当 HUMAN 活动位于 SceneGroup 内部时,SgHumanStrategy 决定是否阻断:

策略

行为

AUTO_HANDLE

SG 内自动处理,不传播到父流程

SCENE_DRIVE

场景驱动,不调用 instance.setStatus(PAUSED)

CRITICAL_PAUSE

关键暂停,传播到父流程

SceneGroupExecutionEngine.handleSgInternalHuman() 只通过 sg.addEvent() 写审计日志,不调用 instance.setStatus(PAUSED),不传播到父流程。


六、guard_escalation 异常介入闭环

当流程遇到「无路由匹配」「回退次数耗尽」「全局回退超限」等异常时,handleHumanEscalation 会创建合成 HUMAN EXCEPTION_INTERVENTION 活动并触发 guard_escalation 事件。完整闭环如下图:

图 6-1:守卫闭环——从限流触发到人工介入决策

6.1 完整事件载荷

代码语言:javascript
复制
{
  "processInstId": "pi-xxx",
  "eventType": "guard_escalation",
  "failedActivityId": "dbfirst_connect",
  "failedActivityName": "数据库连接",
  "interventionActivityId": "intervene-dbfirst_connect-1784799834",
  "interventionActivityDefId": "EXCEPTION_INTERVENTION",
  "reasonCode": "no_route_matched",
  "message": "数据库连接活动无可用路由",
  "humanMode": "EXCEPTION_INTERVENTION",
  "humanModeUiType": "exception_panel",
  "pendingOperation": "INTERVENE_EXCEPTION",
  "operationPermissions": ["INTERVENE_EXCEPTION", "RESUME", "TERMINATE", "SEND"],
  "confirmId": "intervene-dbfirst_connect-1784799834",
  "timestamp": 1784799834000,
  "interventionRequired": true,
  "guardPausedReason": "HUMAN_ESCALATION",
  "exceptionContext": {
    "globalBackwardCount": 0,
    "retryExceeded": false,
    "availableOperations": ["INTERVENE_EXCEPTION", "RESUME", "TERMINATE", "SEND"]
  },
  "availableOperations": ["INTERVENE_EXCEPTION", "RESUME", "TERMINATE", "SEND"],
  "globalBackwardCount": 0
}


七、displayMeta 注入策略:后端优先 + 前端兜底

7.1 双层注入架构

displayMeta 决定前端如何渲染一个活动卡片(icon / template / detailKey / status)。OODER 采用「后端优先注入 + 前端智能兜底」的双层架构:

图 7-1:displayMeta 双层注入架构(后端 priority=30 优先,前端智能兜底)

7.2 前端兜底的智能推断

后端 injectDisplayMetaUniversal 依赖 activityDefCache(从 8 个含 eventDisplayConfigs 的流程定义加载)。当缓存未命中或配置缺失时,前端 _inferDefaultMeta 接管:

代码语言:javascript
复制
_inferDefaultMeta: function (eventType) {
    // 状态推断
    var status = 'running';
    if (eventType.indexOf('_done') >= 0) status = 'done';
    else if (eventType.indexOf('_wait') >= 0) status = 'paused';
    else if (eventType.indexOf('_fail') >= 0) status = 'failed';

    // ★ 智能图标分桶(9 类)
    var icon = 'ri-information-line';
    if (status === 'done') icon = 'ri-checkbox-circle-line';
    else if (status === 'failed') icon = 'ri-error-warning-line';
    else if (etLower.indexOf('parse') >= 0) icon = 'ri-file-search-line';
    else if (etLower.indexOf('generat') >= 0) icon = 'ri-hammer-line';
    // ...

    // ★ 模板推断(5 类)
    var template = 'plain_text';
    if (status === 'paused') template = 'confirm_dialog';
    else if (status === 'failed') template = 'error_card';
    else if (status === 'done') template = 'result_card';

    // ★ detailKey 智能映射
    var detailKey = '';
    if (status === 'failed') detailKey = 'error';
    else if (etLower.indexOf('generat') >= 0) detailKey = 'components';
    // ...
}

7.3 关键设计原则

  1. 后端优先data.meta || host._inferDefaultMeta(eventType) — 后端 meta 永远覆盖前端兜底
  2. 不重复注入eventData.containsKey("meta") 检查会跳过已注入 meta 的事件
  3. 前端兜底要"够用但不完美":前端推断只用于开发期未配置 eventDisplayConfigs 的事件,生产环境应保证后端配置完备


八、AGENT_EVENT 自活设计(前瞻)

AGENT_EVENT 是 OODER 中最复杂的活动类型,采用三阶段自活设计:

代码语言:javascript
复制
1. 埋点 (EVENT_WAITING)
   ↓ AgentEventBridge.registerSubscription() — hookLifecycle.onRegistered
2. 触发 (AgentEventBridge 匹配条件)
   ↓ hookLifecycle.onMatched — pendingEventInstId 设置
3. 自活 (activateEventBranch 创建一级图层)
   ↓ hookLifecycle.onEvent → onComplete

hookLifecycle Map 追踪四个阶段回调:onRegistered → onMatched → onEvent → onCompleteSseEventPushListener 将这四个阶段推送给前端,前端 _renderEventHookTimeline 渲染四阶段时间线。



本文作者:OODER 团队

本文基于 2026 Q3 一次系统性前后端对齐重构实践沉淀。涵盖:三态模型、SSE 四层 23 事件、六大对齐维度、HUMAN 一等公民操作、guard_escalation 异常介入闭环、displayMeta 双层注入、22 项缺口闭环实战、AGENT_EVENT 三阶段自活设计。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、为什么 Workflow-Based Agent 需要重新设计前后端交互?
  • 二、核心架构:三态模型 + SSE 事件驱动
    • 2.1 三态模型(Def / Inst / History)
    • 2.2 SSE 事件驱动
  • 三、SSE 事件分层设计:四层 23 事件
    • 3.1 流程生命周期层(8 事件)
    • 3.2 活动生命周期层(5 事件)
    • 3.3 文件与工具层(4 事件)
    • 3.4 场景编排层(动态事件)
  • 四、前后端对齐的六大维度
    • 五、HUMAN 一等公民操作设计
    • 5.1 HUMAN 活动的"显示生命"
    • 5.2 一等公民操作路由闭环
    • 5.3 SceneGroup 内 HUMAN 不阻断
  • 六、guard_escalation 异常介入闭环
    • 6.1 完整事件载荷
  • 七、displayMeta 注入策略:后端优先 + 前端兜底
    • 7.1 双层注入架构
    • 7.2 前端兜底的智能推断
    • 7.3 关键设计原则
  • 八、AGENT_EVENT 自活设计(前瞻)
    • ----
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档