【从0到1构建一个ClaudeAgent】协作-Worktree+任务隔离

到 s11, 智能体已经能自主认领和完成任务。但所有任务共享一个目录。两个智能体同时重构不同模块 -- A 改 config.py, B 也改 config.py, 未提交的改动互相污染, 谁也没法干净回滚。

任务板管 "做什么" 但不管 "在哪做"。解法: 给每个任务一个独立的 git worktree 目录, 用任务 ID 把两边关联起来。"各干各的目录, 互不干扰",任务管目标,,worktree 管目录,按 ID 绑定。

Java实现代码

public class WorktreeTaskIsolationSystem {
    
    // --- 新增配置 ---
    private static final Path REPO_ROOT = detectRepoRoot(WORKDIR);  // Git仓库根目录
    private static final Path TASKS_DIR = REPO_ROOT.resolve(".tasks");
    private static final Path WORKTREES_DIR = REPO_ROOT.resolve(".worktrees");
    private static final Path EVENTS_LOG_PATH = WORKTREES_DIR.resolve("events.jsonl");
    
    // --- 新增:Git 仓库检测 ---
    private static Path detectRepoRoot(Path cwd) {
        try {
            ProcessBuilder pb = new ProcessBuilder("git", "rev-parse", "--show-toplevel");
            pb.directory(cwd.toFile());
            Process process = pb.start();
            
            boolean finished = process.waitFor(10, TimeUnit.SECONDS);
            if (!finished) {
                return cwd;
            }
            
            String output = new String(process.getInputStream().readAllBytes()).trim();
            Path root = Paths.get(output);
            return Files.exists(root) ? root : cwd;
            // 自动检测:自动发现Git仓库根目录
            // 向后兼容:如果不是Git仓库,使用当前目录
        } catch (Exception e) {
            return cwd;
        }
    }
    
    // --- 新增:事件总线(EventBus)---
    static class EventBus {
        private final Path eventLogPath;
        
        public EventBus(Path eventLogPath) {
            this.eventLogPath = eventLogPath;
            try {
                Files.createDirectories(eventLogPath.getParent());
                if (!Files.exists(eventLogPath)) {
                    Files.writeString(eventLogPath, "");
                }
            } catch (IOException e) {
                throw new RuntimeException("Failed to create event bus", e);
            }
        }
        
        /**
         * 发出事件
         */
        public void emit(String event, Map<String, Object> task, 
                        Map<String, Object> worktree, String error) {
            Map<String, Object> payload = new LinkedHashMap<>();
            payload.put("event", event);
            payload.put("ts", System.currentTimeMillis() / 1000.0);
            payload.put("task", task != null ? task : Map.of());
            payload.put("worktree", worktree != null ? worktree : Map.of());
            
            if (error != null) {
                payload.put("error", error);
            }
            // 结构化事件:事件类型、时间戳、关联数据
            // 错误跟踪:支持记录操作失败信息
            
            try {
                String jsonLine = gson.toJson(payload) + "\n";
                Files.writeString(eventLogPath, jsonLine, 
                    StandardOpenOption.CREATE, StandardOpenOption.APPEND);
                // JSONL格式:每行一个事件,便于流式处理
                // 追加写入:支持长时间运行的事件记录
            } catch (IOException e) {
                System.err.println("Failed to emit event: " + e.getMessage());
            }
        }
        
        /**
         * 列出最近事件
         */
        public String listRecent(int limit) {
            int n = Math.max(1, Math.min(limit, 200));
            
            try {
                List<String> lines = Files.readAllLines(eventLogPath);
                List<Map<String, Object>> events = new ArrayList<>();
                
                int start = Math.max(0, lines.size() - n);
                for (int i = start; i < lines.size(); i++) {
                    try {
                        Type type = new TypeToken<Map<String, Object>>(){}.getType();
                        Map<String, Object> event = gson.fromJson(lines.get(i), type);
                        events.add(event);
                    } catch (Exception e) {
                        Map<String, Object> errorEvent = new HashMap<>();
                        errorEvent.put("event", "parse_error");
                        errorEvent.put("raw", lines.get(i));
                        events.add(errorEvent);
                        // 容错处理:即使解析失败也记录原始数据
                    }
                }
                
                return gson.toJson(events);
            } catch (IOException e) {
                return "[]";
            }
        }
    }
    
    // 初始化事件总线
    private static final EventBus EVENTS = new EventBus(EVENTS_LOG_PATH);
    
