阿里开源AgentScope多智能体框架解析系列(十一)第11章:PlanNotebook 任务管理

68 阅读11分钟

本章导读

本章介绍AgentScope的PlanNotebook系统,这是处理复杂多步骤任务的关键工具。

PlanNotebook允许Agent自主创建和管理分阶段的计划,跨越多个推理循环执行复杂的、需要规划的任务,并在执行过程中动态调整计划

本章概览

  • What(是什么):理解PlanNotebook的结构和生命周期
  • Why(为什么):明确什么时候需要使用计划管理
  • How(怎样做):完整的实现示例和生产可用案例

11.1 PlanNotebook设计理念

什么是PlanNotebook?

PlanNotebook是一个结构化的任务管理系统,为Agent提供:

  • 分阶段计划:将复杂任务分解为多个子任务
  • 状态管理:跟踪每个子任务的执行状态
  • 动态调整:在执行过程中修改计划
  • 持久化支持:保存计划以供后续恢复

PlanNotebook的核心概念

1. Plan(计划)

一个Plan包含:
├─ 名称和描述
├─ 创建时间和完成时间
├─ 优先级和进度
└─ 多个SubTask(子任务)

代码表示

public class Plan {
    private String id;
    private String name;
    private String description;
    private LocalDateTime createdAt;
    private LocalDateTime completedAt;
    private PlanState state;  // CREATED, IN_PROGRESS, PAUSED, COMPLETED, ABANDONED
    private double progress;  // 0.0 - 1.0
    private List<SubTask> subTasks;
    private Map<String, String> metadata;
}

2. SubTask(子任务)

每个SubTask有:
├─ 名称和描述
├─ 状态:TODO, IN_PROGRESS, DONE, ABANDONED
├─ 关键信息(需要完成什么)
├─ 完成条件(如何判断完成)
└─ 执行结果

代码表示

public class SubTask {
    private String id;
    private String name;
    private String description;
    private TaskState state;  // TODO, IN_PROGRESS, DONE, ABANDONED
    private String objective;  // 需要完成什么
    private String completion_criteria;  // 完成条件
    private LocalDateTime startedAt;
    private LocalDateTime completedAt;
    private String result;  // 执行结果
    private Map<String, Object> context;  // 上下文信息
}

3. PlanNotebook的生命周期

创建阶段:
User Input → Agent creates Plan
  ↓
  Plan contains: SubTask1(TODO), SubTask2(TODO), SubTask3(TODO)

执行阶段:
Loop {
    Get current SubTask
    SubTask: TODO → IN_PROGRESS
    Execute and gather result
    SubTask: IN_PROGRESS → DONE (if successful)
    Update progress
    If not done, continue to next SubTask
}

暂停/恢复:
If user pauses: Plan → PAUSED, current SubTask → PAUSED
If user resumes: Plan → IN_PROGRESS, current SubTask → IN_PROGRESS

完成阶段:
All SubTasks: DONE → Plan: COMPLETED
Plan: COMPLETED → save to storage

11.2 PlanNotebook的工作原理(Why - 为什么需要)

为什么需要PlanNotebook?

1. 处理复杂的多步骤任务

问题:简单的推理循环无法处理需要严格顺序的复杂任务

示例场景(产品上线):
第1步:需求评审 → 需要1-2个推理循环
第2步:技术设计 → 需要2-3个推理循环,依赖第1步结果
第3步:开发实现 → 需要多个推理循环
第4步:测试验证 → 需要并行执行
第5步:上线发布 → 需要前面步骤都完成

问题:没有计划,Agent会陷入循环推理,不知道什么时候停止

解决:PlanNotebook将整个过程分解为明确的步骤

2. 跨越推理循环持久化

问题:单个推理循环可能用时数分钟,用户不想等待

解决:PlanNotebook允许保存中间进度,支持恢复继续

