第4章 智能体(Agent)

第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 构建器参数速查

参数类型必需默认值说明
nameString-智能体名称
sysPromptString-系统提示词
modelChatModel-LLM 模型
toolkitToolkitnull工具包
memoryMemoryInMemoryMemory短期记忆
longTermMemoryLongTermMemorynull长期记忆
maxItersint10最大迭代次数
checkRunningbooleantrue并发检查
hooksList[]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 本章小结

本章我们学习了:

  1. Agent 接口

- call():阻塞式调用 - stream():流式调用 - interrupt():中断执行

  1. 有状态设计

- 智能体持有 Memory、Toolkit 等状态 - 同一实例不能并发调用 - checkRunning 选项

  1. ReActAgent 配置

- 基础配置:name、sysPrompt、model - 工具配置:toolkit - 记忆配置:memory、longTermMemory - 执行控制:maxIters、超时配置 - Hook 配置

  1. 中断与恢复

- interrupt() 方法 - GenerateReason 判断 - 从暂停状态恢复

  1. 其他实现

- UserAgent:接收外部输入 - A2aAgent:调用远程服务


练习

  1. 创建自定义智能体:实现一个专门处理数学问题的智能体
  2. 测试并发行为:观察 checkRunning 的效果
  3. 实现中断功能:添加超时自动中断的逻辑
  4. 流式输出:使用 stream() 实现打字机效果

下一章 → 第5章 模型集成

← 返回目录