    // --- 新增:任务管理器(支持工作树绑定)---
    static class TaskManager {
        private final Path tasksDir;
        private int nextId = 1;
        
        public TaskManager(Path tasksDir) {
            this.tasksDir = tasksDir;
            try {
                Files.createDirectories(tasksDir);
                this.nextId = getMaxId() + 1;
            } catch (IOException e) {
                throw new RuntimeException("Failed to initialize task manager", e);
            }
        }
        
        /**
         * 绑定任务到工作树
         */
        public String bindWorktree(int taskId, String worktree, String owner) throws IOException {
            Map<String, Object> task = loadTask(taskId);
            task.put("worktree", worktree);
            // 任务-工作树关联:建立任务和工作树的双向链接
            
            if (owner != null && !owner.isEmpty()) {
                task.put("owner", owner);
            }
            
            if ("pending".equals(task.get("status"))) {
                task.put("status", "in_progress");
                // 状态自动转换:绑定工作树时自动开始任务
            }
            
            task.put("updated_at", System.currentTimeMillis() / 1000.0);
            saveTask(task);
            return gson.toJson(task);
        }
        
        /**
         * 解绑工作树
         */
        public String unbindWorktree(int taskId) throws IOException {
            Map<String, Object> task = loadTask(taskId);
            task.put("worktree", "");
            task.put("updated_at", System.currentTimeMillis() / 1000.0);
            saveTask(task);
            return gson.toJson(task);
            // 解绑机制:支持任务和工作树的解耦
        }
        
        /**
         * 列出所有任务
         */
        public String listAllTasks() throws IOException {
            // ... 列出任务逻辑,包含工作树绑定信息
            sb.append(String.format("%s #%d: %s%s%s\n", 
                marker, id, subject, ownerStr, worktreeStr));
            // 可视化展示:显示任务状态、所有者、工作树绑定
        }
    }
    
    // 初始化任务管理器
    private static final TaskManager TASKS = new TaskManager(TASKS_DIR);
    
    // --- 新增:工作树管理器(WorktreeManager)---
    static class WorktreeManager {
        private final Path repoRoot;
        private final TaskManager taskManager;
        private final EventBus eventBus;
        private final Path worktreesDir;
        private final Path indexPath;
        private final boolean gitAvailable;
        
        public WorktreeManager(Path repoRoot, TaskManager taskManager, EventBus eventBus) {
            this.repoRoot = repoRoot;
            this.taskManager = taskManager;
            this.eventBus = eventBus;
            this.worktreesDir = repoRoot.resolve(".worktrees");
            this.indexPath = worktreesDir.resolve("index.json");
            // 集成架构:工作树管理器与任务管理器、事件总线集成
            
            try {
                Files.createDirectories(worktreesDir);
                if (!Files.exists(indexPath)) {
                    Map<String, Object> index = new HashMap<>();
                    index.put("worktrees", new ArrayList<Map<String, Object>>());
                    Files.writeString(indexPath, gson.toJson(index));
                }
            } catch (IOException e) {
                throw new RuntimeException("Failed to initialize worktree manager", e);
            }
            
            this.gitAvailable = isGitRepo();
        }
        
        /**
         * 检查是否是Git仓库
         */
        private boolean isGitRepo() {
            try {
                ProcessBuilder pb = new ProcessBuilder("git", "rev-parse", "--is-inside-work-tree");
                pb.directory(repoRoot.toFile());
                Process process = pb.start();
                return process.exitValue() == 0;
            } catch (Exception e) {
                return false;
            }
        }
        
        /**
         * 执行Git命令
         */
        private String runGit(List<String> args) throws Exception {
            if (!gitAvailable) {
                throw new RuntimeException("Not in a git repository. worktree tools require git.");
            }
            
            ProcessBuilder pb = new ProcessBuilder("git");
            pb.command().addAll(args);
            pb.directory(repoRoot.toFile());
            
            Process process = pb.start();
            boolean finished = process.waitFor(120, TimeUnit.SECONDS);
            
            if (!finished) {
                process.destroy();
                throw new RuntimeException("git command timeout");
            }
            
            int exitCode = process.exitValue();
            if (exitCode != 0) {
                String error = new String(process.getErrorStream().readAllBytes()).trim();
                String output = new String(process.getInputStream().readAllBytes()).trim();
                String message = error.isEmpty() ? output : error;
                if (message.isEmpty()) {
                    message = "git " + String.join(" ", args) + " failed";
                }
                throw new RuntimeException(message);
            }
            
            String output = new String(process.getInputStream().readAllBytes()).trim();
            String error = new String(process.getErrorStream().readAllBytes()).trim();
            String result = output + (error.isEmpty() ? "" : "\n" + error);
            return result.isEmpty() ? "(no output)" : result;
        }
        
