在claude code源码当中,当用户下达任务后,会由plan agent进行任务拆解,但是当任务复杂并且拆解的任务数量过多时,模型非常容易忘记后续的任务,因此一个行之有效的方法就是设置一个TodoManager,强制让AI大模型将任务转化为todo list,然后再调用大模型进行下一步的规划。
核心思想
- LLM绑定工具,包括文件读写,执行命令,编辑文件,以及任务更新等
- 使用TodoManager让模型自己管理任务状态(待办/进行中/完成)
- 模型连续3轮不更新任务-> 自动注入提醒
- 全程不写四执行步骤,模型自主规划,自主推进
核心创新点
模型自己追踪进度,而不是程序硬编码流程
nag reminder提醒机制,3轮不更新todo就强制提醒
同一时间只能有一个任务进行,防止模型发生混乱
流程分析
下面我们来分析一下流程:
- 如果超过三次没有调用todo工具,则直接在
messages中插入一条提醒消息, 并计数归零
python
if rounds_since_todo >= 3:
# 将提醒作为一条独立的 user 消息注入
messages.append({
"role": "user",
"content": "<reminder>Update your todos.</reminder>"
})
# 注意:注入提醒后,计数器重置为 0 或保持?原逻辑没有重置,这里保持原样:
# 提醒只是附加消息,不影响计数,但为避免无限提醒,可以重置 rounds_since_todo = 0
# 建议根据需求调整,以下重置为 0 更合理:
rounds_since_todo = 0
- 当大模型接受到这条最新的消息后,则会调用
todo工具,其中todo工具的distpatch map是这么定义的:
python
{
"name": "todo",
"description": "Update task list. Track progress on multi-step tasks.",
"input_schema": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {"id": {"type": "string"}, "text": {"type": "string"}, "status": {"type": "string", "enum": ["pending", "in_progress", "completed"]}}, "required": ["id", "text", "status"]}}}, "required": ["items"]}},
从代码中我们可以看出,大模型会返回一个items列表,该任务列表中会详细列举任务的id, text, 状态
然后我们看一下当大模型返回todo列表后,程序会遍历items, 检查是否所有的to do list是否合法,以及当前输入in_progress的任务是否只有一个,如果都符合要求,则进行render。 将render后的字符串组转成消息,发送给大模型,进行下一轮的任务。
这里提一下render的规则:
如果是pending任务: 渲染成: #item_id #itemtext
如果是in_progress任务: 渲染成: \> #item_id #itemtext
如果是completed任务: 渲染成: x #item_id #itemtext
python
def update(self, items: list) -> str:
if len(items) > 20:
raise ValueError("Max 20 todos allowed")
validated = []
in_progress_count = 0
for i, item in enumerate(items):
text = str(item.get("text", "")).strip()
status = str(item.get("status", "pending")).lower()
item_id = str(item.get("id", str(i + 1)))
if not text:
raise ValueError(f"Item {item_id}: text required")
if status not in ("pending", "in_progress", "completed"):
raise ValueError(f"Item {item_id}: invalid status '{status}'")
if status == "in_progress":
in_progress_count += 1
validated.append({"id": item_id, "text": text, "status": status})
if in_progress_count > 1:
raise ValueError("Only one task can be in_progress at a time")
self.items = validated
return self.render()
// reader函数
def render(self) -> str:
if not self.items:
return "No todos."
lines = []
for item in self.items:
marker = {"pending": "[ ]", "in_progress": "[>]", "completed": "[x]"}[item["status"]]
lines.append(f"{marker} #{item['id']}: {item['text']}")
done = sum(1 for t in self.items if t["status"] == "completed")
lines.append(f"\n({done}/{len(self.items)} completed)")
return "\n".join(lines)