场景:
- 长时间报告生成:每个小时执行一个子任务
- 持续优化:每天运行一个优化循环
- 定时任务:每周执行一次计划检查

3. 支持计划调整和自适应

问题:执行中发现初始计划不合理,需要修改

示例:
初始计划:分析过去12个月的数据
执行中发现:数据量太大,超时了
调整方案:改为按季度分析

解决:PlanNotebook支持在执行过程中添加、删除、修改子任务

PlanNotebook的核心价值

┌─────────────────────────────────────┐
│   复杂任务分解                      │
│  (Decomposition)                    │
└─────────────────────────────────────┘
           ↓
┌─────────────────────────────────────┐
│   明确的执行顺序                    │
│  (Ordered Execution)                │
└─────────────────────────────────────┘
           ↓
┌─────────────────────────────────────┐
│   状态跟踪与恢复                    │
│  (State Tracking & Recovery)        │
└─────────────────────────────────────┘
           ↓
┌─────────────────────────────────────┐
│   动态调整与自适应                  │
│  (Dynamic Adjustment)               │
└─────────────────────────────────────┘
           ↓
┌─────────────────────────────────────┐
│   任务完成和知识积累                │
│  (Task Completion & Learning)       │
└─────────────────────────────────────┘

11.3 PlanNotebook的实现方式

基础使用示例

public class BasicPlanNotebookDemo {
    
    public static void main(String[] args) throws Exception {
        Model model = DashScopeModel.builder()
            .modelName("qwen-turbo")
            .apiKey(System.getenv("DASHSCOPE_API_KEY"))
            .build();
        
        // 1. 创建支持PlanNotebook的Agent
        ReActAgent agent = ReActAgent.builder()
            .name("ProjectManager")
            .model(model)
            .withPlanNotebook()  // 启用PlanNotebook工具
            .toolkit(new Toolkit()
                .registerObject(new ProjectManagementTools()))
            .build();
        
        // 2. 向Agent提出规划任务
        System.out.println("=== 请求制定项目计划 ===\n");
        
        Msg planRequest = Msg.builder()
            .role(MsgRole.USER)
            .textContent("""
                请为"智能推荐系统"项目制定一个详细的实现计划:
                项目目标:构建一个基于用户行为的推荐引擎
                时间限制:2周内完成
                
                请:
                1. 分析项目需求
                2. 制定具体的分阶段计划
                3. 为每个阶段制定开发工具和资源
                4. 识别关键风险点
                """)
            .build();
        
        Msg planResponse = agent.call(planRequest).block();
        System.out.println("Agent制定的计划:\n" + planResponse.getTextContent());
        
        // 3. 查询当前计划
        System.out.println("\n=== 查询当前计划 ===\n");
        
        List<Plan> activePlans = agent.getPlanNotebook().getActivePlans();
        System.out.println("活跃计划数:" + activePlans.size());
        
        for (Plan plan : activePlans) {
            System.out.println("\n计划:" + plan.getName());
            System.out.println("描述:" + plan.getDescription());
            System.out.println("进度:" + (plan.getProgress() * 100) + "%");
            System.out.println("状态:" + plan.getState());
            System.out.println("\n子任务:");
            
            for (SubTask task : plan.getSubTasks()) {
                System.out.println("  [" + task.getState() + "] " + 
                    task.getName() + ": " + task.getDescription());
            }
        }
        
        // 4. 执行计划
        System.out.println("\n=== 执行计划 ===\n");
        
        Msg executeRequest = Msg.builder()
            .role(MsgRole.USER)
            .textContent("请开始执行第一个任务:需求分析")
            .build();
        
        Msg executeResponse = agent.call(executeRequest).block();
        System.out.println("执行结果:\n" + executeResponse.getTextContent());
        
        // 5. 再次查询计划进度
        System.out.println("\n=== 更新后的计划进度 ===\n");
        
        Plan plan = activePlans.get(0);
        System.out.println("计划进度:" + (plan.getProgress() * 100) + "%");
        System.out.println("已完成任务:");
        
        for (SubTask task : plan.getSubTasks()) {
            if (task.getState() == TaskState.DONE) {
                System.out.println("  ✓ " + task.getName());
            }
        }
    }
    