        /**
         * 加载索引文件
         */
        @SuppressWarnings("unchecked")
        private Map<String, Object> loadIndex() throws IOException {
            String content = Files.readString(indexPath);
            Type type = new TypeToken<Map<String, Object>>(){}.getType();
            return gson.fromJson(content, type);
        }
        
        /**
         * 保存索引文件
         */
        private void saveIndex(Map<String, Object> index) throws IOException {
            Files.writeString(indexPath, gson.toJson(index));
        }
        
        /**
         * 验证工作树名称
         */
        private void validateName(String name) {
            if (name == null || name.isEmpty() || name.length() > 40) {
                throw new IllegalArgumentException("Worktree name must be 1-40 characters");
            }
            
            if (!name.matches("[A-Za-z0-9._-]+")) {
                throw new IllegalArgumentException(
                    "Invalid worktree name. Use letters, numbers, ., _, - only"
                );
            }
            // 名称规范:确保工作树名称安全且规范
        }
        
        /**
         * 创建工作树
         */
        public String createWorktree(String name, Integer taskId, String baseRef) throws Exception {
            validateName(name);
            
            Map<String, Object> existing = findWorktree(name);
            if (existing != null) {
                throw new IllegalArgumentException("Worktree '" + name + "' already exists");
            }
            
            if (taskId != null && !taskManager.taskExists(taskId)) {
                throw new IllegalArgumentException("Task " + taskId + " not found");
            }
            
            // 发出创建前事件
            Map<String, Object> taskInfo = taskId != null ? 
                Map.of("id", taskId) : Map.of();
            Map<String, Object> worktreeInfo = Map.of("name", name, "base_ref", baseRef);
            eventBus.emit("worktree.create.before", taskInfo, worktreeInfo, null);
            
            try {
                Path worktreePath = worktreesDir.resolve(name);
                String branch = "wt/" + name;  // 自动生成分支名
                
                // Git工作树创建
                runGit(Arrays.asList("worktree", "add", "-b", branch, 
                    worktreePath.toString(), baseRef));
                // Git工作树:独立的代码工作目录
                // 自动分支:为每个工作树创建独立分支
                
                // 创建索引条目
                Map<String, Object> entry = new LinkedHashMap<>();
                entry.put("name", name);
                entry.put("path", worktreePath.toString());
                entry.put("branch", branch);
                entry.put("task_id", taskId);
                entry.put("status", "active");
                entry.put("created_at", System.currentTimeMillis() / 1000.0);
                
                // 保存到索引
                Map<String, Object> index = loadIndex();
                @SuppressWarnings("unchecked")
                List<Map<String, Object>> worktrees = (List<Map<String, Object>>) index.get("worktrees");
                worktrees.add(entry);
                saveIndex(index);
                
                // 绑定任务
                if (taskId != null) {
                    taskManager.bindWorktree(taskId, name, "");
                }
                
                // 发出创建后事件
                Map<String, Object> worktreeAfter = new LinkedHashMap<>();
                worktreeAfter.put("name", name);
                worktreeAfter.put("path", worktreePath.toString());
                worktreeAfter.put("branch", branch);
                worktreeAfter.put("status", "active");
                
                eventBus.emit("worktree.create.after", taskInfo, worktreeAfter, null);
                
                return gson.toJson(entry);
                
            } catch (Exception e) {
                eventBus.emit("worktree.create.failed", taskInfo, 
                    Map.of("name", name, "base_ref", baseRef), e.getMessage());
                throw e;
            }
        }
        
        /**
         * 获取工作树状态
         */
        public String getWorktreeStatus(String name) throws IOException {
            Map<String, Object> worktree = findWorktree(name);
            if (worktree == null) {
                return "Error: Unknown worktree '" + name + "'";
            }
            
            Path worktreePath = Paths.get((String) worktree.get("path"));
            if (!Files.exists(worktreePath)) {
                return "Error: Worktree path missing: " + worktreePath;
            }
            
            try {
                ProcessBuilder pb = new ProcessBuilder("git", "status", "--short", "--branch");
                pb.directory(worktreePath.toFile());
                
                Process process = pb.start();
                boolean finished = process.waitFor(60, TimeUnit.SECONDS);
                
                if (!finished) {
                    process.destroy();
                    return "Error: Git status timeout";
                }
                
                String output = new String(process.getInputStream().readAllBytes()).trim();
                String error = new String(process.getErrorStream().readAllBytes()).trim();
                String result = output + (error.isEmpty() ? "" : "\n" + error);
                
                return result.isEmpty() ? "Clean worktree" : result;
                // Git状态检查:提供详细的工作树状态信息
                
            } catch (Exception e) {
                return "Error: " + e.getMessage();
            }
        }
        
