【从0到1构建一个ClaudeAgent】规划与协调-任务系统

多个任务之间有依赖关系怎么搞?

Java实现代码

java 复制代码
public class TaskSystem {
    // --- 配置 ---
    private static final Path WORKDIR = Paths.get(System.getProperty("user.dir"));
    private static final Path TASKS_DIR = WORKDIR.resolve(".tasks");  // 任务存储目录
    private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
    
    // --- 工具枚举---
    public enum ToolType {
        BASH("bash", "Run a shell command."),
        READ_FILE("read_file", "Read file contents."),
        WRITE_FILE("write_file", "Write content to file."),
        EDIT_FILE("edit_file", "Replace exact text in file."),
        TASK_CREATE("task_create", "Create a new task."),      // 新增:创建任务
        TASK_GET("task_get", "Get full details of a task by ID."),  // 新增:获取任务详情
        TASK_UPDATE("task_update", "Update a task's status or dependencies."),  // 新增:更新任务
        TASK_LIST("task_list", "List all tasks with status summary.");  // 新增:列出任务

        public final String name;
        public final String description;
        ToolType(String name, String description) { this.name = name; this.description = description; }
    }

    // ... 省略相同的 ToolExecutor 接口
    
    // --- 任务管理器 ---
    static class TaskManager {
        private final Path tasksDir;
        private int nextId = 1;
        
        public TaskManager(Path tasksDir) throws IOException {
            this.tasksDir = tasksDir;
            Files.createDirectories(tasksDir);
            this.nextId = getMaxId() + 1;  // 自动计算下一个ID
        }
        
        private int getMaxId() {
            // 扫描已有任务文件,找到最大ID
            try {
                return Files.list(tasksDir)
                    .filter(p -> p.getFileName().toString().startsWith("task_"))
                    .map(p -> {
                        try {
                            String name = p.getFileName().toString();
                            return Integer.parseInt(name.substring(5, name.length() - 5));  // task_xxx.json
                        } catch (NumberFormatException e) {
                            return 0;
                        }
                    })
                    .max(Integer::compare)
                    .orElse(0);
            } catch (IOException e) {
                return 0;
            }
        }
        
        private Map<String, Object> loadTask(int taskId) throws IOException {
            Path path = tasksDir.resolve("task_" + taskId + ".json");
            if (!Files.exists(path)) {
                throw new IllegalArgumentException("Task " + taskId + " not found");
            }
            String content = Files.readString(path);
            Type type = new TypeToken<Map<String, Object>>(){}.getType();
            return gson.fromJson(content, type);
        }
        
        private void saveTask(Map<String, Object> task) throws IOException {
            int id = ((Double) task.get("id")).intValue();
            Path path = tasksDir.resolve("task_" + id + ".json");
            Files.writeString(path, gson.toJson(task));  // JSON格式存储
        }
        
        public String createTask(String subject, String description) throws IOException {
            Map<String, Object> task = new LinkedHashMap<>();
            task.put("id", nextId);
            task.put("subject", subject);
            task.put("description", description != null ? description : "");
            task.put("status", "pending");  // 默认状态
            task.put("blockedBy", new ArrayList<Integer>());  // 被哪些任务阻塞
            task.put("blocks", new ArrayList<Integer>());  // 阻塞哪些任务
            task.put("owner", "");  // 任务负责人
            
            saveTask(task);
            nextId++;
            return gson.toJson(task);
        }
        
        public String getTask(int taskId) throws IOException {
            return gson.toJson(loadTask(taskId));
        }
        
        public String updateTask(int taskId, String status, 
                                List<Integer> addBlockedBy, List<Integer> addBlocks) throws IOException {
            Map<String, Object> task = loadTask(taskId);
            
            if (status != null) {
                if (!Arrays.asList("pending", "in_progress", "completed").contains(status)) {
                    throw new IllegalArgumentException("Invalid status: " + status);
                }
                task.put("status", status);
                
                // 任务完成时,从其他任务的 blockedBy 中移除
                if ("completed".equals(status)) {
                    clearDependency(taskId);
                }
            }
            
            if (addBlockedBy != null && !addBlockedBy.isEmpty()) {
                @SuppressWarnings("unchecked")
                List<Integer> currentBlockedBy = (List<Integer>) task.get("blockedBy");
                List<Integer> newList = new ArrayList<>(currentBlockedBy);
                newList.addAll(addBlockedBy);
                task.put("blockedBy", newList.stream().distinct().collect(Collectors.toList()));
            }
            
            if (addBlocks != null && !addBlocks.isEmpty()) {
                @SuppressWarnings("unchecked")
                List<Integer> currentBlocks = (List<Integer>) task.get("blocks");
                List<Integer> newList = new ArrayList<>(currentBlocks);
                newList.addAll(addBlocks);
                List<Integer> distinctBlocks = newList.stream().distinct().collect(Collectors.toList());
                task.put("blocks", distinctBlocks);
                
                // 双向更新:更新被阻塞任务的 blockedBy 列表
                for (int blockedId : distinctBlocks) {
                    try {
                        Map<String, Object> blockedTask = loadTask(blockedId);
                        @SuppressWarnings("unchecked")
                        List<Integer> blockedByList = (List<Integer>) blockedTask.get("blockedBy");
                        if (!blockedByList.contains(taskId)) {
                            blockedByList.add(taskId);
                            saveTask(blockedTask);
                        }
                    } catch (Exception e) {
                        // 忽略不存在的任务
                    }
                }
            }
            
            saveTask(task);
            return gson.toJson(task);
        }
        
