头孢配酒 發表於 2026-4-11 09:00:00

【从0到1构建一个ClaudeAgent】规划与协调-TodoWrite

<p>这段代码引入了一个非常关键的概念:<strong>“自我反思与状态管理”</strong>。</p>
<p>之前的 Agent 只是单纯的“听指令 -&gt; 干活”,容易干着干着就忘了初衷,或者在复杂的任务中迷失方向。<code>TodoManager</code>&nbsp;就像是给 Agent 装了一个“记事本”和“监工”。</p>
<h2 id="java-实现代码">Java 实现代码</h2>
<pre><code class="language-java">public class AgentWithTodo {
    private static final Path WORKDIR = Paths.get(System.getProperty("user.dir"));
   
    // --- 1. 状态管理:TodoManager ---
   
    // 任务状态枚举
    public enum TaskStatus {
      PENDING("pending"), IN_PROGRESS("in_progress"), COMPLETED("completed");
      public final String label;
      TaskStatus(String label) { this.label = label; }
      public static TaskStatus fromLabel(String s) {
            for (TaskStatus ts : values()) if (ts.label.equals(s)) return ts;
            return PENDING;
      }
    }

    // 任务实体
    public static class TodoItem {
      public String id;
      public String text;
      public TaskStatus status;
      public TodoItem(String id, String text, String status) {
            this.id = id; this.text = text; this.status = TaskStatus.fromLabel(status);
      }
    }

    // 管理器类
    public static class TodoManager {
      private List&lt;TodoItem&gt; items = new ArrayList&lt;&gt;();

      public String update(List&lt;Map&lt;String, Object&gt;&gt; newItems) throws Exception {
            if (newItems.size() &gt; 20) throw new Exception("Max 20 todos allowed");
            List&lt;TodoItem&gt; validated = new ArrayList&lt;&gt;();
            int inProgressCount = 0;

            for (int i = 0; i &lt; newItems.size(); i++) {
                Map&lt;String, Object&gt; item = newItems.get(i);
                String text = (String) item.getOrDefault("text", "");
                String statusStr = (String) item.getOrDefault("status", "pending");
                String id = String.valueOf(item.getOrDefault("id", String.valueOf(i + 1)));

                if (text.trim().isEmpty()) throw new Exception("Item " + id + ": text required");
               
                TaskStatus status = TaskStatus.fromLabel(statusStr.toLowerCase());
                if (status == TaskStatus.IN_PROGRESS) inProgressCount++;

                validated.add(new TodoItem(id, text.trim(), status.label));
            }

            if (inProgressCount &gt; 1) throw new Exception("Only one task can be in_progress at a time");
            this.items = validated;
            return render();
      }

      public String render() {
            if (items.isEmpty()) return "No todos.";
            StringBuilder sb = new StringBuilder();
            for (TodoItem item : items) {
                String marker = item.status == TaskStatus.PENDING ? "[ ]" :
                              item.status == TaskStatus.IN_PROGRESS ? "[&gt;]" : "";
                sb.append(String.format("%s #%s: %s%n", marker, item.id, item.text));
            }
            long done = items.stream().filter(i -&gt; i.status == TaskStatus.COMPLETED).count();
            sb.append(String.format("%n(%d/%d completed)", done, items.size()));
            return sb.toString();
      }
    }

    private static final TodoManager TODO_MANAGER = new TodoManager();

    // --- 2. 工具定义与分发 ---
    public enum ToolType {
      BASH("bash"), READ_FILE("read_file"), WRITE_FILE("write_file"),
      EDIT_FILE("edit_file"), TODO("todo");// 新增 todo 工具
      public final String name;
      ToolType(String name) { this.name = name; }
    }

    private static final Map&lt;String, ToolExecutor&gt; TOOL_HANDLERS = new HashMap&lt;&gt;();

    static {
      // ... 省略已有的工具注册
      
      // 注册 Todo 工具
      TOOL_HANDLERS.put(ToolType.TODO.name, args -&gt; {
            @SuppressWarnings("unchecked")
            List&lt;Map&lt;String, Object&gt;&gt; items = (List&lt;Map&lt;String, Object&gt;&gt;) args.get("items");
            return TODO_MANAGER.update(items);
      });
    }

    // --- 3. 核心循环 ---
    public static void agentLoop(List&lt;Map&lt;String, Object&gt;&gt; messages) {
      int roundsSinceTodo = 0;// 新增:跟踪轮数

      while (true) {
            // ... 省略相同的 LLM 调用、消息追加、停止检查逻辑
            
            // 3. 执行工具
            List&lt;Map&lt;String, Object&gt;&gt; toolResults = new ArrayList&lt;&gt;();
            List&lt;Map&lt;String, Object&gt;&gt; content = (List&lt;Map&lt;String, Object&gt;&gt;) response.get("content");
            boolean usedTodo = false;// 新增:标记是否使用了 todo 工具

            for (Map&lt;String, Object&gt; block : content) {
                if ("tool_use".equals(block.get("type"))) {
                  // ... 省略相同的工具调用逻辑
                  String toolName = (String) block.get("name");
                  // ... 执行工具
                  
                  if (toolName.equals("todo")) usedTodo = true;// 标记 todo 使用
                }
            }

            // 4. 监工逻辑 (Nag Reminder)
            roundsSinceTodo = usedTodo ? 0 : roundsSinceTodo + 1;
            
            if (roundsSinceTodo &gt;= 3) {// 关键:超过3轮没更新就提醒
                Map&lt;String, Object&gt; nag = new HashMap&lt;&gt;();
                nag.put("type", "text");
                nag.put("text", "&lt;reminder&gt;Update your todos.&lt;/reminder&gt;");
                toolResults.add(0, nag); // 插入到结果列表最前面
                System.out.println("&gt;&gt;&gt; 监工提醒:更新待办列表!");
            }

            // 5. 回传结果
            // ... 省略相同的回传逻辑
      }
    }

    // --- 4. 工具实现 (简化版) ---
    // ... 省略已有的工具实现
}
</code></pre>
<h2 id="状态管理todomanager-类">状态管理:TodoManager 类</h2>
<p>为Agent引入<strong>长期记忆和工作进度追踪</strong>能力,让Agent能"记住"自己的任务列表和工作状态。</p>
<pre><code class="language-java">// 任务状态枚举
public enum TaskStatus {
    PENDING("pending"),
    IN_PROGRESS("in_progress"),
    COMPLETED("completed");
    // 状态枚举:明确定义三种状态
    // 状态驱动:Agent根据状态决定下一步操作
}
</code></pre>
<pre><code class="language-java">// 任务实体 - 数据结构
public static class TodoItem {
    public String id;          // 唯一标识
    public String text;      // 任务描述
    public TaskStatus status;// 状态
    // 结构化的任务表示
    // 为LLM提供清晰的上下文
}
</code></pre>
<pre><code class="language-java">// TodoManager - 核心状态管理
public class TodoManager {
    private List&lt;TodoItem&gt; items = new ArrayList&lt;&gt;();// 状态存储