        /**
         * 在工作树中运行命令
         */
        public String runInWorktree(String name, String command) throws Exception {
            // 安全检查
            String[] dangerous = {"rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"};
            for (String d : dangerous) {
                if (command.contains(d)) {
                    return "Error: Dangerous command blocked";
                }
            }
            
            Map<String, Object> worktree = findWorktree(name);
            if (worktree == null) {
                return "Error: Unknown worktree '" + name + "'";
            }
            
            Path worktreePath = Paths.get((String) worktree.get("path"));
            if (!Files.exists(worktreePath)) {
                return "Error: Worktree path missing: " + worktreePath;
            }
            
            try {
                ProcessBuilder pb = new ProcessBuilder("bash", "-c", command);
                pb.directory(worktreePath.toFile());
                // 目录隔离:命令在工作树的独立目录中执行
                
                Process process = pb.start();
                boolean finished = process.waitFor(300, TimeUnit.SECONDS);
                
                if (!finished) {
                    process.destroy();
                    return "Error: Timeout (300s)";
                }
                
                String output = new String(process.getInputStream().readAllBytes()).trim();
                String error = new String(process.getErrorStream().readAllBytes()).trim();
                String result = output + (error.isEmpty() ? "" : "\n" + error);
                
                return result.isEmpty() ? "(no output)" : 
                    result.substring(0, Math.min(result.length(), 50000));
                
            } catch (Exception e) {
                return "Error: " + e.getMessage();
            }
        }
        
        /**
         * 删除工作树
         */
        public String removeWorktree(String name, boolean force, boolean completeTask) throws Exception {
            Map<String, Object> worktree = findWorktree(name);
            if (worktree == null) {
                return "Error: Unknown worktree '" + name + "'";
            }
            
            // 发出删除前事件
            Integer taskId = (Integer) worktree.get("task_id");
            Map<String, Object> taskInfo = taskId != null ? 
                Map.of("id", taskId) : Map.of();
            Map<String, Object> worktreeInfo = Map.of(
                "name", name, 
                "path", worktree.get("path")
            );
            
            eventBus.emit("worktree.remove.before", taskInfo, worktreeInfo, null);
            
            try {
                List<String> args = new ArrayList<>();
                args.addAll(Arrays.asList("worktree", "remove"));
                if (force) {
                    args.add("--force");
                }
                args.add((String) worktree.get("path"));
                
                runGit(args);
                // Git工作树删除:清理Git工作树
                
                // 完成任务
                if (completeTask && taskId != null) {
                    Map<String, Object> task = gson.fromJson(taskManager.getTask(taskId), 
                        new TypeToken<Map<String, Object>>(){}.getType());
                    
                    taskManager.updateTask(taskId, "completed", null);
                    taskManager.unbindWorktree(taskId);
                    // 任务-工作树生命周期协同:删除工作树时自动完成任务
                    
                    Map<String, Object> taskEvent = new HashMap<>();
                    taskEvent.put("id", taskId);
                    taskEvent.put("subject", task.get("subject"));
                    taskEvent.put("status", "completed");
                    
                    eventBus.emit("task.completed", taskEvent, 
                        Map.of("name", name), null);
                }
                
                // 更新索引
                Map<String, Object> index = loadIndex();
                @SuppressWarnings("unchecked")
                List<Map<String, Object>> worktrees = (List<Map<String, Object>>) index.get("worktrees");
                
                for (Map<String, Object> wt : worktrees) {
                    if (name.equals(wt.get("name"))) {
                        wt.put("status", "removed");
                        wt.put("removed_at", System.currentTimeMillis() / 1000.0);
                        break;
                    }
                }
                saveIndex(index);
                // 索引更新:标记工作树为已删除状态
                
                // 发出删除后事件
                Map<String, Object> worktreeAfter = new HashMap<>();
                worktreeAfter.put("name", name);
                worktreeAfter.put("path", worktree.get("path"));
                worktreeAfter.put("status", "removed");
                
                eventBus.emit("worktree.remove.after", taskInfo, worktreeAfter, null);
                
                return "Removed worktree '" + name + "'";
                
            } catch (Exception e) {
                eventBus.emit("worktree.remove.failed", taskInfo, worktreeInfo, e.getMessage());
                throw e;
            }
        }
        
