FEATURED · 精选文章

【Agent开发第八期】给 Agent 一张 TodoList:从“一口气做完“到“拆任务、标进度、勾完成“

发布时间 / 2026/9/6 4:08:11
来源 / 创域科博编辑部
栏目 / 资讯中心
【Agent开发第八期】给 Agent 一张 TodoList:从“一口气做完“到“拆任务、标进度、勾完成“ 文章目录一、前言二、概念对齐:TodoList 三状态机2.1 无规划 vs 有 TodoList2.2 三状态机2.3 双约束:顺序 单 in_progress(核心设计)2.4 三层架构:TodoList 的运作原理2.5 一轮对话的完整流程2.6 纯内存设计:用完即弃三、动手做:三个工具逐行讲解3.1 todo_create:创建任务列表3.2 todo_update:更新状态(双约束核心)3.3 todo_list:查看进度3.4 工具定义(JSON Schema)四、跑起来:演示帮我准备周会4.1 启动程序4.2 自动演示 1:规划任务4.3 自动演示 2:推进任务4.4 原理分析:三层架构怎么协作?4.5 纯内存设计:用完即弃五、执行脚本六、总结核心要点一行代码记住本期适用场景变更对比下期预告一、前言前面七期,我们一步步给 Alex 装上了手脚(循环对话)、“记忆”(三层记忆系统)、“技能”(按需加载)。到第 07 期,Alex 已经能跨会话记住你的偏好了——你跟它说我用 uv 了,下次启动它还记得。但有个问题一直没解决。你说帮我准备周会,Alex 直接一口气回答——不拆任务、不标进度、不勾完成。如果你让它同时做三件事,它同时开三个头、一个没收尾,最后哪个都没做完。这就像一个勤快但没有条理的实习生:手脚很快,但从不列清单,做到哪算哪,你问他做到哪了,他说快了快了,但你根本不知道进度。第 08 期,我们给 Alex 一张TodoList——让它学会拆任务、标进度、勾完成,真正做到做事有条理。二、概念对齐:TodoList 三状态机2.1 无规划 vs 有 TodoList维度无规划(第 07 期)有 TodoList(第 08 期)收到帮我准备周会一口气回答先拆成 4 个子任务进度跟踪无面板一目了然多任务处理乱并行,一个没收尾单 in_progress 强制串行回溯做了什么无任务列表即执行日志类比蒙头干活,不看清单先列清单再动手核心思路:给 Agent 加一个外显的工作记忆。第 07 期的 messages 是对话记忆(记住说了什么),TodoList 是任务记忆(记住要做什么、做到哪了)。把我要做什么从模型的隐式上下文,变成显式的代码数据结构。2.2 三状态机每个任务有三个状态,工作流是单向流水线:pending → in_progress → completed ⬚ ▶ ✓状态含义图标pending待办,还没开始⬚in_progress正在做▶completed已完成✓2.3 双约束:顺序 单 in_progress(核心设计)约束 1(顺序):设为 in_progress 时,排在前面的任务必须全部 completed。不允许跳过 #1 直接开 #3。约束 2(单 in_progress):同一时刻只能有一个任务处于 in_progress。设新的 in_progress 时,旧的自动退回 pending。这两条都不是模型自觉遵守的君子协定,而是工具层面强制执行的硬约束:deftodo_update(task_id,new_status):# 约束 1:顺序 —— 前面的任务必须全部完成ifnew_statusin_progress:blocked_by[t[id]fortin_todo_listift[id]task_idandt[status]!completed]ifblocked_by:returnf顺序约束:前面还有未完成任务{blocked_by}# 约束 2:单 in_progress —— 自动退回旧的ifnew_statusin_progress:fortin_todo_list:ift[status]in_progressandt[id]!task_id:t[status]pending# 自动退回!target[status]new_status约束 1:即使模型想跳过 #1 直接开 #3,工具会返回错误前面还有未完成任务约束 2:即使模型尝试同时设两个 in_progress,第二个调用会把第一个自动退回 pending为什么需要这两条约束?没有约束只有单 in_progress顺序 单 in_progress同时开 5 个头,1 个没收尾不同时开,但可以跳着做必须一条条往下走进度条乱跳进度条不乱跳进度条不乱跳 不跳过用户不知道做到哪了知道哪个在做知道哪个在做 下一个确定两条约束共同强制 Agent:一条条往下执行,不跳过、不并行。多线程并行是计算机的强项,但 Agent 面向的是替人做事——人习惯串行,一次专注一件事。2.4 三层架构:TodoList 的运作原理TodoList 不是单个组件,而是三层协作:┌─────────────────────────────────────────────────────────┐ │ 提示词层(软约束,引导行为) │ │ templates/todo_guide.md → 注入 system prompt │ │ 告诉 LLM:每轮先查 todo_list,有 in_progress 就推进, │ │ 需要用户信息就停下,不要自己编 │ │ 特点:模型可能不遵守(软约束),但 99% 会跟随 │ ├─────────────────────────────────────────────────────────┤ │ 工具层(硬约束,强制执行) │ │ todo_create / todo_update / todo_list 三个 Python 函数 │ │ 双约束在这里硬编码: │ │ 约束 1(顺序):跳过 → 返回错误 │ │ 约束 2(单 in_progress):设新的 → 旧的自动退回 │ │ 特点:模型无法绕过(硬约束),工具直接拒绝非法状态转换 │ ├─────────────────────────────────────────────────────────┤ │ 工具循环(第 05 期建的,复用) │ │ while True: │ │ LLM → 决定调哪个工具、传什么参数 │ │ 代码 → 执行工具、把结果塞回 messages │ │ LLM → 看到结果,决定下一步 │ │ 特点:模型是驾驶员,工具是车,提示词是交通规则 │ └─────────────────────────────────────────────────────────┘三层各司其职:层职责能拦住什么拦不住什么提示词层引导 LLM 该做什么模型不知道该查 todo_list模型假装做完(自己编内容标完成)工具层强制规则执行跳过任务、并行多任务模型编造任务内容工具循环驱动 LLM ↔ 工具交互——关键洞察:硬约束(工具层)能拦住跳过和并行,但拦不住模型自己编内容然后标完成。这类行为只能靠提示词引导(软约束)。这也是为什么 todo_guide.md 里要写需要用户信息就停下,不要自己编——软约束拉上限,硬约束保下限。2.5 一轮对话的完整流程用户输入: 明天开会全体员工总结本周工作 │ ▼ 提示词层: todo_guide 说列表为空时,判断是否复杂任务 │ ▼ LLM 决策: 这是复杂任务,需要拆解 │ ▼ todo_create(tasks[收集要点, 整理议题, ...]) 工具层: 创建 4 个 pending 任务,返回列表 │ ▼ 结果塞回 messages LLM 决策: #1 设为 in_progress │ ▼ todo_update(1, in_progress) 工具层: 双约束检查通过,状态更新 │ ▼ 结果塞回 messages LLM 回复: #1 收集要点,请告诉我本周进展... │ (提示词层:需要用户信息 → 停下,不要自己编) ▼ 等待用户下一轮输入...提示词引导行为 → 工具执行约束 → 工具循环驱动交互。三层缺一不可。2.6 纯内存设计:用完即弃TodoList 运行时存在内存变量_todo_list里,不持久化,程序关了就丢。这是有意为之:维度持久化(如 md 文件)纯内存(本期设计)程序关了任务保留任务清空复杂度需要读写文件、解析格式一个 list 搞定教学焦点分散(格式解析状态机)聚焦(纯状态机逻辑)适用阶段进阶(后续可加)入门(本期够用)本期聚焦规划→执行→勾选的状态机逻辑本身,持久化留给后续进阶。所有任务完成后,列表自动清空(用完即弃)。三、动手做:三个工具逐行讲解3.1 todo_create:创建任务列表收到复杂任务时,Agent 先调这个工具,把模糊需求拆成 3-7 个可执行子任务:deftodo_create(tasks:list[str])-str:created[]forcontentintasks:task{id:_next_todo_id(),# 从 1 开始,单调递增content:content,status:TODO_PENDING,# 全部初始为 pendingcreated_at:datetime.now().isoformat(timespecseconds),}_todo_list.append(task)created.append(task)returnjson.dumps({created:len(created),tasks:[{id:t[id],content:t[content],status:t[status]}fortincreated],},ensure_asciiFalse)关键设计:全部初始为 pending:创建时不能有 in_progress,后续通过todo_update逐个推进ID 单调递增:从 1 开始,方便引用(“任务 #1”)批量创建:一次性拆完所有子任务,而不是拆一个做一个(让 Agent 先有全局规划)3.2 todo_update:更新状态(双约束核心)deftodo_update(task_id:int,new_status:str)-str:# 校验状态值valid_statuses{TODO_PENDING,TODO_IN_PROGRESS,TODO_COMPLETED}ifnew_statusnotinvalid_statuses:returnjson.dumps({error:f无效状态:{new_status}},ensure_asciiFalse)# 找到目标任务targetNonefortin_todo_list:ift[id]task_id:targettbreakiftargetisNone:returnjson.dumps({error:f任务不存在: id{task_id}},ensure_asciiFalse)# ★ 约束 1:顺序约束 —— 前面的任务必须全部完成 ★ifnew_statusin_progress:blocked_by[]fortin_todo_list:ift[id]task_idandt[status]!completed:blocked_by.append(t[id])ifblocked_by:returnjson.dumps({error:f顺序约束:前面还有未完成任务{blocked_by}},ensure_asciiFalse)# ★ 约束 2:单 in_progress —— 自动退回旧的 ★ifnew_statusin_progress:fortin_todo_list:ift[status]in_progressandt[id]!task_id:t[status]pendingprint(f [todo] 任务 #{t[id]}自动退回 pending)target[status]new_statusreturnjson.dumps({updated:task_id,new_status:new_status,current_list:_todo_snapshot()},ensure_asciiFalse)最关键的两段:约束 1拦住跳过(#1 没做完就想开 #2,直接返回错误),约束 2拦住并行(同时只能一个 in_progress,设新的自动退旧的)。两条约束让 Agent 必须一条条往下走。所有任务完成后,列表自动清空:# 全部 completed → 清空列表(用完即弃)ifall(t[status]completedfortin_todo_list):_todo_list.clear()3.3 todo_list:查看进度deftodo_list()-str:returnjson.dumps({total:len(_todo_list),pending:sum(1fortin_todo_listift[status]pending),in_progress:sum(1fortin_todo_listift[status]in_progress),completed:sum(1fortin_todo_listift[status]completed),tasks:_todo_snapshot(),},ensure_asciiFalse)返回每个状态的数量 完整任务快照。Agent 调它来确认现在做到哪了,下一个做什么。3.4 工具定义(JSON Schema)三个工具在TOOLS列表里和第 05-07 期的工具完全平等:TOOLS[# ... 第 05-07 期的 5 个工具(get_current_time / calculate / load_skill / save_user_profile / recall_memory){type:function,function:{name:todo_create,description:把一个模糊需求拆成可执行的子任务列表...,parameters:{type:object,properties:{tasks:{type:array,items:{type:string},description:子任务内容列表,},},required:[tasks],},},},# todo_update / todo_list 类似...]累积式的好处再次体现:加能力 加工具,核心工具循环一行没改。四、跑起来:演示帮我准备周会4.1 启动程序source.venv/bin/activate python code/step08_todo.py启动后先看到记忆面板(第 07 期)和 TodoList 面板(第 08 期新增,初始为空):┌──────────────────────────────────────────────────────────┐ │ Alex 任务面板(第 08 期新增) │ ├──────────────────────────────────────────────────────────┤ │ (空)还没有任务,让 Alex 帮你规划 │ └──────────────────────────────────────────────────────────┘4.2 自动演示 1:规划任务程序自动发送帮我准备下周的周会,观察 Alex 的行为:[工具调用] todo_create({tasks: [收集本周工作要点, 整理待讨论议题, 确定下周重点与分工, 汇总输出周会议程文档]}) ┌──────────────────────────────────────────────────────────┐ │ Alex 任务面板 │ ├──────────────────────────────────────────────────────────┤ │ ⬚ #1 收集本周工作要点与进展 │ │ ⬚ #2 整理待讨论议题 │ │ ⬚ #3 确定下周重点与分工 │ │ ⬚ #4 汇总输出周会议程文档 │ │──────────────────────────────────────────────────────────│ │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0% (0/4) │ └──────────────────────────────────────────────────────────┘Alex 自动拆成了 4 个子任务,全部 pending,进度条 0%。4.3 自动演示 2:推进任务程序自动发送好,开始执行第一个任务吧:[工具调用] todo_update({task_id: 1, new_status: in_progress}) ┌──────────────────────────────────────────────────────────┐ │ ▶ #1 收集本周工作要点与进展 │ │ ⬚ #2 整理待讨论议题 │ │ ⬚ #3 确定下周重点与分工 │ │ ⬚ #4 汇总输出周会议程文档 │ └──────────────────────────────────────────────────────────┘图标从 ⬚ 变成 ▶。注意:进度条还是 0%——因为还没 completed。4.4 原理分析:三层架构怎么协作?Alex 为什么会主动用 TodoList?因为三层架构在协作:第一层:提示词层(软约束)我们在 system prompt 尾部注入了templates/todo_guide.md,核心原则是按任务列表执行,每步都要产出:# TodoList 工作指南 ## 核心原则:按任务列表执行,每步都要产出 1. 先调 todo_list 查看当前任务状态 2. 如果有 in_progress 的任务: - 用户的输入应该视为该任务的补充信息 - 用用户提供的信息执行,完成后调 todo_update(task_id, completed) - 再调 todo_update 把下一个任务设为 in_progress - 停下来等用户输入:如果下一个任务需要用户提供信息, 不要自己编,把问题抛给用户 3. 如果所有任务都 completed: - 把所有步骤的产出汇总成最终交付物 - 列表会自动清空(用完即弃) 4. 如果列表为空: - 判断用户是否提出了新的复杂任务 - 是:调 todo_create 拆成 3-7 个子任务第二层:工具层(硬约束)双约束在todo_update函数里硬编码,模型无法绕过。想跳过 #1 直接开 #3工具返回错误。想同时设两个 in_progress第二个自动把第一个退回 pending。第三层:工具循环(第 05 期复用)while True循环:LLM 决定调哪个工具 → 代码执行工具 → 结果塞回 messages → LLM 看结果决定下一步。模型是驾驶员,工具是车,提示词是交通规则。三层缺一不可:硬约束保下限(拦住跳过和并行),软约束拉上限(引导模型停下等用户,不要自己编)。4.5 纯内存设计:用完即弃TodoList 运行时存在内存变量_todo_list里,不写文件、不持久化。程序关了任务就清空。这是有意为之:本期聚焦规划→执行→勾选的状态机逻辑本身不引入文件读写、格式解析的复杂度所有任务完成后列表自动清空(_todo_list.clear())持久化(跨会话保留任务)留给后续进阶,本期先把机制跑通。五、执行脚本完整代码见code/step08_todo.py,以下是核心结构:#!/usr/bin/env python3step08_todo.py — 第 08 期:任务规划 TodoList 原理一句话:提示词层(todo_guide.md 注入 system prompt,软约束)引导 LLM 该做什么 工具层(todo_create/update/list 三个函数,硬约束)强制规则执行 工具循环(第 05 期复用)驱动 LLM ↔ 工具交互 TodoList 机制。 累积式:step08 step07 TodoList (保留 step07 的三层记忆、技能层、工具循环全部逻辑,只新增 TodoList 层 3 个工具) # TodoList 层(本期核心新增)TODO_PENDINGpendingTODO_IN_PROGRESSin_progressTODO_COMPLETEDcompleted_todo_list:list[dict][]# 纯内存,不持久化def_next_todo_id()-int:ifnot_todo_list:return1returnmax(t[id]fortin_todo_list)1deftodo_create(tasks:list[str])-str:created[]forcontentintasks:task{id:_next_todo_id(),content:content,status:TODO_PENDING,created_at:datetime.now().isoformat(timespecseconds),}_todo_list.append(task)created.append(task)returnjson.dumps({created:len(created),tasks:[{id:t[id],content:t[content],status:t[status]}fortincreated],},ensure_asciiFalse)deftodo_update(task_id:int,new_status:str)-str:valid_statuses{TODO_PENDING,TODO_IN_PROGRESS,TODO_COMPLETED}ifnew_statusnotinvalid_statuses:returnjson.dumps({error:f无效状态:{new_status}},ensure_asciiFalse)targetNonefortin_todo_list:ift[id]task_id:targettbreakiftargetisNone:returnjson.dumps({error:f任务不存在: id{task_id}},ensure_asciiFalse)# 约束 1:顺序 —— 前面的任务必须全部完成ifnew_statusin_progress:blocked_by[t[id]fortin_todo_listift[id]task_idandt[status]!completed]ifblocked_by:returnjson.dumps({error:f顺序约束:前面还有未完成任务{blocked_by}},ensure_asciiFalse)# 约束 2:单 in_progress —— 自动退回旧的ifnew_statusin_progress:fortin_todo_list:ift[status]in_progressandt[id]!task_id:t[status]pendingtarget[status]new_status# 全部完成 → 清空列表(用完即弃)ifall(t[status]completedfortin_todo_list):_todo_list.clear()returnjson.dumps({updated:task_id,new_status:new_status,current_list:_todo_snapshot()},ensure_asciiFalse)deftodo_list()-str:returnjson.dumps({total:len(_todo_list),pending:sum(1fortin_todo_listift[status]pending),in_progress:sum(1fortin_todo_listift[status]in_progress),completed:sum(1fortin_todo_listift[status]completed),tasks:_todo_snapshot(),},ensure_asciiFalse)defprint_todo_panel():打印 TodoList 面板(教学可视化)# ... 打印 ⬚/▶/✓ 图标 进度条# 工具定义(第 07 期 5 个 本期新增 3 个 8 个)TOOLS[# get_current_time / calculate / load_skill / save_user_profile / recall_memory(第 05-07 期)# todo_create / todo_update / todo_list(本期新增)]# system prompt 尾部追加 TodoList 工作指南system_promptPath(templates/todo_guide.md).read_text(encodingutf-8)运行:source.venv/bin/activate python code/step08_todo.py六、总结核心要点三状态机— pending → in_progress → completed,每个任务有明确状态,不模糊双约束:顺序 单 in_progress— 工具层面强制执行:必须按 ID 顺序逐个推进(不能跳过),且同一时刻只能有一个 in_progress三层架构— 提示词层(软约束)引导行为 工具层(硬约束)强制规则 工具循环(第 05 期复用)驱动交互。硬约束保下限,软约束拉上限纯内存,用完即弃— 不持久化,聚焦状态机逻辑本身,所有任务完成后列表自动清空累积式迭代— step08 step07 TodoList,三个新工具和之前的工具完全平等,核心循环一行没改一行代码记住本期# 约束 1(顺序):前面的任务必须全部 completed 才能开始# 约束 2(单 in_progress):同时只能一个 in_progress,设新的自动退旧的ifnew_statusin_progress:blocked[t[id]fortin_todo_listift[id]task_idandt[status]!completed]ifblocked:returnf顺序约束:前面还有未完成{blocked}fortin_todo_list:ift[status]in_progress:t[status]pending# 自动退回!适用场景复杂任务拆解:准备会议、做代码审查、写技术方案多步骤执行:需要拆成子任务、逐个推进、追踪进度串行约束场景:必须做完一个再做下一个(如部署流程)变更对比变更点第 07 期第 08 期工具数量53 → 8system prompt人设 技能 用户画像 TodoList 工作指南(每轮先查 todo_list,有 in_progress 就推进,需要用户信息就停下)启动面板记忆面板 TodoList 面板新增命令— todo新增函数— todo_create / todo_update / todo_list运行时资源templates/USER.md 等 templates/todo_guide.md(工作指南注入 system prompt)持久化三层记忆(文件)纯内存(不持久化,用完即弃)下期预告第 09 期:子代理 Subagent。Alex 会拆任务了,但遇到两个专家同时看还是串行排队。下一期引入子代理:独立上下文、工具白名单、并发派遣——让 Agent 学会派活给别人。感谢各位看官的一路陪伴,大家都再接再厉!
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