
从爆炸到有序的设计哲学
在基于Workflow引擎调度的Agent系统中,子流程和场景流程是两种核心的流程抽象模式。它们使得复杂的人机协作、多Agent协同成为可能,但同时也带来了严峻的上下文管理挑战。
当一个Agent系统需要协调多个子任务、处理复杂的业务流程时,上下文数据会呈现指数级增长:
Main Process Context
├── Input: user query, session info, system config
├── Step 1 Result: intent, entities, confidence
├── Step 2 Result: componentType, fields, layout config
├── Sub-Process A Context (inherited)
│ ├── Parent Context (read-only)
│ ├── Step A1 Result: design metadata
│ ├── Step A2 Result: generated code
│ └── Sub-Sub-Process Context...
├── Scene Flow B Context (injected)
│ ├── Knowledge Repository Query
│ ├── Scene-specific config
│ └── Runtime accumulated data
└── ...exponential growth上下文爆炸的三大根源:
上下文不仅是数据容器,更是知识体系与执行实例的桥梁。然而,这条桥梁常因以下原因断裂:
为了系统性地解决上述问题,我们提出流程-场景-实例三层对齐模型:
维度 | 流程定义层 | 场景概念层 | 执行实例层 |
|---|---|---|---|
结构 | Activity定义、SubFlow引用 | SceneConfig、KnowledgeGroup | ProcessInstance、Context Bean |
产出物 | producedOutputs配置 | stepMetadata注册 | accumulatedData字段 |
输入 | requiredInputs声明 | capabilityRequirements | context.getAccumulated() |
路由 | transition条件表达式 | failureStrategy策略 | guard_escalation事件 |

上下文的初始状态由三部分构成:
// 初始化上下文的配置驱动模式
public class NlpResolutionContext {
private String query; // 用户输入
private String workMode; // 从application.properties注入
private String sceneGroupId; // 从流程定义继承
private List<String> fields; // 运行期填充
public static NlpResolutionContext initialize(SceneConfig config) {
NlpResolutionContext ctx = new NlpResolutionContext();
ctx.setWorkMode(config.getWorkMode());
ctx.setSceneGroupId(config.getSceneGroupId());
return ctx;
}
}随着流程执行,各步骤产生新的上下文数据:
关键设计:安全合并策略
// 避免null覆盖的安全合并
public void mergeAccumulatedData(Map<String, Object> newData) {
for (Map.Entry<String, Object> entry : newData.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
// 只在当前值不存在或为null时覆盖
if (!this.accumulatedData.containsKey(key) || this.accumulatedData.get(key) == null) {
this.accumulatedData.put(key, value);
}
}
}上下文快照通过VFS(虚拟文件系统)持久化,实现:
// VFS持久化接口
public interface NlpResolutionContextVfsHelper {
void save(String processInstId, NlpResolutionContext ctx);
NlpResolutionContext load(String processInstId);
boolean exists(String processInstId);
}这是最关键的阶段,决定了子流程和场景流程如何获取上下文。
特性 | 子流程(Sub-Process) | 场景流程(Scene Flow) |
|---|---|---|
转移方式 | 继承(Inheritance) | 注入(Injection) |
上下文来源 | 父流程上下文的副本 | 场景配置+知识库查询 |
数据可见性 | 只读父流程数据 | 按需注入领域数据 |
生命周期 | 随父流程结束而结束 | 独立于触发流程 |
适用场景 | 任务分解、并行分支 | 领域切换、能力编排 |
子流程通过继承获得父流程的上下文副本。这意味着:
// 子流程上下文继承
public ProcessContext inheritFromParent(ProcessContext parentCtx) {
ProcessContext childCtx = new ProcessContext();
// 浅拷贝accumulatedData(只读)
childCtx.setAccumulatedData(new HashMap<>(parentCtx.getAccumulatedData()));
// 继承系统级配置
childCtx.setWorkMode(parentCtx.getWorkMode());
childCtx.setSceneGroupId(parentCtx.getSceneGroupId());
return childCtx;
}继承模式的优势:
场景流程通过注入获得上下文。注入的数据来源包括:
// 场景流程上下文注入
public ProcessContext injectForScene(SceneConfig sceneConfig,
KnowledgeRepository repo,
ProcessContext triggerCtx) {
ProcessContext sceneCtx = new ProcessContext();
// 1. 注入场景配置
sceneCtx.setSceneGroupId(sceneConfig.getSceneGroupId());
sceneCtx.setCapabilities(sceneConfig.getRequiredCapabilities());
// 2. 注入知识库数据
KnowledgeEntry knowledge = repo.query(sceneConfig.getKnowledgeGroupId());
sceneCtx.setDomainKnowledge(knowledge.getMetadata());
// 3. 注入触发上下文的关键字段(选择性注入,非全量继承)
sceneCtx.setQuery(triggerCtx.getQuery());
sceneCtx.setIntent(triggerCtx.getIntent());
return sceneCtx;
}注入模式的优势:

