team - 示例3 - 持久化与恢复
2026年1月15日 下午6:15:12
模拟场景:Agent 团队在执行中途状态被存入数据库,随后重启并从断点恢复执行。
示例代码
示例也可以改造成这样的编排:
id: persistence_team
layout:
- {id: 'start', type: 'start', link: 'searcher'}
- {id: 'searcher', type: 'activity', task: '@searcher', link: 'router'}
- {id: 'router', type: 'exclusive', task: '@router',
meta: {agentNames: ['planner']},
link: [{nextId: 'planner', when: '"planner".equals(next_agent)'},
{nextId: 'end'}
]
}
- {id: 'planner', type: 'activity', task: '@planner', link: 'end'}
- {id: 'end', type: 'end'}
示例代码:
import demo.ai.agent.LlmUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.noear.solon.ai.agent.Agent;
import org.noear.solon.ai.agent.AgentSession;
import org.noear.solon.ai.agent.react.ReActAgent;
import org.noear.solon.ai.agent.session.InMemoryAgentSession;
import org.noear.solon.ai.agent.team.TeamAgent;
import org.noear.solon.ai.agent.team.TeamTrace;
import org.noear.solon.ai.chat.ChatModel;
import org.noear.solon.ai.chat.ChatRole;
import org.noear.solon.ai.chat.prompt.Prompt;
import org.noear.solon.flow.FlowContext;
/**
* 状态持久化与断点续跑测试
* <p>验证:当 Agent 系统发生崩溃或主动挂起后,能够通过序列化快照重建上下文记忆并继续后续决策。</p>
*/
public class TeamAgentPersistenceAndResumeTest {
@Test
public void testPersistenceAndResume() throws Throwable {
ChatModel chatModel = LlmUtil.getChatModel();
String teamName = "persistent_trip_manager";
// 1. 构建一个带有自定义流程的团队
TeamAgent tripAgent = TeamAgent.of(chatModel)
.name(teamName)
.graphAdjuster(spec -> {
// 自定义流程:Start -> searcher -> Supervisor (决策后续)
spec.addStart(Agent.ID_START).linkAdd("searcher");
spec.addActivity(ReActAgent.of(chatModel)
.name("searcher")
.description("天气搜索员,负责提供实时气候数据")
.build())
.linkAdd(Agent.ID_SUPERVISOR);
}).build();
// --- 阶段 A:模拟第一阶段执行并手动构建持久化快照 ---
// 假设我们在另一台机器上运行,执行完 searcher 后,我们将状态序列化到 DB
FlowContext contextStep1 = FlowContext.of("order_sn_998");
// 手动模拟 Trace 状态:已经完成了天气搜索
TeamTrace snapshot = new TeamTrace(Prompt.of("帮我规划上海行程并给穿衣建议"));
snapshot.addStep(ChatRole.ASSISTANT,"searcher", "上海明日天气:大雨转雷阵雨,气温 12 度。", 800L);
// 设置当前路由断点为 Supervisor,准备让它恢复后进行决策
snapshot.setRoute(Agent.ID_SUPERVISOR);
// 将轨迹存入上下文,key 遵循框架规范 "__" + teamName
contextStep1.put("__" + teamName, snapshot);
// 模拟落库序列化(JSON)
String jsonState = contextStep1.toJson();
System.out.println(">>> 阶段 A:初始状态已持久化至数据库。当前断点:" + snapshot.getRoute());
// --- 阶段 B:从持久化数据恢复并续跑 ---
System.out.println("\n>>> 阶段 B:正在从 JSON 快照恢复任务...");
// 从 JSON 重建 FlowContext,并包装成新的 AgentSession
FlowContext restoredContext = FlowContext.fromJson(jsonState);
AgentSession session = InMemoryAgentSession.of(restoredContext);
// 验证恢复:调用时不传 Prompt,触发“断点续跑”模式
String finalResult = tripAgent.call(session).getContent();
// --- 阶段 C:核心验证 ---
TeamTrace finalTrace = tripAgent.getTrace(session);
// 验证 1:状态恢复完整性
Assertions.assertNotNull(finalTrace, "恢复后的轨迹不应为空");
Assertions.assertTrue(finalTrace.getStepCount() >= 2, "轨迹应包含预设的 searcher 步及后续生成步");
// 验证 2:历史记忆持久性(Agent 是否还记得 searcher 提供的数据)
boolean remembersWeather = finalTrace.getFormattedHistory().contains("上海明日天气");
Assertions.assertTrue(remembersWeather, "恢复后的 Agent 应该记得快照中的天气信息");
// 验证 3:最终决策结果
Assertions.assertNotNull(finalResult);
System.out.println("恢复执行后的最终答复: " + finalResult);
}
}