        /**
         * 保留工作树(标记为保留状态)
         */
        public String keepWorktree(String name) throws IOException {
            Map<String, Object> worktree = findWorktree(name);
            if (worktree == null) {
                return "Error: Unknown worktree '" + name + "'";
            }
            
            Map<String, Object> index = loadIndex();
            @SuppressWarnings("unchecked")
            List<Map<String, Object>> worktrees = (List<Map<String, Object>>) index.get("worktrees");
            
            Map<String, Object> keptWorktree = null;
            
            for (Map<String, Object> wt : worktrees) {
                if (name.equals(wt.get("name"))) {
                    wt.put("status", "kept");
                    wt.put("kept_at", System.currentTimeMillis() / 1000.0);
                    keptWorktree = wt;
                    break;
                }
            }
            
            saveIndex(index);
            // 保留状态:标记工作树为需要保留,不自动清理
            
            // 发出保留事件
            Integer taskId = (Integer) worktree.get("task_id");
            Map<String, Object> taskInfo = taskId != null ? 
                Map.of("id", taskId) : Map.of();
            
            Map<String, Object> worktreeInfo = new HashMap<>();
            worktreeInfo.put("name", name);
            worktreeInfo.put("path", worktree.get("path"));
            worktreeInfo.put("status", "kept");
            
            eventBus.emit("worktree.keep", taskInfo, worktreeInfo, null);
            
            return keptWorktree != null ? gson.toJson(keptWorktree) : 
                "Error: Unknown worktree '" + name + "'";
        }
    }
    
    // 初始化工作树管理器
    private static final WorktreeManager WORKTREES = new WorktreeManager(REPO_ROOT, TASKS, EVENTS);
    
    // --- 新增:工具处理器 ---
    static {
        // 新增任务工具
        TOOL_HANDLERS.put("task_create", args -> {
            String subject = (String) args.get("subject");
            String description = (String) args.get("description");
            return TASKS.createTask(subject, description);
        });
        
        TOOL_HANDLERS.put("task_list", args -> TASKS.listAllTasks());
        
        TOOL_HANDLERS.put("task_get", args -> {
            int taskId = ((Number) args.get("task_id")).intValue();
            return TASKS.getTask(taskId);
        });
        
        TOOL_HANDLERS.put("task_update", args -> {
            int taskId = ((Number) args.get("task_id")).intValue();
            String status = (String) args.get("status");
            String owner = (String) args.get("owner");
            return TASKS.updateTask(taskId, status, owner);
        });
        
        TOOL_HANDLERS.put("task_bind_worktree", args -> {
            int taskId = ((Number) args.get("task_id")).intValue();
            String worktree = (String) args.get("worktree");
            String owner = (String) args.get("owner");
            return TASKS.bindWorktree(taskId, worktree, owner);
        });
        
        // 新增工作树工具
        TOOL_HANDLERS.put("worktree_create", args -> {
            String name = (String) args.get("name");
            Integer taskId = args.get("task_id") != null ? 
                ((Number) args.get("task_id")).intValue() : null;
            String baseRef = (String) args.get("base_ref");
            if (baseRef == null) baseRef = "HEAD";
            return WORKTREES.createWorktree(name, taskId, baseRef);
        });
        
        TOOL_HANDLERS.put("worktree_list", args -> WORKTREES.listWorktrees());
        
        TOOL_HANDLERS.put("worktree_status", args -> {
            String name = (String) args.get("name");
            return WORKTREES.getWorktreeStatus(name);
        });
        
        TOOL_HANDLERS.put("worktree_run", args -> {
            String name = (String) args.get("name");
            String command = (String) args.get("command");
            return WORKTREES.runInWorktree(name, command);
        });
        
        TOOL_HANDLERS.put("worktree_keep", args -> {
            String name = (String) args.get("name");
            return WORKTREES.keepWorktree(name);
        });
        
        TOOL_HANDLERS.put("worktree_remove", args -> {
            String name = (String) args.get("name");
            boolean force = args.get("force") != null && (Boolean) args.get("force");
            boolean completeTask = args.get("complete_task") != null && (Boolean) args.get("complete_task");
            return WORKTREES.removeWorktree(name, force, completeTask);
        });
        
        TOOL_HANDLERS.put("worktree_events", args -> {
            int limit = args.get("limit") != null ? ((Number) args.get("limit")).intValue() : 20;
            return EVENTS.listRecent(limit);
        });
    }
    