策略 | 实现方式 | 适用场景 |
|---|---|---|
字段过滤 | 仅保留requiredInputs定义的字段 | 子流程转移 |
快照差异 | 只存储与上一版本的差异 | VFS持久化 |
知识外置 | 将领域知识移至知识库,按需查询 | 场景流程注入 |
Bean同源 | 使用强类型Bean替代散列Map | 全局上下文管理 |
将散列的Map<String, Object>替换为强类型Bean,实现:
/**
* 实体解析结果上下文Bean
* 替代散列Map传播,提供实体Bean同源处理
*/
public class NlpResolutionContext {
private String query;
private String intent;
private String componentType;
private String moduleName;
private List<String> fields;
private List<String> fieldEnglishNames;
private Map<String, String> fieldTypes;
// 从PipelineResult构建
public static NlpResolutionContext fromPipelineResult(
PipelineResult result, String query) {
NlpResolutionContext ctx = new NlpResolutionContext();
ctx.setQuery(query);
ctx.setIntent(result.getIntent());
ctx.setComponentType(result.getComponentType());
ctx.setModuleName(result.getModuleName());
ctx.setFields(result.getFields());
ctx.setFieldEnglishNames(result.getEntityResult().getFieldEnglishNames());
ctx.setFieldTypes(result.getEntityResult().getFieldTypes());
return ctx;
}
// 判断是否包含Grid所需字段
public boolean hasGridFields() {
return fields != null && !fields.isEmpty()
&& fieldEnglishNames != null && !fieldEnglishNames.isEmpty();
}
}将硬编码的字段映射逻辑抽取到JSON配置文件,实现:
// nlp-skill-field-mapping.json
{
"pipelineContextFields": [
{"mapKey": "query", "beanProperty": "query", "type": "String"},
{"mapKey": "workMode", "beanProperty": "workMode", "type": "String"},
{"mapKey": "sceneGroupId", "beanProperty": "sceneGroupId", "type": "String"}
],
"pipelineResultEntityFields": [
{"mapKey": "moduleName", "beanProperty": "moduleName", "type": "String"},
{"mapKey": "fields", "beanProperty": "fields", "type": "List<String>"},
{"mapKey": "fieldEnglishNames", "beanProperty": "fieldEnglishNames", "type": "List<String>"}
]
}在上下文转换方法中嵌入验证逻辑,及时发现遗漏的字段映射:
public static PipelineContext toPipelineContext(Map<String, Object> context) {
// CONFIG验证: 检测未注册的Map key
Set<String> unmapped = detectUnmappedKeys(context);
if (!unmapped.isEmpty()) {
log.debug("[NlpSkillContextHelper] 未注册Map key={}", unmapped);
}
// ... 字段映射逻辑
}在实际系统中,子流程和场景流程往往结合使用:
Main Process (architect-pipeline)
├── Sub-Process: understand-subflow (继承)
│ ├── Activity: intent_classification
│ ├── Activity: entity_extraction
│ └── Activity: entity_resolution
├── Scene Flow: rad-scene (注入)
│ ├── Knowledge: component-type-registry
│ └── Capability: nlp.design
└── Sub-Process: quality-validation (继承)
├── Activity: llm_fallback
└── Activity: cls_audit这种混合架构既保证了数据透明性(继承),又实现了领域隔离(注入),是多Agent协作系统的最佳实践。
© 2026 OODER技术团队 | 子流程与场景流程上下文管理设计
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。