← 返回

Agent Loop 深度调研(二):主流框架实操拆解

本文逐一拆解四大主流 Agent Loop 框架的实现细节。每个框架都从执行循环、工具调用、上下文管理、多 Agent 协作四个维度分析,并附可运行代码。

一、Claude Code:工业级 Agent Loop 标杆

Claude Code 是当前最成熟的 Agent Loop 实现之一。其源码虽未公开,但 VILA-Lab 的 Dive-into-Claude-Code 项目对其做了完整逆向分析,Anthropic 也发布了 Agent SDK 提供编程接口。

1.1 执行循环核心

Claude Code 的 Agent Loop 实现为一个 AsyncGenerator,yield 出流式事件。核心伪代码:

async def agent_loop(messages, tools, config):
    turn = 0
    while True:
        # 1. 解析配置
        resolved_config = resolve_config(config)
        
        # 2. 初始化轮次状态
        state = init_turn_state(turn)
        
        # 3. 组装上下文(9 个有序来源)
        context = assemble_context(
            system_prompt,
            memory_files,      # CLAUDE.md / AGENTS.md
            project_config,
            tool_definitions,
            conversation_history,
            # ... 更多来源
        )
        
        # 4. 应用五级压缩(最廉价优先)
        context = apply_compaction_shapers(context, [
            budget_reduction,
            snipping,
            micro_compaction,
            context_collapsing,
            auto_compaction,     # 最后手段:调用模型做全量摘要
        ])
        
        # 5. 调用模型
        response = await call_model(context)
        
        # 6. 分派工具调用
        tool_calls = extract_tool_calls(response)
        
        if not tool_calls:
            # 终止条件 1:无工具调用
            yield FinalResult(response.text)
            break
        
        # 7. 权限门控
        approved = await permission_gate(tool_calls, config.permission_mode)
        
        # 8. 执行工具(两条路径)
        # 路径 A:流式执行(低延迟)
        results = await streaming_tool_executor(approved)
        # 路径 B:降级执行(并发安全分类)
        results = await fallback_run_tools(approved)
        
        # 9. 评估终止条件
        if should_stop(turn, context, hooks):
            break
        
        # 将工具结果追加到消息历史
        messages.append(tool_results(results))
        turn += 1

1.2 工具系统

工具通过五步组装流程确定可用集:枚举基础能力 → 按模式过滤 → 应用黑名单 → 集成外部协议(MCP)→ 去重。

工具执行有两条路径:

  • StreamingToolExecutor:工具在模型流式生成时就开始执行,不等全部输出完毕。这是延迟优化的关键。
  • Fallback runTools:二级分类器评估工具是否可以安全并发,或需要独占执行。

1.3 上下文压缩的五级策略

Level 1: Budget Reduction    → 删除已知低价值 token(最廉价)
Level 2: Snipping            → 截断过长的工具输出(保留首尾)
Level 3: Micro-compaction    → 合并相邻相似消息
Level 4: Context Collapsing  → 折叠早期对话轮次
Level 5: Auto-compaction     → 调用模型做全量摘要(最昂贵,最后手段)

恢复机制包括:三次重试时逐步增加 max_tokens、每轮一次反应式压缩、超长提示降级、流式降级、辅助模型兜底。

1.4 子代理委托

子代理运行在隔离的 sidechain 上下文中(可以是独立 worktree 或远程环境),完成后只返回摘要 transcript,保护主上下文窗口不被膨胀。

1.5 使用 Agent SDK 编程调用

Anthropic 的 Agent SDK for Python 提供两种模式:

一次性任务(standalone 函数)

from claude_code import query

async def main():
    async for message in query(
        prompt="用 Python 写一个 Web 服务器",
        system_prompt="你是一个高级后端工程师",
        cwd="/path/to/project",
        max_turns=10,
    ):
        if hasattr(message, 'content'):
            for block in message.content:
                if hasattr(block, 'text'):
                    print(block.text)

连续对话(client 类)

from claude_code import ClaudeCodeClient

async def main():
    async with ClaudeCodeClient() as client:
        # 第一轮
        async for msg in client.query("分析一下这个项目的架构"):
            print(msg)
        
        # 第二轮(保持上下文)
        async for msg in client.query("针对你发现的性能问题给出优化方案"):
            print(msg)

自定义工具 + 权限控制

from claude_code import query, tool

