本章导读
本章将深入讲解AgentScope中最核心的抽象:Agent接口。Agent是整个框架的基石,定义了所有智能体的行为契约。无论是简单的问答Bot,还是复杂的多Agent协作系统,都建立在Agent接口之上。
导读:
- 理解Agent接口的核心方法和设计理念
- 掌握call()、observe()等方法的使用场景
- 学会使用AgentBase基类构建自定义Agent
- 了解流式执行和事件系统
- 掌握Hook系统的集成方式
4.1 Agent接口设计
4.1.1 Agent接口的核心结构
Agent接口定义了AgentScope中所有智能体必须实现的方法。它采用响应式编程模型,所有方法都返回Mono<T>或Flux<T>,支持异步、非阻塞的执行。
完整的Agent接口:
java
package io.agentscope.core.agent;
import io.agentscope.core.message.Msg;
import io.agentscope.core.interruption.InterruptContext;
import reactor.core.publisher.Mono;
import java.util.List;
public interface Agent {
// ============ 基础属性 ============
/**
* 获取Agent的唯一标识
* 用于在多Agent系统中区分不同的Agent实例
*/
String getAgentId();
/**
* 获取Agent的名称
* 用于日志、监控、用户界面展示
*/
String getName();
/**
* 获取Agent的描述
* 说明Agent的功能和用途
*/
String getDescription();
// ============ 核心方法 ============
/**
* 处理单条消息
* 这是最常用的方法
*
* @param msg 用户输入的消息
* @return Agent的回复消息
*/
default Mono<Msg> call(Msg msg) {
return call(msg == null ? List.of() : List.of(msg));
}
/**
* 处理多条消息
* 用于需要同时考虑多条上下文消息的场景
*
* @param msgs 消息列表
* @return Agent的回复消息
*/
Mono<Msg> call(List<Msg> msgs);
/**
* 继续生成(无新输入)
* 用于让Agent继续之前的生成或推理
*
* @return Agent的回复消息
*/
default Mono<Msg> call() {
return call(List.of());
}
// ============ 结构化输出 ============
/**
* 返回结构化数据的版本
* 要求Agent生成符合指定类结构的数据
*
* @param msg 用户输入的消息
* @param structuredModel 期望的输出结构类
* @return 包含结构化数据的消息
*/
default Mono<Msg> call(Msg msg, Class<?> structuredModel) {
return call(msg == null ? List.of() : List.of(msg), structuredModel);
}
Mono<Msg> call(List<Msg> msgs, Class<?> structuredModel);
default Mono<Msg> call(Class<?> structuredModel) {
return call(List.of(), structuredModel);
}
// ============ 多Agent协作 ============
/**
* 观察消息而不回复
* 用于多Agent协作场景,Agent需要接收信息但不需要回复
*
* @param msg 要观察的消息
* @return 完成信号
*/
Mono<Void> observe(Msg msg);
Mono<Void> observe(List<Msg> msgs);
// ============ 中断控制 ============
/**
* 中断Agent执行
* 用于人机协同或安全控制场景
*
* @param reason 中断原因
* @return 中断上下文
*/
default Mono<InterruptContext> interrupt(String reason) {
return Mono.empty();
}
/**
* 检查Agent是否可中断
*
* @return true表示Agent支持中断
*/
default boolean isInterruptible() {
return false;
}
}
4.1.2 设计理念
理念1:响应式编程模型
为什么使用Mono/Flux?
python
传统同步模型的问题:
❌ 阻塞线程,资源浪费
❌ 难以处理超时
❌ 无法有效组合多个异步操作
❌ 扩展性差
响应式模型的优势:
✓ 非阻塞,高并发
✓ 声明式的错误处理
✓ 灵活的操作组合(map、flatMap、zip等)
✓ 天然支持流式处理
✓ 易于集成背压(backpressure)机制
代码对比:
java
// 传统同步方式(阻塞)
public Msg call(Msg msg) {
// 阻塞线程等待LLM响应(可能需要几秒)
Msg response = llmService.generate(msg);
return response;
}
// 响应式方式(非阻塞)
public Mono<Msg> call(Msg msg) {
// 立即返回,不阻塞线程
return llmService.generateAsync(msg)
.timeout(Duration.ofSeconds(30)) // 声明式超时
.retryWhen(Retry.backoff(3, Duration.ofSeconds(1))) // 声明式重试
.doOnError(e -> log.error("Generation failed", e)); // 声明式错误处理
}
理念2:call()方法的多态设计
为什么需要多个call()重载?
scss
场景1:简单对话
├─ call(Msg msg)
└─ 用户提问,Agent回答
场景2:多轮上下文
├─ call(List<Msg> msgs)
└─ 需要考虑多条历史消息
场景3:无输入续写
├─ call()
└─ Agent继续之前的生成
场景4:结构化输出
├─ call(Msg msg, Class<?> structuredModel)
└─ 要求Agent返回特定格式的数据
理念3:observe()的必要性
为什么需要observe()?
在多Agent协作中,并非所有Agent都需要对每条消息做出回复。observe()提供了一种"只听不说"的机制。
scss
示例场景:三个Agent协作
Agent A(数据收集器)
↓ 发送数据
Agent B(观察者)→ observe(msg) → 只记录,不回复
↓
Agent C(决策者)→ call(msg) → 基于所有数据做决策
代码示例:
java
// Agent B 作为观察者
ReActAgent observerAgent = ReActAgent.builder()
.name("DataObserver")
.memory(new InMemoryMemory()) // 有记忆但不回复
.build();
// 接收其他Agent的消息
Msg dataFromAgentA = ...;
observerAgent.observe(dataFromAgentA).block(); // 只记录到Memory
// 稍后可以基于观察到的数据生成总结
Msg summary = observerAgent.call("请总结之前观察到的所有数据").block();
4.2 call()方法的使用场景(How - 如何使用)
4.2.1 场景1:简单问答对话
java
import io.agentscope.core.ReActAgent;
import io.agentscope.core.message.Msg;
import io.agentscope.core.model.DashScopeChatModel;
public class SimpleQAExample {
public static void main(String[] args) {
// 创建一个简单的问答Agent
ReActAgent agent = ReActAgent.builder()
.name("QABot")
.model(DashScopeChatModel.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.modelName("qwen-plus")
.build())
.sysPrompt("你是一个友好的AI助手,请简洁地回答用户的问题。")
.build();
// 用户提问
Msg userMsg = Msg.builder()
.textContent("什么是量子计算?")
.build();
// 获取回复(阻塞方式)
Msg response = agent.call(userMsg).block();
System.out.println("Agent回复: " + response.getTextContent());
// 异步方式
agent.call(userMsg)
.subscribe(
msg -> System.out.println("Agent回复: " + msg.getTextContent()),
error -> System.err.println("错误: " + error.getMessage()),
() -> System.out.println("完成")
);
}
}
4.2.2 场景2:多轮对话上下文
java
public class MultiTurnConversationExample {
public static void main(String[] args) {
ReActAgent agent = ReActAgent.builder()
.name("ConversationBot")
.model(model)
.memory(new InMemoryMemory()) // 启用记忆
.build();
// 第一轮对话
Msg msg1 = Msg.builder().textContent("我叫李明,今年28岁").build();
agent.call(msg1).block();
// 第二轮对话(Agent能记住前面的信息)
Msg msg2 = Msg.builder().textContent("我的名字是什么?").build();
Msg response2 = agent.call(msg2).block();
System.out.println(response2.getTextContent());
// 输出:您的名字是李明。
// 批量处理多条消息
List<Msg> messages = List.of(
Msg.builder().role(MsgRole.USER).textContent("今天天气怎么样?").build(),
Msg.builder().role(MsgRole.ASSISTANT).textContent("今天天气晴朗。").build(),
Msg.builder().role(MsgRole.USER).textContent("适合户外运动吗?").build()
);
Msg response = agent.call(messages).block();
// Agent基于完整上下文回复
}
}
4.2.3 场景3:结构化输出
这是Agent接口的强大特性之一:要求Agent生成符合特定结构的数据。
java
import lombok.Data;
public class StructuredOutputExample {
// 定义期望的输出结构
@Data
public static class UserProfile {
private String name;
private Integer age;
private String email;
private String phone;
private String city;
}
@Data
public static class ProductReview {
private String productName;
private Integer rating; // 1-5星
private List<String> pros; // 优点
private List<String> cons; // 缺点
private String summary;
}
public static void main(String[] args) {
ReActAgent agent = ReActAgent.builder()
.name("StructuredAgent")
.model(model)
.build();
// 场景1:提取用户信息
Msg userInfoMsg = Msg.builder()
.textContent("我叫张三,28岁,邮箱是zhangsan@example.com," +
"手机13800138000,住在北京")
.build();
Msg response1 = agent.call(userInfoMsg, UserProfile.class).block();
if (response1.hasStructuredData()) {
UserProfile profile = response1.getStructuredData(UserProfile.class);
System.out.println("姓名: " + profile.getName());
System.out.println("年龄: " + profile.getAge());
System.out.println("邮箱: " + profile.getEmail());
System.out.println("手机: " + profile.getPhone());
System.out.println("城市: " + profile.getCity());
}
// 场景2:分析产品评论
Msg reviewMsg = Msg.builder()
.textContent("这款iPhone 15 Pro很不错,性能强劲,拍照效果好," +
"但是价格偏贵,电池续航一般。总体来说值得购买。")
.build();
Msg response2 = agent.call(reviewMsg, ProductReview.class).block();
if (response2.hasStructuredData()) {
ProductReview review = response2.getStructuredData(ProductReview.class);
System.out.println("产品: " + review.getProductName());
System.out.println("评分: " + review.getRating() + "星");
System.out.println("优点: " + String.join(", ", review.getPros()));
System.out.println("缺点: " + String.join(", ", review.getCons()));
System.out.println("总结: " + review.getSummary());
}
}
}
输出示例:
makefile
姓名: 张三
年龄: 28
邮箱: zhangsan@example.com
手机: 13800138000
城市: 北京
产品: iPhone 15 Pro
评分: 4星
优点: 性能强劲, 拍照效果好
缺点: 价格偏贵, 电池续航一般
总结: 总体来说值得购买
4.2.4 场景4:无输入续写
java
public class ContinuationExample {
public static void main(String[] args) {
ReActAgent agent = ReActAgent.builder()
.name("WriterAgent")
.model(model)
.sysPrompt("你是一个创意写作助手。")
.build();
// 第一次调用:开始创作
Msg msg1 = Msg.builder()
.textContent("请写一个科幻小说的开头")
.build();
Msg response1 = agent.call(msg1).block();
System.out.println("第一段:\n" + response1.getTextContent());
// 第二次调用:继续创作(无新输入)
Msg response2 = agent.call().block();
System.out.println("\n第二段:\n" + response2.getTextContent());
// 第三次调用:继续
Msg response3 = agent.call().block();
System.out.println("\n第三段:\n" + response3.getTextContent());
}
}
4.3 AgentBase 基础实现
4.3.1 AgentBase的架构设计
AgentBase是Agent接口的抽象基类,提供了大量通用功能,让开发者可以专注于核心逻辑。
java
package io.agentscope.core.agent;
import io.agentscope.core.hook.Hook;
import io.agentscope.core.message.Msg;
import io.agentscope.core.state.StateModule;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Map;
public abstract class AgentBase implements Agent {
// ============ 核心状态 ============
/**
* Agent的唯一标识
* 自动生成的UUID
*/
protected final String agentId;
/**
* Agent的名称
* 用于日志和展示
*/
protected final String name;
/**
* Agent的描述
* 说明Agent的功能
*/
protected final String description;
/**
* Hook列表
* 用于事件拦截和增强
*/
protected final List<Hook> hooks;
/**
* 嵌套的状态模块
* 例如Memory、PlanNotebook等
* 用于会话持久化
*/
protected final Map<String, StateModule> modules;
// ============ 执行控制 ============
/**
* 标记Agent是否正在执行
*/
protected boolean isRunning = false;
/**
* 是否检查重入调用
* 防止Agent被递归调用
*/
protected boolean checkRunning = false;
// ============ 子类实现 ============
/**
* 子类必须实现:处理消息的核心逻辑
*/
protected abstract Mono<Msg> doCall(List<Msg> msgs);
/**
* 子类必须实现:处理结构化输出
*/
protected abstract Mono<Msg> doCall(List<Msg> msgs, Class<?> structuredOutputClass);
/**
* 子类必须实现:观察消息
*/
protected abstract Mono<Void> doObserve(Msg msg);
// ============ 模板方法 ============
@Override
public final Mono<Msg> call(List<Msg> msgs) {
return Mono.defer(() -> {
// 1. 重入检查
if (checkRunning && isRunning) {
return Mono.error(new IllegalStateException(
"Agent [" + name + "] is already running. " +
"Reentrant calls are not allowed."));
}
// 2. 设置运行标志
isRunning = true;
// 3. 执行实际逻辑
return doCall(msgs)
// 4. 无论成功或失败,都要重置运行标志
.doFinally(signal -> isRunning = false);
});
}
@Override
public final Mono<Msg> call(List<Msg> msgs, Class<?> structuredModel) {
return Mono.defer(() -> {
if (checkRunning && isRunning) {
return Mono.error(new IllegalStateException(
"Agent [" + name + "] is already running."));
}
isRunning = true;
return doCall(msgs, structuredModel)
.doFinally(signal -> isRunning = false);
});
}
// ============ 状态管理 ============
/**
* 获取所有嵌套的状态模块
* 用于会话持久化
*/
public Map<String, StateModule> getStateModules() {
return modules;
}
}
4.3.2 AgentBase的核心特性
特性1:Hook系统集成
AgentBase内置了Hook系统支持,可以在Agent执行的各个阶段插入自定义逻辑。
java
import io.agentscope.core.hook.Hook;
import io.agentscope.core.hook.HookEvent;
import io.agentscope.core.hook.PostReasoningEvent;
import io.agentscope.core.hook.PreReasoningEvent;
public class HookIntegrationExample {
public static void main(String[] args) {
// 创建自定义Hook
Hook loggingHook = new Hook() {
@Override
public <T extends HookEvent> Mono<T> onEvent(T event) {
switch(event) {
case PreReasoningEvent pre ->
System.out.println(" 开始推理...");
case PostReasoningEvent post ->
System.out.println("✓ 推理完成,耗时: " + post.getExecutionTime() + "ms");
default -> {}
}
return Mono.just(event);
}
};
// 性能监控Hook
Hook performanceHook = new Hook() {
@Override
public <T extends HookEvent> Mono<T> onEvent(T event) {
if (event instanceof PostReasoningEvent post) {
long duration = post.getExecutionTime();
if (duration > 5000) {
System.err.println("⚠️ 推理耗时过长: " + duration + "ms");
}
}
return Mono.just(event);
}
};
// 在Agent中注册Hook
ReActAgent agent = ReActAgent.builder()
.name("MonitoredAgent")
.model(model)
.hooks(List.of(loggingHook, performanceHook))
.build();
// Hook会在Agent执行过程中自动触发
agent.call("请分析一下当前的市场趋势").block();
}
}
特性2:嵌套模块管理
AgentBase自动管理Memory、PlanNotebook等StateModule,支持会话持久化。
java
import io.agentscope.core.memory.InMemoryMemory;
import io.agentscope.core.plan.notebook.PlanNotebook;
import io.agentscope.core.session.Session;
public class StateModuleExample {
public static void main(String[] args) {
// 创建带有多个状态模块的Agent
InMemoryMemory memory = new InMemoryMemory();
PlanNotebook planNotebook = new PlanNotebook();
ReActAgent agent = ReActAgent.builder()
.name("StatefulAgent")
.model(model)
.memory(memory) // 自动注册为状态模块
.withPlanNotebook() // 自动注册为状态模块
.build();
// 使用Agent
agent.call("请制定一个市场分析计划").block();
agent.call("执行第一步:收集数据").block();
// 保存整个Agent的状态
Session session = Session.create();
session.saveAgent(agent);
// ... 程序重启后 ...
// 恢复Agent的状态
ReActAgent restoredAgent = session.loadAgent("StatefulAgent", ReActAgent.class);
// Agent的Memory和PlanNotebook都已恢复
restoredAgent.call("继续执行计划").block();
}
}
特性3:重入检查
防止Agent被递归调用,避免潜在的死锁和资源耗尽。
java
public class ReentrantCheckExample {
public static void main(String[] args) {
// 启用重入检查
ReActAgent agent = ReActAgent.builder()
.name("ProtectedAgent")
.model(model)
.checkRunning(true) // 启用重入检查
.build();
// 场景1:正常使用(没问题)
agent.call("问题1").block();
agent.call("问题2").block(); // OK,前一个调用已完成
// 场景2:嵌套调用(会报错)
try {
agent.call("外层问题")
.flatMap(response -> {
// 在处理响应时又调用了同一个Agent
return agent.call("内层问题"); // ❌ 错误!
})
.block();
} catch (IllegalStateException e) {
System.err.println("错误: " + e.getMessage());
// 输出: Agent [ProtectedAgent] is already running.
// Reentrant calls are not allowed.
}
// 场景3:禁用重入检查(允许嵌套调用)
ReActAgent flexibleAgent = ReActAgent.builder()
.name("FlexibleAgent")
.model(model)
.checkRunning(false) // 禁用重入检查
.build();
// 这样可以工作(但要小心死锁)
flexibleAgent.call("外层问题")
.flatMap(response -> flexibleAgent.call("内层问题"))
.block();
}
}
4.4 流式执行与事件系统
4.4.1 stream()方法
除了call()方法,Agent还提供stream()方法,支持流式输出,实时获取Agent的推理过程。
java
import io.agentscope.core.hook.*;
import reactor.core.publisher.Flux;
public class StreamingExample {
public static void main(String[] args) {
ReActAgent agent = ReActAgent.builder()
.name("StreamingAgent")
.model(model)
.toolkit(toolkit)
.build();
Msg userMsg = Msg.builder()
.textContent("请分析一下当前的经济形势")
.build();
// 方式1:订阅所有事件
agent.stream(userMsg)
.subscribe(
event -> handleEvent(event), // 处理每个事件
error -> System.err.println("错误: " + error),
() -> System.out.println("\n[完成]")
);
// 方式2:只关心推理内容
String fullText = agent.stream(userMsg)
.filter(event -> event instanceof ReasoningChunkEvent)
.map(event -> ((ReasoningChunkEvent) event).getDelta())
.collect(Collectors.joining())
.block();
System.out.println("完整内容: " + fullText);
// 方式3:实时更新UI
agent.stream(userMsg)
.doOnNext(event -> {
if (event instanceof ReasoningChunkEvent chunk) {
updateUI(chunk.getDelta()); // 实时更新UI组件
}
})
.subscribe();
}
private static void handleEvent(HookEvent event) {
switch(event) {
case PreReasoningEvent pre ->
System.out.println("🤔 开始思考...");
case ReasoningChunkEvent chunk ->
System.out.print(chunk.getDelta()); // 实时打印推理内容
case PostReasoningEvent post ->
System.out.println("\n✓ 思考完成");
case PreActingEvent pre ->
System.out.println("🔧 开始执行工具: " + pre.getToolName());
case ActingChunkEvent chunk ->
System.out.print("⚙️ " + chunk.getDelta());
case PostActingEvent post ->
System.out.println("\n✓ 工具执行完成");
default -> {}
}
}
private static void updateUI(String delta) {
// 在JavaFX或Swing中更新UI
Platform.runLater(() -> textArea.appendText(delta));
}
}
4.4.2 事件类型详解
Agent在执行过程中会发出多种事件,这些事件构成了完整的执行生命周期。
ini
Agent执行生命周期事件:
第N轮推理-行动循环:
├─ PreReasoningEvent # 推理开始前
├─ ReasoningChunkEvent # 推理内容流(多次)
├─ PostReasoningEvent # 推理完成后
│
├─ PreActingEvent # 行动开始前(如果有工具调用)
├─ ActingChunkEvent # 行动内容流(多次)
├─ PostActingEvent # 行动完成后
│
└─ [循环或结束]
事件对象的属性:
java
// PreReasoningEvent - 推理前事件
public class PreReasoningEvent extends HookEvent {
private List<Msg> inputMessages; // 输入消息
private int iterationCount; // 当前是第几轮推理
private long timestamp; // 时间戳
}
// ReasoningChunkEvent - 推理内容流
public class ReasoningChunkEvent extends HookEvent {
private String delta; // 增量文本
private ContentBlock contentBlock; // 内容块(可能包含工具调用)
}
// PostReasoningEvent - 推理后事件
public class PostReasoningEvent extends HookEvent {
private Msg outputMessage; // 输出消息
private long executionTime; // 执行耗时(毫秒)
private ChatUsage usage; // Token使用情况
}
// PreActingEvent - 行动前事件
public class PreActingEvent extends HookEvent {
private String toolName; // 工具名称
private Map<String, Object> toolArgs; // 工具参数
}
// ActingChunkEvent - 行动内容流
public class ActingChunkEvent extends HookEvent {
private String delta; // 增量内容
}
// PostActingEvent - 行动后事件
public class PostActingEvent extends HookEvent {
private String toolName; // 工具名称
private String toolResult; // 工具执行结果
private boolean isError; // 是否出错
private long executionTime; // 执行耗时
}
4.5 生产场景:构建智能客服系统
让我们通过一个完整的生产级示例,展示如何使用Agent接口构建一个功能完善的智能客服系统。
java
import io.agentscope.core.ReActAgent;
import io.agentscope.core.agent.Agent;
import io.agentscope.core.hook.Hook;
import io.agentscope.core.hook.HookEvent;
import io.agentscope.core.hook.PostActingEvent;
import io.agentscope.core.hook.PostReasoningEvent;
import io.agentscope.core.memory.InMemoryMemory;
import io.agentscope.core.message.Msg;
import io.agentscope.core.model.DashScopeChatModel;
import io.agentscope.core.tool.Toolkit;
import lombok.Data;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
/**
* 生产级智能客服系统
*
* 功能:
* 1. 多用户并发支持
* 2. 会话管理
* 3. 性能监控
* 4. 日志记录
* 5. 异常处理
* 6. 超时控制
*/
public class CustomerServiceSystem {
// ============ 系统组件 ============
private final ConcurrentHashMap<String, Agent> userAgents = new ConcurrentHashMap<>();
private final PerformanceMonitor monitor = new PerformanceMonitor();
private final Model model;
private final Toolkit toolkit;
public CustomerServiceSystem(String apiKey) {
// 初始化Model
this.model = DashScopeChatModel.builder()
.apiKey(apiKey)
.modelName("qwen-plus")
.generateOptions(GenerateOptions.builder()
.temperature(0.7)
.maxTokens(2000)
.build())
.build();
// 初始化Toolkit
this.toolkit = new Toolkit();
toolkit.registerObject(new CustomerServiceTools());
}
// ============ 核心方法 ============
/**
* 为用户创建或获取Agent
*/
public Agent getOrCreateAgentForUser(String userId) {
return userAgents.computeIfAbsent(userId, id -> createCustomerServiceAgent(id));
}
/**
* 创建客服Agent
*/
private Agent createCustomerServiceAgent(String userId) {
return ReActAgent.builder()
.name("CustomerService-" + userId)
.description("为用户 " + userId + " 服务的智能客服")
.model(model)
.toolkit(toolkit)
.memory(new InMemoryMemory())
.sysPrompt(buildSystemPrompt())
.maxIters(8)
.checkRunning(true)
.hooks(List.of(
createLoggingHook(userId),
createMonitoringHook(userId)
))
.build();
}
/**
* 处理用户请求
*/
public Mono<CustomerServiceResponse> handleUserRequest(
String userId,
String message) {
Agent agent = getOrCreateAgentForUser(userId);
Msg userMsg = Msg.builder()
.textContent(message)
.metadata("userId", userId)
.metadata("timestamp", System.currentTimeMillis())
.build();
return agent.call(userMsg)
.timeout(Duration.ofSeconds(30)) // 30秒超时
.map(response -> CustomerServiceResponse.builder()
.userId(userId)
.message(response.getTextContent())
.timestamp(System.currentTimeMillis())
.usage(response.getChatUsage())
.build())
.doOnError(error -> handleError(userId, error))
.onErrorResume(error -> Mono.just(
CustomerServiceResponse.builder()
.userId(userId)
.message("抱歉,系统暂时无法处理您的请求,请稍后再试。")
.isError(true)
.build()));
}
/**
* 结构化查询(用于获取订单信息等)
*/
public <T> Mono<T> handleStructuredQuery(
String userId,
String message,
Class<T> responseType) {
Agent agent = getOrCreateAgentForUser(userId);
Msg userMsg = Msg.builder()
.textContent(message)
.build();
return agent.call(userMsg, responseType)
.timeout(Duration.ofSeconds(30))
.map(response -> response.getStructuredData(responseType));
}
/**
* 清理用户会话
*/
public void cleanupUserSession(String userId) {
Agent agent = userAgents.remove(userId);
if (agent != null) {
System.out.println("清理用户会话: " + userId);
}
}
// ============ Hook实现 ============
/**
* 创建日志Hook
*/
private Hook createLoggingHook(String userId) {
return new Hook() {
@Override
public <T extends HookEvent> Mono<T> onEvent(T event) {
switch(event) {
case PostReasoningEvent post ->
System.out.printf("[%s] 推理完成,耗时: %dms, Tokens: %d%n",
userId, post.getExecutionTime(),
post.getUsage() != null ? post.getUsage().getTotalTokens() : 0);
case PostActingEvent post ->
System.out.printf("[%s] 工具执行: %s, 耗时: %dms%n",
userId, post.getToolName(), post.getExecutionTime());
default -> {}
}
return Mono.just(event);
}
};
}
/**
* 创建监控Hook
*/
private Hook createMonitoringHook(String userId) {
return new Hook() {
@Override
public <T extends HookEvent> Mono<T> onEvent(T event) {
switch(event) {
case PostReasoningEvent post -> {
monitor.recordReasoningTime(post.getExecutionTime());
if (post.getUsage() != null) {
monitor.recordTokenUsage(post.getUsage().getTotalTokens());
}
}
case PostActingEvent post -> {
monitor.recordToolExecution(post.getToolName(), post.getExecutionTime());
}
default -> {}
}
return Mono.just(event);
}
};
}
/**
* 系统提示词
*/
private String buildSystemPrompt() {
return """
你是一个专业的智能客服助手。
工作原则:
1. 友好、耐心、专业
2. 优先使用工具查询准确信息,不要猜测
3. 如果无法解决问题,及时转人工客服
4. 保护用户隐私,不泄露敏感信息
5. 每次回复要简洁明了
可用工具:
- search_faq: 搜索常见问题
- query_order: 查询订单状态
- process_return: 处理退货请求
- escalate_to_human: 转人工客服
""";
}
/**
* 错误处理
*/
private void handleError(String userId, Throwable error) {
System.err.printf("[%s] 错误: %s%n", userId, error.getMessage());
monitor.recordError();
}
/**
* 获取系统统计
*/
public PerformanceStats getStats() {
return monitor.getStats();
}
// ============ 数据类 ============
@Data
public static class CustomerServiceResponse {
private String userId;
private String message;
private long timestamp;
private ChatUsage usage;
private boolean isError;
public static CustomerServiceResponseBuilder builder() {
return new CustomerServiceResponseBuilder();
}
}
// ============ 性能监控 ============
public static class PerformanceMonitor {
private final AtomicLong totalReasoningTime = new AtomicLong(0);
private final AtomicLong totalTokens = new AtomicLong(0);
private final AtomicLong errorCount = new AtomicLong(0);
private final ConcurrentHashMap<String, AtomicLong> toolUsage = new ConcurrentHashMap<>();
public void recordReasoningTime(long ms) {
totalReasoningTime.addAndGet(ms);
}
public void recordTokenUsage(long tokens) {
totalTokens.addAndGet(tokens);
}
public void recordToolExecution(String toolName, long ms) {
toolUsage.computeIfAbsent(toolName, k -> new AtomicLong(0)).incrementAndGet();
}
public void recordError() {
errorCount.incrementAndGet();
}
public PerformanceStats getStats() {
return new PerformanceStats(
totalReasoningTime.get(),
totalTokens.get(),
errorCount.get(),
toolUsage
);
}
}
@Data
public static class PerformanceStats {
private final long totalReasoningTime;
private final long totalTokens;
private final long errorCount;
private final Map<String, AtomicLong> toolUsage;
}
// ============ 使用示例 ============
public static void main(String[] args) {
CustomerServiceSystem system = new CustomerServiceSystem(
System.getenv("DASHSCOPE_API_KEY"));
// 场景1:处理用户请求
system.handleUserRequest("user123", "我的订单什么时候发货?")
.subscribe(response -> {
System.out.println("客服回复: " + response.getMessage());
System.out.println("Token使用: " + response.getUsage().getTotalTokens());
});
// 场景2:结构化查询订单信息
@Data
class OrderInfo {
private String orderId;
private String status;
private String trackingNumber;
}
system.handleStructuredQuery("user123",
"查询订单ORD-2024-001的信息",
OrderInfo.class)
.subscribe(orderInfo -> {
System.out.println("订单ID: " + orderInfo.getOrderId());
System.out.println("状态: " + orderInfo.getStatus());
System.out.println("物流单号: " + orderInfo.getTrackingNumber());
});
// 场景3:清理会话
system.cleanupUserSession("user123");
// 场景4:查看统计
PerformanceStats stats = system.getStats();
System.out.println("总推理时间: " + stats.getTotalReasoningTime() + "ms");
System.out.println("总Token使用: " + stats.getTotalTokens());
System.out.println("错误次数: " + stats.getErrorCount());
}
}
4.6 本章总结
关键要点
-
Agent接口设计
- 响应式编程模型:Mono/Flux支持异步非阻塞
- 多态call()方法:适配不同使用场景
- observe()方法:支持多Agent协作
- 结构化输出:要求Agent返回特定格式数据
-
AgentBase基类
- 提供通用功能实现
- Hook系统集成
- 状态模块管理
- 重入检查保护
-
流式执行
- stream()方法支持实时输出
- 丰富的事件类型
- 适用于UI交互场景
-
生产实践
- 多用户并发支持
- 性能监控
- 异常处理
- 超时控制
最佳实践
scss
✓ 使用响应式API,避免阻塞
✓ 合理设置超时时间
✓ 启用重入检查,防止递归调用
✓ 使用Hook进行日志和监控
✓ 为不同用户创建独立的Agent实例
✓ 定期清理不活跃的会话
✓ 使用结构化输出获取精确数据
✗ 不要在工具中调用Agent(避免死锁)
✗ 不要忽略错误处理
✗ 不要在生产环境中使用block()(除非必要)
下一章预告
第5章将深入讲解ReActAgent的核心实现,探讨推理-行动循环的工作原理、迭代控制、工具调用流程等内容。