    // --- 新增:工具定义 ---
    private static List<Map<String, Object>> getToolSpecs() {
        List<Map<String, Object>> tools = new ArrayList<>();
        
        // 任务工具定义...
        
        // 工作树工具定义...
        
        return tools;
    }
}

这段代码引入了工作树任务隔离系统,实现了:

  1. 目录级隔离:每个任务在自己的工作树中执行
  2. 控制平面与执行平面分离
    • 控制平面:任务管理(.tasks/)
    • 执行平面:工作树管理(.worktrees/)
  3. 事件总线:记录完整生命周期事件
  4. Git 工作树集成:利用 git worktree 实现隔离

关键洞察:通过目录隔离并行任务,通过任务ID进行协调。

Git工作树隔离架构

核心思想:从文件系统隔离升级为Git仓库级别的代码隔离,通过Git工作树实现完全隔离的并行开发环境,为每个任务提供独立的代码工作空间,避免冲突,支持真正的并行开发。

// 核心路径配置
private static final Path REPO_ROOT = detectRepoRoot(WORKDIR);  // Git仓库根目录
private static final Path WORKTREES_DIR = REPO_ROOT.resolve(".worktrees");  // 工作树目录
// Git仓库感知:自动检测Git仓库根目录
// 工作树管理:专门的工作树存储目录
// 版本控制集成:与Git深度集成
// Git仓库检测
private static Path detectRepoRoot(Path cwd) {
    try {
        ProcessBuilder pb = new ProcessBuilder("git", "rev-parse", "--show-toplevel");
        pb.directory(cwd.toFile());
        Process process = pb.start();
        
        String output = new String(process.getInputStream().readAllBytes()).trim();
        Path root = Paths.get(output);
        return Files.exists(root) ? root : cwd;
        // 自动发现:自动定位Git仓库根目录
        // 兼容处理:非Git仓库时回退到当前目录
    } catch (Exception e) {
        return cwd;
    }
}
  • Git原生集成:深度集成Git版本控制系统
  • 仓库感知:自动识别和适应Git仓库结构
  • 向后兼容:非Git环境也能工作
  • 专业级隔离:为专业软件开发设计

工作树管理系统

// 工作树管理器核心
static class WorktreeManager {
    private final Path repoRoot;
    private final TaskManager taskManager;
    private final EventBus eventBus;
    private final Path worktreesDir;
    private final Path indexPath;
    private final boolean gitAvailable;
    // 三组件集成:仓库、任务、事件总线
    // 索引管理:工作树元数据索引
    // 可用性检查:检测Git环境
}
  • 集成架构:与任务管理和事件系统深度集成
  • 元数据管理:工作树元数据集中管理
  • 环境感知:自动检测和适应Git环境
  • 专业级管理:企业级的工作树生命周期管理

Git工作树创建机制

// 创建工作树
public String createWorktree(String name, Integer taskId, String baseRef) throws Exception {
    validateName(name);
    
    Path worktreePath = worktreesDir.resolve(name);
    String branch = "wt/" + name;  // 自动生成分支名
    
    // Git工作树创建
    runGit(Arrays.asList("worktree", "add", "-b", branch, 
        worktreePath.toString(), baseRef));
    // Git工作树:独立的工作目录
    // 自动分支:为每个工作树创建独立分支
    
    // 创建索引条目
    Map<String, Object> entry = new LinkedHashMap<>();
    entry.put("name", name);
    entry.put("path", worktreePath.toString());
    entry.put("branch", branch);
    entry.put("task_id", taskId);
    entry.put("status", "active");
    entry.put("created_at", System.currentTimeMillis() / 1000.0);
    // 完整元数据:名称、路径、分支、关联任务
    // 状态管理:明确的工作树生命周期状态
    
    // 保存到索引
    Map<String, Object> index = loadIndex();
    @SuppressWarnings("unchecked")
    List<Map<String, Object>> worktrees = (List<Map<String, Object>>) index.get("worktrees");
    worktrees.add(entry);
    saveIndex(index);
    // 索引管理:集中式工作树元数据管理
    // 持久化:工作树信息持久化存储
}
  • Git原生:使用Git工作树实现真正的代码隔离
  • 自动分支:为每个工作树自动创建分支
  • 元数据完整:完整的工作树元数据记录
  • 持久化索引:工作树信息持久化存储
  • 状态管理:明确的工作树生命周期状态