@tool(read_only=True)
def search_docs(query: str) -> str:
    """搜索项目文档"""
    # ... 实现搜索逻辑
    return results

@tool()
def run_tests(test_path: str) -> str:
    """运行测试"""
    import subprocess
    result = subprocess.run(
        ["pytest", test_path], capture_output=True, text=True
    )
    return result.stdout

async def main():
    async for message in query(
        prompt="运行所有单元测试并修复失败的",
        tools=[search_docs, run_tests],
        permission_mode="acceptEdits",  # 自动批准文件编辑
        max_turns=20,
    ):
        pass

1.6 Claude Code 的关键设计决策

  • 基于文件的记忆系统:用 CLAUDE.md / AGENTS.md 而非向量数据库,可版本控制
  • 指令作为概率性上下文注入:而非确定性系统提示
  • deny-first 权限模型:七种权限模式,默认拒绝
  • append-only 日志:会话状态通过追加式 JSONL 日志持久化,恢复时丢弃授权状态以确保审计链

二、OpenAI Agents SDK:极简 Agent Loop

OpenAI 的 Agents SDK 代表了另一种设计哲学——最小化抽象,让开发者直接控制循环。

2.1 核心执行循环

Runner 类驱动 Agent Loop,核心逻辑非常直接:

# Runner 内部的简化伪代码
async def run(agent, input, max_turns=10):
    messages = [{"role": "user", "content": input}]
    
    for turn in range(max_turns):
        # 1. 调用模型
        response = await model.chat(messages, tools=agent.tools)
        
        # 2. 判断下一步
        if response.has_text and not response.has_tool_calls:
            # 终止:模型输出文本,无工具调用
            return RunResult(final_output=response.text)
        
        if response.has_handoff:
            # 切换:控制权转移到另一个 Agent
            agent = response.handoff_target
            messages = update_context(messages, agent)
            continue
        
        if response.has_tool_calls:
            # 继续:执行工具,结果回传模型
            tool_results = await execute_tools(response.tool_calls)
            messages.extend(tool_results)
            continue
    
    # 超过最大轮次
    raise MaxTurnsExceeded(f"Exceeded {max_turns} turns")

2.2 最小可运行示例

from agents import Agent, Runner, function_tool

@function_tool
def get_weather(city: str) -> str:
    """获取指定城市的天气信息"""
    # 模拟天气 API 调用
    weather_data = {"北京": "晴 25°C", "上海": "多云 22°C"}
    return weather_data.get(city, "未知城市")

agent = Agent(
    name="WeatherAssistant",
    instructions="你是一个天气助手,用 get_weather 工具查询天气。",
    tools=[get_weather],
)

# 同步执行
result = Runner.run_sync(agent, "北京今天天气怎么样?")
print(result.final_output)
# → "北京今天天气晴朗,气温 25°C。"

2.3 多 Agent 协作:Handoffs

OpenAI Agents SDK 用 Handoff(交接) 而非子代理来实现多 Agent 协作:

from agents import Agent, Runner

# 专门的工具集
triage_agent = Agent(
    name="TriageAgent",
    instructions="分析用户问题类型,转交给对应专家。",
    handoffs=[],  # 运行时注入
)

billing_agent = Agent(
    name="BillingAgent",
    instructions="处理账单和支付相关问题。",
    tools=[refund_tool, query_invoice_tool],
)

tech_agent = Agent(
    name="TechAgent",
    instructions="处理技术问题和故障排查。",
    tools=[diagnose_tool, restart_service_tool],
)

# 设置交接关系
triage_agent.handoffs = [billing_agent, tech_agent]

# 运行:triage_agent 会根据问题自动 handoff 到对应专家
result = Runner.run_sync(triage_agent, "我的信用卡被重复扣款了")

2.4 Guardrails(护栏)

护栏与 Agent 执行并行运行,快速失败:

from agents import Agent, Runner, InputGuardrail, GuardrailFunctionOutput

async def check_for_jailbreak(ctx, agent, input):
    # 自定义安全检查
    result = await safety_model.evaluate(input)
    return GuardrailFunctionOutput(
        output_info=result,
        tripwire_triggered=result.is_unsafe,
    )

agent = Agent(
    name="SafeAgent",
    instructions="You are a helpful assistant.",
    input_guardrails=[
        InputGuardrail(guardrail_function=check_for_jailbreak)
    ],
)

2.5 工具配置

