一、前言
上一节我们完成了Agent的兵工厂装填,也就是经过装填,我们能拿到一个实例化的客户端了,接下来就要去考虑怎么使用这个客户端了,我们给出了三种形式,分别是
工作流、顺序循环、ReAct (Reasoning + Acting)Loop

我们这一节先做ReAct Loop,因为前两类严格意义上来讲都叫做工作流而不是Agent,一个Agent应该具备自我思考、行动、观察、判断的能力,也就是应该自动化去判断用户的意图,而前面两种是给定剧本去执行的,不是自动化的,因此应该叫做工作流。
二、动态决策(ReAct)
主要分为三步:思考-行动-观察。
这也是Agent的经典行动范式,当然,最终还是会有一些问题,比如没有收敛,导致熵增速度过高,保存很多污染的上下文进行观察,这些问题会导致Agent执行效率逐渐降低,目前还没有很好的解决方案。
1.整体框架搭建
和动态实例化类似,我们还是按照规则树节点的形式来抽象每个步骤,因此我们会有入口根节点、思考节点、行动节点、观察节点,当然最后得到了结论还应该总结一下,所以还有总结节点,这些节点都需要继承一个抽象类,实现规则树的路由传递:抽象类如下:
java
public abstract class AbstractExecuteSupport extends AbstractMultiThreadStrategyRouter<ExecuteCommandEntity, DefaultAutoAgentExecuteStrategyFactory.DynamicContext, String> {
private final Logger log = LoggerFactory.getLogger(AbstractArmorySupport.class);
@Resource
protected ApplicationContext applicationContext;
@Resource
private IAgentRepository agentRepository;
public static final String CHAT_MEMORY_CONVERSATION_ID_KEY = "chat_memory_conversation_id_key";
public static final String CHAT_MEMORY_RETRIEVE_SIZE_KEY = "chat_memory_retrieve_size_key";
@Override
protected void multiThread(ExecuteCommandEntity executeCommandEntity, DefaultAutoAgentExecuteStrategyFactory.DynamicContext dynamicContext) throws ExecutionException, InterruptedException, TimeoutException {
}
protected ChatClient getChatClientByClientId(String clientId){
return getBean(AiAgentEnumVO.AI_CLIENT.getBeanName(clientId));
}
protected <T> T getBean(String beanName){
return(T) applicationContext.getBean(beanName);
}
}
同样的,我们当然也还需要一个动态上下文,记录ReAct循环期间的必要信息:
java
@Service
public class DefaultAutoAgentExecuteStrategyFactory {
private final RootNode executeRootNode;
public DefaultAutoAgentExecuteStrategyFactory(RootNode executeRootNode) {
this.executeRootNode = executeRootNode;
}
public StrategyHandler<ExecuteCommandEntity, DefaultAutoAgentExecuteStrategyFactory.DynamicContext, String> armoryStrategyHandler(){
return executeRootNode;
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class DynamicContext {
// 任务执行步骤
private int step = 1;
// 最大任务步骤
private int maxStep = 1;
private StringBuilder executionHistory;
private String currentTask;
boolean isCompleted = false;
private Map<String, AiAgentClientFlowConfigVO> aiAgentClientFlowConfigVOMap;
private Map<String, Object> dataObjects = new HashMap<>();
public <T> void setValue(String key, T value) {
dataObjects.put(key, value);
}
public <T> T getValue(String key) {
return (T) dataObjects.get(key);
}
}
}
2.RootNode
主要是作为入口使用,将ReAct步骤中需要的数据全部从数据库中取出来,装载到动态上下文。
java
/**
* @author 印东升
* @description 根节点 用于装载数据和指向下一个节点
* @create 2026-05-27 16:57
*/
@Slf4j
@Service("executeRootNode")
public class RootNode extends AbstractExecuteSupport {
@Resource
private Step1AnalyzerNode step1AnalyzerNode;
@Resource
private IAgentRepository repository;
@Override
protected String doApply(ExecuteCommandEntity requestParameter, DefaultAutoAgentExecuteStrategyFactory.DynamicContext dynamicContext) throws Exception {
log.info("=== 动态多轮执行测试开始 ====");
log.info("用户输入: {}", requestParameter.getMessage());
log.info("最大执行步数: {}", requestParameter.getMaxStep());
log.info("会话ID: {}", requestParameter.getSessionId());
Map<String, AiAgentClientFlowConfigVO> aiAgentClientFlowConfigVOMap = repository.queryAiAgentClientFlowConfig(requestParameter.getAiAgentId());
// 客户端对话组
dynamicContext.setAiAgentClientFlowConfigVOMap(aiAgentClientFlowConfigVOMap);
// 上下文信息
dynamicContext.setExecutionHistory(new StringBuilder());
// 当前任务信息
dynamicContext.setCurrentTask(requestParameter.getMessage());
// 最大任务步骤
dynamicContext.setMaxStep(requestParameter.getMaxStep());
return router(requestParameter, dynamicContext);
}
@Override
public StrategyHandler<ExecuteCommandEntity, DefaultAutoAgentExecuteStrategyFactory.DynamicContext, String> get(ExecuteCommandEntity requestParameter, DefaultAutoAgentExecuteStrategyFactory.DynamicContext dynamicContext) throws Exception {
return step1AnalyzerNode;
}
}
3.思考节点
主要任务是制定目标供执行节点使用,同时检测是否达到最后步骤,如果达到就直接去总结了。
java
@Slf4j
@Service
public class Step1AnalyzerNode extends AbstractExecuteSupport {
@Override
protected String doApply(ExecuteCommandEntity requestParameter, DefaultAutoAgentExecuteStrategyFactory.DynamicContext dynamicContext) throws Exception {
log.info("\n🎯 === 执行第 {} 步 ===", dynamicContext.getStep());
// 第一阶段:任务分析
log.info("\n📊 阶段1: 任务状态分析");
String analysisPrompt = String.format("""
**原始用户需求:** %s
**当前执行步骤:** 第 %d 步 (最大 %d 步)
**历史执行记录:**
%s
**当前任务:** %s
请分析当前任务状态,评估执行进度,并制定下一步策略。
""",
requestParameter.getMessage(),
dynamicContext.getStep(),
dynamicContext.getMaxStep(),
!dynamicContext.getExecutionHistory().isEmpty() ? dynamicContext.getExecutionHistory().toString() : "[首次执行]",
dynamicContext.getCurrentTask()
);
// 获取对话客户端
AiAgentClientFlowConfigVO aiAgentClientFlowConfigVO = dynamicContext.getAiAgentClientFlowConfigVOMap().get(AiClientTypeEnumVO.TASK_ANALYZER_CLIENT.getCode());
ChatClient chatClient = getChatClientByClientId(aiAgentClientFlowConfigVO.getClientId());
String analysisResult = chatClient
.prompt(analysisPrompt)
.advisors(a -> a
.param(CHAT_MEMORY_CONVERSATION_ID_KEY, requestParameter.getSessionId())
.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 1024))
.call().content();
assert analysisResult != null;
parseAnalysisResult(dynamicContext.getStep(), analysisResult);
// 将分析结果保存到动态上下文中,供下一步使用
dynamicContext.setValue("analysisResult", analysisResult);
// 检查是否已完成
if (analysisResult.contains("任务状态: COMPLETED") ||
analysisResult.contains("完成度评估: 100%")) {
dynamicContext.setCompleted(true);
log.info("✅ 任务分析显示已完成!");
return router(requestParameter, dynamicContext);
}
return router(requestParameter, dynamicContext);
}
@Override
public StrategyHandler<ExecuteCommandEntity, DefaultAutoAgentExecuteStrategyFactory.DynamicContext, String> get(ExecuteCommandEntity requestParameter, DefaultAutoAgentExecuteStrategyFactory.DynamicContext dynamicContext) throws Exception {
// 如果任务已完成或达到最大步数,进入总结阶段
if (dynamicContext.isCompleted() || dynamicContext.getStep() > dynamicContext.getMaxStep()) {
return getBean("step4LogExecutionSummaryNode");
}
// 否则继续执行下一步
return getBean("step2PrecisionExecutorNode");
}
private void parseAnalysisResult(int step, String analysisResult) {
log.info("\n📊 === 第 {} 步分析结果 ===", step);
String[] lines = analysisResult.split("\n");
String currentSection = "";
for (String line : lines) {
line = line.trim();
if (line.isEmpty()) continue;
if (line.contains("任务状态分析:")) {
currentSection = "status";
log.info("\n🎯 任务状态分析:");
continue;
} else if (line.contains("执行历史评估:")) {
currentSection = "history";
log.info("\n📈 执行历史评估:");
continue;
} else if (line.contains("下一步策略:")) {
currentSection = "strategy";
log.info("\n🚀 下一步策略:");
continue;
} else if (line.contains("完成度评估:")) {
currentSection = "progress";
String progress = line.substring(line.indexOf(":") + 1).trim();
log.info("\n📊 完成度评估: {}", progress);
continue;
} else if (line.contains("任务状态:")) {
currentSection = "task_status";
String status = line.substring(line.indexOf(":") + 1).trim();
if (status.equals("COMPLETED")) {
log.info("\n✅ 任务状态: 已完成");
} else {
log.info("\n🔄 任务状态: 继续执行");
}
continue;
}
switch (currentSection) {
case "status":
log.info(" 📋 {}", line);
break;
case "history":
log.info(" 📊 {}", line);
break;
case "strategy":
log.info(" 🎯 {}", line);
break;
default:
log.info(" 📝 {}", line);
break;
}
}
}
}
4.行动节点
主要目标是提供执行结果给总结节点使用,同时要把结果告诉观察节点,来判断是否还需要循环。
java
@Slf4j
@Service
public class Step2PrecisionExecutorNode extends AbstractExecuteSupport{
@Override
protected String doApply(ExecuteCommandEntity requestParameter, DefaultAutoAgentExecuteStrategyFactory.DynamicContext dynamicContext) throws Exception {
log.info("\n⚡ 阶段2: 精准任务执行");
// 从动态上下文中获取分析结果
String analysisResult = dynamicContext.getValue("analysisResult");
if (analysisResult == null || analysisResult.trim().isEmpty()) {
log.warn("⚠️ 分析结果为空,使用默认执行策略");
analysisResult = "执行当前任务步骤";
}
String executionPrompt = String.format("""
**分析师策略:** %s
**执行指令:** 根据上述分析师的策略,执行具体的任务步骤。
**执行要求:**
1. 严格按照策略执行
2. 使用必要的工具
3. 确保执行质量
4. 详细记录过程
**输出格式:**
执行目标: [明确的执行目标]
执行过程: [详细的执行步骤]
执行结果: [具体的执行成果]
质量检查: [自我质量评估]
""", analysisResult);
// 获取对话客户端
AiAgentClientFlowConfigVO aiAgentClientFlowConfigVO = dynamicContext.getAiAgentClientFlowConfigVOMap().get(AiClientTypeEnumVO.PRECISION_EXECUTOR_CLIENT.getCode());
ChatClient chatClient = getChatClientByClientId(aiAgentClientFlowConfigVO.getClientId());
String executionResult = chatClient
.prompt(executionPrompt)
.advisors(a -> a
.param(CHAT_MEMORY_CONVERSATION_ID_KEY, requestParameter.getSessionId())
.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 1024))
.call().content();
parseExecutionResult(dynamicContext.getStep(), executionResult);
// 将执行结果保存到动态上下文中,供下一步使用
dynamicContext.setValue("executionResult", executionResult);
// 更新执行历史
String stepSummary = String.format("""
=== 第 %d 步执行记录 ===
【分析阶段】%s
【执行阶段】%s
""", dynamicContext.getStep(), analysisResult, executionResult);
dynamicContext.getExecutionHistory().append(stepSummary);
return router(requestParameter, dynamicContext);
}
@Override
public StrategyHandler<ExecuteCommandEntity, DefaultAutoAgentExecuteStrategyFactory.DynamicContext, String> get(ExecuteCommandEntity requestParameter, DefaultAutoAgentExecuteStrategyFactory.DynamicContext dynamicContext) throws Exception {
return getBean("step3QualitySupervisorNode");
}
/**
* 解析执行结果
*/
private void parseExecutionResult(int step, String executionResult) {
log.info("\n⚡ === 第 {} 步执行结果 ===", step);
String[] lines = executionResult.split("\n");
String currentSection = "";
for (String line : lines) {
line = line.trim();
if (line.isEmpty()) continue;
if (line.contains("执行目标:")) {
currentSection = "target";
log.info("\n🎯 执行目标:");
continue;
} else if (line.contains("执行过程:")) {
currentSection = "process";
log.info("\n🔧 执行过程:");
continue;
} else if (line.contains("执行结果:")) {
currentSection = "result";
log.info("\n📈 执行结果:");
continue;
} else if (line.contains("质量检查:")) {
currentSection = "quality";
log.info("\n🔍 质量检查:");
continue;
}
switch (currentSection) {
case "target":
log.info(" 🎯 {}", line);
break;
case "process":
log.info(" ⚙️ {}", line);
break;
case "result":
log.info(" 📊 {}", line);
break;
case "quality":
log.info(" ✅ {}", line);
break;
default:
log.info(" 📝 {}", line);
break;
}
}
}
}
5.观察节点
主要任务是判断是否循环执行。
java
@Slf4j
@Service
public class Step3QualitySupervisorNode extends AbstractExecuteSupport {
@Override
protected String doApply(ExecuteCommandEntity requestParameter, DefaultAutoAgentExecuteStrategyFactory.DynamicContext dynamicContext) throws Exception {
// 第三阶段:质量监督
log.info("\n🔍 阶段3: 质量监督检查");
// 从动态上下文中获取执行结果
String executionResult = dynamicContext.getValue("executionResult");
if (executionResult == null || executionResult.trim().isEmpty()) {
log.warn("⚠️ 执行结果为空,跳过质量监督");
return "质量监督跳过";
}
String supervisionPrompt = String.format("""
**用户原始需求:** %s
**执行结果:** %s
**监督要求:** 请评估执行结果的质量,识别问题,并提供改进建议。
**输出格式:**
质量评估: [对执行结果的整体评估]
问题识别: [发现的问题和不足]
改进建议: [具体的改进建议]
质量评分: [1-10分的质量评分]
是否通过: [PASS/FAIL/OPTIMIZE]
""", requestParameter.getMessage(), executionResult);
// 获取对话客户端
AiAgentClientFlowConfigVO aiAgentClientFlowConfigVO = dynamicContext.getAiAgentClientFlowConfigVOMap().get(AiClientTypeEnumVO.QUALITY_SUPERVISOR_CLIENT.getCode());
ChatClient chatClient = getChatClientByClientId(aiAgentClientFlowConfigVO.getClientId());
String supervisionResult = chatClient
.prompt(supervisionPrompt)
.advisors(a -> a
.param(CHAT_MEMORY_CONVERSATION_ID_KEY, requestParameter.getSessionId())
.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 1024))
.call().content();
parseSupervisionResult(dynamicContext.getStep(), supervisionResult);
// 将监督结果保存到动态上下文中
dynamicContext.setValue("supervisionResult", supervisionResult);
// 根据监督结果决定是否需要重新执行
if (supervisionResult.contains("是否通过: FAIL")) {
log.info("❌ 质量检查未通过,需要重新执行");
dynamicContext.setCurrentTask("根据质量监督的建议重新执行任务");
} else if (supervisionResult.contains("是否通过: OPTIMIZE")) {
log.info("🔧 质量检查建议优化,继续改进");
dynamicContext.setCurrentTask("根据质量监督的建议优化执行结果");
} else {
log.info("✅ 质量检查通过");
dynamicContext.setCompleted(true);
}
// 更新执行历史
String stepSummary = String.format("""
=== 第 %d 步完整记录 ===
【分析阶段】%s
【执行阶段】%s
【监督阶段】%s
""", dynamicContext.getStep(),
dynamicContext.getValue("analysisResult"),
executionResult,
supervisionResult);
dynamicContext.getExecutionHistory().append(stepSummary);
// 增加步骤计数
dynamicContext.setStep(dynamicContext.getStep() + 1);
// 如果任务已完成或达到最大步数,进入总结阶段
if (dynamicContext.isCompleted() || dynamicContext.getStep() > dynamicContext.getMaxStep()) {
return router(requestParameter, dynamicContext);
}
// 否则继续下一轮执行,返回到Step1AnalyzerNode
return router(requestParameter, dynamicContext);
}
@Override
public StrategyHandler<ExecuteCommandEntity, DefaultAutoAgentExecuteStrategyFactory.DynamicContext, String> get(ExecuteCommandEntity requestParameter, DefaultAutoAgentExecuteStrategyFactory.DynamicContext dynamicContext) throws Exception {
// 如果任务已完成或达到最大步数,进入总结阶段
if (dynamicContext.isCompleted() || dynamicContext.getStep() > dynamicContext.getMaxStep()) {
return getBean("step4LogExecutionSummaryNode");
}
// 否则返回到Step1AnalyzerNode进行下一轮分析
return getBean("step1AnalyzerNode");
}
/**
* 解析监督结果
*/
private void parseSupervisionResult(int step, String supervisionResult) {
log.info("\n🔍 === 第 {} 步监督结果 ===", step);
String[] lines = supervisionResult.split("\n");
String currentSection = "";
for (String line : lines) {
line = line.trim();
if (line.isEmpty()) continue;
if (line.contains("质量评估:")) {
currentSection = "assessment";
log.info("\n📊 质量评估:");
continue;
} else if (line.contains("问题识别:")) {
currentSection = "issues";
log.info("\n⚠️ 问题识别:");
continue;
} else if (line.contains("改进建议:")) {
currentSection = "suggestions";
log.info("\n💡 改进建议:");
continue;
} else if (line.contains("质量评分:")) {
currentSection = "score";
String score = line.substring(line.indexOf(":") + 1).trim();
log.info("\n📊 质量评分: {}", score);
continue;
} else if (line.contains("是否通过:")) {
currentSection = "pass";
String status = line.substring(line.indexOf(":") + 1).trim();
if (status.equals("PASS")) {
log.info("\n✅ 检查结果: 通过");
} else if (status.equals("FAIL")) {
log.info("\n❌ 检查结果: 未通过");
} else {
log.info("\n🔧 检查结果: 需要优化");
}
continue;
}
switch (currentSection) {
case "assessment":
log.info(" 📋 {}", line);
break;
case "issues":
log.info(" ⚠️ {}", line);
break;
case "suggestions":
log.info(" 💡 {}", line);
break;
default:
log.info(" 📝 {}", line);
break;
}
}
}
}
6.总结节点
作为最后一个节点兜底,同时分析执行结果,给出最后的总结。
java
@Slf4j
@Service
public class Step4LogExecutionSummaryNode extends AbstractExecuteSupport {
@Override
protected String doApply(ExecuteCommandEntity requestParameter, DefaultAutoAgentExecuteStrategyFactory.DynamicContext dynamicContext) throws Exception {
log.info("\n📊 === 执行第 {} 步 ===", dynamicContext.getStep());
// 第四阶段:执行总结
log.info("\n📊 阶段4: 执行总结分析");
// 记录执行总结
logExecutionSummary(dynamicContext.getMaxStep(), dynamicContext.getExecutionHistory(), dynamicContext.isCompleted());
// 如果任务未完成,生成最终总结报告
if (!dynamicContext.isCompleted()) {
generateFinalReport(requestParameter, dynamicContext);
}
log.info("\n🏁 === 动态多轮执行测试结束 ====");
return "ai agent execution summary completed!";
}
@Override
public StrategyHandler<ExecuteCommandEntity, DefaultAutoAgentExecuteStrategyFactory.DynamicContext, String> get(ExecuteCommandEntity requestParameter, DefaultAutoAgentExecuteStrategyFactory.DynamicContext dynamicContext) throws Exception {
// 总结节点是最后一个节点,返回null表示执行结束
return defaultStrategyHandler;
}
/**
* 记录执行总结
*/
private void logExecutionSummary(int maxSteps, StringBuilder executionHistory, boolean isCompleted) {
log.info("\n📊 === 动态多轮执行总结 ====");
int actualSteps = Math.min(maxSteps, executionHistory.toString().split("=== 第").length - 1);
log.info("📈 总执行步数: {} 步", actualSteps);
if (isCompleted) {
log.info("✅ 任务完成状态: 已完成");
} else {
log.info("⏸️ 任务完成状态: 未完成(达到最大步数限制)");
}
// 计算执行效率
double efficiency = isCompleted ? 100.0 : (double) actualSteps / maxSteps * 100;
log.info("📊 执行效率: {:.1f}%", efficiency);
}
/**
* 生成最终总结报告
*/
private void generateFinalReport(ExecuteCommandEntity requestParameter, DefaultAutoAgentExecuteStrategyFactory.DynamicContext dynamicContext) {
try {
log.info("\n--- 生成未完成任务的总结报告 ---");
String summaryPrompt = String.format("""
请对以下未完成的任务执行过程进行总结分析:
**原始用户需求:** %s
**执行历史:**
%s
**分析要求:**
1. 总结已完成的工作内容
2. 分析未完成的原因
3. 提出完成剩余任务的建议
4. 评估整体执行效果
""",
requestParameter.getMessage(),
dynamicContext.getExecutionHistory().toString());
// 获取对话客户端 - 使用任务分析客户端进行总结
AiAgentClientFlowConfigVO aiAgentClientFlowConfigVO = dynamicContext.getAiAgentClientFlowConfigVOMap().get(AiClientTypeEnumVO.TASK_ANALYZER_CLIENT.getCode());
ChatClient chatClient = getChatClientByClientId(aiAgentClientFlowConfigVO.getClientId());
String summaryResult = chatClient
.prompt(summaryPrompt)
.advisors(a -> a
.param(CHAT_MEMORY_CONVERSATION_ID_KEY, requestParameter.getSessionId() + "-summary")
.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 50))
.call().content();
logFinalReport(summaryResult);
// 将总结结果保存到动态上下文中
dynamicContext.setValue("finalSummary", summaryResult);
} catch (Exception e) {
log.error("生成最终总结报告时出现异常: {}", e.getMessage(), e);
}
}
/**
* 输出最终总结报告
*/
private void logFinalReport(String summaryResult) {
log.info("\n📋 === 最终总结报告 ===");
String[] lines = summaryResult.split("\n");
for (String line : lines) {
line = line.trim();
if (line.isEmpty()) continue;
// 根据内容类型添加不同图标
if (line.contains("已完成") || line.contains("完成的工作")) {
log.info("✅ {}", line);
} else if (line.contains("未完成") || line.contains("原因")) {
log.info("❌ {}", line);
} else if (line.contains("建议") || line.contains("推荐")) {
log.info("💡 {}", line);
} else if (line.contains("评估") || line.contains("效果")) {
log.info("📊 {}", line);
} else {
log.info("📝 {}", line);
}
}
}
}
三、测试
这里记得要去申请百度搜索的Api,并在数据库更改配置,否则会报错。
java
@Test
public void autoAgent() throws Exception {
StrategyHandler<ExecuteCommandEntity, DefaultAutoAgentExecuteStrategyFactory.DynamicContext, String> executeHandler
= defaultAutoAgentExecuteStrategyFactory.armoryStrategyHandler();
ExecuteCommandEntity executeCommandEntity = new ExecuteCommandEntity();
executeCommandEntity.setAiAgentId("3");
executeCommandEntity.setMessage("搜索小傅哥,技术项目列表。编写成一份文档,说明不同项目的学习目标,以及不同阶段的伙伴应该学习哪个项目。");
executeCommandEntity.setSessionId("session-id-" + System.currentTimeMillis());
executeCommandEntity.setMaxStep(3);
String apply = executeHandler.apply(executeCommandEntity, new DefaultAutoAgentExecuteStrategyFactory.DynamicContext());
log.info("测试结果:{}", apply);
}