    /**
     * 项目管理相关工具
     */
    public static class ProjectManagementTools {
        
        @Tool(description = "分析项目需求")
        public String analyzeRequirements(String projectName, String description) {
            return "需求分析完成:\n" +
                   "- 功能需求:3个核心功能\n" +
                   "- 非功能需求:性能、安全、可维护性\n" +
                   "- 技术栈:Java、Spring Boot、Redis";
        }
        
        @Tool(description = "分配开发资源")
        public String allocateResources(String taskName, int devCount, String skills) {
            return "资源分配完成:" +
                   "\n- 开发人员:" + devCount + "人\n" +
                   "- 技能要求:" + skills;
        }
        
        @Tool(description = "评估风险")
        public String assessRisks(String taskName) {
            return "风险评估完成:\n" +
                   "- 高风险:1项\n" +
                   "- 中风险:2项\n" +
                   "- 低风险:3项";
        }
    }
}

高级:动态调整计划

public class DynamicPlanAdjustment {
    
    public static void main(String[] args) throws Exception {
        Model model = DashScopeModel.builder()
            .modelName("qwen-turbo")
            .apiKey(System.getenv("DASHSCOPE_API_KEY"))
            .build();
        
        ReActAgent agent = ReActAgent.builder()
            .name("AdaptiveProjectManager")
            .model(model)
            .withPlanNotebook()
            .toolkit(new Toolkit()
                .registerObject(new AdaptiveTools()))
            .build();
        
        // 1. 创建初始计划
        System.out.println("=== 创建初始计划 ===\n");
        
        agent.call(Msg.builder()
            .textContent("为数据分析项目制定计划:收集数据 → 清洗数据 → 分析数据 → 生成报告")
            .build()).block();
        
        // 2. 在执行过程中发现问题
        System.out.println("\n=== 执行中发现问题 ===\n");
        
        agent.call(Msg.builder()
            .textContent("执行中发现:数据量超过预期,原计划的清洗步骤会超时。" +
                        "请调整计划,将'清洗数据'分为两个步骤:'样本清洗'和'批量清洗'")
            .build()).block();
        
        // 3. 添加新的风险应对任务
        System.out.println("\n=== 添加风险应对措施 ===\n");
        
        agent.call(Msg.builder()
            .textContent("识别到新风险:数据质量不达标。请在'分析数据'前插入一个'质量验证'步骤")
            .build()).block();
        
        // 4. 查询最终的计划
        System.out.println("\n=== 最终的计划结构 ===\n");
        
        PlanNotebook notebook = agent.getPlanNotebook();
        List<Plan> plans = notebook.getActivePlans();
        
        if (!plans.isEmpty()) {
            Plan plan = plans.get(0);
            System.out.println("计划名称:" + plan.getName());
            System.out.println("总任务数:" + plan.getSubTasks().size());
            System.out.println("\n任务顺序:");
            
            for (int i = 0; i < plan.getSubTasks().size(); i++) {
                SubTask task = plan.getSubTasks().get(i);
                System.out.println("  " + (i + 1) + ". " + task.getName() + 
                    " [" + task.getState() + "]");
            }
        }
    }
    
    public static class AdaptiveTools {
        
        @Tool(description = "添加任务到计划")
        public String addTaskToplan(String planId, int position, String taskName, String description) {
            return "任务已添加到计划中:位置=" + position + ", 名称=" + taskName;
        }
        
        @Tool(description = "修改任务")
        public String modifyTask(String taskId, String newName, String newDescription) {
            return "任务已修改:" + taskId;
        }
        
        @Tool(description = "标记任务完成")
        public String completeTask(String taskId, String result) {
            return "任务已标记为完成:" + taskId;
        }
    }
}

