Agent 执行循环
拆开智能体的"大脑"。SuperAGI 有两级 step(宏观工作流步 + 微观迭代步),经典的 think→选工具→执行→观察 就发生在迭代步里。读 jobs/agent_executor.py + agent/。
两级 step(一定要分清)
外层 AgentWorkflowStep(宏观流程节点)
- 类型:TOOL / ITERATION_WORKFLOW / WAIT_STEP
- 是"整个流程的一个大步骤"
- 节点之间用 next_steps 连成有向图
内层 IterationWorkflowStep(微观迭代步)
- 当外层是 ITERATION_WORKFLOW 时,内部还有迭代小步
- 经典 think→选工具→执行→观察 在这里
- 常指回自己形成循环
🏢 生活类比(贯穿今天):把智能体想成一个带实习生的项目组。外层工作流 = 项目经理手里的排期表/SOP:先立项 → 再开发 → 最后验收,顺序和卡点都定死;内层迭代步 = 实习生在"开发"这个阶段里自由发挥(想一步、写一段、跑一下、再想)。排期表保证不跑偏,实习生保证灵活——两级各司其职。
👶 小白:外层 workflow step、内层 iteration step,我老是绕晕——到底哪一级在"循环"?
👨🏫 老师:记一句话——外层是"排期表",内层才是"反复干"。外层工作流图可能只是一站一站往前走(初始化 → 处理 → 汇总),不一定循环;真正 think→act→observe 一轮轮重复的是内层迭代步——它的 next_step_id 指回自己、被 Celery 反复重排,直到 LLM 调 finish。所以"循环"主要发生在内层迭代步里。
单步执行器
AgentExecutor.execute_next_step(jobs/agent_executor.py:39)——注意名字是"下一步",只跑一步:
# 39-56: 取 AgentExecution,防呆检查(旧执行跳过、状态不对退出)
# 59-61: _check_for_max_iterations 检查迭代上限(Day 02 熔断)
# 83-87: 按 current_agent_step_id 取当前 workflow step,调 __execute_workflow_step 跑这一步
🍜 生活类比:很像餐厅叫号取餐——你(执行)拿个号排队,后厨(Celery worker)叫到号才做你这一道菜,做完你再排队等下一道。没人守着你从头做到尾,而是"叫一次、做一步"。
按类型分派 handler(策略模式)
# agent_executor.py:105
if step.action_type == "TOOL":
AgentToolStepHandler(...).execute_step() # 编排者钦定工具
elif step.action_type == "ITERATION_WORKFLOW":
AgentIterationStepHandler(...).execute_step() # LLM 自选工具(经典循环)
elif step.action_type == "WAIT_STEP":
AgentWaitStepHandler(...).execute_step() # 等待
迭代步的自治循环(经典 think→act→observe)
AgentIterationStepHandler.execute_step(agent/agent_iteration_step_handler.py:46)最贴近经典循环:
system: "你是旅行助手,目标:订一张最便宜的北京→上海机票。可用工具:SearchFlight, BookFlight, finish"历史 Feed: "(第 1 轮)我调了 SearchFlight,得到 3 个航班:MU5101 ¥560 最便宜"→ LLM 本轮输出(结构化 JSON,Day 09 解析):
{"thoughts":{"reasoning":"已找到最便宜航班,下一步预订"}, "tool":{"name":"BookFlight","args":{"flight":"MU5101"}}}→ 执行 + 观察:跑 BookFlight → 结果 "预订成功,订单号 X" 写进 Feed → 下一轮 LLM 读到它,可能就调
finish 收尾。🔍 单步走查表:跟踪任务"查一下北京天气并汇报",看它怎么一轮轮走到结束(关注 Feed 和 next_step 怎么变):
| 轮次 | 这一迭代步发生什么 | 关键状态(Feed / next_step) |
|---|---|---|
| 第 1 轮 | LLM think:"要汇报天气,得先查" → 选工具 GetWeather(city=北京) | Feed += "调用 GetWeather";next_step = 自己 |
| 第 2 轮 | 读回 Feed 看到 "晴 25℃" → think:"信息够了,可以汇报" → 选 finish | Feed += "晴 25℃";LLM 调 finish |
| 结束 | ToolExecutor 见工具名是 finish → 返回 COMPLETE | 执行状态 = COMPLETED,不再重排 |
AgentExecution:你先被 API 创建、丢进 Redis 队列排队 → Celery 把你领出来,让你跑一个迭代步 → 你想一步、调个工具、把结果写进 Feed、把 next_step 指回自己 → 然后你又被丢回队列排队 → 下次被领出来,读回自己上次写的 Feed,接着想……如此往复,直到某一轮你调了 finish,才算走完。🧠 记忆口诀:拼提示 → 调模型 → 干活 → 记笔记 → 排回队(想·调·干·记·排)。
执行工具与观察(闭环)
ToolOutputHandler.handle(agent/output_handler.py:33)执行工具并形成观察闭环:
# 45: handle_tool_response 真正执行工具(ToolExecutor.execute)
# 48-61: 把 assistant 回复 + 工具结果都写进 AgentExecutionFeed(role=assistant/system)
# 65: add_text_to_memory 额外写入向量库(长期记忆,Day 13)
build_agent_messages 会把这些 Feed 读回来、拼进 LLM 的对话上下文——于是 LLM 看到"我上一步调了什么工具、得到什么结果",据此想下一步。这就是 observe → 下一次 think 的闭环——通过 Feed 落库再读回实现,而非内存变量。ToolExecutor.execute(tool_executor.py:18)里:工具名是 finish → 返回 COMPLETE(智能体自己宣布结束);未知工具 → 返回错误让 LLM 改正(自我纠错)。📓 生活类比:Feed 就像实习生的工作笔记本——每做完一件事记一笔("查了天气,晴 25℃"),下次接着干前先翻笔记才知道上次干到哪。结果不是记在脑子(内存)里、而是写进笔记本(数据库),所以换个人(重启 / 换 worker)接手也不会断。
循环从哪来
迭代步执行完,决定"下一步去哪"(agent_iteration_step_handler.py:101)。关键:内层迭代步的 next_step_id 常常指回自己(如 workflow_seed.py:188 的 add_next_workflow_step(step1.id, step1.id))。
next_step_id 设成自己 → Celery 重排后又进同一个迭代步 → 再 think-act-observe 一轮 → 又指回自己……形成 think→act→observe 的反复循环,直到 LLM 调用 finish 工具或超迭代上限。所以"循环"= 迭代步的 next_step 指向自身 + Celery 反复重排。这就是"循环在数据(next_step_id)和队列里"的具体体现(Day 02 的啊哈点)。while True: 在某个函数里一圈圈转。其实没有——每次只跑一个迭代步就返回,"下一轮"靠 next_step_id 指回自己 + Celery 重新排队触发。循环是"跨进程、可中断、可续跑"的,而不是一个卡在内存里的死循环——所以服务重启后任务还能接着跑。结束条件
- LLM 调 finish 工具:智能体判断目标达成,调用内置
finish→ 返回 COMPLETE。 - 任务队列空:任务分解型工作流,所有任务处理完。
- 走到 COMPLETE 边:工作流走到
step_id=-1。 - max_iterations 熔断:超上限强制停(Day 02)。
- 等待人工:遇到 WAITING_FOR_PERMISSION 挂起(Day 07/受限模式)。
今日小结 + 动手
🧠 今天你应该能回答
- 两级 step(外层工作流步 vs 内层迭代步)的区别?
- TOOL 步和 ITERATION_WORKFLOW 步的"选工具"方式区别?
- 迭代步的 think→act→observe 具体几步?
- "观察"怎么通过 Feed 喂回下一轮?
- 循环怎么来的?(next_step 指回自己 + Celery 重排)结束条件?
✋ 动手
P=superagi
sed -n '39,127p' $P/jobs/agent_executor.py | head -50 # 单步执行器 + 分派
sed -n '46,121p' $P/agent/agent_iteration_step_handler.py | head -50 # 迭代步循环
sed -n '33,66p' $P/agent/output_handler.py # 执行工具+观察闭环
sed -n '18,65p' $P/agent/tool_executor.py # finish/未知工具
fetch_next_step 怎么用"结果字符串"路由、TOOL 步怎么让 LLM 当路由器。