不管哪种架构,Agent 主循环的数学本质只有一个:
ReAct(Reasoning + Acting)是出现频率最高的主循环模式。核心思路:每一步都让 LLM 先推理、再决定行动、观察结果、继续推理。
| 项目 | 循环实现 | 终止条件 | 特色 |
|---|---|---|---|
| OpenHands | Conversation.run() → Agent.step() |
FinishTool / max 500 iter / StuckDetector | Event Stream + Condenser 压缩 + ParallelToolExecutor |
| SWE-agent | DefaultAgent.step() → forward_with_handling() |
StepOutput.done=True / total_execution_timeout |
8 种解析器 + 错误重查询(FormatError/BlocklistError 重试) |
| Aider | Coder.run_one() → send_message() + Reflection Loop |
无 reflection message / max_reflections | Reflection Loop(lint/test 失败自动重试)+ 15 种编辑格式 |
| Codex CLI | 内部 Agent loop + 三级审批 | 任务完成 / 用户取消 | Auto-approve / Suggest / Full-confirm 三级安全模式 |
| Agent Zero | 嵌套 ReAct(主 Agent + 子 Agent) | Agent 自行判断完成 | 层次化记忆 + 自发学习循环 |
| Goose | Session-based ReAct loop | 任务完成 / 用户中断 | Provider 抽象层 + Session 状态恢复 |
| Browser Use | ReAct over browser tabs | LLM 判断完成 / max_steps | Tab 管理 + 页面快照 + DOM 选择器 |
| GPT Researcher | Research + Report 双阶段 ReAct | 信息充足度评估 | Scraping + VectorStore + 评估器决定是否继续搜索 |
| Hermes | ReAct + Background Review Fork | 工具返回完成标志 | 自进化:后台 fork 审查自身行为并优化提示词 |
OpenHands 在标准 ReAct 基础上做了两个关键创新:
所有交互(LLM 调用、工具执行、用户消息)都记录为不可变 Event 对象,追加到 append-only 事件流。这意味着:
# 事件层级
Event (frozen Pydantic, append-only)
├─ LLMConvertibleEvent → 可转为 LLM Message
│ ├─ ActionEvent # Agent 工具调用
│ ├─ ObservationEvent # 工具返回结果
│ ├─ MessageEvent # 用户/Agent 文本消息
│ └─ SystemPromptEvent # 初始系统提示词
├─ Condensation # 上下文压缩事件
├─ CondensationRequest # 请求压缩(超出窗口时触发)
├─ TokenEvent # 流式 token
└─ AgentErrorEvent # 错误
事件流会无限增长,但 LLM 上下文窗口有限。Condenser 在 prepare_llm_messages() 时压缩历史:
class LLMSummarizingCondenser(Condenser):
"""将早期事件总结为一条摘要,保留近期事件不变"""
def condense(self, events: list[Event]) -> list[Event]:
if token_count(events) <= max_size:
return events
# 1. 保留最近 N 个事件
recent = events[-keep_recent:]
# 2. 对早期事件做 LLM 摘要
summary = self.llm.summarize(events[:-keep_recent])
# 3. 返回 [Condensation(summary), ...recent]
return [Condensation(content=summary)] + recent
SWE-agent 的 ReAct 循环有独特的 错误重查询 机制——当 LLM 输出格式错误或触发 blocklist 时,不是直接失败,而是将错误信息注入历史重新查询:
# forward_with_handling 伪代码
def forward_with_handling(self, history):
n_format_fails = 0
while n_format_fails < self.max_requeries:
try:
return self.forward(history) # 正常 ReAct 步
except FormatError as e:
n_format_fails += 1
history = requery_history(e) # 把错误加入历史重问
except BlocklistError as e:
history = requery_history(e) # 被禁命令也重问
except BashSyntaxError as e:
history = requery_history(e) # Bash 语法错误也重问
except AgentGaveUpError:
return attempt_autosubmission() # 放弃但尝试提交
str_replace_editor 命令比裸 bash sed 更适合 LLM 操作文件。你的 Agent 不应该像人类一样操作电脑,它应该有自己专用的交互方式。
Aider 在标准 ReAct 之外增加了 Reflection Loop——每次编辑后自动检查 lint 和测试,失败则自我修正:
# Aider run_one 伪代码
def run_one(self, user_message):
message = user_message
while message:
self.reflected_message = None
self.send_message(message) # 标准 ReAct 步
if not self.reflected_message:
break # 无问题,完成
if self.num_reflections >= self.max_reflections:
break # 超过最大反思次数
self.num_reflections += 1
message = self.reflected_message # 用反思消息继续循环
reflected_message 可能来自:
Plan-Execute 模式先让 LLM 规划完整步骤列表,再逐步执行。优势是能做出全局最优决策,劣势是计划可能过时。
AutoGPT 采用了 Block 流式架构,将 Plan-Execute 推向了极致——每个步骤是一个 Block,Block 之间通过 DAG 图连接:
# Block 是执行的最小单元
class Block:
id: str
input_schema: dict # 输入数据定义
output_schema: dict # 输出数据定义
def execute(self, input_data) -> dict:
"""执行 Block,返回输出数据"""
# 关键 Block 类型:
OrchestratorBlock # ReAct 循环:LLM → 工具调用 → 观察 → 继续
AutoPilotBlock # 嵌套子代理:创建子 Agent 执行子任务
LLMBlock # 纯 LLM 调用(不执行工具)
CodeBlock # 代码执行
ForLoopBlock # 循环控制
IfElseBlock # 条件分支
AutoGPT 的 OrchestratorBlock 本质上是一个 ReAct 循环,但嵌在 DAG 图执行引擎中。这意味着你可以把 ReAct 和 Plan-Execute 混合使用——这正是混合架构的趋势。
GPT Researcher 的 Plan-Execute 更加显式:
State Machine(状态机)模式用显式的图定义控制流:每个状态是一个节点,状态之间的转移是边。LangGraph 是这一模式的集大成者。
LangGraph 不仅仅是一个状态机——它的执行引擎基于 Google Pregel 论文的 BSP(Bulk Synchronous Parallel) 超步模型:
# 超步循环核心
while there_are_nodes_to_run:
# 1. READ: 从 Channel 读取
for node in nodes_to_run:
node.read_channels(state)
# 2. COMPUTE: 并行执行(asyncio.gather)
results = await gather(*[node.execute() for node in nodes_to_run])
# 3. WRITE: 写入 Channel(Reducer 合并)
for result in results:
for key, value in result.items():
channel[key] = reducer(key, value) # Reducer 决定合并策略
# 4. CHECKPOINT: 保存快照
await checkpointer.save(state, config)
# 5. 确定下一轮要执行的节点(条件边路由)
nodes_to_run = determine_next_nodes()
add_messages 追加、operator.add 求和、无 reducer 则覆盖)。没有这个机制,并行写入会导致不可预测的竞态条件。
from langgraph.graph import StateGraph, START, END
from typing import Annotated
class State(TypedDict):
messages: Annotated[list, add_messages] # Reducer: 追加消息
next_action: str
graph = StateGraph(State)
# 添加节点
graph.add_node("analyze", analyze_fn)
graph.add_node("search", search_fn)
graph.add_node("respond", respond_fn)
# 添加边
graph.add_edge(START, "analyze")
graph.add_conditional_edges("analyze", route_fn, { # 条件路由
"search": "search",
"respond": "respond",
})
graph.add_edge("search", "analyze") # 搜索后回到分析
graph.add_edge("respond", END)
# 编译(加入 Checkpoint、中断点等)
app = graph.compile(
checkpointer=SqliteSaver("checkpoints.db"),
interrupt_before=["respond"], # 人机协作中断点
)
LangGraph 是唯一原生支持 Time Travel 的框架:
interrupt_before + resume 提供了最优雅的中断-恢复机制。
PydanticAI 也提供了状态机模式,但更轻量:
# PydanticAI GraphAgent
class MyGraph(GraphAgent):
node_a = Node(handler=analyze, next="node_b")
node_b = Node(handler=execute, next=END_or_loop)
与 LangGraph 的区别:PydanticAI 没有 Pregel 并行模型,没有 Checkpoint,但集成度更高(类型安全 + 依赖注入 + result_type)。
Actor Model 把每个 Agent 视为一个独立的 Actor,通过消息传递通信。AutoGen 和 MetaGPT 是这一模式的代表。
AutoGen 的精妙之处在于双层解耦:
autogen-core):Actor Model 运行时,完全通用autogen-agentchat):在 Core 之上构建 Chat Agent 抽象# 每个 Agent 是一个 Actor
class Agent(Protocol):
@property
def metadata(self) -> AgentMetadata: ...
async def on_message(self, message, ctx) -> Response:
"""收到消息时的处理逻辑"""
# 两种通信
result = await runtime.send_message(msg, recipient=AgentId("agent_b"))
runtime.publish_message(event, topic_id=TopicId("news"))
Actor Model 的真正威力在于群组编排——多个 Agent 如何协作完成同一个任务:
| 编排模式 | 原理 | 适用场景 |
|---|---|---|
RoundRobinGroupChat |
Agent 轮流发言 | 简单讨论、头脑风暴 |
SelectorGroupChat |
LLM 选择下一个发言者 | 需要智能路由的任务 |
Swarm |
Handoff 机制:Agent 自主转移控制权 | 多专业技能协作 |
DiGraphGroupChat |
DAG 图定义执行流程 | 有明确依赖关系的流水线 |
MagenticOne |
Ledger 编排:协调者维护任务清单 | 复杂多步任务 |
MetaGPT 用 SOP(Standard Operating Procedure) 模式——模拟软件团队的标准化流程:
class Role:
"""每个 Role 监听特定消息,发布处理结果"""
def _watch(self, actions: list[Type[Action]]):
"""订阅特定 Action 的输出"""
self._watching = set(actions)
async def _observe(self) -> int:
"""从 MessageQueue 取出自己关心的消息"""
news = self.rc.msg_queue.pop()
return len(news)
async def run(self):
"""Role 主循环:Observe → Think → Act → Publish"""
await self._observe() # 1. 取消息
reaction = await self._react() # 2. 思考+行动
await self._publish(reaction) # 3. 发布结果
典型的 SOP 流水线:ProductManager 写 PRD → Architect 写设计 → ProjectManager 拆任务 → Engineer 写代码 → QaEngineer 测试。每个 Role 通过 _watch 订阅上游的输出,实现松耦合的流水线。
CrewAI 的模式介于 AutoGen 和 MetaGPT 之间:
Process.sequential — 类似 MetaGPT 的流水线Process.hierarchical — Manager Agent 分配任务给其他 AgentFlow 装饰器 — 更灵活的编排(@start、@listen、@router)2024-2025 年的 Agent 架构趋势:ReAct 内核 + 外层编排。单一模式已经不够用,混合架构成为主流。
| 项目 | 外层编排 | 内层循环 | 混合方式 |
|---|---|---|---|
| AutoGPT | DAG 图 + Block 流式 | OrchestratorBlock 内部 ReAct | Block 是 DAG 节点,OrchestratorBlock 自身是 ReAct |
| AutoGen | Actor Model Runtime | 每个 Agent 内部 ReAct | Agent 是 Actor,通过 Handoff 转移控制权 |
| MetaGPT | SOP Watch-Publish | Role._react() 内部 ReAct | Watch-Publish 流水线,每个 Role 内部 ReAct |
| CrewAI | Process Sequential/Hierarchical | Agent 内部 ReAct | 外层 Process 编排,内层 Agent 自主 |
| LangGraph | Pregel 超步图执行 | 每个 Node 可以是 ReAct Agent | Node 内部嵌套子图(子 ReAct 循环) |
| Aider | ArchitectCoder 两阶段 | Architect 思考 + Editor 编辑 | 双模型分离:强模型推理 + 经济模型执行 |
| SWE-agent | RetryAgent 多尝试 | DefaultAgent 内部 ReAct | 外层多尝试 + Reviewer 评分,内层标准 ReAct |
事件驱动不是一种主循环模式,而是任何主循环都应采用的实现方式。OpenHands 和 OpenClaw 是最佳实践。
OpenClaw 的整个 Gateway 是事件驱动的——每个 session 是一个事件流:
user_message 事件thinking / text / tool_call 事件tool_result 事件不管哪种循环模式,内部都有 6 个必须正确实现的环节:
prepare_llm_messages() + Condenser,SWE-agent 用 HistoryProcessor 链(Cache + LastN + ClosedWindow + RemoveRegex)。Agent 循环最大的运行时风险是卡死——Agent 在同一个错误上反复尝试,永远无法逃脱。只有 OpenHands 实现了完整的卡死检测系统。
class StuckDetector:
"""5 种卡死模式检测"""
def is_stuck(self, events: list[Event]) -> bool:
# 1. Action-Observation Loop:同一动作重复 N 次
# 例:反复搜索同一个文件,结果相同
if self._detect_action_observation_loop(events):
return True
# 2. Action-Error Loop:同一失败动作重复 N 次
# 例:反复尝试同一个语法错误的命令
if self._detect_action_error_loop(events):
return True
# 3. Monologue:Agent 自说自话,不调用任何工具
if self._detect_monologue(events):
return True
# 4. Alternating Pattern:两个动作来回切换
# 例:搜索 → 编辑 → 搜索 → 编辑(编辑总被撤销)
if self._detect_alternating_pattern(events):
return True
# 5. 上下文膨胀:Condensation 请求过于频繁
if self._detect_excessive_condensation(events):
return True
return False
| 项目 | 卡死处理 | 详细 |
|---|---|---|
| SWE-agent | total_execution_timeout + 自动提交 | 超时后 attempt_autosubmission(),尝试提取当前 patch |
| Aider | max_reflections 限制 | Reflection Loop 最多 N 轮,超过则强制结束 |
| AutoGPT | max_iterations | 硬性上限,到达后停止 |
| LangGraph | recursion_limit | 图的最大递归深度(默认 25),超过抛异常 |
| AutoGen | TerminationCondition | 可组合的终止条件(MaxMessageCount、TextMention、TokenUsage) |
当 LLM 返回多个工具调用时(Parallel Function Calling),如何并行执行?核心问题是原子性——多个工具调用应该全部成功还是允许部分失败?
@dataclass(frozen=True, slots=True)
class _ActionBatch:
action_events: list[ActionEvent]
has_finish: bool
blocked_reasons: dict[str, str] # Hook 拦截的动作
results_by_id: dict[str, list[Event]]
@classmethod
def prepare(cls, action_events, state, executor, tool_runner, tools):
# 1. 如果包含 FinishTool,截断后续调用
# 2. 分区:blocked(Hook 拦截)vs executable
# 3. ParallelToolExecutor 批量执行
# - 声明相同资源的工具串行执行
# - 不同资源的工具并行执行
# 4. 返回不可变批次结果
关键:ParallelToolExecutor 使用资源键锁机制——工具通过 declared_resources() 声明它要访问的资源(如文件、URL),声明相同资源的工具串行执行,不同资源的并行执行。
LangGraph 的并行更根本——在 Pregel 超步模型中,所有节点天然并行执行,Reducer 保证写入合并的正确性:
class State(TypedDict):
messages: Annotated[list, add_messages] # 并行写入 → 追加合并
score: Annotated[int, operator.add] # 并行写入 → 求和
| 策略 | 代表项目 | 优势 | 劣势 |
|---|---|---|---|
| 资源键锁并行 | OpenHands | 细粒度控制,避免竞态 | 工具必须声明资源依赖 |
| Pregel + Reducer | LangGraph | 形式化正确性保证 | 仅限 State Machine 模式 |
| 串行执行 | 大多数项目 | 简单,无竞态 | 性能差 |
| 项目 | 主循环 | 编排方式 | 终止条件 | 卡死检测 | 并行执行 | 特色 |
|---|---|---|---|---|---|---|
| OpenHands | ReAct | Event Stream | FinishTool / 500 iter / StuckDetector | 5 种检测 | 资源键锁 | Condenser + CriticMixin |
| LangGraph | State Machine | Pregel BSP | END 节点 / recursion_limit | recursion_limit | Pregel + Reducer | Checkpoint / Time Travel |
| Aider | ReAct + Reflection | Strategy Pattern | 无 reflection / max_reflections | max_reflections | 无 | ArchitectCoder 双模型 |
| AutoGen | Actor Model | 5 种群组编排 | TerminationCondition 组合 | MaxMessageCount | Actor 天然并行 | Handoff + SocietyOfMind |
| AutoGPT | DAG + ReAct | Block 流式 + DAG 图 | max_iterations | 硬性上限 | DAG 并行 | AutoPilotBlock 子代理 |
| MetaGPT | Actor + SOP | Watch-Publish | SOP 流程完成 | 无 | Role 天然并行 | ActionNode Review-Revise |
| CrewAI | Process-driven | Sequential / Hierarchical / Flow | 任务完成 | 无 | Flow 并行 | Flow 装饰器 |
| SWE-agent | ReAct + Retry | RetryAgent 包装 | done=True / total_execution_timeout | 超时自动提交 | 无 | ACI + 8 种解析器 |
| Agent Zero | 嵌套 ReAct | 主 Agent → 子 Agent | Agent 自行判断 | 无 | 子 Agent 并行 | 层次化记忆 |
| GPT Researcher | Plan-Execute | Research + Report | 信息充足度 | 无 | 搜索并行 | VectorStore + 评估器 |
| Browser Use | ReAct | Tab 管理 | LLM 判断 / max_steps | max_steps | 多 Tab 并行 | DOM 选择器 |
| Hermes | ReAct + 自进化 | 76 工具 + Toolset | 工具返回完成 | 无 | 无 | Background Review Fork |
| OpenClaw | Event-driven ReAct | Session + Channel | 完成 / 用户终止 | 超时保护 | Policy Pipeline | Cache Boundary + 14 平台 |
| Codex CLI | ReAct | 三级审批 | 任务完成 / 用户取消 | 无 | 无 | Auto-approve/Suggest/Confirm |
| Smolagents | ReAct | CodeAgent / ToolCallingAgent | Final Answer | 无 | 无 | 极简实现 |
| CAMEL | Role-playing | Role-playing + Knowledge Graph | 对话轮数 / 任务完成 | 无 | 无 | Inception Prompting |
| Google ADK | ReAct | Runner + Session | Agent 自行判断 | 无 | 无 | A2A 协议 |
| PydanticAI | ReAct / Graph | Agent + GraphAgent | result_type 返回 | 无 | 无 | 类型安全 + 依赖注入 |
| Goose | ReAct | Session-based | 任务完成 | 无 | 无 | Provider 抽象 |
| Agno | ReAct | Agent + Toolkit + Session | Agent 自行判断 | 无 | 无 | 极简 API |
不管选择哪种模式,一个完整的主循环需要覆盖以下所有项:
run() / step() / execute())