任务-工作树双向绑定

// 任务管理器中的工作树绑定
public String bindWorktree(int taskId, String worktree, String owner) throws IOException {
    Map<String, Object> task = loadTask(taskId);
    task.put("worktree", worktree);
    // 双向绑定:任务记录工作树,工作树记录任务
    // 关联管理:建立任务和工作树的一对一关系
    
    if (owner != null && !owner.isEmpty()) {
        task.put("owner", owner);
    }
    
    if ("pending".equals(task.get("status"))) {
        task.put("status", "in_progress");
        // 状态自动转换:绑定工作树时自动开始任务
        // 工作流优化:简化任务启动流程
    }
    
    task.put("updated_at", System.currentTimeMillis() / 1000.0);
    saveTask(task);
    return gson.toJson(task);
}
// 工作树管理器中的任务绑定
if (taskId != null) {
    taskManager.bindWorktree(taskId, name, "");
    // 协同绑定:创建工作树时自动绑定任务
    // 双向同步:确保任务和工作树状态一致
}
  • 双向关联:任务和工作树相互引用
  • 状态协同:任务和工作树状态自动同步
  • 工作流简化:自动的状态转换和绑定
  • 数据一致性:确保关联数据的一致性

工作树操作隔离

// 在工作树中运行命令
public String runInWorktree(String name, String command) throws Exception {
    Map<String, Object> worktree = findWorktree(name);
    Path worktreePath = Paths.get((String) worktree.get("path"));
    
    try {
        ProcessBuilder pb = new ProcessBuilder("bash", "-c", command);
        pb.directory(worktreePath.toFile());
        // 目录隔离:命令在工作树的独立目录中执行
        // 环境隔离:每个工作树有自己的Git环境
        
        Process process = pb.start();
        boolean finished = process.waitFor(300, TimeUnit.SECONDS);
        
        String output = new String(process.getInputStream().readAllBytes()).trim();
        String error = new String(process.getErrorStream().readAllBytes()).trim();
        String result = output + (error.isEmpty() ? "" : "\n" + error);
        
        return result.isEmpty() ? "(no output)" : 
            result.substring(0, Math.min(result.length(), 50000));
        
    } catch (Exception e) {
        return "Error: " + e.getMessage();
    }
}
  • 完全隔离:每个工作树是独立的文件系统目录
  • Git环境隔离:每个工作树有自己的Git配置和历史
  • 安全执行:命令在隔离环境中执行
  • 结果返回:返回完整的命令执行结果

Git状态检查

// 获取工作树状态
public String getWorktreeStatus(String name) throws IOException {
    Path worktreePath = Paths.get((String) worktree.get("path"));
    
    try {
        ProcessBuilder pb = new ProcessBuilder("git", "status", "--short", "--branch");
        pb.directory(worktreePath.toFile());
        // Git状态:获取详细的工作树Git状态
        // 信息丰富:包括分支信息、变更状态等
        
        Process process = pb.start();
        boolean finished = process.waitFor(60, TimeUnit.SECONDS);
        
        String output = new String(process.getInputStream().readAllBytes()).trim();
        String error = new String(process.getErrorStream().readAllBytes()).trim();
        String result = output + (error.isEmpty() ? "" : "\n" + error);
        
        return result.isEmpty() ? "Clean worktree" : result;
        
    } catch (Exception e) {
        return "Error: " + e.getMessage();
    }
}
  • Git专业:提供专业的Git状态信息
  • 实时状态:实时检查工作树的Git状态
  • 变更追踪:跟踪代码变更情况
  • 问题诊断:便于诊断Git相关问题

工作树生命周期管理

