Coding Agent Loop 机制分类全览
基于 awesome-cli-coding-agents 完整列表,以 learn-claude-code 伪代码风格呈现 聚焦:loop 的驱动方式、终止条件、核心机制差异
分类地图
┌─────────────────────────────────────────────────────────────────────────┐
│ Type 1 · Tool-Use Loop 最主流范式 │
│ Claude Code / OpenCode / Kode / Goose / Gemini CLI / Codex / Qwen... │
├─────────────────────────────────────────────────────────────────────────┤
│ Type 2 · CodeAct Loop 代码即 action,不用 tool_use JSON │
│ OpenHands / CodeActAgent │
├─────────────────────────────────────────────────────────────────────────┤
│ Type 3 · Edit-Patch Loop LLM 输出 diff 文本,框架解析 apply │
│ Aider │
├─────────────────────────────────────────────────────────────────────────┤
│ Type 4 · ACI Loop 为 LLM 定制的 shell 接口 │
│ SWE-agent / AutoCodeRover │
├─────────────────────────────────────────────────────────────────────────┤
│ Type 5 · Proactive Heartbeat 主动唤醒,不是被动响应 │
│ OpenClaw / NanoClaw / ZeroClaw / claw0 │
├─────────────────────────────────────────────────────────────────────────┤
│ Type 6 · Plan-First Loop 先生成计划,再逐步执行 │
│ Plandex / Devon │
├─────────────────────────────────────────────────────────────────────────┤
│ Type 7 · Graph Pipeline 多阶段有向图,职责严格分离 │
│ RA.Aid (LangGraph Research→Plan→Implement) │
├─────────────────────────────────────────────────────────────────────────┤
│ Type 8 · Agentless Pipeline 没有 loop,纯三段流水线 │
│ Agentless │
└─────────────────────────────────────────────────────────────────────────┘Type 1 · Tool-Use Loop(Claude Code 范式)
代表项目: Claude Code · OpenCode · Kode CLI · Goose · Gemini CLI · Codex CLI · Qwen Code · Kimi CLI · Mistral Vibe · Amazon Q · Cline · Roo Code · Kilo Code · Crush · gptme · ForgeCode · Dexto · CLAII · cursor-agent · 2501 CLI …
一句话: LLM 决定调用哪个工具,框架执行,结果追加到消息历史,循环直到 LLM 停止调用工具。 这是绝大多数项目采用的范式,差异在于工具集 + harness 机制的丰富程度。
# ── 最小核心(所有 Type 1 的共同骨架) ──────────────────────────────────
def agent_loop(user_message: str):
messages = [{"role": "user", "content": user_message}]
while True:
response = llm.create(messages=messages, tools=TOOLS)
if response.stop_reason != "tool_use":
return response.text # 任务完成,退出
for tool_call in response.tool_calls:
result = dispatch(tool_call) # 执行工具
messages.append(result) # 追加观察结果
# 循环,把结果交回给 LLM 继续判断
# ── Claude Code:在骨架上叠加的 harness 机制 ──────────────────────────────
# 真实路径:packages/claude-code/src/core/agent-loop.ts
class ClaudeCodeHarness:
"""
Claude Code = 最小 loop + 以下 harness 机制的叠加
每个机制都是独立的,loop 本身不变
"""
def agent_loop(self, user_message: str):
messages = self.session.load_history() # 恢复会话历史
messages.append(user_message)
while True:
# 【机制 1】context 压缩:防止超出 token 限制
if self.token_estimator.count(messages) > COMPACT_THRESHOLD:
messages = self.compactor.compress(messages)
# 把旧消息总结成摘要,保留关键信息
response = self.llm.create(
messages=messages,
tools=self.tool_registry.all(),
system=self.load_system_prompt() # 读取 CLAUDE.md
)
# 【机制 2】hooks:在工具执行前/后触发用户自定义脚本
for tool_call in response.tool_calls:
self.hooks.fire("PreToolUse", tool_call) # 可阻断
result = self.tool_registry.execute(tool_call)
self.hooks.fire("PostToolUse", tool_call, result)
messages.append(result)
# 【机制 3】subagent 派生:Task 工具触发子任务
if tool_call.name == "Task":
subtask_result = self.spawn_subagent(tool_call.input)
# 子 agent 有独立的 loop 和 context,完成后回报结果
if response.stop_reason != "tool_use":
self.hooks.fire("Stop")
return response.text
def load_system_prompt(self):
"""按优先级合并多个 CLAUDE.md 文件"""
return merge([
read("~/.claude/CLAUDE.md"), # 全局个人配置
read(".claude/CLAUDE.md"), # 项目级配置
*self.skills.load_relevant() # 按需加载的技能文档
])
# ── Kode CLI:在 Tool-Use Loop 上加 SDK 层(TypeScript / Bun) ────────────
# 真实路径:packages/sdk/src/agent.ts
# 来源:shareAI-lab/Kode-Agent shareAI-lab/kode-agent-sdk
class KodeAgent:
"""
Kode 的核心创新:在 tool_use loop 之外引入
三通道事件系统 + 七阶段 checkpoint(可恢复)
适合需要嵌入到后端/浏览器扩展/IoT 设备的场景
"""
# 三通道事件系统(关注点分离)
CHANNELS = {
"progress": "text_chunk, tool_use, tool_result", # 实时输出
"control": "pause, resume, abort", # 控制指令
"monitor": "cost, latency, checkpoint", # 可观测性
}
# 七阶段 checkpoint(Safe-Fork-Point = 安全恢复点)
CHECKPOINTS = [
"INIT", # 初始化
"CONTEXT_READY", # 上下文加载完毕
"TOOL_CALLED", # 工具调用发出 ← Safe-Fork-Point
"TOOL_DONE", # 工具执行完毕 ← Safe-Fork-Point
"LLM_CALLED", # LLM 请求发出
"LLM_DONE", # LLM 回复完整
"COMMITTED", # 状态持久化完毕 ← Safe-Fork-Point
]
async def run(self, task: str):
state = await self.store.restore_or_init(task) # 从 checkpoint 恢复
while True:
# 每个关键阶段都写 checkpoint,崩溃后可从最近 Safe-Fork-Point 续跑
await self.checkpoint("CONTEXT_READY", state)
response = await self.provider.create(state.messages, self.tools)
await self.checkpoint("LLM_DONE", state)
if response.stop_reason != "tool_use":
break
for tool_call in response.tool_calls:
await self.checkpoint("TOOL_CALLED", state)
result = await self.tools.execute(tool_call)
await self.checkpoint("TOOL_DONE", state)
# 向所有 subscriber 广播(前端/监控/日志)
await self.emit("progress", {"type": "tool_result", "data": result})
state.messages.append(result)
await self.checkpoint("COMMITTED", state)
# 支持 @mention 多模型路由(Kode 独有)
# @ask-claude-sonnet-4 → 路由到 Anthropic
# @ask-gpt-5 → 路由到 OpenAI
# @run-agent-architect → 派生专用 subagent
# Kode vs Claude Code 的核心差异
# Claude Code SDK:每个并发用户 = 一个独立的 CLI 进程(进程开销大)
# Kode SDK:无进程开销,可嵌入任意 JS runtime,单进程多租户Type 2 · CodeAct Loop(OpenHands 范式)
代表项目: OpenHands / CodeActAgent / OpenDevin
一句话: 不给 LLM 20 个 JSON tool schema,而是给它一个 bash + Python + 浏览器, 让它用代码本身表达任何 action。代码就是工具。
# openhands/controller/agent_controller.py ← 真实路径
# openhands/agenthub/codeact_agent/codeact_agent.py
class AgentController:
"""
核心设计:agent 是一个纯函数 f(event_history) → next_action
所有历史(对话、代码执行结果、浏览器截图)都在 EventStream 里
"""
async def run(self, task: str):
# EventStream = 追加日志,不可变,是整个 session 的唯一事实来源
self.event_stream.append(MessageAction(content=task))
while True:
# agent.step() 读取全部历史,输出下一个 action
action = self.agent.step(self.state)
# action 类型:
# CmdRunAction → 执行 bash 命令
# IPythonRunCellAction → 执行 Python(Jupyter kernel)
# BrowserAction → 点击/导航/截图
# AgentDelegateAction → 派生专用 sub-agent
# AgentFinishAction → 任务完成
if isinstance(action, AgentFinishAction):
break
# 在 Docker 沙箱里执行 action,得到 observation
observation = await self.runtime.execute(action)
# observation 类型:
# CmdOutputObservation → stdout + exit_code
# IPythonRunCellObservation → Python 执行结果
# BrowserOutputObservation → 页面截图 + DOM
# ErrorObservation → 错误信息
self.event_stream.append(action)
self.event_stream.append(observation)
# 下一轮 agent.step() 会读到这次的 action + observation
class CodeActAgent:
"""
关键论文洞见(Wang et al. 2024):
代码是比 JSON schema 更好的通用 action 空间。
- 可以表达条件逻辑、循环、复杂数据处理
- LLM 写代码的能力比遵循特定 JSON schema 强得多
- 解析错误率更低(代码有语法检查,JSON schema 没有)
"""
def step(self, state: State) -> Action:
# 把 EventStream 格式化成 LLM 能理解的对话
messages = self._build_messages(state.history)
# LLM 返回的是包含代码块的普通文本,不是 tool_use JSON
response = self.llm.completion(messages)
# 从文本中解析 action 类型和代码
# <execute_bash>
# ls -la src/
# </execute_bash>
# <execute_ipython>
# import pandas as pd
# df = pd.read_csv('data.csv')
# df.describe()
# </execute_ipython>
action = self._parse_action(response.content)
return action
# ── 与 Type 1 (tool_use) 的本质差别 ──────────────────────────────────────
# tool_use:
# LLM → {"name": "bash", "input": {"command": "ls -la"}}
# 框架解析 JSON,按 schema 校验,执行
#
# CodeAct:
# LLM → "<execute_bash>ls -la</execute_bash>"
# 框架解析 XML 标签,直接执行代码
# 优点:LLM 可以在代码里写循环、条件、复杂逻辑
# 缺点:依赖 LLM 是强代码生成器;弱模型容易越界Type 3 · Edit-Patch Loop(Aider 范式)
代表项目: Aider
一句话: 不用 tool_use,也不写可执行代码。让 LLM 在普通文本里直接写 diff, 框架解析 patch 格式后 apply 到磁盘。兼容任何 LLM,包括不支持 function calling 的模型。
# aider/coders/base_coder.py ← 真实路径
class BaseCoder:
"""
Aider 的根本选择:把 LLM 当"写 diff 的工具",而不是"调用工具的 agent"
好处:模型无关;坏处:格式解析脆弱(LLM 可能输出格式不对)
"""
def run(self):
while True:
user_message = self.io.get_input()
if not user_message:
break
# 【核心】构建上下文(这是 Aider 的核心竞争力 RepoMap)
context = self._build_context(user_message)
# 普通对话,不传 tools 参数
response = self.llm.chat(context)
# 【关键】从纯文本中解析编辑块
edits = self.get_edits(response)
#
# EditBlockCoder 期望的格式(默认,强模型用):
# <<<<<<< SEARCH
# def authenticate(user, pwd):
# return user == "admin" ← 原始代码(精确匹配)
# =======
# def authenticate(user, pwd):
# return check_hash(user, pwd) ← 新代码
# >>>>>>> REPLACE
#
# WholeFileCoder 期望的格式(弱模型用):
# 直接输出整个文件的新内容,框架做全文替换
#
# UnifiedDiffCoder 期望的格式(研究用):
# 标准 unified diff(+/-行)
# apply 到磁盘
edited_files = self.apply_edits(edits)
# 自动 git commit(Aider 的招牌功能)
if edited_files:
commit_msg = self.get_commit_msg(edits)
self.repo.commit(edited_files, message=commit_msg)
# commit 消息格式:
# "fix: authenticate with bcrypt hash comparison
#
# Co-authored-by: aider (GPT-4o)"
def _build_context(self, user_message: str):
"""
RepoMap:Aider 的核心差异化机制
用 tree-sitter 解析整个仓库,用 PageRank 找最重要的符号
效果:几 KB 的文本 = 整个大仓库的结构摘要
"""
repo_map = self.repomap.get_ranked_map(
chat_files=self.chat_files, # 当前对话文件
mentioned_fnames=self.mentioned_files # LLM 提到的文件
)
# repo_map 示例:
# src/auth.py:
# def authenticate(user, pwd)
# def hash_password(pwd) -> str
# class UserSession
return [system_prompt + diff_format_instructions, repo_map,
*self.chat_files_content, *self.history, user_message]
# ── Architect 模式:两模型协作(Aider 独有) ─────────────────────────────
class ArchitectCoder(BaseCoder):
"""
想清楚再动手:大模型规划,快模型实现。
适合复杂重构(不知道改哪几个文件时)。
"""
def send_message(self, user_message: str):
# Step 1:architect 模型只思考,不输出 diff
plan = self.architect_llm.chat(
user_message + "\n\n只描述修改方案,不要输出代码"
)
# Step 2:editor 模型按计划写 diff
diff_response = self.editor_llm.chat(
plan + "\n\n现在按上述方案输出 SEARCH/REPLACE 格式的代码修改"
)
edits = self.get_edits(diff_response)
self.apply_edits(edits)Type 4 · ACI Loop(SWE-agent 范式)
代表项目: SWE-agent · AutoCodeRover
一句话: 核心创新不是 loop 本身,而是为 LLM 设计的计算机接口(ACI)。 人类用 vim/grep,AI 用专门为它设计的命令集,在真实 shell 沙箱里运行。
# sweagent/agent/agents.py ← 真实路径(DefaultAgent)
# sweagent/environment/swe_env.py ← SWEEnv
class DefaultAgent:
"""
论文核心洞见(Yang et al. NeurIPS 2024):
语言模型是一种新型用户,它们需要为自己定制的软件接口,
而不是为人类设计的 vim/grep/less。
"""
def run(self, issue: GitHubIssue) -> Trajectory:
# 初始化:clone 仓库到沙箱,安装 ACI 工具集
self.env.setup(issue.repo_url)
self._trajectory = []
# 把 issue 描述作为初始 prompt
self.history = [self._build_issue_prompt(issue)]
while True:
# LLM 输出一个 ACI 命令(不是 JSON tool_use,是类 shell 文本)
response = self.model.query(self.history)
action_str = self._parse_action(response)
# 记录轨迹(用于研究和评估)
step = StepOutput(thought=response, action=action_str)
# 在真实 Docker 沙箱里执行
observation = self.env.communicate(action_str)
# observation = 真实 shell 输出,比如:
# "Found test_auth.py at src/tests/test_auth.py"
# "Traceback (most recent call last): ..."
# "All 12 tests passed."
step.observation = observation
self._trajectory.append(step)
# 把观察结果作为下一轮的输入
self.history.append({"role": "user", "content": observation})
# 检查:agent 调用 submit → 任务完成
if "submit" in action_str:
break
return Trajectory(steps=self._trajectory, patch=self.env.get_diff())
class ACIToolBundle:
"""
专为 LLM 设计的工具集 vs 为人类设计的 shell 工具:
人类工具 → LLM 使用困难的原因:
grep -r "pattern" . → 输出太多,LLM 不知道看哪里
vim +42 file.py → 交互式编辑器,LLM 无法使用
find . -name "*.py" → 路径太多,噪音大
ACI 工具 → 专为 LLM 优化:
"""
def find_file(self, filename: str) -> str:
"""返回精确路径列表,而不是递归输出"""
# 输出:["src/auth.py", "tests/test_auth.py"]
def open(self, path: str, line: int = 1) -> str:
"""在指定行附近显示内容,每行带编号,方便 LLM 引用行号"""
# 输出:
# [42] def authenticate(user, pwd):
# [43] return user == "admin" # TODO: use hash
# [44]
def edit(self, start_line: int, end_line: int, new_content: str) -> str:
"""按行号范围替换,不需要匹配原始文本(比 SEARCH/REPLACE 更可靠)"""
def search_file(self, query: str, file: str = None) -> str:
"""返回带上下文的匹配行(比 grep 友好得多)"""
# 输出:
# Line 42 in src/auth.py:
# def authenticate(user, pwd): ← match
# return user == "admin" ← context
def submit(self) -> str:
"""显式结束信号,生成 git diff 作为最终 patch 提交"""
class SWEEnv:
"""沙箱抽象:统一 Local / Docker / Modal / AWS Fargate 接口"""
def communicate(self, action: str) -> str:
"""用 shell sentinel 检测完成,不靠超时"""
sentinel = f"__DONE_{uuid4()}__"
self.shell.write(f"{action}\necho {sentinel}$?\n")
output = self.shell.read_until(sentinel)
return outputType 5 · Proactive Heartbeat Loop(OpenClaw 范式)
代表项目: OpenClaw · NanoClaw · ZeroClaw · claw0
一句话: 颠覆"你问我答"的交互模式。Harness 每隔 N 秒主动唤醒 agent, agent 检查有无待办,有则行动,无则休眠。agent 还能给自己安排未来任务(cron)。 这是从"工具"到"同事"的本质跨越。
# 来源:shareAI-lab/claw0, shareAI-lab/learn-claude-code s11
# 机制:Heartbeat(30s 唤醒)+ Cron(定时任务)+ IM 频道(多渠道输入)
HEARTBEAT_INTERVAL = 30 # 秒
class ProactiveHarness:
"""
Claude Code 模式:用户发消息 → agent 响应 → 会话结束 → 下次从空白开始
OpenClaw 模式:agent 是持续运行的后台进程,主动检查并完成任务
"""
def __init__(self):
self.agent = CoreAgent()
self.mailbox = MailBox() # 收集来自各渠道的消息(IM/webhook/cron)
self.cron = CronQueue() # agent 自己安排的未来任务
def start(self):
"""主循环:永不退出,定时唤醒"""
while True:
self._heartbeat_tick()
time.sleep(HEARTBEAT_INTERVAL)
def _heartbeat_tick(self):
"""每 30 秒触发一次:收集 → 决策 → 行动"""
# 1. 收集所有待处理的输入
inputs = []
inputs += self.mailbox.drain() # WhatsApp/Slack/Discord/微信 消息
inputs += self.cron.due_tasks() # 已到期的定时任务
inputs += self.channel.poll() # Webhook 事件
if not inputs:
return # 没事情,继续睡眠
# 2. 把所有 input 组织成 prompt,调用 agent loop
prompt = self._format_inputs(inputs)
result = self.agent.run(prompt)
# agent.run() 就是标准的 Type 1 tool_use loop
# 3. 发回响应
for input_item in inputs:
input_item.reply(result)
# ── Cron:agent 给自己安排未来任务 ─────────────────────────────────
# Agent 调用 schedule_task 工具时:
# {
# "name": "schedule_task",
# "input": {
# "task": "检查 CI/CD 是否成功,如果失败发通知",
# "cron": "*/5 * * * *" ← 每 5 分钟
# }
# }
# Harness 把这个任务存入 CronQueue,到期时注入 mailbox
# 效果:agent 可以自我调度,实现真正的自主性
# ── 与 Claude Code 的核心差异 ──────────────────────────────────────
# Claude Code:
# 会话驱动 — 用户启动 → agent 响应 → 会话结束
# 每次会话从空白开始(无状态)
#
# OpenClaw:
# 时间驱动 — harness 定时触发,不需要用户
# 持久记忆跨会话(向量存储 + 结构化记忆)
# 多渠道输入(Telegram/Slack/Discord/WhatsApp 统一入口)
# ── ZeroClaw:Rust 实现的轻量版(<5MB RAM) ──────────────────────────────
# 相同 heartbeat 逻辑,但:
# - trait-driven 架构(Provider/Tool/Memory/Channel 各自可替换)
# - 沙箱执行(不直接操作宿主文件系统)
# - 混合向量+关键词搜索(本地记忆检索)
# - 目标:运行在 $10 硬件上Type 6 · Plan-First Loop(Plandex 范式)
代表项目: Plandex · Devon
一句话: 先让 LLM 生成结构化的执行计划(改哪些文件、分几步、顺序如何), 人类确认后再进入执行循环。减少中途偏离,适合跨多文件的大功能开发。
# 来源:plandex-ai/plandex ← Go 实现
# 机制:Plan 阶段 → 确认 → Execute 阶段(两个独立循环)
class PlandexAgent:
"""
核心理念:先想清楚整个任务再动手。
这与 Claude Code"边想边做"的直觉式执行形成对比。
"""
def run(self, task: str):
# ── Phase 1: Plan(规划循环) ───────────────────────────────────
plan = self._build_plan(task)
# 展示计划,等待用户确认
print(plan.render())
# 输出示例:
# Plan: Add OAuth2 authentication
# ─────────────────────────────────
# Step 1: Create src/auth/oauth.go
# → Implement OAuth2 client with PKCE flow
# Step 2: Modify src/server/routes.go
# → Add /auth/callback and /auth/login routes
# Step 3: Create tests/auth_test.go
# → Unit tests for token exchange
# Step 4: Update docs/API.md
# → Document new auth endpoints
if not self.io.confirm("Execute this plan?"):
return
# ── Phase 2: Execute(执行循环) ────────────────────────────────
for step in plan.steps:
self._execute_step(step)
def _build_plan(self, task: str) -> Plan:
"""规划阶段:只读不写,用 2M token 上下文理解整个仓库"""
messages = [task]
while True:
# 规划阶段只允许只读工具
response = self.llm.create(
messages=messages,
tools=READ_ONLY_TOOLS, # read_file, list_dir, search_code
context_size="2M" # Plandex 的特色:超大 context window
)
if response.stop_reason != "tool_use":
# LLM 认为已经理解足够,输出计划
return Plan.parse(response.text)
# 继续读取文件理解上下文
for tool_call in response.tool_calls:
result = self.read_tools.execute(tool_call)
messages.append(result)
def _execute_step(self, step: PlanStep):
"""执行单个步骤:标准 tool_use loop,但只针对这一步"""
messages = [f"Execute step: {step.description}\n\nFile: {step.target_file}"]
while True:
response = self.llm.create(messages=messages, tools=ALL_TOOLS)
if response.stop_reason != "tool_use":
break
for tool_call in response.tool_calls:
result = self.tools.execute(tool_call)
messages.append(result)
# 每步完成后 git commit(原子性:每步独立可回滚)
self.repo.commit(f"step {step.index}: {step.description}")Type 7 · Graph Pipeline(RA.Aid 范式)
代表项目: RA.Aid
一句话: 不是一个 loop,而是三个职责严格分离的 agent 节点, 用 LangGraph 有向图串联。每段有自己的工具集和 system prompt,互不干扰。
# 来源:ai-christianson/RA.Aid
# 机制:LangGraph StateGraph,Research → Plan → Implement
# ── 全局状态:贯穿三阶段 ──────────────────────────────────────────────────
class PipelineState(TypedDict):
task: str
research: str # 研究阶段产出
plan: str # 规划阶段产出
impl_result: str # 实现阶段产出
human_notes: str # 可选:人类在审查节点插入的反馈
# ── 节点 1:Research Agent ─────────────────────────────────────────────────
def research_node(state):
"""只读,不写任何文件。目标:理解问题,收集上下文。"""
agent = create_react_agent(
llm=llm,
tools=[read_file, list_dir, search_code, grep, web_search],
# web_search 由 Tavily API 支持,agent 自主决定是否搜索
system_prompt="你是研究员。理解问题,找相关代码,不写任何文件。"
)
result = agent.invoke({"messages": [("user", state["task"])]})
return {**state, "research": extract_summary(result)}
# ── 节点 2:Plan Agent ─────────────────────────────────────────────────────
def plan_node(state):
"""基于研究结果制定详细计划,仍然不写代码。"""
agent = create_react_agent(
llm=llm,
tools=[read_file], # 只能读,不能写
system_prompt="你是架构师。按研究报告制定修改计划,列出文件和步骤,不写代码。"
)
context = f"研究结论:{state['research']}\n任务:{state['task']}"
result = agent.invoke({"messages": [("user", context)]})
return {**state, "plan": extract_plan(result)}
# ── 节点 3:Implement Agent ────────────────────────────────────────────────
def implement_node(state):
"""按计划执行,可选集成 Aider 作为子工具。"""
context = f"计划:{state['plan']}\n人类备注:{state.get('human_notes', '')}"
agent = create_react_agent(
llm=llm,
tools=[read_file, write_file, edit_file, bash, aider_run],
# aider_run:把 Aider 作为子工具调用(RA.Aid 的特色集成)
# 这样 Implement agent 可以用 Aider 的 RepoMap 能力
system_prompt="按计划严格执行,每步完成后运行测试验证。"
)
result = agent.invoke({"messages": [("user", context)]})
return {**state, "impl_result": extract_result(result)}
# ── 构建有向图 ──────────────────────────────────────────────────────────────
graph = StateGraph(PipelineState)
graph.add_node("research", research_node)
graph.add_node("plan", plan_node)
graph.add_node("implement", implement_node)
# 可选:在 Plan → Implement 之间插入人类审查节点
# graph.add_node("human_review", human_review_node)
# LangGraph interrupt() 机制:暂停,序列化状态,等待外部输入后恢复
graph.set_entry_point("research")
graph.add_edge("research", "plan")
graph.add_edge("plan", "implement")
graph.add_edge("implement", END)
pipeline = graph.compile(
checkpointer=SqliteSaver() # 状态持久化,支持从任意节点重跑
)
# 运行(thread_id 支持暂停和恢复)
result = pipeline.invoke(
{"task": "修复登录模块的 SQL 注入漏洞"},
config={"configurable": {"thread_id": "session-001"}}
)Type 8 · Agentless Pipeline(无 Loop)
代表项目: Agentless
一句话: 没有 agent loop,没有持续运行的 agent。 三步纯流水线:Localize → Edit → Validate,每步是一次独立的 LLM 调用。 反直觉的发现:在 SWE-bench 上,去掉 loop 之后效果反而不差。
# 来源:OpenAutoCoder/Agentless ← 论文实现
# 关键论点:"持续运行的 agent loop 并非必须"
class AgentlessPipeline:
"""
与所有其他范式的根本不同:
这里没有 while loop,没有持续的 agent 状态,
每步都是独立的、幂等的 LLM 调用。
"""
def run(self, issue: GitHubIssue) -> Patch:
# ── Step 1: Localize(定位) ─────────────────────────────────────
# 三层定位,从粗到细
suspicious_files = self._localize_files(issue)
# LLM 调用 1:读 issue + 仓库结构 → 输出可能相关的文件列表
suspicious_functions = self._localize_functions(issue, suspicious_files)
# LLM 调用 2:读相关文件 → 输出可能有问题的函数列表
edit_locations = self._localize_lines(issue, suspicious_functions)
# LLM 调用 3:读具体函数 → 输出需要修改的行号范围
# ── Step 2: Edit(生成 patch) ────────────────────────────────────
# 对每个定位出的位置,独立生成修复 patch
candidates = []
for location in edit_locations:
patch = self._generate_patch(issue, location)
# LLM 调用 N:根据 issue + 具体位置 → 生成 unified diff
candidates.append(patch)
# 生成多个候选,后面选最好的
# ── Step 3: Validate(验证并筛选) ───────────────────────────────
best_patch = self._select_best(candidates)
# 运行测试套件,选通过率最高的 patch
# 如果都不通过,可以重新生成(但这不是 loop,是重试)
return best_patch
# 与 agent loop 的本质区别:
# agent loop:LLM 在运行时动态决定下一步做什么
# Agentless:所有步骤在设计时就固定了,LLM 只填充每步的内容
#
# 实验结论(论文):
# 在 SWE-bench 上 Agentless 与有 loop 的 agent 相比并不弱,
# 原因:减少了 agent 在 loop 中"走弯路"的概率分类汇总对比
┌──────┬────────────────┬──────────────────────────┬──────────────────────────────────┐
│ Type │ 代表项目 │ Loop 驱动方式 │ 核心机制 │
├──────┼────────────────┼──────────────────────────┼──────────────────────────────────┤
│ 1 │ Claude Code │ LLM 决定调用工具 │ tool_use JSON + harness 叠加 │
│ │ Kode / OpenCode│ │ (hooks/checkpoint/subagent) │
│ │ Goose 等大多数 │ │ │
├──────┼────────────────┼──────────────────────────┼──────────────────────────────────┤
│ 2 │ OpenHands │ LLM 输出代码作为 action │ EventStream + Docker 沙箱 │
│ │ CodeActAgent │ │ 代码即工具,无 JSON schema │
├──────┼────────────────┼──────────────────────────┼──────────────────────────────────┤
│ 3 │ Aider │ LLM 输出 diff 文本 │ RepoMap + 多种 patch 格式 │
│ │ │ │ Architect 双模型协作模式 │
├──────┼────────────────┼──────────────────────────┼──────────────────────────────────┤
│ 4 │ SWE-agent │ LLM 输出 ACI 命令 │ 专为 LLM 设计的 shell 工具集 │
│ │ │ │ 轨迹录制 + shell sentinel │
├──────┼────────────────┼──────────────────────────┼──────────────────────────────────┤
│ 5 │ OpenClaw │ Harness 定时主动唤醒 │ Heartbeat + Cron + 多渠道 IM │
│ │ ZeroClaw │ │ 持久记忆,从"工具"变"同事" │
├──────┼────────────────┼──────────────────────────┼──────────────────────────────────┤
│ 6 │ Plandex │ 先 Plan 后 Execute │ 规划循环(只读)+ 执行循环 │
│ │ │ │ 每步原子 commit,2M 上下文 │
├──────┼────────────────┼──────────────────────────┼──────────────────────────────────┤
│ 7 │ RA.Aid │ LangGraph 有向图驱动 │ 三段职责分离 + human-in-loop │
│ │ │ │ 状态可序列化,可从任意节点重跑 │
├──────┼────────────────┼──────────────────────────┼──────────────────────────────────┤
│ 8 │ Agentless │ 无 loop,固定三步流水线 │ Localize → Edit → Validate │
│ │ │ │ 幂等,可并行,去掉"走弯路"概率 │
└──────┴────────────────┴──────────────────────────┴──────────────────────────────────┘学习路径建议
入门(理解基础范式)
→ learn-claude-code s01-s04 — 从零构建 Type 1 loop
→ mini-claude-code v0→v4 — 16行→550行,逐步叠加机制
深入(读各范式核心文件)
→ Trae Agent: trae_agent/agent/trae_agent.py — 最干净的 Type 1 实现
→ Aider: aider/coders/base_coder.py — Type 3,理解 RepoMap
→ SWE-agent: sweagent/agent/agents.py — Type 4,理解 ACI
→ OpenHands: openhands/controller/ — Type 2,理解 CodeAct
→ mini-SWE-agent(100行版本) — SWE-agent 核心提炼
进阶(harness 机制)
→ learn-claude-code s05-s12 — skill/context压缩/subagent/团队/worktree
→ kode-agent-sdk — Type 1 + checkpoint + 事件系统的工程实现
→ claw0 — Type 5 Heartbeat/Cron 的最小实现