夜半钟声 發表於 2026-4-20 09:00:00

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

<p>到 s11, 智能体已经能自主认领和完成任务。但所有任务共享一个目录。两个智能体同时重构不同模块 -- A 改&nbsp;<code>config.py</code>, B 也改&nbsp;<code>config.py</code>, 未提交的改动互相污染, 谁也没法干净回滚。</p>
<p>任务板管 "做什么" 但不管 "在哪做"。解法: 给每个任务一个独立的 git worktree 目录, 用任务 ID 把两边关联起来。"各干各的目录, 互不干扰",任务管目标,,worktree 管目录,按 ID 绑定。</p>
<h2 id="java实现代码">Java实现代码</h2>
<pre><code class="language-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&lt;String, Object&gt; task,
                        Map&lt;String, Object&gt; worktree, String error) {
            Map&lt;String, Object&gt; payload = new LinkedHashMap&lt;&gt;();
            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&lt;String&gt; lines = Files.readAllLines(eventLogPath);
                List&lt;Map&lt;String, Object&gt;&gt; events = new ArrayList&lt;&gt;();
               
                int start = Math.max(0, lines.size() - n);
                for (int i = start; i &lt; lines.size(); i++) {
                  try {
                        Type type = new TypeToken&lt;Map&lt;String, Object&gt;&gt;(){}.getType();
                        Map&lt;String, Object&gt; event = gson.fromJson(lines.get(i), type);
                        events.add(event);
                  } catch (Exception e) {
                        Map&lt;String, Object&gt; errorEvent = new HashMap&lt;&gt;();
                        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&lt;String, Object&gt; task = loadTask(taskId);
            task.put("worktree", worktree);
            // 任务-工作树关联:建立任务和工作树的双向链接
            
            if (owner != null &amp;&amp; !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&lt;String, Object&gt; 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&lt;String, Object&gt; index = new HashMap&lt;&gt;();
                  index.put("worktrees", new ArrayList&lt;Map&lt;String, Object&gt;&gt;());
                  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&lt;String&gt; 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&lt;String, Object&gt; loadIndex() throws IOException {
            String content = Files.readString(indexPath);
            Type type = new TypeToken&lt;Map&lt;String, Object&gt;&gt;(){}.getType();
            return gson.fromJson(content, type);
      }
      
      /**
         * 保存索引文件
         */
      private void saveIndex(Map&lt;String, Object&gt; index) throws IOException {
            Files.writeString(indexPath, gson.toJson(index));
      }
      
      /**
         * 验证工作树名称
         */
      private void validateName(String name) {
            if (name == null || name.isEmpty() || name.length() &gt; 40) {
                throw new IllegalArgumentException("Worktree name must be 1-40 characters");
            }
            
            if (!name.matches("+")) {
                throw new IllegalArgumentException(
                  "Invalid worktree name. Use letters, numbers, ., _, - only"
                );
            }
            // 名称规范:确保工作树名称安全且规范
      }
      
      /**
         * 创建工作树
         */
      public String createWorktree(String name, Integer taskId, String baseRef) throws Exception {
            validateName(name);
            
            Map&lt;String, Object&gt; existing = findWorktree(name);
            if (existing != null) {
                throw new IllegalArgumentException("Worktree '" + name + "' already exists");
            }
            
            if (taskId != null &amp;&amp; !taskManager.taskExists(taskId)) {
                throw new IllegalArgumentException("Task " + taskId + " not found");
            }
            
            // 发出创建前事件
            Map&lt;String, Object&gt; taskInfo = taskId != null ?
                Map.of("id", taskId) : Map.of();
            Map&lt;String, Object&gt; 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&lt;String, Object&gt; entry = new LinkedHashMap&lt;&gt;();
                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&lt;String, Object&gt; index = loadIndex();
                @SuppressWarnings("unchecked")
                List&lt;Map&lt;String, Object&gt;&gt; worktrees = (List&lt;Map&lt;String, Object&gt;&gt;) index.get("worktrees");
                worktrees.add(entry);
                saveIndex(index);
               
                // 绑定任务
                if (taskId != null) {
                  taskManager.bindWorktree(taskId, name, "");
                }
               
                // 发出创建后事件
                Map&lt;String, Object&gt; worktreeAfter = new LinkedHashMap&lt;&gt;();
                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&lt;String, Object&gt; 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", "&gt; /dev/"};
            for (String d : dangerous) {
                if (command.contains(d)) {
                  return "Error: Dangerous command blocked";
                }
            }
            
            Map&lt;String, Object&gt; 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&lt;String, Object&gt; worktree = findWorktree(name);
            if (worktree == null) {
                return "Error: Unknown worktree '" + name + "'";
            }
            
            // 发出删除前事件
            Integer taskId = (Integer) worktree.get("task_id");
            Map&lt;String, Object&gt; taskInfo = taskId != null ?
                Map.of("id", taskId) : Map.of();
            Map&lt;String, Object&gt; worktreeInfo = Map.of(
                "name", name,
                "path", worktree.get("path")
            );
            
            eventBus.emit("worktree.remove.before", taskInfo, worktreeInfo, null);
            
            try {
                List&lt;String&gt; args = new ArrayList&lt;&gt;();
                args.addAll(Arrays.asList("worktree", "remove"));
                if (force) {
                  args.add("--force");
                }
                args.add((String) worktree.get("path"));
               
                runGit(args);
                // Git工作树删除:清理Git工作树
               
                // 完成任务
                if (completeTask &amp;&amp; taskId != null) {
                  Map&lt;String, Object&gt; task = gson.fromJson(taskManager.getTask(taskId),
                        new TypeToken&lt;Map&lt;String, Object&gt;&gt;(){}.getType());
                  
                  taskManager.updateTask(taskId, "completed", null);
                  taskManager.unbindWorktree(taskId);
                  // 任务-工作树生命周期协同:删除工作树时自动完成任务
                  
                  Map&lt;String, Object&gt; taskEvent = new HashMap&lt;&gt;();
                  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&lt;String, Object&gt; index = loadIndex();
                @SuppressWarnings("unchecked")
                List&lt;Map&lt;String, Object&gt;&gt; worktrees = (List&lt;Map&lt;String, Object&gt;&gt;) index.get("worktrees");
               
                for (Map&lt;String, Object&gt; wt : worktrees) {
                  if (name.equals(wt.get("name"))) {
                        wt.put("status", "removed");
                        wt.put("removed_at", System.currentTimeMillis() / 1000.0);
                        break;
                  }
                }
                saveIndex(index);
                // 索引更新:标记工作树为已删除状态
               
                // 发出删除后事件
                Map&lt;String, Object&gt; worktreeAfter = new HashMap&lt;&gt;();
                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&lt;String, Object&gt; worktree = findWorktree(name);
            if (worktree == null) {
                return "Error: Unknown worktree '" + name + "'";
            }
            
            Map&lt;String, Object&gt; index = loadIndex();
            @SuppressWarnings("unchecked")
            List&lt;Map&lt;String, Object&gt;&gt; worktrees = (List&lt;Map&lt;String, Object&gt;&gt;) index.get("worktrees");
            
            Map&lt;String, Object&gt; keptWorktree = null;
            
            for (Map&lt;String, Object&gt; 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&lt;String, Object&gt; taskInfo = taskId != null ?
                Map.of("id", taskId) : Map.of();
            
            Map&lt;String, Object&gt; worktreeInfo = new HashMap&lt;&gt;();
            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 -&gt; {
            String subject = (String) args.get("subject");
            String description = (String) args.get("description");
            return TASKS.createTask(subject, description);
      });
      
      TOOL_HANDLERS.put("task_list", args -&gt; TASKS.listAllTasks());
      
      TOOL_HANDLERS.put("task_get", args -&gt; {
            int taskId = ((Number) args.get("task_id")).intValue();
            return TASKS.getTask(taskId);
      });
      
      TOOL_HANDLERS.put("task_update", args -&gt; {
            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 -&gt; {
            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 -&gt; {
            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 -&gt; WORKTREES.listWorktrees());
      
      TOOL_HANDLERS.put("worktree_status", args -&gt; {
            String name = (String) args.get("name");
            return WORKTREES.getWorktreeStatus(name);
      });
      
      TOOL_HANDLERS.put("worktree_run", args -&gt; {
            String name = (String) args.get("name");
            String command = (String) args.get("command");
            return WORKTREES.runInWorktree(name, command);
      });
      
      TOOL_HANDLERS.put("worktree_keep", args -&gt; {
            String name = (String) args.get("name");
            return WORKTREES.keepWorktree(name);
      });
      
      TOOL_HANDLERS.put("worktree_remove", args -&gt; {
            String name = (String) args.get("name");
            boolean force = args.get("force") != null &amp;&amp; (Boolean) args.get("force");
            boolean completeTask = args.get("complete_task") != null &amp;&amp; (Boolean) args.get("complete_task");
            return WORKTREES.removeWorktree(name, force, completeTask);
      });
      
      TOOL_HANDLERS.put("worktree_events", args -&gt; {
            int limit = args.get("limit") != null ? ((Number) args.get("limit")).intValue() : 20;
            return EVENTS.listRecent(limit);
      });
    }
   
    // --- 新增:工具定义 ---
    private static List&lt;Map&lt;String, Object&gt;&gt; getToolSpecs() {
      List&lt;Map&lt;String, Object&gt;&gt; tools = new ArrayList&lt;&gt;();
      
      // 任务工具定义...
      
      // 工作树工具定义...
      
      return tools;
    }
}
</code></pre>
<p>这段代码引入了<strong>工作树任务隔离</strong>系统,实现了:</p>
<ol>
<li><strong>目录级隔离</strong>:每个任务在自己的工作树中执行</li>
<li><strong>控制平面与执行平面分离</strong>:
<ul>
<li>控制平面:任务管理(.tasks/)</li>
<li>执行平面:工作树管理(.worktrees/)</li>
</ul>
</li>
<li><strong>事件总线</strong>:记录完整生命周期事件</li>
<li><strong>Git 工作树集成</strong>:利用 git worktree 实现隔离</li>
</ol>
<p><strong>关键洞察</strong>:通过目录隔离并行任务,通过任务ID进行协调。</p>
<h2 id="git工作树隔离架构">Git工作树隔离架构</h2>
<p><strong>核心思想</strong>:从文件系统隔离升级为<strong>Git仓库级别的代码隔离</strong>,通过Git工作树实现<strong>完全隔离的并行开发环境</strong>,为每个任务提供独立的代码工作空间,避免冲突,支持真正的并行开发。</p>
<pre><code class="language-java">// 核心路径配置
private static final Path REPO_ROOT = detectRepoRoot(WORKDIR);// Git仓库根目录
private static final Path WORKTREES_DIR = REPO_ROOT.resolve(".worktrees");// 工作树目录
// Git仓库感知:自动检测Git仓库根目录
// 工作树管理:专门的工作树存储目录
// 版本控制集成:与Git深度集成
</code></pre>
<pre><code class="language-java">// 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;
    }
}
</code></pre>
<ul>
<li><strong>Git原生集成</strong>:深度集成Git版本控制系统</li>
<li><strong>仓库感知</strong>:自动识别和适应Git仓库结构</li>
<li><strong>向后兼容</strong>:非Git环境也能工作</li>
<li><strong>专业级隔离</strong>:为专业软件开发设计</li>
</ul>
<h2 id="工作树管理系统">工作树管理系统</h2>
<pre><code class="language-java">// 工作树管理器核心
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环境
}
</code></pre>
<ul>
<li><strong>集成架构</strong>:与任务管理和事件系统深度集成</li>
<li><strong>元数据管理</strong>:工作树元数据集中管理</li>
<li><strong>环境感知</strong>:自动检测和适应Git环境</li>
<li><strong>专业级管理</strong>:企业级的工作树生命周期管理</li>
</ul>
<h2 id="git工作树创建机制">Git工作树创建机制</h2>
<pre><code class="language-java">// 创建工作树
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&lt;String, Object&gt; entry = new LinkedHashMap&lt;&gt;();
    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&lt;String, Object&gt; index = loadIndex();
    @SuppressWarnings("unchecked")
    List&lt;Map&lt;String, Object&gt;&gt; worktrees = (List&lt;Map&lt;String, Object&gt;&gt;) index.get("worktrees");
    worktrees.add(entry);
    saveIndex(index);
    // 索引管理:集中式工作树元数据管理
    // 持久化:工作树信息持久化存储
}
</code></pre>
<ul>
<li><strong>Git原生</strong>:使用Git工作树实现真正的代码隔离</li>
<li><strong>自动分支</strong>:为每个工作树自动创建分支</li>
<li><strong>元数据完整</strong>:完整的工作树元数据记录</li>
<li><strong>持久化索引</strong>:工作树信息持久化存储</li>
<li><strong>状态管理</strong>:明确的工作树生命周期状态</li>
</ul>
<h2 id="任务-工作树双向绑定">任务-工作树双向绑定</h2>
<pre><code class="language-java">// 任务管理器中的工作树绑定
public String bindWorktree(int taskId, String worktree, String owner) throws IOException {
    Map&lt;String, Object&gt; task = loadTask(taskId);
    task.put("worktree", worktree);
    // 双向绑定:任务记录工作树,工作树记录任务
    // 关联管理:建立任务和工作树的一对一关系
   
    if (owner != null &amp;&amp; !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);
}
</code></pre>
<pre><code class="language-java">// 工作树管理器中的任务绑定
if (taskId != null) {
    taskManager.bindWorktree(taskId, name, "");
    // 协同绑定:创建工作树时自动绑定任务
    // 双向同步:确保任务和工作树状态一致
}
</code></pre>
<ul>
<li><strong>双向关联</strong>:任务和工作树相互引用</li>
<li><strong>状态协同</strong>:任务和工作树状态自动同步</li>
<li><strong>工作流简化</strong>:自动的状态转换和绑定</li>
<li><strong>数据一致性</strong>:确保关联数据的一致性</li>
</ul>
<h2 id="工作树操作隔离">工作树操作隔离</h2>
<pre><code class="language-java">// 在工作树中运行命令
public String runInWorktree(String name, String command) throws Exception {
    Map&lt;String, Object&gt; 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();
    }
}
</code></pre>
<ul>
<li><strong>完全隔离</strong>:每个工作树是独立的文件系统目录</li>
<li><strong>Git环境隔离</strong>:每个工作树有自己的Git配置和历史</li>
<li><strong>安全执行</strong>:命令在隔离环境中执行</li>
<li><strong>结果返回</strong>:返回完整的命令执行结果</li>
</ul>
<h2 id="git状态检查">Git状态检查</h2>
<pre><code class="language-java">// 获取工作树状态
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();
    }
}
</code></pre>
<ul>
<li><strong>Git专业</strong>:提供专业的Git状态信息</li>
<li><strong>实时状态</strong>:实时检查工作树的Git状态</li>
<li><strong>变更追踪</strong>:跟踪代码变更情况</li>
<li><strong>问题诊断</strong>:便于诊断Git相关问题</li>
</ul>
<h2 id="工作树生命周期管理">工作树生命周期管理</h2>
<pre><code class="language-java">// 删除工作树
public String removeWorktree(String name, boolean force, boolean completeTask) throws Exception {
    // Git工作树删除
    List&lt;String&gt; args = new ArrayList&lt;&gt;();
    args.addAll(Arrays.asList("worktree", "remove"));
    if (force) {
      args.add("--force");
    }
    args.add((String) worktree.get("path"));
   
    runGit(args);
    // Git清理:使用Git命令清理工作树
    // 选项支持:支持强制删除等选项
   
    // 完成任务
    if (completeTask &amp;&amp; taskId != null) {
      taskManager.updateTask(taskId, "completed", null);
      taskManager.unbindWorktree(taskId);
      // 生命周期协同:删除工作树时自动完成任务
      // 状态清理:清理任务和工作树的关联
      
      Map&lt;String, Object&gt; taskEvent = new HashMap&lt;&gt;();
      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&lt;String, Object&gt; wt : worktrees) {
      if (name.equals(wt.get("name"))) {
            wt.put("status", "removed");
            wt.put("removed_at", System.currentTimeMillis() / 1000.0);
            break;
      }
    }
    saveIndex(index);
    // 状态更新:标记工作树为已删除状态
    // 历史保留:保留工作树的删除记录
}
</code></pre>
<ul>
<li><strong>完整生命周期</strong>:创建、使用、保留、删除的完整生命周期</li>
<li><strong>Git原生</strong>:使用Git命令管理工作树</li>
<li><strong>任务协同</strong>:工作树删除时自动处理关联任务</li>
<li><strong>状态管理</strong>:明确的工作树状态管理</li>
<li><strong>历史追踪</strong>:保留工作树的历史记录</li>
</ul>
<h2 id="工作树保留机制">工作树保留机制</h2>
<pre><code class="language-java">// 保留工作树
public String keepWorktree(String name) throws IOException {
    for (Map&lt;String, Object&gt; 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);
    // 保留状态:标记工作树为需要保留
    // 不自动清理:保留的工作树不会被自动清理
    // 时间戳记录:记录保留时间
}
</code></pre>
<ul>
<li><strong>选择性保留</strong>:可以保留有价值的工作树</li>
<li><strong>长期存储</strong>:保留的工作树可以长期存在</li>
<li><strong>状态明确</strong>:明确的保留状态标记</li>
<li><strong>时间追踪</strong>:记录保留时间</li>
</ul>
<h2 id="事件总线集成">事件总线集成</h2>
<pre><code class="language-java">// 事件总线
static class EventBus {
    public void emit(String event, Map&lt;String, Object&gt; task,
                  Map&lt;String, Object&gt; worktree, String error) {
      Map&lt;String, Object&gt; payload = new LinkedHashMap&lt;&gt;();
      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格式:便于流式处理和分析
      // 追加写入:支持长时间运行的事件记录
    }
}
</code></pre>
<ul>
<li><strong>全面审计</strong>:所有工作树操作都有完整的事件记录</li>
<li><strong>结构化日志</strong>:标准化的JSON事件格式</li>
<li><strong>时间序列</strong>:精确的时间戳序列</li>
<li><strong>错误追踪</strong>:完整的错误事件追踪</li>
<li><strong>可分析性</strong>:便于后续的数据分析</li>
</ul>
<h2 id="架构演进与价值">架构演进与价值</h2>
<p><strong>从 AutonomousAgentsSystem 到 WorktreeTaskIsolationSystem 的升级</strong>:</p>
<table>
<thead>
<tr>
<th>维度</th>
<th>AutonomousAgentsSystem</th>
<th>WorktreeTaskIsolationSystem</th>
</tr>
</thead>
<tbody>
<tr>
<td>代码隔离</td>
<td>文件系统级</td>
<td>Git仓库级</td>
</tr>
<tr>
<td>并行开发</td>
<td>有限并行</td>
<td>完全并行</td>
</tr>
<tr>
<td>版本控制</td>
<td>外部集成</td>
<td>原生集成</td>
</tr>
<tr>
<td>冲突避免</td>
<td>手动管理</td>
<td>自动隔离</td>
</tr>
<tr>
<td>专业程度</td>
<td>通用系统</td>
<td>专业开发系统</td>
</tr>
</tbody>
</table>
<h2 id="总结">总结</h2>
<p>完结撒花。。。</p>
<p>总体来说,这个开源网站系统性地拆解了一个高级AI编程智能体的构建过程,展现了一条清晰的能力演进路径:从具备基础感知和执行能力的单一智能体(工具与执行),发展到能够进行内部规划、分解任务、调用外部知识的协调者(规划与协调),再升级为可以管理资源、并行处理、支撑长时对话的高效执行者(内存管理与并发),最终演化为一个角色分明、通信顺畅、协同作战的智能体团队(协作)。</p>
<p>这条路径揭示了一个核心范式:强大的AI Agent系统并非一蹴而就,可以说是通过不断叠加精心设计的、模块化的机制(如状态管理、上下文隔离、知识外挂、异步处理、消息传递)逐步构建而成。</p>
<p>每个阶段都解决了前一阶段遇到的关键瓶颈,最终将这些模块像积木一样组合,形成一个既可应对复杂项目开发,又能高效管理自身资源与协作的成熟智能体系统。这为我们构建自己的生产级AI Agent提供了宝贵的蓝图和实战指南。</p>


</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自在线网站:seven的菜鸟成长之路,作者:seven,转载请注明原文链接:www.seven97.top</p><br><br>
来源:https://www.cnblogs.com/sevencoding/p/19855214
頁: [1]
查看完整版本: 【从0到1构建一个ClaudeAgent】协作-Worktree+任务隔离