【从0到1构建一个ClaudeAgent】规划与协调-TodoWrite
<p>这段代码引入了一个非常关键的概念:<strong>“自我反思与状态管理”</strong>。</p><p>之前的 Agent 只是单纯的“听指令 -> 干活”,容易干着干着就忘了初衷,或者在复杂的任务中迷失方向。<code>TodoManager</code> 就像是给 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<TodoItem> items = new ArrayList<>();
public String update(List<Map<String, Object>> newItems) throws Exception {
if (newItems.size() > 20) throw new Exception("Max 20 todos allowed");
List<TodoItem> validated = new ArrayList<>();
int inProgressCount = 0;
for (int i = 0; i < newItems.size(); i++) {
Map<String, Object> 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 > 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 ? "[>]" : "";
sb.append(String.format("%s #%s: %s%n", marker, item.id, item.text));
}
long done = items.stream().filter(i -> 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<String, ToolExecutor> TOOL_HANDLERS = new HashMap<>();
static {
// ... 省略已有的工具注册
// 注册 Todo 工具
TOOL_HANDLERS.put(ToolType.TODO.name, args -> {
@SuppressWarnings("unchecked")
List<Map<String, Object>> items = (List<Map<String, Object>>) args.get("items");
return TODO_MANAGER.update(items);
});
}
// --- 3. 核心循环 ---
public static void agentLoop(List<Map<String, Object>> messages) {
int roundsSinceTodo = 0;// 新增:跟踪轮数
while (true) {
// ... 省略相同的 LLM 调用、消息追加、停止检查逻辑
// 3. 执行工具
List<Map<String, Object>> toolResults = new ArrayList<>();
List<Map<String, Object>> content = (List<Map<String, Object>>) response.get("content");
boolean usedTodo = false;// 新增:标记是否使用了 todo 工具
for (Map<String, Object> 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 >= 3) {// 关键:超过3轮没更新就提醒
Map<String, Object> nag = new HashMap<>();
nag.put("type", "text");
nag.put("text", "<reminder>Update your todos.</reminder>");
toolResults.add(0, nag); // 插入到结果列表最前面
System.out.println(">>> 监工提醒:更新待办列表!");
}
// 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<TodoItem> items = new ArrayList<>();// 状态存储
public String update(List<Map<String, Object>> newItems) throws Exception {
if (newItems.size() > 20) throw new Exception("Max 20 todos allowed");
// 业务规则1:限制任务数量,防止滥用
int inProgressCount = 0;
List<TodoItem> validated = new ArrayList<>();
for (int i = 0; i < newItems.size(); i++) {
Map<String, Object> 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 > 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 ? "[>]" : "";
sb.append(String.format("%s #%s: %s%n", marker, item.id, item.text));
// 可视化格式:[ ] 待办, [>] 进行中, 已完成
}
long done = items.stream().filter(i -> 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 -> {
@SuppressWarnings("unchecked")
List<Map<String, Object>> items = (List<Map<String, Object>>) 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 >= 3) {// 如果超过3轮没更新待办
Map<String, Object> nag = new HashMap<>();
nag.put("type", "text");
nag.put("text", "<reminder>Update your todos.</reminder>");
toolResults.add(0, nag); // 插入到结果列表最前面
System.out.println(">>> 监工提醒:更新待办列表!");
// 强制提醒:防止LLM忘记更新状态
}
</code></pre>
<ul>
<li><strong>防遗忘机制</strong>:LLM可能会忘记更新状态,需要外部提醒</li>
<li><strong>渐进式提醒</strong>:容忍短期遗忘,超过阈值再干预</li>
<li><strong>结构化提示</strong>:使用特殊标签<code><reminder></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]