        private void clearDependency(int completedId) throws IOException {
            Files.list(tasksDir)
                .filter(p -> p.getFileName().toString().endsWith(".json"))
                .forEach(p -> {
                    try {
                        String content = Files.readString(p);
                        Type type = new TypeToken<Map<String, Object>>(){}.getType();
                        Map<String, Object> task = gson.fromJson(content, type);
                        
                        @SuppressWarnings("unchecked")
                        List<Integer> blockedBy = (List<Integer>) task.get("blockedBy");
                        if (blockedBy != null && blockedBy.contains(completedId)) {
                            blockedBy.remove(Integer.valueOf(completedId));
                            Files.writeString(p, gson.toJson(task));
                        }
                    } catch (IOException e) {
                        // 忽略读取错误
                    }
                });
        }
        
        public String listAllTasks() throws IOException {
            List<Map<String, Object>> tasks = new ArrayList<>();
            
            Files.list(tasksDir)
                .filter(p -> p.getFileName().toString().endsWith(".json"))
                .sorted()
                .forEach(p -> {
                    try {
                        String content = Files.readString(p);
                        Type type = new TypeToken<Map<String, Object>>(){}.getType();
                        tasks.add(gson.fromJson(content, type));
                    } catch (IOException e) {
                        // 忽略错误
                    }
                });
            
            if (tasks.isEmpty()) {
                return "No tasks.";
            }
            
            StringBuilder sb = new StringBuilder();
            for (Map<String, Object> task : tasks) {
                String status = (String) task.get("status");
                String marker = switch(status) {
                    case "pending" -> "[ ]";
                    case "in_progress" -> "[>]";
                    case "completed" -> "[x]";
                    default -> "[?]";
                };
                
                int id = ((Double) task.get("id")).intValue();
                String subject = (String) task.get("subject");
                
                @SuppressWarnings("unchecked")
                List<Integer> blockedBy = (List<Integer>) task.get("blockedBy");
                String blockedStr = (blockedBy != null && !blockedBy.isEmpty()) 
                    ? " (blocked by: " + blockedBy + ")" 
                    : "";
                
                sb.append(String.format("%s #%d: %s%s\n", marker, id, subject, blockedStr));
            }
            
            return sb.toString().trim();
        }
    }
    
    // --- 工具处理器映射 ---
    private static final Map<String, ToolExecutor> TOOL_HANDLERS = new HashMap<>();
    
    static {
        // 初始化任务管理器
        TaskManager taskManager;
        try {
            taskManager = new TaskManager(TASKS_DIR);
        } catch (IOException e) {
            throw new RuntimeException("Failed to initialize task manager", e);
        }
        
        // ... 省略基础工具注册
        
        // 注册 Task Create 工具
        TOOL_HANDLERS.put(ToolType.TASK_CREATE.name, args -> {
            String subject = (String) args.get("subject");
            String description = (String) args.get("description");
            return taskManager.createTask(subject, description);
        });
        
        // 注册 Task Get 工具
        TOOL_HANDLERS.put(ToolType.TASK_GET.name, args -> {
            int taskId = ((Number) args.get("task_id")).intValue();
            return taskManager.getTask(taskId);
        });
        
        // 注册 Task Update 工具
        TOOL_HANDLERS.put(ToolType.TASK_UPDATE.name, args -> {
            int taskId = ((Number) args.get("task_id")).intValue();
            String status = (String) args.get("status");
            
            @SuppressWarnings("unchecked")
            List<Integer> addBlockedBy = (List<Integer>) args.get("addBlockedBy");
            
            @SuppressWarnings("unchecked")
            List<Integer> addBlocks = (List<Integer>) args.get("addBlocks");
            
            return taskManager.updateTask(taskId, status, addBlockedBy, addBlocks);
        });
        
        // 注册 Task List 工具
        TOOL_HANDLERS.put(ToolType.TASK_LIST.name, args -> {
            return taskManager.listAllTasks();
        });
    }
    