// 删除工作树
public String removeWorktree(String name, boolean force, boolean completeTask) throws Exception {
    // Git工作树删除
    List<String> args = new ArrayList<>();
    args.addAll(Arrays.asList("worktree", "remove"));
    if (force) {
        args.add("--force");
    }
    args.add((String) worktree.get("path"));
    
    runGit(args);
    // Git清理:使用Git命令清理工作树
    // 选项支持:支持强制删除等选项
    
    // 完成任务
    if (completeTask && taskId != null) {
        taskManager.updateTask(taskId, "completed", null);
        taskManager.unbindWorktree(taskId);
        // 生命周期协同:删除工作树时自动完成任务
        // 状态清理:清理任务和工作树的关联
        
        Map<String, Object> taskEvent = new HashMap<>();
        taskEvent.put("id", taskId);
        taskEvent.put("subject", task.get("subject"));
        taskEvent.put("status", "completed");
        
        eventBus.emit("task.completed", taskEvent, 
            Map.of("name", name), null);
    }
    
    // 更新索引
    for (Map<String, Object> wt : worktrees) {
        if (name.equals(wt.get("name"))) {
            wt.put("status", "removed");
            wt.put("removed_at", System.currentTimeMillis() / 1000.0);
            break;
        }
    }
    saveIndex(index);
    // 状态更新:标记工作树为已删除状态
    // 历史保留:保留工作树的删除记录
}
  • 完整生命周期:创建、使用、保留、删除的完整生命周期
  • Git原生:使用Git命令管理工作树
  • 任务协同:工作树删除时自动处理关联任务
  • 状态管理:明确的工作树状态管理
  • 历史追踪:保留工作树的历史记录

工作树保留机制

// 保留工作树
public String keepWorktree(String name) throws IOException {
    for (Map<String, Object> wt : worktrees) {
        if (name.equals(wt.get("name"))) {
            wt.put("status", "kept");
            wt.put("kept_at", System.currentTimeMillis() / 1000.0);
            keptWorktree = wt;
            break;
        }
    }
    saveIndex(index);
    // 保留状态:标记工作树为需要保留
    // 不自动清理:保留的工作树不会被自动清理
    // 时间戳记录:记录保留时间
}
  • 选择性保留:可以保留有价值的工作树
  • 长期存储:保留的工作树可以长期存在
  • 状态明确:明确的保留状态标记
  • 时间追踪:记录保留时间

事件总线集成

// 事件总线
static class EventBus {
    public void emit(String event, Map<String, Object> task, 
                    Map<String, Object> worktree, String error) {
        Map<String, Object> payload = new LinkedHashMap<>();
        payload.put("event", event);
        payload.put("ts", System.currentTimeMillis() / 1000.0);
        payload.put("task", task != null ? task : Map.of());
        payload.put("worktree", worktree != null ? worktree : Map.of());
        
        if (error != null) {
            payload.put("error", error);
        }
        // 结构化事件:标准的事件格式
        // 时间戳:精确的事件时间
        // 关联数据:关联的任务和工作树信息
        // 错误信息:支持错误事件记录
        
        String jsonLine = gson.toJson(payload) + "\n";
        Files.writeString(eventLogPath, jsonLine, 
            StandardOpenOption.CREATE, StandardOpenOption.APPEND);
        // JSONL格式:便于流式处理和分析
        // 追加写入:支持长时间运行的事件记录
    }
}
  • 全面审计:所有工作树操作都有完整的事件记录
  • 结构化日志:标准化的JSON事件格式
  • 时间序列:精确的时间戳序列
  • 错误追踪:完整的错误事件追踪
  • 可分析性:便于后续的数据分析

架构演进与价值

从 AutonomousAgentsSystem 到 WorktreeTaskIsolationSystem 的升级

维度 AutonomousAgentsSystem WorktreeTaskIsolationSystem
代码隔离 文件系统级 Git仓库级
并行开发 有限并行 完全并行
版本控制 外部集成 原生集成
冲突避免 手动管理 自动隔离
专业程度 通用系统 专业开发系统

总结

完结撒花。。。

总体来说,这个开源网站系统性地拆解了一个高级AI编程智能体的构建过程,展现了一条清晰的能力演进路径:从具备基础感知和执行能力的单一智能体(工具与执行),发展到能够进行内部规划、分解任务、调用外部知识的协调者(规划与协调),再升级为可以管理资源、并行处理、支撑长时对话的高效执行者(内存管理与并发),最终演化为一个角色分明、通信顺畅、协同作战的智能体团队(协作)。

这条路径揭示了一个核心范式:强大的AI Agent系统并非一蹴而就,可以说是通过不断叠加精心设计的、模块化的机制(如状态管理、上下文隔离、知识外挂、异步处理、消息传递)逐步构建而成。

每个阶段都解决了前一阶段遇到的关键瓶颈,最终将这些模块像积木一样组合,形成一个既可应对复杂项目开发,又能高效管理自身资源与协作的成熟智能体系统。这为我们构建自己的生产级AI Agent提供了宝贵的蓝图和实战指南。

posted @ 2026-04-20 09:00  程序员Seven  阅读(0)  评论(0)    收藏  举报