from agents import RunConfig, ToolExecutionConfig

result = await Runner.run(
    agent,
    "执行所有必要的操作",
    run_config=RunConfig(
        tool_execution=ToolExecutionConfig(
            max_function_tool_concurrency=2,  # 限制并发
            pre_approval_tool_input_guardrails=True,  # 工具执行前审批
        ),
    ),
)

2.6 OpenAI Agents SDK 的关键设计决策

  • 极简抽象:Runner + Agent + Tool 三个核心概念,没有复杂的图结构
  • Handoff 而非子代理:Agent 间是平等交接,不是层级委派
  • 并行 Guardrails:安全检查不阻塞主循环
  • MCP 原生支持:MCP 服务器工具与函数工具无差异使用

三、Google ADK:多 Agent 拓扑优先

Google 的 Agent Development Kit 从多 Agent 拓扑设计出发,Agent Loop 是拓扑中的一个节点行为。

3.1 核心执行模型

from google.adk.agents import LlmAgent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types

def search_database(query: str) -> str:
    """搜索内部数据库"""
    # ... 实现
    return results

# 创建 Agent
agent = LlmAgent(
    model="gemini-2.0-flash",
    name="research_agent",
    instruction="你是一个研究助手。使用 search_database 工具查找信息。",
    tools=[search_database],
    description="专门负责信息检索和数据分析的 Agent",  # 用于多 Agent 路由
)

# 执行
async def main():
    session_service = InMemorySessionService()
    runner = Runner(
        agent=agent,
        app_name="research_app",
        session_service=session_service,
    )
    
    content = types.Content(
        role='user',
        parts=[types.Part(text='查找最近一季度的销售数据')]
    )
    
    async for event in runner.run_async(
        user_id="user1",
        session_id="session1",
        new_message=content,
    ):
        if event.is_final_response() and event.content:
            print(event.content.parts[0].text)

3.2 多 Agent 拓扑模式

Google ADK 原生支持四种拓扑:

Sequential(顺序) — Agent A → Agent B → Agent C

from google.adk.agents import SequentialAgent

pipeline = SequentialAgent(
    name="content_pipeline",
    sub_agents=[research_agent, writer_agent, editor_agent],
)

Parallel(并行) — Agent A || Agent B

from google.adk.agents import ParallelAgent

fan_out = ParallelAgent(
    name="parallel_research",
    sub_agents=[web_search_agent, db_query_agent, doc_search_agent],
)

Loop(循环) — Agent A → Agent B → 评估 → 回到 A

from google.adk.agents import LoopAgent

improvement_loop = LoopAgent(
    name="code_improvement",
    sub_agents=[code_generator, code_reviewer],
    # 循环直到 reviewer 认为代码合格
)

Routing(路由) — 根据输入选择不同 Agent

# 通过 Agent 的 description 字段实现自动路由
# 当多个 Agent 注册在同一 Runner 中时,
# 系统根据用户意图和 Agent 描述匹配最佳执行者

3.3 回调系统

Google ADK 提供 Agent 生命周期的细粒度回调:

agent = LlmAgent(
    name="callback_agent",
    model="gemini-2.0-flash",
    instruction="...",
    tools=[...],
    # 回调钩子
    before_model_callback=on_before_model,    # 模型调用前
    after_model_callback=on_after_model,      # 模型调用后
    before_tool_callback=on_before_tool,      # 工具执行前
    after_tool_callback=on_after_tool,        # 工具执行后
)

3.4 Google ADK 的关键设计决策

  • 多语言支持:Python / TypeScript / Go / Java / Kotlin
  • 拓扑优先:先定义 Agent 间的协作模式,再定义单个 Agent 的行为
  • 事件流驱动:Runner 产出事件流而非阻塞结果
  • Session 服务抽象:可插拔的会话存储后端

四、LangGraph:图结构 Agent Loop

LangGraph 将 Agent Loop 建模为 有向图,节点是计算步骤,边是控制流。

4.1 核心概念

StateGraph = 有向图
  ├── State(状态)= 类型化的消息列表 + 自定义字段
  ├── Nodes(节点)= 处理函数
  ├── Edges(边)= 节点间的转移
  └── Conditional Edges(条件边)= 动态路由

4.2 ReAct Agent 实现

from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import ToolNode
from langchain_openai import ChatOpenAI
import operator

# 1. 定义状态
class AgentState(TypedDict):
    messages: Annotated[list, operator.add]  # 消息列表,自动追加

