阿里开源AgentScope多智能体框架解析系列(四)第4章:Agent 接口与 AgentBase 基类

本章导读

本章将深入讲解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 本章总结

关键要点

  1. Agent接口设计

    • 响应式编程模型:Mono/Flux支持异步非阻塞
    • 多态call()方法:适配不同使用场景
    • observe()方法:支持多Agent协作
    • 结构化输出:要求Agent返回特定格式数据
  2. AgentBase基类

    • 提供通用功能实现
    • Hook系统集成
    • 状态模块管理
    • 重入检查保护
  3. 流式执行

    • stream()方法支持实时输出
    • 丰富的事件类型
    • 适用于UI交互场景
  4. 生产实践

    • 多用户并发支持
    • 性能监控
    • 异常处理
    • 超时控制

最佳实践

scss 复制代码
✓ 使用响应式API,避免阻塞
✓ 合理设置超时时间
✓ 启用重入检查,防止递归调用
✓ 使用Hook进行日志和监控
✓ 为不同用户创建独立的Agent实例
✓ 定期清理不活跃的会话
✓ 使用结构化输出获取精确数据
✗ 不要在工具中调用Agent(避免死锁)
✗ 不要忽略错误处理
✗ 不要在生产环境中使用block()(除非必要)

下一章预告

第5章将深入讲解ReActAgent的核心实现,探讨推理-行动循环的工作原理、迭代控制、工具调用流程等内容。

相关推荐
语落心生2 小时前
阿里开源AgentScope多智能体框架解析系列(一)第1章:AgentScope-Java 概述与快速入门
agent
潘锦3 小时前
AI Agent 核心管理逻辑:工具的管理和调度
agent
Jay Kay5 小时前
Agent沙箱执行服务
agent·沙箱服务
小马过河R5 小时前
ReAct和Function Calling之间的纠葛与恩恩怨怨
人工智能·语言模型·agent·智能体
大模型真好玩7 小时前
LangGraph智能体开发设计模式(二)——协调器-工作者模式、评估器-优化器模式
人工智能·langchain·agent
沛沛老爹8 小时前
Web开发者实战AI Agent:基于Dify实现OpenAI Deep Research智能体
前端·人工智能·gpt·agent·rag·web转型
沛沛老爹8 小时前
Web开发者实战AI Agent:基于Dify的多模态文生图与文生视频智能体项目
前端·人工智能·llm·agent·rag·web转型
EdisonZhou21 小时前
MAF快速入门(9)多路分支路由工作流
llm·aigc·agent·.net core
sandwu1 天前
AI自动化测试(一)
人工智能·agent·playwright·ai自动化测试·midscene