如何构建从0到1的ClaudeAgent规划与协调的TodoWrite流程?

摘要:这段代码引入了一个非常关键的概念:“自我反思与状态管理”。 之前的 Agent 只是单纯的“听指令 -> 干活”,容易干着干着就忘了初衷,或者在复杂的任务中迷失方向。TodoManager 就
这段代码引入了一个非常关键的概念:“自我反思与状态管理”。 之前的 Agent 只是单纯的“听指令 -> 干活”,容易干着干着就忘了初衷,或者在复杂的任务中迷失方向。TodoManager就像是给 Agent 装了一个“记事本”和“监工”。 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 ? "[>]" : "[x]"; 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. 工具实现 (简化版) --- // ... 省略已有的工具实现 } 状态管理:TodoManager 类 为Agent引入长期记忆和工作进度追踪能力,让Agent能"记住"自己的任务列表和工作状态。 // 任务状态枚举 public enum TaskStatus { PENDING("pending"), IN_PROGRESS("in_progress"), COMPLETED("completed"); // 状态枚举:明确定义三种状态 // 状态驱动:Agent根据状态决定下一步操作 } // 任务实体 - 数据结构 public static class TodoItem { public String id; // 唯一标识 public String text; // 任务描述 public TaskStatus status; // 状态 // 结构化的任务表示 // 为LLM提供清晰的上下文 } // 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 ? "[>]" : "[x]"; sb.append(String.format("%s #%s: %s%n", marker, item.id, item.text)); // 可视化格式:[ ] 待办, [>] 进行中, [x] 已完成 } 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(); } } 状态持久化:Agent有了"记忆",不再是完全无状态的 结构化表示:用面向对象的方式管理任务状态 业务约束:通过校验规则确保状态一致性 可视化输出:为LLM提供人类可读的进度展示 Todo工具集成 // 在工具枚举中新增 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传入的任务列表,更新内部状态 }); 状态操作作为工具:将状态管理抽象为工具调用 双向通信:LLM可以通过工具更新状态,也能获取状态 统一接口:与其他工具使用相同的调用模式 监工逻辑(Nag Reminder) // 在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忘记更新状态 } 防遗忘机制:LLM可能会忘记更新状态,需要外部提醒 渐进式提醒:容忍短期遗忘,超过阈值再干预 结构化提示:使用特殊标签<reminder>,让LLM识别这是系统提示 优先级:插入到结果列表最前面,确保LLM先看到 架构演进与价值 从 AgentWithTools 到 AgentWithTodo 的升级: 维度 AgentWithTools AgentWithTodo 状态管理 无状态 有状态(TodoManager) 进度追踪 不支持 支持任务进度管理 长期记忆 不支持 支持任务列表记忆 监督机制 无 有监工提醒 任务管理 工具级 项目级