    public String update(List&lt;Map&lt;String, Object&gt;&gt; newItems) throws Exception {
      if (newItems.size() &gt; 20) throw new Exception("Max 20 todos allowed");
      // 业务规则1:限制任务数量,防止滥用
      
      int inProgressCount = 0;
      List&lt;TodoItem&gt; validated = new ArrayList&lt;&gt;();

      for (int i = 0; i &lt; newItems.size(); i++) {
            Map&lt;String, Object&gt; item = newItems.get(i);
            String text = (String) item.getOrDefault("text", "");
            String statusStr = (String) item.getOrDefault("status", "pending");
            String id = String.valueOf(item.getOrDefault("id", String.valueOf(i + 1)));
            
            if (text.trim().isEmpty()) throw new Exception("Item " + id + ": text required");
            // 业务规则2:任务文本必填
            
            TaskStatus status = TaskStatus.fromLabel(statusStr.toLowerCase());
            if (status == TaskStatus.IN_PROGRESS) inProgressCount++;
            // 业务规则3:跟踪进行中任务数量
      }

      if (inProgressCount &gt; 1) throw new Exception("Only one task can be in_progress at a time");
      // 业务规则4:一次只能进行一个任务,聚焦执行
      
      this.items = validated;// 原子性更新
      return render();// 返回可视化表示
    }

