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