异常治理、事件、观测与测试
Agent Graph 同时包含确定性拓扑和不确定模型输出。生产治理不能只比较最终文本,而应分别观察结构、恢复位置、协作记录、Agent 执行和业务状态。
1、五层观察模型
| 层次 | 对象 | 主要内容 |
|---|---|---|
| 定义层 | Graph | 节点、连接、网关和静态拓扑 |
| 恢复层 | FlowTrace | 每张 Graph 的最后节点 |
| 协作层 | TeamTrace | 成员记录、协议状态、团队最终答案和指标 |
| Agent 执行层 | SimpleTrace / ReActTrace | Prompt、模型与工具过程、指标 |
| 业务层 | FlowContext | 路由条件、审批状态、中间结果和最终输出 |
导出定义:
Graph graph = team.getGraph();
System.out.println(graph.toYaml());
System.out.println(graph.toJson());
System.out.println(graph.toPlantuml());
导出的是拓扑描述。Java 实例形式的 Agent 和 Lambda task 不会因此变成可无损重建的定义。
2、FlowTrace 不是完整审计日志
FlowContext context = session.getContext();
System.out.println(context.trace().lastRecords());
FlowTrace 只保存每张 Graph 的最后节点,用于恢复定位;它不能回答完整路径、节点执行次数或并行时序。
完整节点审计应通过 FlowInterceptor 或 TeamInterceptor 写入外部日志。审计状态至少区分:
STARTED
COMPLETED
SUSPENDED
INTERRUPTED
FAILED
节点 task 内调用 stop/interrupt 后,不能假设 onNodeEnd 一定触发;挂起前应显式记录 SUSPENDED。
3、实时 AgentEvent
流式 TeamAgent 请求返回 AgentEvent:
team.prompt("评估迁移方案")
.session(session)
.stream()
.doOnNext(event -> {
System.out.println(event.getClass().getSimpleName());
})
.blockLast();
常见事件包括团队开始/结束、Agent 节点开始/结束、ReAct 推理、工具调用和 HITL 事件。它们的产生位置并不完全对称:
| 事件 | 产生位置 |
|---|---|
TeamStartEvent | 具有 stream sink 的每次 TeamAgent.call 开始时 |
TeamEndEvent | 发起 .stream() 的 TeamRequest 成功完成时 |
AgentEvent 的 NodeStartEvent / NodeEndEvent | Agent.run 包裹的直接 Agent 节点 |
| 普通 Activity / Gateway 生命周期 | 使用 FlowInterceptor.onNodeStart/onNodeEnd 观察 |
因此,嵌套 TeamAgent 作为父团队的 Agent 节点执行时,可能出现子团队的 TeamStartEvent,但没有与之对称的子团队 TeamEndEvent;父图仍会为这个 TeamAgent 节点产生 AgentEvent 的 NodeStartEvent / NodeEndEvent。不要把 AgentEvent 的节点事件和 FlowInterceptor 的同名生命周期概念混为一谈。
注意:
AgentEvent主要服务流式请求;普通call()不等于外部自动收到事件流;- AgentEvent 的
NodeStartEvent/NodeEndEvent主要描述 Agent 节点,不是所有普通 Activity/Gateway 的通用审计; - 并行事件可能交错,不能依赖到达顺序推断业务顺序。
框架没有通用内建 branchId。需要分支关联时,应使用稳定 Node ID、业务实例 ID、runId,或自行把分支 ID 写入事件 meta/外部日志。
4、指标
TeamTrace trace = team.getTrace(session);
Metrics metrics = trace.getMetrics();
System.out.println("durationMs=" + metrics.getTotalDuration());
System.out.println("promptTokens=" + metrics.getPromptTokens());
System.out.println("thinkTokens=" + metrics.getThinkTokens());
System.out.println("completionTokens=" + metrics.getCompletionTokens());
System.out.println("totalTokens=" + metrics.getTotalTokens());
还应记录:
- Graph ID、实例 ID、Node ID、Agent 名称;
- 模型、工具名称和调用 UUID;
- 路由决定和置信度;
- 重试、降级、人工拒绝;
- 并发分支和幂等键。
不要记录密钥、完整敏感 Prompt 或未经脱敏的工具结果。
5、失败分类和处置
| 失败类型 | 典型处置 |
|---|---|
| 模型超时/限流 | 有上限重试、切换模型、降级 |
| Agent 空输出/结构错误 | 校验失败进入人工或兜底分支 |
| 工具失败 | 按幂等性重试、补偿或挂起 |
| 路由无匹配 | 默认分支或人工审核 |
| Parallel 某分支失败 | 明确失败键,决定继续、降级或整体失败 |
| 恢复冲突 | 分布式锁/版本号拒绝重复执行 |
| Graph 定义不兼容 | 使用旧版本或迁移快照 |
重试必须放在正确边界。对模型读操作可以重试;对付款、删除、发布等副作用必须先建立幂等性。
6、使用确定性 Agent 测试拓扑
Graph 单元测试不应依赖真实 LLM 文风。可以创建返回固定值的 Agent:
Agent analyst = new Agent() {
@Override
public String name() {
return "analyst";
}
@Override
public String role() {
return "分析员";
}
@Override
public AssistantMessage call(Prompt prompt, AgentSession session) {
session.getContext().put("analysis.result", "HIGH");
return ChatMessage.ofAssistant("HIGH");
}
};
再使用拦截器记录节点路径:
List<String> started = new CopyOnWriteArrayList<>();
TeamInterceptor recorder = new TeamInterceptor() {
@Override
public void onNodeStart(FlowContext context, Node node) {
started.add(node.getId());
}
};
构建时加入:
.defaultInterceptorAdd(recorder)
断言业务结果和路径:
Assertions.assertEquals("MANUAL",
session.getContext().get("route.result"));
Assertions.assertTrue(started.contains("analyst"));
Assertions.assertTrue(started.contains("route"));
Assertions.assertTrue(started.contains("manual"));
Assertions.assertFalse(started.contains("auto"));
不要用 TeamTrace records 代替全部 Flow 节点路径;它主要记录 Agent 协作。
7、分层测试
Graph 单元测试
- 固定 Agent 或 Mock ChatModel;
- 验证节点集合、分支、Context 和最终状态;
- 不访问网络,稳定进入 CI。
Agent 能力测试
- 单独测试 Prompt、工具和结构化输出;
- 可使用真实模型;
- 做结构或语义断言,不比较整段文本。
端到端验收测试
- 使用真实模型和外部服务;
- 独立配置密钥、配额、超时和重试;
- 不作为每次提交都必须通过的快速测试。
8、专项测试矩阵
至少覆盖:
- Exclusive 命中、默认和低置信度人工分支;
- Inclusive 零个、一个和多个分支;
- Parallel 分支集合、汇聚和非固定完成顺序;
- 循环结束条件和最大轮次;
- Graph pending、同 Session 恢复和 JSON 恢复;
stop()与interrupt()的差异;- ReAct HITL 按调用 UUID 审批;
- File/Redis Session 保存后重新构造的闭环;
- 子 TeamTrace 和子图 FlowTrace;
- 节点异常、工具失败和外部副作用幂等性。
9、生产检查表
Graph
- Graph ID 在共享执行树和注册空间内唯一;
- Node ID 在图内唯一;
- Start、End 和连接完整;
- Exclusive 只有一个默认边;
- Inclusive 至少能命中一个分支;
- Parallel split/join 严格配对;
- 循环有轮次、Token 和耗时上限。
Agent
- Agent 名称稳定;
- Prompt 输入来源明确;
- 并行输出键隔离;
- 结构化输出经过程序校验;
- 工具权限不只依赖自然语言;
- 敏感动作接入 HITL。
Context 与恢复
- 使用业务实例 ID;
- 不保存密钥;
- 快照加密、脱敏和过期;
- 恢复时重建运行时组件;
- 同一实例禁止并发恢复;
- 保存定义版本和幂等键。
Graph 与 Agent 结合后,仍要保持职责清晰:Agent 负责智能,Graph 负责结构,FlowContext 负责业务数据,AgentSession 负责会话边界,FlowEngine 负责执行。