本章导读
本章将深入剖析AgentScope的核心实现:ReActAgent。ReAct(Reasoning + Acting)是一种将推理和行动相结合的Agent执行模式,使Agent能够通过多轮推理-行动循环解决复杂任务。
- 理解ReAct原理和推理-行动循环
- 掌握ReActAgent的配置和使用
- 深入了解推理阶段和行动阶段的实现细节
- 学会迭代控制和终止条件设计
- 掌握生产环境中的复杂任务处理
5.1 ReAct原理
5.1.1 什么是ReAct
ReAct是"Reasoning + Acting"的缩写,是一种让大语言模型(LLM)能够交替进行推理和行动的方法。
传统LLM的局限:
用户: 请告诉我北京今天的天气
传统LLM: 抱歉,我无法获取实时天气信息。(只能基于训练数据回答)
用户: 计算 123456 * 789012
传统LLM: 大约是97000000000...(可能计算错误,因为LLM不擅长精确计算)
ReAct模式的优势:
用户: 请告诉我北京今天的天气
ReAct Agent:
[推理] 用户想知道北京的实时天气,我需要调用天气API
[行动] 调用 get_weather(city="北京")
[结果] 北京今天晴天,温度15-25°C
[推理] 已获取天气信息,可以回答用户了
[回复] 北京今天晴天,温度在15-25°C之间,适合户外活动。
用户: 计算 123456 * 789012
ReAct Agent:
[推理] 这是一个数学计算,我应该使用计算器工具
[行动] 调用 calculator(expression="123456 * 789012")
[结果] 97408265472
[推理] 计算完成,可以回答用户了
[回复] 123456 * 789012 = 97408265472
5.1.2 ReAct的执行流程
完整的ReAct循环示例:
用户输入: "帮我分析一下苹果公司的最新财报"
━━━ 第1轮循环 ━━━
[推理阶段]
Agent思考: "用户想要分析苹果公司的财报,我需要先获取最新的财报数据"
决策: 调用工具 get_financial_report
[行动阶段]
执行工具: get_financial_report(company="Apple", period="latest")
工具返回: "2024 Q3财报:营收850亿美元,同比增长8%,净利润230亿美元..."
━━━ 第2轮循环 ━━━
[推理阶段]
Agent思考: "已经获取了财报数据,现在需要分析关键指标"
决策: 调用工具 analyze_metrics
[行动阶段]
执行工具: analyze_metrics(data="营收850亿...", metrics=["revenue", "profit", "growth"])
工具返回: "营收同比增长8%,利润率27%,增长稳健..."
━━━ 第3轮循环 ━━━
[推理阶段]
Agent思考: "已完成数据收集和分析,现在可以生成报告了"
决策: 不需要工具,直接生成回复
[最终回复]
"根据苹果公司2024年Q3财报分析:
1. 营收达到850亿美元,同比增长8%
2. 净利润230亿美元,利润率27%
3. 各项指标表现稳健,增长势头良好
..."
5.1.3 ReAct vs 传统Chain-of-Thought
Chain-of-Thought (CoT):
用户: 计算复杂问题
Agent: 让我一步步思考...
第一步: ...
第二步: ...
第三步: ...
结论: ...
优点: 推理过程清晰
缺点: 无法调用外部工具,只能基于知识推理
━━━━━━━━━━━━━━━━━━
ReAct:
用户: 计算复杂问题
Agent: [思考] 我需要查询数据
[工具] 调用API获取数据
[结果] 获得数据
[思考] 我需要计算
[工具] 调用计算器
[结果] 得到答案
[回复] 最终答案
优点: 可以调用工具,处理实际问题
缺点: 需要更多轮次,Token消耗较大
5.2 ReActAgent的配置
5.2.1 基础配置
import io.agentscope.core.ReActAgent;
import io.agentscope.core.memory.InMemoryMemory;
import io.agentscope.core.model.DashScopeChatModel;
import io.agentscope.core.tool.Toolkit;
public class BasicReActAgentExample {
public static void main(String[] args) {
// 步骤1:创建Model
DashScopeChatModel model = DashScopeChatModel.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.modelName("qwen-plus")
.build();
// 步骤2:创建Toolkit
Toolkit toolkit = new Toolkit();
toolkit.registerObject(new MyTools()); // 注册工具类
// 步骤3:创建ReActAgent
ReActAgent agent = ReActAgent.builder()
// ===== 基本信息 =====
.name("MyAssistant")
.description("一个智能助手")
// ===== 核心组件 =====
.model(model) // LLM模型
.toolkit(toolkit) // 工具箱
.memory(new InMemoryMemory()) // 记忆系统
// ===== 系统提示词 =====
.sysPrompt(
"你是一个有帮助的AI助手。\n" +
"你可以调用提供的工具来解决用户的问题。\n" +
"如果需要工具,请先思考需要哪个工具,然后调用它。\n" +
"如果不需要工具,直接回答用户的问题。"
)
// ===== 执行控制 =====
.maxIters(10) // 最多执行10轮推理-行动循环
.checkRunning(true) // 启用重入检查
.build();
// 步骤4:使用Agent
Msg response = agent.call("北京今天的天气怎么样?").block();
System.out.println(response.getTextContent());
}
}
5.2.2 高级配置
import io.agentscope.core.hook.Hook;
import io.agentscope.core.plan.LongTermMemory;
import io.agentscope.core.plan.LongTermMemoryMode;
import io.agentscope.core.tool.ExecutionConfig;
import java.time.Duration;
public class AdvancedReActAgentExample {
public static ReActAgent createAdvancedAgent(String apiKey) {
// 创建高级配置的Agent
return ReActAgent.builder()
.name("AdvancedAssistant")
// ===== Model配置 =====
.model(DashScopeChatModel.builder()
.apiKey(apiKey)
.modelName("qwen-max") // 使用更强大的模型
.generateOptions(GenerateOptions.builder()
.temperature(0.7) // 控制随机性
.topP(0.9)
.maxTokens(4000) // 最大生成Token数
.build())
.build())
// ===== 工具执行配置 =====
.toolkit(toolkit)
.toolExecutionConfig(ExecutionConfig.builder()
.timeout(Duration.ofMinutes(2)) // 工具执行超时
.maxAttempts(1) // 工具不重试
.build())
// ===== Model调用配置 =====
.modelExecutionConfig(ExecutionConfig.builder()
.timeout(Duration.ofMinutes(1)) // Model调用超时
.maxAttempts(3) // 失败重试3次
.build())
// ===== 记忆系统 =====
.memory(new InMemoryMemory())
// ===== 长期记忆(可选)=====
.withLongTermMemory(
createLongTermMemory(), // 长期记忆实现
LongTermMemoryMode.HYBRID // 混合模式:同时检索和存储
)
// ===== 任务规划(可选)=====
.withPlanNotebook() // 启用PlanNotebook
// ===== Hook系统 =====
.hooks(List.of(
createLoggingHook(), // 日志Hook
createMonitoringHook(), // 监控Hook
createRAGHook() // RAG增强Hook
))
// ===== 执行控制 =====
.maxIters(15) // 允许更多轮次
.checkRunning(true) // 启用重入检查
// ===== 系统提示词 =====
.sysPrompt(buildAdvancedSystemPrompt())
.build();
}
private static String buildAdvancedSystemPrompt() {
return """
你是一个高级AI助手,具备以下能力:
1. 工具使用
- 优先使用工具获取准确信息
- 合理选择工具,避免不必要的调用
- 如果工具调用失败,尝试其他方法或告知用户
2. 任务规划
- 对于复杂任务,先制定计划
- 使用PlanNotebook管理任务进度
- 定期总结进展
3. 记忆管理
- 记住用户的偏好和历史对话
- 利用长期记忆提供个性化服务
4. 质量控制
- 确保回复准确、简洁
- 如果不确定,明确告知用户
- 保护用户隐私
""";
}
}
5.2.3 配置参数详解
/**
* ReActAgent配置参数说明
*/
public class ReActAgentConfigGuide {
/**
* maxIters(最大迭代次数)
*
* 用途: 限制推理-行动循环的最大轮次
* 默认值: 10
* 推荐值:
* - 简单对话: 5-8
* - 一般任务: 10-15
* - 复杂任务: 15-20
*
* 注意: 过大会导致Token消耗过多和响应延迟
*/
int maxIters = 10;
/**
* checkRunning(重入检查)
*
* 用途: 防止Agent被递归调用
* 默认值: false
* 推荐值: true(生产环境)
*
* 场景:
* - 在工具中调用同一个Agent → 会报错
* - 多线程并发调用同一个Agent → 会报错
*/
boolean checkRunning = true;
/**
* sysPrompt(系统提示词)
*
* 用途: 定义Agent的角色和行为规范
*
* 最佳实践:
* 1. 清晰定义Agent的角色
* 2. 说明可用的工具和使用场景
* 3. 设定行为准则(例如:不要猜测,使用工具)
* 4. 控制在200-500字以内
*/
String sysPrompt = "...";
/**
* toolExecutionConfig(工具执行配置)
*
* timeout: 工具执行超时时间
* - 快速工具(数据库查询): 5-10秒
* - 慢速工具(文件处理): 30秒-2分钟
* - 外部API: 30秒
*
* maxAttempts: 工具失败重试次数
* - 通常设置为1(不重试)
* - 对于不稳定的外部API,可设置为2-3
*/
ExecutionConfig toolExecutionConfig = ExecutionConfig.builder()
.timeout(Duration.ofSeconds(30))
.maxAttempts(1)
.build();
/**
* modelExecutionConfig(Model调用配置)
*
* timeout: Model调用超时时间
* - 短文本生成: 30秒
* - 长文本生成: 1-2分钟
*
* maxAttempts: 失败重试次数
* - 推荐2-3次(处理网络抖动)
*/
ExecutionConfig modelExecutionConfig = ExecutionConfig.builder()
.timeout(Duration.ofMinutes(1))
.maxAttempts(3)
.build();
}
5.3 推理-行动循环的实现细节
5.3.1 推理阶段(Reasoning)
推理阶段是Agent思考"应该做什么"的过程。
推理阶段要做什么?
输入:
├─ 用户消息
├─ 历史对话(从Memory获取)
├─ 可用工具列表(ToolSchema)
└─ 系统提示词
处理流程:
1. 准备消息列表
├─ 系统消息(sysPrompt)
├─ 历史消息(Memory)
└─ 当前用户消息
2. Hook增强
├─ RAG Hook: 检索相关知识
├─ LongTermMemory Hook: 检索长期记忆
└─ 其他自定义Hook
3. 调用LLM
├─ 发送消息和ToolSchema给Model
├─ Model生成回复(可能包含工具调用)
└─ 流式接收响应
4. 解析响应
├─ 提取文本内容
├─ 提取ToolUseBlock(如果有)
└─ 记录到Memory
输出:
├─ Agent的回复消息(Msg)
├─ 可能包含ToolUseBlock
└─ Token使用统计
代码流程(简化版):
/**
* 推理阶段的核心逻辑
*/
private Mono<Msg> reasoningPhase(GenerateOptions options) {
return Mono.defer(() -> {
// 1. 触发PreReasoningEvent Hook
PreReasoningEvent preEvent = new PreReasoningEvent(
this.name,
this.currentIterCount,
System.currentTimeMillis()
);
return notifyHooks(preEvent)
.flatMap(event -> {
// 2. 准备消息列表
List<Msg> messages = prepareMessagesForModel();
// 3. 准备工具Schema
List<ToolSchema> toolSchemas = toolkit.getToolSchemas();
// 4. 调用Model(流式)
return model.stream(messages, toolSchemas, options)
// 5. 累积响应块
.reduce(
ContentAccumulator.empty(),
ContentAccumulator::accumulate
)
// 6. 构建最终消息
.flatMap(accumulator -> {
Msg response = accumulator.toMsg(MsgRole.ASSISTANT);
// 7. 添加到Memory
memory.add(response);
// 8. 触发PostReasoningEvent Hook
PostReasoningEvent postEvent = new PostReasoningEvent(
this.name,
response,
accumulator.getUsage(),
System.currentTimeMillis() - preEvent.getTimestamp()
);
return notifyHooks(postEvent)
.thenReturn(response);
});
});
});
}
/**
* 准备发送给Model的消息
*/
private List<Msg> prepareMessagesForModel() {
List<Msg> messages = new ArrayList<>();
// 1. 添加系统消息
if (sysPrompt != null && !sysPrompt.isEmpty()) {
messages.add(Msg.builder()
.role(MsgRole.SYSTEM)
.textContent(sysPrompt)
.build());
}
// 2. 添加历史消息(从Memory获取)
List<Msg> history = memory.getMemory();
messages.addAll(history);
return messages;
}
5.3.2 行动阶段(Acting)
行动阶段是Agent执行工具的过程。
行动阶段要做什么?
输入:
└─ 推理阶段的响应(可能包含ToolUseBlock)
处理流程:
1. 检查是否需要执行工具
├─ 如果没有ToolUseBlock → 跳过,直接返回
└─ 如果有ToolUseBlock → 继续
2. 提取工具调用
├─ 工具名称
├─ 工具参数(JSON格式)
└─ 调用ID
3. 执行工具
├─ 触发PreActingEvent Hook
├─ 查找工具实现
├─ 参数验证和转换
├─ 反射调用方法
└─ 捕获返回值或异常
4. 生成工具结果
├─ ToolResultBlock(成功)
└─ ToolResultBlock(失败,包含错误信息)
5. 添加到Memory
├─ 包含ToolUseBlock的消息
└─ 包含ToolResultBlock的消息
6. 触发PostActingEvent Hook
输出:
└─ 工具执行结果(ToolResultBlock)
代码流程(简化版):
/**
* 行动阶段的核心逻辑
*/
private Mono<Void> actingPhase(Msg reasoningResponse) {
return Mono.defer(() -> {
// 1. 提取所有ToolUseBlock
List<ToolUseBlock> toolCalls = reasoningResponse.getContent().stream()
.filter(block -> block instanceof ToolUseBlock)
.map(block -> (ToolUseBlock) block)
.toList();
// 2. 如果没有工具调用,直接返回
if (toolCalls.isEmpty()) {
return Mono.empty();
}
// 3. 执行每个工具调用
return Flux.fromIterable(toolCalls)
.flatMap(toolCall -> executeToolCall(toolCall))
.collectList()
.flatMap(toolResults -> {
// 4. 创建包含工具结果的消息
Msg toolResultMsg = Msg.builder()
.role(MsgRole.TOOL)
.content(toolResults.toArray(new ContentBlock[0]))
.build();
// 5. 添加到Memory
memory.add(toolResultMsg);
return Mono.empty();
});
});
}
/**
* 执行单个工具调用
*/
private Mono<ToolResultBlock> executeToolCall(ToolUseBlock toolCall) {
return Mono.defer(() -> {
// 1. 触发PreActingEvent Hook
PreActingEvent preEvent = new PreActingEvent(
this.name,
toolCall.getName(),
toolCall.getArguments(),
System.currentTimeMillis()
);
return notifyHooks(preEvent)
.flatMap(event -> {
long startTime = System.currentTimeMillis();
try {
// 2. 查找工具
AgentTool tool = toolkit.getTool(toolCall.getName());
if (tool == null) {
return Mono.just(createErrorResult(
toolCall,
"Tool not found: " + toolCall.getName()
));
}
// 3. 执行工具
Object result = tool.invoke(toolCall.getArguments());
// 4. 转换为字符串
String resultText = result instanceof String
? (String) result
: result.toString();
// 5. 创建成功结果
ToolResultBlock resultBlock = ToolResultBlock.builder()
.id(toolCall.getId())
.name(toolCall.getName())
.content(resultText)
.isError(false)
.build();
// 6. 触发PostActingEvent Hook
PostActingEvent postEvent = new PostActingEvent(
this.name,
toolCall.getName(),
resultText,
false,
System.currentTimeMillis() - startTime
);
return notifyHooks(postEvent)
.thenReturn(resultBlock);
} catch (Exception e) {
// 7. 处理异常
ToolResultBlock errorBlock = createErrorResult(
toolCall,
"Tool execution failed: " + e.getMessage()
);
PostActingEvent postEvent = new PostActingEvent(
this.name,
toolCall.getName(),
e.getMessage(),
true,
System.currentTimeMillis() - startTime
);
return notifyHooks(postEvent)
.thenReturn(errorBlock);
}
});
});
}
5.3.3 迭代控制与终止条件
ReActAgent的核心是推理-行动循环。如何控制循环次数和终止条件非常关键。
循环控制逻辑:
while (iterCount < maxIters) {
// 1. 推理阶段
Msg response = reasoning();
// 2. 检查终止条件
if (shouldStop(response)) {
return response; // 完成
}
// 3. 行动阶段
acting(response);
// 4. 增加计数
iterCount++;
}
// 达到最大迭代次数
return finalResponse;
终止条件:
/**
* 判断是否应该停止循环
*/
private boolean shouldStop(Msg response) {
// 条件1:没有工具调用(表示Agent生成了最终答案)
boolean hasToolCall = response.getContent().stream()
.anyMatch(block -> block instanceof ToolUseBlock);
if (!hasToolCall) {
return true; // 停止
}
// 条件2:Agent明确表示完成
// (某些Model会在文本中说"已完成")
String text = response.getTextContent().toLowerCase();
if (text.contains("已完成") || text.contains("任务完成") ||
text.contains("finished") || text.contains("done")) {
return true;
}
// 条件3:工具调用失败且无法继续
// (这个逻辑可以在Hook中实现)
return false; // 继续循环
}
完整的循环实现:
/**
* ReActAgent的核心循环(简化版)
*/
private Mono<Msg> executeReActLoop(List<Msg> inputMsgs) {
return Mono.defer(() -> {
// 1. 初始化
int iterCount = 0;
Msg lastResponse = null;
// 2. 添加输入消息到Memory
inputMsgs.forEach(msg -> memory.add(msg));
// 3. 循环执行
while (iterCount < maxIters) {
try {
// (1) 推理阶段
Msg reasoningResult = reasoningPhase(null).block();
lastResponse = reasoningResult;
// (2) 检查终止条件
if (shouldStop(reasoningResult)) {
return Mono.just(reasoningResult); // 完成
}
// (3) 行动阶段
actingPhase(reasoningResult).block();
// (4) 增加计数
iterCount++;
} catch (Exception e) {
// 处理异常
return Mono.error(new RuntimeException(
"ReAct loop failed at iteration " + iterCount, e));
}
}
// 4. 达到最大迭代次数
if (lastResponse != null) {
// 添加提示信息
String warning = "\n\n[注意: 已达到最大迭代次数 " + maxIters + "]";
Msg finalMsg = Msg.builder()
.role(lastResponse.getRole())
.textContent(lastResponse.getTextContent() + warning)
.build();
return Mono.just(finalMsg);
}
return Mono.error(new RuntimeException(
"Max iterations reached without completion"));
});
}
5.4 生产场景:复杂任务处理
5.4.1 场景:数据分析Agent
import io.agentscope.core.ReActAgent;
import io.agentscope.core.tool.Tool;
import io.agentscope.core.tool.ToolParam;
import io.agentscope.core.tool.Toolkit;
import java.util.List;
/**
* 数据分析Agent
*
* 功能:
* 1. 查询数据库
* 2. 统计分析
* 3. 生成可视化图表
* 4. 输出分析报告
*/
public class DataAnalysisAgentExample {
// ============ 工具类 ============
public static class DataAnalysisTools {
@Tool(name = "query_database",
description = "Query data from database with SQL")
public String queryDatabase(
@ToolParam(name = "sql", description = "SQL query statement")
String sql,
@ToolParam(name = "limit", description = "Maximum number of rows to return")
Integer limit) {
// 实际实现:连接数据库执行查询
try {
List<Map<String, Object>> results = executeSQL(sql, limit);
return formatQueryResults(results);
} catch (Exception e) {
return "Error executing query: " + e.getMessage();
}
}
@Tool(name = "calculate_statistics",
description = "Calculate statistical metrics (mean, median, std, etc.)")
public String calculateStatistics(
@ToolParam(name = "data", description = "Comma-separated numbers")
String data,
@ToolParam(name = "metrics", description = "Metrics to calculate: mean,median,std,min,max")
String metrics) {
try {
double[] numbers = parseNumbers(data);
List<String> requestedMetrics = Arrays.asList(metrics.split(","));
StringBuilder result = new StringBuilder("Statistics:\n");
if (requestedMetrics.contains("mean")) {
result.append("Mean: ").append(calculateMean(numbers)).append("\n");
}
if (requestedMetrics.contains("median")) {
result.append("Median: ").append(calculateMedian(numbers)).append("\n");
}
if (requestedMetrics.contains("std")) {
result.append("Std Dev: ").append(calculateStd(numbers)).append("\n");
}
if (requestedMetrics.contains("min")) {
result.append("Min: ").append(Arrays.stream(numbers).min().orElse(0)).append("\n");
}
if (requestedMetrics.contains("max")) {
result.append("Max: ").append(Arrays.stream(numbers).max().orElse(0)).append("\n");
}
return result.toString();
} catch (Exception e) {
return "Error calculating statistics: " + e.getMessage();
}
}
@Tool(name = "generate_chart",
description = "Generate a chart visualization")
public String generateChart(
@ToolParam(name = "data", description = "Data points in JSON format")
String data,
@ToolParam(name = "chart_type", description = "Chart type: bar, line, pie, scatter")
String chartType,
@ToolParam(name = "title", description = "Chart title")
String title) {
try {
// 实际实现:生成图表并保存为文件
String chartPath = generateChartImage(data, chartType, title);
return "Chart generated successfully at: " + chartPath;
} catch (Exception e) {
return "Error generating chart: " + e.getMessage();
}
}
@Tool(name = "create_report",
description = "Create a formatted analysis report")
public String createReport(
@ToolParam(name = "title", description = "Report title")
String title,
@ToolParam(name = "sections", description = "Report sections in JSON format")
String sections) {
try {
// 实际实现:生成PDF或HTML报告
String reportPath = generateReport(title, sections);
return "Report created successfully at: " + reportPath;
} catch (Exception e) {
return "Error creating report: " + e.getMessage();
}
}
// 辅助方法...
private List<Map<String, Object>> executeSQL(String sql, Integer limit) {
// 数据库查询实现
return null;
}
private double[] parseNumbers(String data) {
return Arrays.stream(data.split(","))
.mapToDouble(Double::parseDouble)
.toArray();
}
private double calculateMean(double[] numbers) {
return Arrays.stream(numbers).average().orElse(0);
}
private double calculateMedian(double[] numbers) {
double[] sorted = Arrays.copyOf(numbers, numbers.length);
Arrays.sort(sorted);
int mid = sorted.length / 2;
return sorted.length % 2 == 0
? (sorted[mid - 1] + sorted[mid]) / 2
: sorted[mid];
}
private double calculateStd(double[] numbers) {
double mean = calculateMean(numbers);
double variance = Arrays.stream(numbers)
.map(x -> Math.pow(x - mean, 2))
.average()
.orElse(0);
return Math.sqrt(variance);
}
}
// ============ 创建Agent ============
public static ReActAgent createDataAnalysisAgent(String apiKey) {
// 创建Toolkit
Toolkit toolkit = new Toolkit();
toolkit.registerObject(new DataAnalysisTools());
// 创建Agent
return ReActAgent.builder()
.name("DataAnalyst")
.description("专业的数据分析助手")
.model(DashScopeChatModel.builder()
.apiKey(apiKey)
.modelName("qwen-max") // 使用更强大的模型
.generateOptions(GenerateOptions.builder()
.temperature(0.3) // 降低随机性,提高准确性
.build())
.build())
.toolkit(toolkit)
.memory(new InMemoryMemory())
.maxIters(12) // 允许更多轮次
.sysPrompt("""
你是一个专业的数据分析师。
工作流程:
1. 理解用户的分析需求
2. 使用query_database查询所需数据
3. 使用calculate_statistics计算统计指标
4. 使用generate_chart生成可视化图表
5. 使用create_report生成分析报告
注意事项:
- 查询数据时要设置合理的limit避免数据过多
- 统计分析要选择合适的指标
- 图表类型要符合数据特点
- 报告要包含关键发现和建议
""")
.build();
}
// ============ 使用示例 ============
public static void main(String[] args) {
ReActAgent analyst = createDataAnalysisAgent(
System.getenv("DASHSCOPE_API_KEY"));
// 场景:分析销售数据
String task = """
请分析2024年Q1的销售数据:
1. 查询所有销售记录
2. 计算总销售额、平均订单金额、销售趋势
3. 生成月度销售柱状图
4. 输出分析报告,包含关键发现和建议
""";
Msg response = analyst.call(task).block();
System.out.println("=== 分析结果 ===");
System.out.println(response.getTextContent());
// 查看Token使用
if (response.getChatUsage() != null) {
System.out.println("\n=== 资源使用 ===");
System.out.println("Total Tokens: " + response.getChatUsage().getTotalTokens());
System.out.println("Estimated Cost: $" +
calculateCost(response.getChatUsage()));
}
}
private static double calculateCost(ChatUsage usage) {
// qwen-max的定价(示例)
double inputCostPerK = 0.04; // 每1K input tokens
double outputCostPerK = 0.12; // 每1K output tokens
return (usage.getPromptTokens() / 1000.0 * inputCostPerK) +
(usage.getCompletionTokens() / 1000.0 * outputCostPerK);
}
}
执行过程示例:
用户: 请分析2024年Q1的销售数据...
━━━ 第1轮 ━━━
[推理] 需要先查询销售数据
[工具] query_database(sql="SELECT * FROM sales WHERE date >= '2024-01-01' AND date < '2024-04-01'", limit=1000)
[结果] 返回856条销售记录
━━━ 第2轮 ━━━
[推理] 需要计算统计指标
[工具] calculate_statistics(data="销售额数据...", metrics="mean,median,std,min,max")
[结果] Mean: ¥1250, Median: ¥980, Std: ¥450...
━━━ 第3轮 ━━━
[推理] 需要生成可视化图表
[工具] generate_chart(data="月度数据...", chart_type="bar", title="Q1月度销售趋势")
[结果] Chart generated at: /path/to/chart.png
━━━ 第4轮 ━━━
[推理] 可以生成最终报告了
[工具] create_report(title="2024 Q1销售分析报告", sections="...")
[结果] Report created at: /path/to/report.pdf
━━━ 第5轮 ━━━
[推理] 所有工具已执行完成,现在可以总结了
[回复]
2024年Q1销售数据分析已完成:
关键发现:
1. 总销售额:¥1,070,000
2. 订单总数:856单
3. 平均订单金额:¥1,250
4. 销售趋势:1月最高(¥420,000),2月略有下降,3月回升
建议:
1. 加强2月的营销活动
2. 重点关注高价值客户(订单金额>¥2000)
3. 优化库存管理,减少缺货情况
详细报告和图表已生成:
- 图表: /path/to/chart.png
- 报告: /path/to/report.pdf
5.5 性能优化和最佳实践
5.5.1 Token使用优化
/**
* 优化Token使用的策略
*/
public class TokenOptimizationExample {
public static ReActAgent createOptimizedAgent(String apiKey) {
return ReActAgent.builder()
.name("OptimizedAgent")
.model(DashScopeChatModel.builder()
.apiKey(apiKey)
.modelName("qwen-plus")
.generateOptions(GenerateOptions.builder()
// 策略1:限制最大Token数
.maxTokens(1500) // 避免生成过长的回复
// 策略2:降低temperature
.temperature(0.5) // 减少随机性,提高确定性
build())
.build())
// 策略3:限制最大迭代次数
.maxIters(8) // 避免过多的推理循环
// 策略4:使用简洁的系统提示词
.sysPrompt(
"你是AI助手。使用工具获取信息,回复要简洁。" // 100字以内
)
// 策略5:限制Memory大小
.memory(new InMemoryMemory(20)) // 只保留最近20条消息
.build();
}
}
5.5.2 错误处理和重试
/**
* 错误处理最佳实践
*/
public class ErrorHandlingExample {
public static void robustAgentExecution() {
ReActAgent agent = ReActAgent.builder()
.name("RobustAgent")
.model(model)
.toolkit(toolkit)
// 配置超时和重试
.modelExecutionConfig(ExecutionConfig.builder()
.timeout(Duration.ofSeconds(30))
.maxAttempts(3) // Model调用失败重试3次
.build())
.toolExecutionConfig(ExecutionConfig.builder()
.timeout(Duration.ofSeconds(20))
.maxAttempts(1) // 工具不重试(避免副作用)
.build())
.build();
// 使用时添加错误处理
agent.call("用户请求")
.timeout(Duration.ofMinutes(2)) // 整体超时
.retry(2) // 整个调用重试2次
.doOnError(error -> {
// 记录错误日志
log.error("Agent execution failed", error);
// 发送告警
sendAlert("Agent failed: " + error.getMessage());
})
.onErrorResume(error -> {
// 返回降级响应
return Mono.just(Msg.builder()
.textContent("抱歉,系统暂时无法处理您的请求。")
.build());
})
.subscribe();
}
}
5.5.3 并发控制
/**
* 多用户并发场景
*/
public class ConcurrencyExample {
private final Map<String, ReActAgent> userAgents = new ConcurrentHashMap<>();
/**
* 为每个用户创建独立的Agent实例
*/
public ReActAgent getOrCreateAgent(String userId) {
return userAgents.computeIfAbsent(userId, id ->
ReActAgent.builder()
.name("Agent-" + id)
.model(model)
.memory(new InMemoryMemory()) // 独立的Memory
.checkRunning(true) // 防止重入
.build()
);
}
/**
* 处理并发请求
*/
public Flux<Msg> handleConcurrentRequests(List<UserRequest> requests) {
return Flux.fromIterable(requests)
// 并发执行,最多10个并发
.flatMap(request -> {
ReActAgent agent = getOrCreateAgent(request.getUserId());
return agent.call(request.getMessage());
}, 10)
.onErrorContinue((error, item) -> {
// 单个请求失败不影响其他请求
log.error("Request failed: " + item, error);
});
}
}
5.6 本章总结
关键要点
-
ReAct原理
- 推理(Reasoning)+ 行动(Acting)
- 交替执行,多轮循环
- 支持工具调用,解决实际问题
-
推理阶段
- 准备消息(系统提示+历史+工具Schema)
- 调用LLM生成回复
- 解析响应(文本+工具调用)
-
行动阶段
- 提取ToolUseBlock
- 执行工具
- 生成ToolResultBlock
- 添加到Memory
-
循环控制
- maxIters限制最大轮次
- shouldStop()判断终止条件
- 达到最大轮次强制结束
-
性能优化
- 限制Token使用
- 控制迭代次数
- 合理设置超时
- 错误处理和重试
最佳实践
✓ 合理设置maxIters(通常5-15)
✓ 简洁的系统提示词(200-500字)
✓ 限制Memory大小(10-50条消息)
✓ 为每个用户创建独立Agent
✓ 启用checkRunning防止重入
✓ 配置超时和重试策略
✓ 监控Token使用和成本
✗ 不要在工具中调用同一个Agent
✗ 不要忽略错误处理
✗ 不要使用过长的系统提示词
下一章预告
第6章将深入讲解工具系统,探讨@Tool注解、工具注册、工具调用流程、参数验证等内容,并提供生产级的工具开发案例。