    // ... 省略相同的工具实现和主循环
}

核心思想:利用基于文件的任务图,Agent 开始理解任务间的先后顺序与并行逻辑,成为真正的项目协调者。

企业级任务管理系统架构

核心思想 :从简单的内存中Todo管理器升级为持久化、结构化的企业级任务管理系统 ,支持复杂依赖关系、多任务协同、状态持久化,适用于真实的项目管理和协作场景。

java 复制代码
// 任务管理器 - 持久化存储架构
static class TaskManager {
    private final Path tasksDir;  // 任务存储目录
    private int nextId = 1;       // 自增ID
    
    public TaskManager(Path tasksDir) throws IOException {
        this.tasksDir = tasksDir;
        Files.createDirectories(tasksDir);
        this.nextId = getMaxId() + 1;  // 启动时自动计算下一个ID
    }
    // 文件系统存储:每个任务存储为独立的JSON文件
    // 自动ID管理:启动时扫描现有文件,避免ID冲突
    // 持久化:重启后任务状态不丢失
}
  • 企业级存储:从内存中Todo升级为文件系统持久化存储
  • 原子操作:每个任务独立文件,避免并发问题
  • 增量ID:自动管理任务ID,支持大规模任务
  • 灾备恢复:文件存储支持手动备份和恢复

任务数据结构与依赖管理

java 复制代码
// 创建任务时初始化完整数据结构
public String createTask(String subject, String description) throws IOException {
    Map<String, Object> task = new LinkedHashMap<>();
    task.put("id", nextId);
    task.put("subject", subject);        // 任务主题
    task.put("description", description != null ? description : "");  // 详细描述
    task.put("status", "pending");       // 状态:pending/in_progress/completed
    task.put("blockedBy", new ArrayList<Integer>());  // 被哪些任务阻塞
    task.put("blocks", new ArrayList<Integer>());     // 阻塞哪些任务
    task.put("owner", "");               // 任务负责人,支持分派
    // 结构化任务:包含完整元数据和关系
    // 依赖管理:blockedBy和blocks双向记录依赖关系
    // 权限控制:owner字段支持任务分派
    
    saveTask(task);
    nextId++;
    return gson.toJson(task);
}
  • 结构化元数据:任务包含丰富的信息字段
  • 依赖管理:支持任务间的阻塞/被阻塞关系
  • 扩展性:预留owner字段支持团队协作
  • JSON格式:人类可读,便于调试和手动修改

双向依赖同步机制

java 复制代码
// 更新任务时自动同步依赖关系
public String updateTask(int taskId, String status, 
                        List<Integer> addBlockedBy, List<Integer> addBlocks) throws IOException {
    Map<String, Object> task = loadTask(taskId);
    
    if (status != null) {
        task.put("status", status);
        
        // 任务完成时,从其他任务的 blockedBy 中移除
        if ("completed".equals(status)) {
            clearDependency(taskId);
        }
    }
    
    if (addBlocks != null && !addBlocks.isEmpty()) {
        // 双向更新:更新被阻塞任务的 blockedBy 列表
        for (int blockedId : distinctBlocks) {
            try {
                Map<String, Object> blockedTask = loadTask(blockedId);
                @SuppressWarnings("unchecked")
                List<Integer> blockedByList = (List<Integer>) blockedTask.get("blockedBy");
                if (!blockedByList.contains(taskId)) {
                    blockedByList.add(taskId);
                    saveTask(blockedTask);
                }
            } catch (Exception e) {
                // 忽略不存在的任务
            }
        }
    }
    // 依赖自动化:更新一个任务时,自动更新相关任务的依赖关系
    // 完成清理:任务完成后自动清理对它的阻塞依赖
    // 容错处理:忽略不存在的任务ID
}
  • 关系自动维护:更新一个任务的依赖时,自动更新相关任务
  • 完成时清理:任务完成后自动清理阻塞关系
  • 容错设计:忽略不存在任务的引用
  • 数据一致性:确保依赖关系的双向一致性

复杂查询与可视化展示