    public String render() {
      if (items.isEmpty()) return "No todos.";
      StringBuilder sb = new StringBuilder();
      for (TodoItem item : items) {
            String marker = item.status == TaskStatus.PENDING ? "[ ]" :
                           item.status == TaskStatus.IN_PROGRESS ? "[&gt;]" : "";
            sb.append(String.format("%s #%s: %s%n", marker, item.id, item.text));
            // 可视化格式:[ ] 待办, [&gt;] 进行中, 已完成
      }
      
      long done = items.stream().filter(i -&gt; i.status == TaskStatus.COMPLETED).count();
      sb.append(String.format("%n(%d/%d completed)", done, items.size()));
      // 进度统计:为LLM提供进度反馈
      return sb.toString();
    }
}
</code></pre>
<ul>
<li><strong>状态持久化</strong>:Agent有了"记忆",不再是完全无状态的</li>
<li><strong>结构化表示</strong>:用面向对象的方式管理任务状态</li>
<li><strong>业务约束</strong>:通过校验规则确保状态一致性</li>
<li><strong>可视化输出</strong>:为LLM提供人类可读的进度展示</li>
</ul>
<h2 id="todo工具集成">Todo工具集成</h2>
<pre><code class="language-java">// 在工具枚举中新增
TODO("todo");// 扩展工具集,添加状态管理工具

// 注册Todo工具实现
TOOL_HANDLERS.put(ToolType.TODO.name, args -&gt; {
    @SuppressWarnings("unchecked")
    List&lt;Map&lt;String, Object&gt;&gt; items = (List&lt;Map&lt;String, Object&gt;&gt;) args.get("items");
    return TODO_MANAGER.update(items);
    // 状态更新工具:让LLM能操作任务状态
    // 接受LLM传入的任务列表,更新内部状态
});
</code></pre>
<ul>
<li><strong>状态操作作为工具</strong>:将状态管理抽象为工具调用</li>
<li><strong>双向通信</strong>:LLM可以通过工具更新状态,也能获取状态</li>
<li><strong>统一接口</strong>:与其他工具使用相同的调用模式</li>
</ul>
<h2 id="监工逻辑nag-reminder">监工逻辑(Nag Reminder)</h2>
<pre><code class="language-java">// 在agentLoop中新增
int roundsSinceTodo = 0;// 计数器:记录多少轮没使用todo工具
boolean usedTodo = false; // 标记当前轮是否使用了todo

// 执行工具时记录
if (toolName.equals("todo")) usedTodo = true;

// 每轮结束后的监工检查
roundsSinceTodo = usedTodo ? 0 : roundsSinceTodo + 1;// 重置或递增

if (roundsSinceTodo &gt;= 3) {// 如果超过3轮没更新待办
    Map&lt;String, Object&gt; nag = new HashMap&lt;&gt;();
    nag.put("type", "text");
    nag.put("text", "&lt;reminder&gt;Update your todos.&lt;/reminder&gt;");
    toolResults.add(0, nag); // 插入到结果列表最前面
   
    System.out.println("&gt;&gt;&gt; 监工提醒:更新待办列表!");
    // 强制提醒:防止LLM忘记更新状态
}
</code></pre>
<ul>
<li><strong>防遗忘机制</strong>:LLM可能会忘记更新状态,需要外部提醒</li>
<li><strong>渐进式提醒</strong>:容忍短期遗忘,超过阈值再干预</li>
<li><strong>结构化提示</strong>:使用特殊标签<code>&lt;reminder&gt;</code>,让LLM识别这是系统提示</li>
<li><strong>优先级</strong>:插入到结果列表最前面,确保LLM先看到</li>
</ul>
<h2 id="架构演进与价值">架构演进与价值</h2>
<p><strong>从 AgentWithTools 到 AgentWithTodo 的升级</strong>:</p>
<table>
<thead>
<tr>
<th>维度</th>
<th>AgentWithTools</th>
<th>AgentWithTodo</th>
</tr>
</thead>
<tbody>
<tr>
<td>状态管理</td>
<td>无状态</td>
<td>有状态(TodoManager)</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>


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