11.4 生产场景:产品上线流程管理

场景描述

完整的产品上线流程,从需求评审到上线后运维:

第一阶段(评审)
├─ 需求分析
├─ 技术评审
└─ 风险评估

第二阶段(开发)
├─ 详细设计
├─ 开发实现
└─ 单元测试

第三阶段(测试)
├─ 集成测试
├─ 性能测试
├─ 安全测试
└─ UAT

第四阶段(上线)
├─ 灰度发布
├─ 线上验证
└─ 全量发布

第五阶段(运维)
├─ 性能监控
├─ 告警管理
└─ 问题处理

实现代码

public class ProductReleaseManagementSystem {
    
    /**
     * 产品上线计划管理器
     */
    public static class ReleaseManager {
        private final ReActAgent agent;
        private final ReleaseStorage storage;
        
        public ReleaseManager(Model model, ReleaseStorage storage) {
            this.storage = storage;
            this.agent = ReActAgent.builder()
                .name("ReleaseManager")
                .model(model)
                .withPlanNotebook()
                .toolkit(new Toolkit()
                    .registerObject(new ReleaseTools()))
                .hooks(List.of(
                    new ReleaseCheckpointHook(storage),
                    new ReleaseNotificationHook()))
                .build();
        }
        
        /**
         * 创建产品上线计划
         */
        public void createReleaseplan(String productName, String version) {
            System.out.println("=== 创建产品上线计划 ===");
            System.out.println("产品:" + productName + " v" + version + "\n");
            
            String prompt = String.format("""
                请为产品 %s v%s 创建一个完整的上线计划:
                
                需要包含以下阶段:
                1. 需求/设计评审
                2. 开发实现
                3. 测试验证
                4. 上线发布
                5. 运维监控
                
                对每个阶段:
                - 列出具体的子任务
                - 标识关键决策点
                - 评估风险
                - 估计完成时间
                
                特别注意:
                - 上线前需要经过3个阶段的测试
                - 灰度发布需要监控至少1小时
                - 全量发布后需要24小时的密集监控
                """, productName, version);
            
            Msg response = agent.call(Msg.builder()
                .textContent(prompt)
                .build()).block();
            
            System.out.println("计划内容:\n" + response.getTextContent());
        }
        
        /**
         * 执行计划的某个阶段
         */
        public void executePhase(String phaseName) {
            System.out.println("\n=== 执行阶段:" + phaseName + " ===\n");
            
            Msg response = agent.call(Msg.builder()
                .textContent("请开始执行'" + phaseName + "'阶段," +
                           "并通过相应的工具完成该阶段的所有子任务")
                .build()).block();
            
            System.out.println("执行结果:\n" + response.getTextContent());
            
            // 保存执行进度
            storage.savePhaseCompletion(phaseName);
        }
        
        /**
         * 处理上线过程中发现的问题
         */
        public void handleIssue(String issueName, String severity, String impact) {
            System.out.println("\n=== 处理上线问题 ===");
            System.out.println("问题:" + issueName);
            System.out.println("严重性:" + severity);
            System.out.println("影响:" + impact + "\n");
            
            String prompt = String.format("""
                在上线过程中发现以下问题:
                问题名称:%s
                严重性:%s
                影响范围:%s
                
                请:
                1. 分析问题的根本原因
                2. 评估是否需要回滚
                3. 如果不回滚,制定快速修复方案
                4. 更新上线计划
                """, issueName, severity, impact);
            
            Msg response = agent.call(Msg.builder()
                .textContent(prompt)
                .build()).block();
            
            System.out.println("问题处理方案:\n" + response.getTextContent());
        }
        