java 复制代码
// 列出所有任务的摘要信息
public String listAllTasks() throws IOException {
    List<Map<String, Object>> tasks = new ArrayList<>();
    
    Files.list(tasksDir)
        .filter(p -> p.getFileName().toString().endsWith(".json"))
        .sorted()  // 按文件名排序,通常是ID顺序
        .forEach(p -> {
            // 逐个加载任务文件
        });
    
    StringBuilder sb = new StringBuilder();
    for (Map<String, Object> task : tasks) {
        String status = (String) task.get("status");
        String marker = switch(status) {
            case "pending" -> "[ ]";
            case "in_progress" -> "[>]";
            case "completed" -> "[x]";
            default -> "[?]";
        };
        
        int id = ((Double) task.get("id")).intValue();
        String subject = (String) task.get("subject");
        
        @SuppressWarnings("unchecked")
        List<Integer> blockedBy = (List<Integer>) task.get("blockedBy");
        String blockedStr = (blockedBy != null && !blockedBy.isEmpty()) 
            ? " (blocked by: " + blockedBy + ")" 
            : "";
        // 状态可视化:[ ]待办 [>]进行中 [x]已完成
        // 依赖提示:显示哪些任务阻塞了当前任务
        // 简洁摘要:只显示关键信息
        
        sb.append(String.format("%s #%d: %s%s\n", marker, id, subject, blockedStr));
    }
    
    return sb.toString().trim();
}
  • 状态可视化:用图标清晰展示任务状态
  • 依赖提示:明确显示阻塞关系
  • 批量加载:高效加载所有任务
  • 人性化格式:便于人类阅读和理解

任务工具生态系统

java 复制代码
// 完整的任务工具集定义
public enum ToolType {
    TASK_CREATE("task_create", "Create a new task."),      // CRUD: Create
    TASK_GET("task_get", "Get full details of a task by ID."),  // CRUD: Read
    TASK_UPDATE("task_update", "Update a task's status or dependencies."),  // CRUD: Update
    TASK_LIST("task_list", "List all tasks with status summary.");  // CRUD: List
    // 完整CRUD:创建、读取、更新、删除(通过更新状态为完成)
    // 语义清晰:每个工具单一职责
    // 与基础工具分离:任务管理工具独立于文件操作工具
}
  • 完整CRUD:提供完整的任务管理操作
  • 单一职责:每个工具功能明确
  • 语义接口:名称明确,便于LLM理解
  • 分离关注:任务工具与基础文件工具分离

JSON存储格式

java 复制代码
// 任务存储格式示例
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();

private void saveTask(Map<String, Object> task) throws IOException {
    int id = ((Double) task.get("id")).intValue();
    Path path = tasksDir.resolve("task_" + id + ".json");
    Files.writeString(path, gson.toJson(task));  // 美化的JSON格式
}
// 标准化格式:每个任务存储为格式化的JSON文件
// 命名规范:task_<id>.json
// 人类可读:美化的JSON便于手动查看和编辑
// 可互操作:标准JSON格式支持外部工具处理
  • 标准化存储:JSON是通用的数据交换格式
  • 可读性:美化格式便于调试
  • 可扩展:随时可以添加新字段
  • 互操作性:其他工具可以读取任务文件

架构演进与价值

从 AgentWithTodo 到 TaskSystem 的升级

维度 AgentWithTodo TaskSystem
存储方式 内存存储 文件系统持久化
依赖管理 无依赖关系 双向依赖管理
数据持久性 重启丢失 永久保存
任务复杂性 简单待办 复杂项目管理
协同能力 单人使用 支持团队协作
可扩展性 有限 强大
相关推荐
abcnull13 小时前
传统的JavaWeb项目Demo快速学习!
java·servlet·elementui·vue·javaweb
risc12345613 小时前
【lucene】PostingsEnum跟TermsEnum 的区别是啥?
java·lucene
小江的记录本13 小时前
【Kafka核心】Kafka高性能的四大核心支柱:零拷贝、批量发送、页缓存、压缩
java·数据库·分布式·后端·缓存·kafka·rabbitmq
SamDeepThinking13 小时前
程序员过35岁之前,应该完成的三件事
java·后端·程序员
大数据三康13 小时前
Java字符统计:从输入到输出的完整解析
java·学习·循环结构
Mr_pyx13 小时前
【LeetHOT100】LRU缓存——Java多解法详解
java·开发语言
9523614 小时前
SpringAOP
java·后端·学习·spring
浩冉学编程14 小时前
微信小程序中基于java后端实现官方的文本内容安全识别msgSecCheck
java·前端·安全·微信小程序·小程序·微信公众平台·内容安全审核
A__tao14 小时前
JSON 转 Java 实体类工具(支持嵌套与注释解析)
java·python·json