---
title: "FlowContext：Agent Graph 的数据交换契约"
---



Graph 负责控制流，`FlowContext` 负责业务数据。`AgentSession`、`TeamTrace` 和 Agent Trace 则分别承担会话与执行轨迹职责。

### 1、状态分层

| 对象 | 职责 | 典型内容 |
|---|---|---|
| `FlowContext` | Graph 实例数据和恢复游标 | 输入、中间结果、路由键、FlowTrace |
| `AgentSession` | Agent 会话边界 | 消息历史、FlowContext、pending 状态 |
| `TeamTrace` | TeamAgent 协作状态 | 原始 Prompt、成员记录、协议状态、最终答案、指标 |
| `AgentTrace` | 单个 Agent 执行状态 | working memory、模型/工具过程、指标 |

关系可以理解为：

```text
AgentSession
  ├─ ChatSession 消息历史
  └─ FlowContext
       ├─ 业务变量
       ├─ FlowTrace
       ├─ TeamTrace
       └─ SimpleTrace / ReActTrace
```

### 2、为 Context 建立键契约

建议按输入、中间状态、路由和输出分层：

```java
public interface GraphKeys {
    String INPUT_QUESTION = "input.question";
    String ANALYSIS_TECH = "analysis.tech";
    String ANALYSIS_RISK = "analysis.risk";
    String ROUTE_LEVEL = "route.level";
    String OUTPUT_FINAL = "output.final";
}
```

Agent 与业务节点使用同一契约：

```java
Agent riskAgent = SimpleAgent.of(chatModel)
        .name("risk_agent")
        .role("风险分析员")
        .instruction("分析风险并给出依据。")
        .outputKey(GraphKeys.ANALYSIS_RISK)
        .build();
```

### 3、确定性任务留给普通 Activity

```java
spec.addActivity("load_order")
        .task((ctx, node) -> {
            ctx.put("order.id", "SO-1001");
            ctx.put("order.amount", 15000D);
            ctx.put(GraphKeys.ROUTE_LEVEL, "HIGH");
        })
        .linkAdd(riskAgent.name());
```

但 Context 中存在订单数据，不代表 riskAgent 会自动看见。需要显式引用：

```java
Agent riskAgent = SimpleAgent.of(chatModel)
        .name("risk_agent")
        .role("风险分析员")
        .instruction("分析订单：编号=#{order.id}，金额=#{order.amount}，"
                + "初始等级=#{route.level}")
        .outputKey(GraphKeys.ANALYSIS_RISK)
        .build();
```

建议分工：

- 查询、权限、金额计算、枚举校验：普通 Activity；
- 语义理解、归纳、生成、工具推理：Agent；
- 关键业务路径：Graph 网关；
- 最终结果：明确写入固定输出键。

### 4、outputKey 只做回填

```java
.outputKey("analysis.risk")
```

它只完成：

```text
Agent 最终文本 -> FlowContext[analysis.risk]
```

后续普通 Activity 可直接 `ctx.getAs(...)`；后续 Agent 则需要通过 Context 模板、自定义 TeamProtocol 或显式 `agent.prompt(...)` 消费该值。

### 5、独立 Graph 共用同一 Context

```java
FlowContext context = FlowContext.of("risk-job-001");
AgentSession session = InMemoryAgentSession.of(context);

context.put(GraphKeys.INPUT_QUESTION, "分析订单 SO-1001 的风险");
FlowEngine.newInstance().eval(graph, context);
```

不要只让 ID 相同：

```java
FlowContext context = FlowContext.of("risk-job-001");
AgentSession session = InMemoryAgentSession.of("risk-job-001");
```

这仍是两个 Context 实例。Agent 结果会写入 Session 自己的 Context，外部 Graph 无法读取。

### 6、并行分支的数据隔离

并行 Agent 应使用不同输出键：

```text
analysis.tech
analysis.cost
analysis.risk
```

不要共同写：

```text
analysis.result
```

当前默认实现 `FlowContextDefault` 使用并发 Map 保存顶层数据，但其中的普通 `List`、`Map` 或业务对象不会自动变得线程安全，读—改—写组合也不是原子操作。最稳妥的做法是分支各写独立键，汇聚节点统一读取。

### 7、Context 与 Trace 不混用

业务结果：

```java
String result = session.getContext().getAs(GraphKeys.OUTPUT_FINAL);
```

团队协作记录：

```java
TeamTrace trace = team.getTrace(session);
trace.getRecords().forEach(record ->
        System.out.println(record.getSource()));
```

Graph 诊断位置：

```java
String lastNodeId = session.getContext().lastNodeId();
```

`lastNodeId()` 只表示根图最后记录的节点，不是完整历史，也不足以表达并行或嵌套图的全部恢复状态。持久化时应保存完整 `FlowContext` 和 `FlowTrace`。

### 8、快照边界

```java
String json = session.getContext().toJson();
```

快照保存可序列化 Context 数据和 FlowTrace。`AgentSession` 等运行时对象不会作为业务数据保存；TeamTrace 中的配置、调用选项和 Session 引用也需要在恢复执行时重新注入。

恢复时：

```java
FlowContext restored = FlowContext.fromJson(json);
AgentSession restoredSession = InMemoryAgentSession.of(restored);
```

ChatModel、Agent、TeamAgent、Graph、Driver、Executor、事件监听器和外部连接都应重新构建。