第4章 智能体(Agent)
本章目标:深入理解智能体的设计、接口和实现,掌握 ReActAgent 的配置和使用
4.1 Agent 接口设计
4.1.1 核心接口
/**
* Agent 接口是所有智能体的核心契约
* 定义了智能体必须具备的基本能力
*/
public interface Agent {
/**
* 处理消息,返回响应
*
* @param msg 用户输入的消息
* @return Mono<Msg> 响应式的响应消息
*/
Mono<Msg> call(Msg msg);
/**
* 无参数调用,继续处理上下文中的任务
* 常用于:
* - 从暂停状态恢复执行
* - 多智能体场景中的轮询
*/
Mono<Msg> call();
/**
* 带结构化输出的调用
*
* @param msg 用户输入的消息
* @param outputType 期望的输出类型
* @return 包含结构化数据的响应
*/
<T> Mono<Msg> call(Msg msg, Class<T> outputType);
/**
* 流式返回响应
* 每个中间状态和最终结果都会发射
*/
Flux<Msg> stream(Msg msg);
/**
* 中断正在执行的任务
*/
void interrupt();
/**
* 带消息的中断
* @param msg 中断时发送给智能体的消息
*/
void interrupt(Msg msg);
/**
* 获取智能体名称
*/
String getName();
}
4.1.2 调用方式对比
ReActAgent agent = ReActAgent.builder()...build();
// ========================================
// 【方式一】阻塞式调用
// 适用于:简单场景、脚本、测试
// ========================================
Msg response = agent.call(userMsg).block();
System.out.println(response.getTextContent());
// ========================================
// 【方式二】响应式调用
// 适用于:高并发、Web 服务
// ========================================
agent.call(userMsg)
.doOnSuccess(msg -> System.out.println("完成: " + msg.getTextContent()))
.doOnError(e -> System.err.println("错误: " + e.getMessage()))
.subscribe();
// ========================================
// 【方式三】流式调用
// 适用于:实时输出、进度展示
// ========================================
agent.stream(userMsg)
.doOnNext(msg -> {
// 每次发射都是当前状态的快照
System.out.print(msg.getIncrementalTextContent());
})
.doOnComplete(() -> System.out.println("\n完成"))
.subscribe();
// ========================================
// 【方式四】阻塞式流
// 适用于:需要实时输出但代码结构简单
// ========================================
agent.stream(userMsg)
.doOnNext(msg -> System.out.print(msg.getIncrementalTextContent()))
.blockLast(); // 阻塞直到流结束
4.2 有状态设计
4.2.1 智能体是有状态的
AgentScope 中的 Agent 是有状态对象。每个实例持有:
┌─────────────────────────────────────────────────────────────┐
│ ReActAgent 实例 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Memory │ │ Toolkit │ │
│ │ ─────────── │ │ ─────────── │ │
│ │ 对话历史 │ │ 注册的工具 │ │
│ │ 系统提示 │ │ 工具组状态 │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Model │ │ Configuration │ │
│ │ ─────────── │ │ ─────────── │ │
│ │ LLM 连接 │ │ maxIters │ │
│ │ 模型配置 │ │ sysPrompt │ │
│ └─────────────────┘ │ hooks │ │
│ └─────────────────┘ │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ Execution State │ │
│ │ ─────────────────────────────── │ │
│ │ running: false (是否正在执行) │ │
│ │ iteration: 0 (当前迭代次数) │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
4.2.2 并发调用的问题
警告:同一个 Agent 实例不能被并发调用!
// ========================================
// 【错误示例】并发调用同一实例
// ========================================
ReActAgent agent = ReActAgent.builder()...build();
// 错误!两个线程共享同一个 agent
CompletableFuture.runAsync(() -> agent.call(msg1).block());
CompletableFuture.runAsync(() -> agent.call(msg2).block());
// 可能导致:
// - 对话历史混乱
// - 状态不一致
// - 运行时异常
// ========================================
// 【正确示例一】为每个请求创建新实例
// ========================================
// 工厂方法
Supplier<ReActAgent> agentFactory = () -> ReActAgent.builder()
.name("Assistant")
.sysPrompt("...")
.model(model) // Model 可以共享
.build();
// 并发处理
CompletableFuture.runAsync(() -> {
ReActAgent agent = agentFactory.get(); // 新实例
agent.call(msg1).block();
});
CompletableFuture.runAsync(() -> {
ReActAgent agent = agentFactory.get(); // 新实例
agent.call(msg2).block();
});
// ========================================
// 【正确示例二】使用对象池
// ========================================
// 对于高并发场景,可以使用对象池复用智能体
// 需要确保智能体在归还前清理状态
4.2.3 checkRunning 选项
// 启用并发检查(默认开启)
ReActAgent agent = ReActAgent.builder()
.name("Assistant")
.model(model)
.checkRunning(true) // 如果正在执行,新调用会抛出异常
.build();
// 第一次调用
Mono<Msg> call1 = agent.call(msg1);
// 在 call1 完成前调用会抛出异常
try {
agent.call(msg2).block(); // 抛出 IllegalStateException
} catch (IllegalStateException e) {
System.out.println("智能体正忙: " + e.getMessage());
}
4.3 ReActAgent 详解
4.3.1 ReAct 算法原理
ReAct(Reasoning + Acting)是一种让 LLM 能够交错进行推理和行动的方法:
┌─────────────────────────────────────────────────────────────┐
│ ReAct 执行流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 输入: "帮我查一下北京明天的天气,然后推荐穿什么衣服" │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 第1轮 │ │
│ │ │ │
│ │ 推理: "用户想知道北京明天天气和穿搭建议。 │ │
│ │ 我需要先查询天气信息。" │ │
│ │ │ │
│ │ 行动: get_weather(city="北京", date="明天") │ │
│ │ │ │
│ │ 观察: {"weather": "晴", "temp": "15-22℃", ...} │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 第2轮 │ │
│ │ │ │
│ │ 推理: "天气是晴天,15-22℃。这个温度适合穿... │ │
│ │ 我已经有足够信息回答用户了。" │ │
│ │ │ │
│ │ 行动: 无(直接生成回复) │ │
│ │ │ │
│ │ 输出: "明天北京天气晴朗,气温15-22℃。建议穿..." │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
4.3.2 完整配置示例
// ========================================
// 【ReActAgent 完整配置示例】
// ========================================
ReActAgent agent = ReActAgent.builder()
// ======== 基础配置 ========
.name("CustomerService")
// 智能体名称
// - 用于日志和调试
// - 在多智能体场景中作为身份标识
// - 会出现在消息的 name 字段
.sysPrompt("""
你是一个专业的客服助手。
## 职责
- 回答客户关于产品的问题
- 处理订单查询和投诉
- 提供购买建议
## 行为规范
- 保持专业友好的语气
- 遇到不确定的问题,使用工具查询
- 涉及敏感操作时,确认后再执行
""")
// 系统提示词
// - 定义智能体的角色和行为
// - 作为第一条 SYSTEM 消息发送给 LLM
// - 支持 Markdown 格式
.description("客户服务智能体,处理产品咨询和订单问题")
// 描述信息
// - 用于 Agent as Tool 场景
// - 用于 A2A 协议的 AgentCard
// ======== 模型配置 ========
.model(model)
// LLM 模型(必需)
// - 智能体的"大脑"
// - 决定推理和生成能力
// ======== 工具配置 ========
.toolkit(toolkit)
// 工具包
// - 包含智能体可调用的所有工具
// - 可以在运行时动态修改工具组状态
// ======== 记忆配置 ========
.memory(new InMemoryMemory())
// 短期记忆
// - 存储对话历史
// - InMemoryMemory: 内存存储
// - AutoContextMemory: 自动压缩
.longTermMemory(longTermMemory)
// 长期记忆(可选)
// - 存储跨会话的用户偏好和知识
.longTermMemoryMode(LongTermMemoryMode.STATIC_CONTROL)
// 长期记忆模式
// - STATIC_CONTROL: 框架自动控制
// - AGENT_CONTROL: 智能体通过工具控制
// - BOTH: 两者兼用
// ======== 执行控制 ========
.maxIters(10)
// 最大迭代次数
// - 防止无限循环
// - 达到上限时返回 MAX_ITERATIONS
// - 默认值: 10
.checkRunning(true)
// 并发检查
// - true: 如果正在执行,新调用抛异常
// - false: 允许并发(不推荐)
// - 默认值: true
// ======== 超时配置 ========
.modelExecutionConfig(ExecutionConfig.builder()
.timeout(Duration.ofMinutes(2))
.maxAttempts(3)
.build())
// 模型调用配置
// - timeout: 单次调用超时
// - maxAttempts: 最大重试次数
.toolExecutionConfig(ExecutionConfig.builder()
.timeout(Duration.ofSeconds(30))
.maxAttempts(2)
.build())
// 工具执行配置
// ======== Hook 配置 ========
.hooks(List.of(
new LoggingHook(),
new MetricsHook()
))
// Hook 列表
// - 用于监控、修改智能体行为
// - 按优先级顺序执行
// ======== 高级功能 ========
.enablePlan()
// 启用计划功能
// - 自动添加计划相关工具
// - 复杂任务会分解为子任务
.structuredOutputReminder(StructuredOutputReminder.TOOL_CHOICE)
// 结构化输出模式
// - TOOL_CHOICE: 强制工具调用(推荐)
// - PROMPT: 提示词引导
.build();
4.3.3 构建器参数速查
| 参数 | 类型 | 必需 | 默认值 | 说明 |
|---|---|---|---|---|
name | String | ✅ | - | 智能体名称 |
sysPrompt | String | ✅ | - | 系统提示词 |
model | ChatModel | ✅ | - | LLM 模型 |
toolkit | Toolkit | ❌ | null | 工具包 |
memory | Memory | ❌ | InMemoryMemory | 短期记忆 |
longTermMemory | LongTermMemory | ❌ | null | 长期记忆 |
maxIters | int | ❌ | 10 | 最大迭代次数 |
checkRunning | boolean | ❌ | true | 并发检查 |
hooks | List | ❌ | [] | Hook 列表 |
4.4 中断与恢复
4.4.1 中断执行
// ========================================
// 【中断示例】
// ========================================
ReActAgent agent = ReActAgent.builder()...build();
// 异步调用
Mono<Msg> responseMono = agent.call(
Msg.builder().textContent("分析这个大型数据集...").build()
);
// 订阅但不阻塞
Disposable subscription = responseMono
.doOnSuccess(msg -> System.out.println("完成: " + msg.getTextContent()))
.subscribe();
// 模拟:5秒后用户取消
Thread.sleep(5000);
// 中断执行
agent.interrupt(); // 无消息中断
// 或者带消息中断
agent.interrupt(Msg.builder()
.textContent("用户取消了操作")
.build());
4.4.2 处理中断响应
Msg response = responseMono.block();
if (response.getGenerateReason() == GenerateReason.INTERRUPTED) {
System.out.println("任务被中断");
// 可以选择:
// 1. 保存当前状态,稍后继续
// 2. 清理并返回部分结果
// 3. 提示用户任务未完成
}
4.4.3 从暂停状态恢复
// ========================================
// 【恢复执行】
// 当 GenerateReason 是 REASONING_STOP_REQUESTED
// 或 ACTING_STOP_REQUESTED 时
// ========================================
Msg response = agent.call(userMsg).block();
if (response.getGenerateReason() == GenerateReason.REASONING_STOP_REQUESTED) {
// 显示待执行的工具,等待用户确认
List<ToolUseBlock> pendingTools = response.getContentBlocks(ToolUseBlock.class);
showPendingTools(pendingTools);
if (userConfirms()) {
// 用户确认,继续执行
response = agent.call().block(); // 无参数调用,继续执行
} else {
// 用户拒绝,提供替代结果
Msg cancelMsg = Msg.builder()
.role(MsgRole.TOOL)
.content(pendingTools.stream()
.map(t -> ToolResultBlock.of(t.getId(), t.getName(),
TextBlock.builder().text("操作已取消").build()))
.toList())
.build();
response = agent.call(cancelMsg).block();
}
}
4.5 其他 Agent 实现
4.5.1 UserAgent - 用户代理
// ========================================
// 【UserAgent】接收外部输入
// ========================================
import io.agentscope.core.UserAgent;
// 创建用户代理
UserAgent userAgent = UserAgent.builder()
.name("User")
.inputSource(msg -> {
// 从标准输入读取
System.out.print("你: ");
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
return Mono.just(Msg.builder()
.textContent(input)
.build());
})
.build();
// 使用
Msg userInput = userAgent.call(null).block();
4.5.2 A2aAgent - 远程代理
// ========================================
// 【A2aAgent】调用远程智能体服务
// ========================================
import io.agentscope.core.a2a.agent.A2aAgent;
import io.agentscope.core.a2a.agent.card.WellKnownAgentCardResolver;
// 创建远程代理
A2aAgent remoteAgent = A2aAgent.builder()
.name("RemoteExpert")
.agentCardResolver(new WellKnownAgentCardResolver(
"https://agent.example.com", // 服务地址
"/.well-known/agent-card.json", // AgentCard 路径
Map.of("Authorization", "Bearer xxx") // 请求头
))
.memory(new InMemoryMemory())
.build();
// 像调用本地智能体一样调用
Msg response = remoteAgent.call(userMsg).block();
4.6 本章小结
本章我们学习了:
- Agent 接口
- call():阻塞式调用 - stream():流式调用 - interrupt():中断执行
- 有状态设计
- 智能体持有 Memory、Toolkit 等状态 - 同一实例不能并发调用 - checkRunning 选项
- ReActAgent 配置
- 基础配置:name、sysPrompt、model - 工具配置:toolkit - 记忆配置:memory、longTermMemory - 执行控制:maxIters、超时配置 - Hook 配置
- 中断与恢复
- interrupt() 方法 - GenerateReason 判断 - 从暂停状态恢复
- 其他实现
- UserAgent:接收外部输入 - A2aAgent:调用远程服务
练习
- 创建自定义智能体:实现一个专门处理数学问题的智能体
- 测试并发行为:观察 checkRunning 的效果
- 实现中断功能:添加超时自动中断的逻辑
- 流式输出:使用 stream() 实现打字机效果
下一章 → 第5章 模型集成