# 2. 定义工具
def search(query: str) -> str:
    """搜索互联网"""
    return f"搜索结果:{query} 的相关信息..."

def calculator(expression: str) -> str:
    """计算数学表达式"""
    return str(eval(expression))

tools = [search, calculator]
model = ChatOpenAI(model="gpt-4o").bind_tools(tools)

# 3. 定义节点
def agent_node(state: AgentState):
    """Agent 决策节点:调用模型"""
    response = model.invoke(state["messages"])
    return {"messages": [response]}

tool_node = ToolNode(tools)

# 4. 定义条件边
def should_continue(state: AgentState):
    """判断是否需要继续工具调用"""
    last_message = state["messages"][-1]
    if last_message.tool_calls:
        return "tools"    # 有工具调用 → 执行工具
    return END            # 无工具调用 → 结束

# 5. 构建图
graph = StateGraph(AgentState)

# 添加节点
graph.add_node("agent", agent_node)
graph.add_node("tools", tool_node)

# 添加边
graph.add_edge(START, "agent")              # 入口 → Agent
graph.add_conditional_edges("agent", should_continue)  # Agent → 工具 or 结束
graph.add_edge("tools", "agent")             # 工具 → Agent(回到循环)

# 6. 编译并运行
app = graph.compile()

result = app.invoke({
    "messages": [("user", "搜索一下 Python 3.12 有什么新特性")]
})

for msg in result["messages"]:
    print(f"[{msg.type}] {msg.content}")

4.3 状态检查点

LangGraph 的核心优势是 持久化检查点,支持长时间运行的 Agent:

from langgraph.checkpoint.memory import MemorySaver

checkpointer = MemorySaver()
app = graph.compile(checkpointer=checkpointer)

# 第一轮
config = {"configurable": {"thread_id": "user-123"}}
result1 = app.invoke(
    {"messages": [("user", "帮我分析这个项目的依赖")]},
    config,
)

# 第二轮(自动恢复上下文)
result2 = app.invoke(
    {"messages": [("user", "根据刚才的分析,生成一份报告")]},
    config,  # 同一个 thread_id,自动拼接历史
)

4.4 人机协作断点

from langgraph.types import interrupt, Command

def sensitive_tool_node(state):
    """需要人工审批的工具执行"""
    tool_calls = state["messages"][-1].tool_calls
    
    for tc in tool_calls:
        if tc["name"] == "delete_record":
            # 中断执行,等待人工审批
            decision = interrupt({
                "question": f"确认删除记录 {tc['args']['id']}?",
                "tool_call": tc,
            })
            if decision != "approved":
                return {"messages": [{"role": "tool", "content": "操作被取消"}]}
    
    # 审批通过,执行工具
    return execute_tools(tool_calls)

# 恢复中断的执行
app.invoke(
    Command(resume="approved"),
    config,
)

4.5 LangGraph 的关键设计决策

  • 图即程序:Agent Loop 是显式的图结构,控制流可视化
  • 类型化状态:State 是 TypedDict,编译时检查
  • 条件边:动态路由决策,而非固定转移
  • 检查点持久化:任意时刻可暂停/恢复,支持人机协作

五、框架对比总结

维度Claude CodeOpenAI SDKGoogle ADKLangGraph
循环实现AsyncGenerator while-loopRunner for-loopEvent Loop有向图遍历
工具系统5 步组装 + MCP@function_tool + MCP函数/OpenAPI/MCPLangChain Tools
上下文压缩5 级渐进压缩开发者自行实现Session 服务管理Checkpoint + 手动
多 Agent子代理 sidechainHandoff 交接4 种拓扑模式图嵌套/子图
安全检查deny-first 7 模式并行 Guardrails回调钩子人机断点
状态持久化JSONL append-only可插拔 SessionSession 服务检查点系统
抽象程度高(完整 Harness)低(最小 SDK)中(拓扑框架)中(图引擎)
适用场景编码 Agent通用 Agent企业多 Agent复杂工作流

如何选择

  • 要开箱即用的编码 Agent → Claude Code + Agent SDK
  • 要最小化依赖、完全控制 → OpenAI Agents SDK
  • 要构建企业级多 Agent 系统 → Google ADK
  • 要精确控制执行流、需要人机协作 → LangGraph
  • 要自建生产级 Harness → 下一篇详述

参考资料