        /**
         * 查询计划进度
         */
        public void displayProgress() {
            System.out.println("\n=== 上线计划进度 ===\n");
            
            PlanNotebook notebook = agent.getPlanNotebook();
            List<Plan> plans = notebook.getActivePlans();
            
            if (!plans.isEmpty()) {
                Plan plan = plans.get(0);
                
                System.out.println("计划名称:" + plan.getName());
                System.out.println("总进度:" + String.format("%.1f%%", plan.getProgress() * 100));
                System.out.println("状态:" + plan.getState());
                System.out.println("\n阶段进度:");
                
                for (SubTask task : plan.getSubTasks()) {
                    String status = task.getState() == TaskState.DONE ? "✓" : 
                                   task.getState() == TaskState.IN_PROGRESS ? "⚙" : "○";
                    System.out.println("  " + status + " " + task.getName() + 
                        " [" + task.getState() + "]");
                    
                    if (!task.getResult().isEmpty()) {
                        System.out.println("    结果:" + task.getResult());
                    }
                }
            }
        }
    }
    
    /**
     * 上线相关工具集
     */
    public static class ReleaseTools {
        
        @Tool(description = "执行代码审查")
        public String codeReview(String commitHash, int reviewerCount) {
            return "代码审查已执行:\n" +
                   "- 提交:" + commitHash + "\n" +
                   "- 审查人数:" + reviewerCount + "\n" +
                   "- 结果:已通过";
        }
        
        @Tool(description = "运行测试套件")
        public String runTestSuite(String testType, String environment) {
            return "测试套件执行完成:\n" +
                   "- 测试类型:" + testType + "\n" +
                   "- 环境:" + environment + "\n" +
                   "- 通过率:98.5%";
        }
        
        @Tool(description = "部署到特定环境")
        public String deploy(String version, String environment, double grayPercentage) {
            return "部署完成:\n" +
                   "- 版本:" + version + "\n" +
                   "- 环境:" + environment + "\n" +
                   "- 灰度比例:" + grayPercentage + "%";
        }
        
        @Tool(description = "检查系统监控指标")
        public String checkMetrics(String metricName, String environment) {
            return "监控指标检查完成:\n" +
                   "- 指标:" + metricName + "\n" +
                   "- 环境:" + environment + "\n" +
                   "- 状态:正常";
        }
        
        @Tool(description = "回滚到上一个版本")
        public String rollback(String version) {
            return "已回滚到版本:" + version;
        }
    }
    
    /**
     * 上线检查点Hook:在关键步骤保存进度
     */
    public static class ReleaseCheckpointHook implements Hook {
        private final ReleaseStorage storage;
        
        public ReleaseCheckpointHook(ReleaseStorage storage) {
            this.storage = storage;
        }
        
        @Override
        public <T extends HookEvent> Mono<T> onEvent(T event) {
            if (event instanceof PostActingEvent post) {
                // 在执行完每个工具调用后保存进度
                String toolName = extractToolName(post.getActingMessage());
                storage.saveCheckpoint(toolName);
                
                System.out.println("✓ 检查点已保存:" + toolName);
            }
            return Mono.just(event);
        }
        
        private String extractToolName(Msg msg) {
            // 从消息中提取工具名称
            String content = msg.getTextContent();
            if (content.contains("code-review")) return "CodeReview";
            if (content.contains("test")) return "Testing";
            if (content.contains("deploy")) return "Deployment";
            return "Unknown";
        }
        
        @Override
        public int getPriority() {
            return 100;
        }
    }
    
    /**
     * 上线通知Hook:通知相关人员
     */
    public static class ReleaseNotificationHook implements Hook {
        
        @Override
        public <T extends HookEvent> Mono<T> onEvent(T event) {
            if (event instanceof PostReasoningEvent post) {
                // 每个推理循环后通知进度
                return Mono.just(event)
                    .doOnNext(e -> {
                        System.out.println("\n[通知] 上线计划更新");
                        System.out.println("时间:" + LocalDateTime.now());
                        System.out.println("推理轮次:" + post.getReasoningCount());
                    });
            }
            return Mono.just(event);
        }
        
        @Override
        public int getPriority() {
            return 50;
        }
    }
    
    /**
     * 上线进度存储接口
     */
    public interface ReleaseStorage {
        void savePhaseCompletion(String phaseName);
        void saveCheckpoint(String checkpointName);
        List<String> getCompletedPhases();
    }
    
    /**
     * 测试主程序
     */
    public static void main(String[] args) throws Exception {
        Model model = DashScopeModel.builder()
            .modelName("qwen-turbo")
            .apiKey(System.getenv("DASHSCOPE_API_KEY"))
            .build();
        
        ReleaseStorage storage = new SimpleReleaseStorage();
        ReleaseManager manager = new ReleaseManager(model, storage);
        
        // 创建上线计划
        manager.createReleaseplan("推荐系统", "2.1.0");
        
        // 执行各个阶段
        manager.displayProgress();
        
        manager.executePhase("需求/设计评审");
        manager.displayProgress();
        
        manager.executePhase("开发实现");
        manager.displayProgress();
        
        manager.executePhase("测试验证");
        manager.displayProgress();
        
        // 处理上线过程中发现的问题
        manager.handleIssue(
            "缓存预热不足导致首页加载缓慢",
            "HIGH",
            "影响用户体验,但不影响功能"
        );
        
        manager.executePhase("上线发布");
        manager.displayProgress();
    }
    
    /**
     * 简单的存储实现
     */
    public static class SimpleReleaseStorage implements ReleaseStorage {
        private final List<String> completedPhases = new CopyOnWriteArrayList<>();
        private final List<String> checkpoints = new CopyOnWriteArrayList<>();
        
        @Override
        public void savePhaseCompletion(String phaseName) {
            completedPhases.add(phaseName);
        }
        
        @Override
        public void saveCheckpoint(String checkpointName) {
            checkpoints.add(checkpointName);
        }
        
        @Override
        public List<String> getCompletedPhases() {
            return new ArrayList<>(completedPhases);
        }
    }
}

11.5 最佳实践

✅ 最佳实践

1. 计划设计
   - 遵循SMART原则(Specific, Measurable, Achievable, Relevant, Time-bound)
   - 子任务数不超过20个(否则难以跟踪)
   - 每个子任务耗时不超过1小时(便于检查点恢复)
   - 明确定义"完成条件"

2. 执行管理
   - 每个推理循环处理一个子任务
   - 在Task完成前保存检查点
   - 定期检查计划进度并显示给用户
   - 发现偏差时及时调整

3. 错误恢复
   - 支持从任意检查点恢复
   - 记录所有失败的尝试和修改
   - 为每个阶段准备备选方案
   - 定期验证恢复机制

4. 生产运维
   - 将计划持久化到数据库
   - 支持多个并行计划
   - 实现计划的版本控制
   - 提供计划审计日志

本章总结

本章介绍了PlanNotebook系统,这是处理复杂多步骤任务的核心工具:

核心要点

  • What:PlanNotebook是结构化的任务管理系统
  • Why:支持复杂任务分解、跨循环执行、动态调整
  • How:通过Plan和SubTask进行阶段化管理

关键特性

  • ✓ 任务分解:将复杂任务拆分为可管理的子任务
  • ✓ 状态跟踪:明确的状态转移和进度追踪
  • ✓ 动态调整:在执行过程中修改计划
  • ✓ 持久化:支持保存和恢复计划进度

使用场景

  • 复杂的产品上线流程
  • 长时间的数据分析项目
  • 涉及多个部门的协作任务
  • 需要人工干预的自动化工作流

下一章预告

第12章:RAG(检索增强生成)

当Agent需要回答基于最新知识库的问题时,仅依赖训练数据是不够的。第12章将介绍RAG系统,它允许Agent:

  • 从海量文档中快速检索相关信息
  • 基于最新知识生成准确的回答
  • 支持多种知识库后端集成
  • 实现私有知识库的保护