Skip to content

第22章:主流框架实战

你已经读完了前 21 章,知道了 Agent 是什么、怎么用工具、怎么管记忆、怎么做规划、怎么保证安全。现在的问题是:在真实项目里,我到底用哪个框架?

这个问题没有唯一答案,但选错了会很痛。LangGraph 学习曲线陡峭,三天还没跑起来第一个 Agent;CrewAI 入门快,但到了需要精细控制的地方开始抓狂;OpenAI Agents SDK 极简,却默认绑定 OpenAI 生态。框架选型是一个工程决策,而不是"哪个更强"的竞技。

这一章的目标不是告诉你哪个框架最好,而是帮你理解每个框架背后的设计哲学——它试图解决什么问题、用什么抽象来解决、这个抽象在什么条件下会成为阻碍。明白了这些,选型才能落地。

框架到底买你什么

在展开每个框架之前,先建立一个前提:框架不会让 Agent 更智能,只会让你少写胶水代码

一个没有框架的 Agent 需要你自己维护:工具注册和调用、LLM 交互循环、消息历史管理、状态持久化、错误重试、多 Agent 路由。这些代码不难写,但每个项目写一遍很烦。框架的价值就在于把这些重复模式封装成固定抽象,让你聚焦在业务逻辑上。

代价是:你的代码被框架的思维模型约束了。LangGraph 要求你用图来思考任务流;CrewAI 要求你用角色来组织 Agent;OpenAI SDK 要求你用 handoff 来理解控制权转移。如果你的任务天然符合这个思维模型,框架是放大器;如果不符合,框架会变成枷锁。

Anthropic 在官方文档中给出了一个务实建议 [1]:

当你首次使用框架时,建议先用原生 LLM API 实现相同的功能,这样你才能理解框架底层在做什么。

不是叫你不用框架,而是说别绕过框架直接学框架。

22.1 LangGraph

核心直觉

LangGraph 的核心思想只有一句话:把 Agent 的执行过程建模成一个有状态的有向图

有向图里,节点是函数(每个节点做一件事),边是控制流(决定下一步去哪个节点),所有节点共享一个中央状态对象。每次节点执行完,把自己的输出合并回这个状态;下一个节点从这个状态里读自己需要的部分。

这个设计让 LangGraph 在两件事上特别擅长:

  1. 复杂的条件路由:根据当前状态动态决定走哪条边
  2. 持久化与中断恢复:每执行完一个节点就存一次状态,任意位置都可以暂停、恢复、回溯

关键概念

StateGraph:主体数据结构。你先定义状态的 schema(用 Python TypedDict),然后往图里添加节点和边。

Node(节点):一个 Python 函数,接收当前状态,返回一个字典(部分状态更新)。节点不需要返回完整状态,只返回它修改的字段。

Edge(边):连接两个节点的有向路径。分两种:

  • 普通边:固定的下一个节点,add_edge("node_a", "node_b")
  • 条件边:根据函数的返回值决定下一节点,add_conditional_edges("node_a", router_fn, {"result_1": "node_b", "result_2": "node_c"})

Checkpoint(检查点):把图执行过程保存为可恢复的状态快照。LangGraph 的持久化层按 thread 组织 checkpoint,支持内存(MemorySaver / InMemorySaver)、SQLite(SqliteSaver)、PostgreSQL(PostgresSaver)等多种存储后端。开启 checkpoint 后,你可以做 human-in-the-loop、time travel 调试、失败恢复和跨轮次状态延续。

Reducer:控制状态字段如何合并。默认是覆盖(新值替换旧值),但对于列表类字段,你通常想追加而不是覆盖——用 Annotated[List[str], operator.add] 声明。

构建一个有状态的研究 Agent

下面是一个可运行的示例:Agent 接收查询,搜索互联网,判断结果是否充分,最终生成报告。

python
import operator
from typing import Annotated, TypedDict, List
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver
from langchain_anthropic import ChatAnthropic
from langchain_community.tools.tavily_search import TavilySearchResults

# 1. 定义共享状态
class ResearchState(TypedDict):
    query: str
    search_results: Annotated[List[str], operator.add]  # 列表追加而非覆盖
    search_count: int
    final_report: str

# 2. 初始化工具和模型
llm = ChatAnthropic(model="<your-claude-model>")
search_tool = TavilySearchResults(max_results=3)

# 3. 定义节点函数
def search_node(state: ResearchState) -> dict:
    """执行搜索,返回结果追加到列表"""
    results = search_tool.invoke(state["query"])
    snippets = [r["content"] for r in results]
    return {
        "search_results": snippets,
        "search_count": state.get("search_count", 0) + 1
    }

def analyze_node(state: ResearchState) -> dict:
    """分析搜索结果,生成最终报告"""
    context = "\n".join(state["search_results"])
    response = llm.invoke(
        f"基于以下搜索结果,为查询 '{state['query']}' 生成详细报告:\n\n{context}"
    )
    return {"final_report": response.content}

# 4. 路由函数:决定继续搜索还是进入分析
def should_continue_search(state: ResearchState) -> str:
    # 超过 2 次搜索或结果足够多时停止
    if state.get("search_count", 0) >= 2 or len(state["search_results"]) >= 6:
        return "analyze"
    return "search"

# 5. 构建图
builder = StateGraph(ResearchState)
builder.add_node("search", search_node)
builder.add_node("analyze", analyze_node)
builder.set_entry_point("search")

# 条件路由:search 节点之后,根据条件决定去哪里
builder.add_conditional_edges(
    "search",
    should_continue_search,
    {"search": "search", "analyze": "analyze"}  # 可以循环
)
builder.add_edge("analyze", END)

# 6. 编译图,启用持久化
checkpointer = MemorySaver()
graph = builder.compile(checkpointer=checkpointer)

# 7. 运行
config = {"configurable": {"thread_id": "research-001"}}
result = graph.invoke({"query": "LangGraph 最新特性"}, config=config)
print(result["final_report"])

这里有几个值得注意的设计细节:

  • search_results 用了 Annotated[List[str], operator.add],这意味着每次搜索的结果会追加到列表,而不是覆盖。如果用默认的覆盖语义,只会保留最后一次搜索结果。
  • thread_id 是 Checkpoint 的键。同一个 thread_id 的多次调用会复用状态,实现多轮对话。
  • 图里有一个循环:search → search(条件边指向自己),LangGraph 天然支持这种循环,但你需要确保有退出条件,否则会无限循环。

Human-in-the-Loop(人工介入)

LangGraph 的 Checkpoint 机制天然支持人工介入。简单场景可以用 interrupt_before=["node_name"] 在某个节点前暂停;更复杂的生产场景通常会用动态 interrupt(),让节点把需要审批的 payload 返回给外部系统,再用 Command(resume=...) 恢复执行。下面先展示静态暂停的基本形态:

python
graph = builder.compile(
    checkpointer=checkpointer,
    interrupt_before=["analyze"]  # 进入分析前暂停
)

# 第一步:执行到暂停点
state = graph.invoke({"query": "..."}, config=config)
# 此时图暂停在 analyze 节点之前

# 人工查看搜索结果,确认是否继续
print("搜索结果:", state["search_results"])
user_input = input("是否继续生成报告?(y/n): ")

if user_input == "y":
    # 第二步:继续执行
    result = graph.invoke(None, config=config)  # None 表示继续而非重新开始

真正上线时还要注意一个细节:从暂停点恢复时,暂停所在节点前面的代码可能会重新执行,因此有副作用的操作(发邮件、扣款、写数据库)必须做到幂等,或者放到 interrupt 之后执行。

优势与劣势

适合用 LangGraph 的场景

  • 任务流程有多个分支、循环、条件判断
  • 需要在任意步骤插入人工审批
  • 长任务需要可靠的中断恢复
  • 需要精细控制每个节点的执行逻辑

不适合的场景

  • 简单的线性任务(3-5 步的 Sequential Agent,用 CrewAI、OpenAI SDK 或原生 API 更快)
  • 快速原型验证(图的思维模型有额外概念负担)
  • 团队不熟悉图论基础

LangGraph 的主要成本是学习曲线和工程纪律:State、Reducer、条件边、checkpoint、interrupt 组合在一起,一开始会比"写一个 while 循环调工具"重很多。但当任务需要长时间运行、可恢复、可审计、可人工介入时,这些概念会变成优势。

截至 2026.05,LangGraph 已进入 v1.x 文档体系,官方定位是面向 long-running、stateful agent 的底层编排基础设施。官方文档 [2]。

22.2 CrewAI

核心直觉

CrewAI 的核心思想是用角色扮演来组织 Multi-Agent 协作。你定义一个"研究员"、一个"作家"、一个"编辑",给他们各自的目标和工具,然后让他们按照某个流程(顺序、层级)协作完成任务。

它把 Agent 系统类比为一个职能团队:每个成员有自己的专长,有明确分工,通过任务传递协作。这个类比对非技术背景的团队来说非常直觉,是 CrewAI 快速普及的重要原因。

架构演进:从 Crew 到 Flows + Crews

CrewAI 的架构在 2025 年经历了一次重要演进,从单一的 Crew 概念演进为 Flows + Crews 双层架构(截至 2026.05,以官方文档 [3] 为准):

  • Flow(工作流):应用的骨架。定义整体流程步骤、状态管理、条件逻辑和事件驱动执行。Flow 是确定性的编排层。
  • Crew(团队):智能执行单元。在 Flow 的某个步骤里,当需要多个 Agent 协作处理复杂任务时,调用 Crew。Crew 是自主的 Agent 协作层。

用官方文档的话说:Flow 是"应用的管理者或流程定义者",Crew 是"完成繁重工作的团队"。

关键概念

Agent:一个角色化的 LLM 实例,有 role(角色)、goal(目标)、backstory(背景故事)和 tools(工具列表)。背景故事不是装饰——它是 System Prompt 的一部分,会影响模型的行为风格。

Task:分配给 Agent 的工作单元,包含 description(任务描述)、expected_output(期望输出格式)和 agent(负责执行的 Agent)。

Crew:Agent + Task 的组合,加上执行流程(Process.sequentialProcess.hierarchical)。

  • sequential:任务按顺序执行,前一个任务的输出自动成为下一个任务的上下文
  • hierarchical:有一个 Manager Agent 负责任务分配和质量把关,其他 Agent 是执行者

Flow:事件驱动的状态机,用 @start()@listen() 等装饰器定义步骤。

快速构建内容生产流水线

python
from crewai import Agent, Task, Crew, Process
from crewai_tools import SerperDevTool

# 1. 创建工具
search_tool = SerperDevTool()

# 2. 定义 Agent(注意 backstory 会直接影响行为)
researcher = Agent(
    role="资深研究分析师",
    goal="发现关于 {topic} 最新、最重要的信息",
    backstory=(
        "你是一位经验丰富的研究分析师,擅长从海量信息中"
        "提取关键洞察。你只引用可验证的来源,从不编造数据。"
    ),
    tools=[search_tool],
    verbose=True,
)

writer = Agent(
    role="技术内容作家",
    goal="将研究结果转化为清晰、准确的技术文章",
    backstory=(
        "你是一位技术写作专家,能将复杂概念转化为"
        "工程师易读的内容。你重视准确性,不喜欢废话。"
    ),
    verbose=True,
)

# 3. 定义 Task(expected_output 要具体,不要模糊)
research_task = Task(
    description="研究 {topic},整理关键发现、技术细节和最新进展",
    expected_output=(
        "一份结构化的研究报告,包含:\n"
        "- 核心概念解释\n"
        "- 3-5 个关键技术点\n"
        "- 引用来源列表"
    ),
    agent=researcher,
)

write_task = Task(
    description="基于研究报告,撰写一篇 1500 字的技术博客文章",
    expected_output="完整的 Markdown 格式技术文章,包含标题、小节和代码示例",
    agent=writer,
    context=[research_task],  # 明确声明依赖研究任务的输出
)

# 4. 组建 Crew
crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, write_task],
    process=Process.sequential,
    verbose=True,
)

# 5. 运行
result = crew.kickoff(inputs={"topic": "LLM Agent 评估方法"})
print(result.raw)

Flows 的使用

对于需要跨步骤管理状态的复杂任务,新版 CrewAI 推荐使用 Flows:

python
from crewai.flow.flow import Flow, start, listen
from crewai import Agent, Task, Crew, Process
from pydantic import BaseModel

class ContentState(BaseModel):
    topic: str = ""
    research: str = ""
    article: str = ""

class ContentCreationFlow(Flow[ContentState]):
    @start()
    def receive_topic(self):
        # 从外部接收 topic(实际项目里可能来自 API)
        self.state.topic = "AI Agent 框架对比"
        return self.state.topic

    @listen(receive_topic)
    def research_topic(self, topic):
        # 调用 Crew 做研究
        researcher = Agent(role="研究员", goal="深入研究 {topic}", backstory="...")
        task = Task(description="研究 {topic}", expected_output="详细报告", agent=researcher)
        crew = Crew(agents=[researcher], tasks=[task])
        result = crew.kickoff(inputs={"topic": topic})
        self.state.research = result.raw
        return result.raw

    @listen(research_topic)
    def write_article(self, research):
        # 基于研究结果写文章
        writer = Agent(role="作家", goal="将研究转化为文章", backstory="...")
        task = Task(
            description=f"基于以下研究写文章:{research}",
            expected_output="完整文章",
            agent=writer
        )
        crew = Crew(agents=[writer], tasks=[task])
        result = crew.kickoff()
        self.state.article = result.raw
        return result.raw

# 运行 Flow
flow = ContentCreationFlow()
flow.kickoff()
print(flow.state.article)

优势与劣势

适合用 CrewAI 的场景

  • 内容生产、研究报告、数据分析等有自然角色分工的任务
  • 需要快速搭出 Multi-Agent 原型(入门时间以小时计)
  • 团队背景多样,非工程人员也要能理解 Agent 配置

不适合的场景

  • 需要精细控制执行路径(比如在第 3 步里根据第 1 步结果做复杂分支)
  • 对 Token 消耗敏感(角色背景故事会消耗 context,verbose=True 默认大量输出)
  • 需要强一致、可恢复的长事务状态管理(例如严格 checkpoint、exactly-once 副作用、细粒度 replay)

一个常见的踩坑:expected_output 写得太模糊会导致输出不稳定。"写一篇好文章"和"写一篇 1500 字的 Markdown 技术文章,包含标题、小节和至少一个代码示例",后者的结果可预期得多。

截至 2026.05,CrewAI 官方文档 [3] 仍在快速演进。选型时不要只看 GitHub Star 或教程数量,更应该看:Flows 是否满足你的状态管理需求、企业部署/观测能力是否符合团队要求、MCP 工具接入是否稳定、以及真实生产案例是否接近你的业务形态。

22.3 OpenAI Agents SDK

核心直觉

OpenAI Agents SDK 的设计哲学是用最少的原语解决最多的问题。它的核心团队来自早期的 Swarm 项目,那个项目证明了:三个原语——Agent、Handoff、Guardrails——足以描述绝大多数 Multi-Agent 场景 [4]。

和 LangGraph(图思维)、CrewAI(角色思维)不同,OpenAI SDK 用的是控制权转移的思维模型:系统里有多个 Agent,某一时刻只有一个 Agent 在"说话",Handoff 就是把控制权从一个 Agent 交给另一个 Agent。

三大原语

Agent:最基本的单元。一个 LLM + 一套 instructions + 一组 tools + 可选的 handoffs。

python
from agents import Agent

customer_service_agent = Agent(
    name="客服助手",
    instructions="你是一个友好的客服代表,帮助用户解决问题。",
    tools=[get_order_status, process_refund],
)

Handoffs(移交):把对话控制权交给另一个 Agent。移交后,那个 Agent 接管后续交互,前一个 Agent 退出。

Agents as Tools(Agent 作为工具):调用另一个 Agent 完成一个子任务,但调用 Agent 保留控制权,子任务的结果作为工具调用结果返回。

这两者的区别很关键,SDK 官方文档给出了清晰的对比 [4]:

模式控制权适用场景
Handoff转移给目标 Agent路由式工作流,希望专家直接接管对话
Agent as Tool保留在调用方需要一个 Agent 协调多个专家、汇总结果

可以混用:一个分诊 Agent 用 Handoff 把用户转给专家,专家用 Agent as Tool 调用更小粒度的专项 Agent。

Guardrails(护栏):与主 Agent 并行执行的安全检查,输入校验或输出校验都可以。触发时抛出异常中断执行。第15章详细介绍了 Guardrails 的设计,这里专注 SDK 的使用方式。

构建一个多 Agent 客服系统

python
from agents import Agent, Runner, handoff, GuardrailFunctionOutput
from agents import input_guardrail, output_guardrail
from pydantic import BaseModel
import asyncio

# 1. 定义业务工具(实际项目里调用数据库/API)
def get_order_status(order_id: str) -> dict:
    """查询订单状态"""
    # 模拟数据库查询
    return {"order_id": order_id, "status": "已发货", "eta": "2026-05-03"}

def process_refund(order_id: str, reason: str) -> dict:
    """处理退款请求"""
    return {"order_id": order_id, "refund_status": "退款已受理", "amount": 99.0}

def answer_faq(question: str) -> str:
    """回答常见问题"""
    faqs = {
        "配送时间": "标准配送 3-5 天,急速配送 1-2 天",
        "退换货": "支持 7 天无理由退换货",
    }
    return faqs.get(question, "请联系人工客服")

# 2. 定义专业 Agent
order_agent = Agent(
    name="订单专员",
    instructions=(
        "你专门处理订单相关问题:查询状态、处理退款。"
        "处理完问题后,礼貌地询问是否有其他需要。"
    ),
    tools=[get_order_status, process_refund],
)

faq_agent = Agent(
    name="FAQ 专员",
    instructions=(
        "你负责回答常见问题:配送、退换货、优惠活动等。"
        "用简洁准确的语言回答,避免废话。"
    ),
    tools=[answer_faq],
)

# 3. Guardrail:检测恶意输入
class SafetyCheck(BaseModel):
    is_safe: bool
    reason: str

@input_guardrail
async def safety_guardrail(ctx, agent, input_text):
    """简单的安全检查:拦截明显恶意请求"""
    dangerous_keywords = ["hack", "bypass", "injection", "ignore previous"]
    is_safe = not any(kw in str(input_text).lower() for kw in dangerous_keywords)
    return GuardrailFunctionOutput(
        output_info=SafetyCheck(
            is_safe=is_safe,
            reason="检测到潜在恶意输入" if not is_safe else "输入安全"
        ),
        tripwire_triggered=not is_safe,
    )

# 4. 分诊 Agent(入口,根据用户意图转发)
triage_agent = Agent(
    name="客服入口",
    instructions=(
        "你是客服入口,负责判断用户需求并转给合适的专员。\n"
        "- 涉及订单、退款 → 转给订单专员\n"
        "- 涉及配送、退换货政策等 FAQ → 转给 FAQ 专员\n"
        "如果无法判断,直接回答用户,不要随意转发。"
    ),
    handoffs=[
        handoff(order_agent),
        handoff(faq_agent),
    ],
    input_guardrails=[safety_guardrail],
)

# 5. 运行
async def main():
    result = await Runner.run(triage_agent, "我的订单 ORD-12345 到哪了?")
    print(result.final_output)

asyncio.run(main())

Sessions:跨轮次记忆

OpenAI SDK 的 Sessions 机制让 Agent 在多次运行间保持对话历史(截至 2026.05 [4])。注意:Session 是客户端侧的 conversation history 管理,不等于长期语义记忆;如果使用 OpenAI server-managed continuation(如 conversation / previous_response 机制),不要再叠加 SDK Session:

python
from agents import Agent, Runner, SQLiteSession

# SQLite 持久化 Session
session = SQLiteSession("user-123", "sessions.db")

agent = Agent(
    name="助手",
    instructions="你是一个记得用户偏好的助手。"
)

# 第一次对话
result1 = await Runner.run(
    agent,
    "我叫张三,我喜欢简洁的回答",
    session=session
)

# 第二次对话(Agent 记得上次内容)
result2 = await Runner.run(
    agent,
    "我是谁?",
    session=session  # 同一个 session
)
print(result2.final_output)  # "你是张三"

Session 支持 SQLite,也可以通过 SDK 的自定义 Session 接口接入 Redis、PostgreSQL 等后端。多 Worker 生产部署时,不要用进程内内存 Session。

Sandbox Agents(沙箱 Agent)

对于需要在隔离环境运行代码的场景,SDK 在 2026 年 4 月新增了 Sandbox Agent 能力 [4][8]。截至 2026.05,这仍是 beta 能力,API 和默认行为可能继续变化。它提供受控工作区、文件系统、Shell / 代码执行和可恢复的 sandbox session,适合代码 Agent、数据分析 Agent、长任务文件处理。

python
from agents import Runner
from agents.run import RunConfig
from agents.sandbox import SandboxAgent, SandboxRunConfig
from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient

sandbox_agent = SandboxAgent(
    name="数据分析师",
    instructions="你是一个数据分析专家,可以在受控工作区里读写文件并运行代码。",
)

result = await Runner.run(
    sandbox_agent,
    "分析工作区里的 data.csv,告诉我平均年龄",
    run_config=RunConfig(
        sandbox=SandboxRunConfig(client=UnixLocalSandboxClient())
    ),
)

这里的重点不是"SDK 里有一个沙箱类"这么简单,而是执行边界:文件挂载、网络访问、持久化 workspace、外部存储(如 S3 / GCS / Azure Blob)、资源限制和审计都要按第 16、20 章的原则配置。默认本地 sandbox 不等于生产级隔离。

优势与劣势

适合用 OpenAI Agents SDK 的场景

  • 已经深入使用 OpenAI 生态(GPT 系列、Responses API)
  • 快速构建 Multi-Agent 系统,不想学习图论
  • 需要内置 Guardrails 一等公民支持
  • Sandbox 执行是核心需求(代码 Agent、数据分析 Agent)

不适合的场景

  • 需要使用 Anthropic、Google 等非 OpenAI 模型作为主力(虽然 SDK 通过 LiteLLM 扩展支持其他模型,但默认优化是 OpenAI)
  • 需要复杂的状态图和循环控制(Handoff 是有限状态机,不如 LangGraph 灵活)
  • 需要对 Agent 执行轨迹进行精细 Replay 和调试

一个常见误解:Handoff 和函数工具是不同层次的抽象。函数工具是 Agent 在推理时调用外部 API;Handoff 是把整个对话上下文移交给另一个 Agent。前者类比打电话问信息,后者类比把客户转接给另一个客服。

22.4 其他框架速览

以下五个框架各有特定适用场景,了解它们的定位有助于在主流三个框架之外做出更合适的选型。

Google ADK

定位:Google 开源的 Agent 开发框架,深度绑定 Google Cloud、Vertex AI 和 Gemini 生态。截至 2026.05,ADK 2.0 处于 Alpha 阶段,官方明确提示不建议在需要向后兼容的生产环境中直接使用;但它展示了 Google 对图工作流、协作 Agent 和确定性编排的方向。

ADK 区分两种 Agent 类型 [5]:

  • LLM Agent:由大模型驱动,动态决策,类似其他框架的 Agent
  • Workflow Agent:确定性编排,包括 Sequential(顺序)、Loop(循环)、Parallel(并行)三种子类型

这个区分让 ADK 能优雅地混用 LLM 决策和确定性逻辑:

python
from google.adk import Agent
from google.adk.agents import SequentialAgent, LoopAgent
from google.adk.tools import google_search

# LLM Agent:动态决策
researcher = Agent(
    name="researcher",
    model="gemini-flash-latest",
    instruction="You help users research topics thoroughly.",
    tools=[google_search],
)

# Workflow Agent:确定性编排
pipeline = SequentialAgent(
    name="research_pipeline",
    agents=[researcher, writer_agent, reviewer_agent],
)

ADK 的 A2A(Agent-to-Agent)协议支持(第14章有详细介绍)让它在多 Agent 互操作场景下有天然优势。但 A2A 仍在快速演进,跨组织互操作不能只看协议名,还要验证认证、授权、审计和数据边界。

适用场景:已使用 Google Cloud、Vertex AI 或 Gemini 的团队;需要混用 LLM 决策和确定性 Workflow 的系统;愿意跟进 ADK 生态变化、并能接受 Alpha / Preview 能力边界的团队。

Microsoft Agent Framework

定位:Microsoft Agent Framework 是 Microsoft 新一代 Agent 框架,融合了 AutoGen 的多 Agent 抽象和 Semantic Kernel 的企业能力。官方定位是 Semantic Kernel / AutoGen 在 Agent 场景下的后继方向,目前处于 public preview [6]。

它提供两类能力:

  • Agents:单个 Agent 处理输入、调用工具 / MCP Server、生成响应
  • Workflows:图式工作流,连接 Agent 和函数,支持多步骤任务、类型安全路由、checkpoint 和 human-in-the-loop

它还提供 session state、context providers、middleware、telemetry、MCP client 和多模型 provider,面向 Azure / Microsoft 生态里的企业应用。

python
# 简化示意,具体导入以 Microsoft Agent Framework 当前版本为准
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential

client = FoundryChatClient(
    project_endpoint="https://your-foundry-service.services.ai.azure.com/api/projects/your-project",
    model="<your-model>",
    credential=AzureCliCredential(),
)

agent = client.as_agent(
    name="HelloAgent",
    instructions="你是一个简洁的企业助手。",
)

result = await agent.run("帮我总结这个工单")
print(result)

适用场景:已深度使用 Azure / Microsoft 生态;需要 .NET / Python 双栈;企业内部需要 Foundry、M365、Azure Functions、MCP、A2A、AG-UI 等平台集成;团队愿意接受 public preview 带来的 API 变化风险。

Claude Agent SDK

定位:Anthropic 面向 Claude Agent / Claude Code 能力开放的开发 SDK。它不是一个通用工作流框架,而是把 Claude Code 的 agentic loop、上下文管理、文件/命令行工具、权限控制和流式输出能力提供给开发者,用于构建代码 Agent、内部工程助手和自动化开发工具 [7]。

最需要区分的是两种形态:

  • Claude Agent SDK:你在自己的进程或服务里运行 Agent,自己负责部署、权限、工具边界、审计和成本控制。
  • Managed Claude Agents:Anthropic 托管 Agent 执行环境,适合希望少管基础设施、直接接入托管执行能力的团队。

一个最小示意:

python
import asyncio
from claude_agent_sdk import ClaudeAgentOptions, query

async def main():
    options = ClaudeAgentOptions(
        allowed_tools=["Read", "Edit", "Bash"],
        permission_mode="acceptEdits",
    )

    async for message in query(
        prompt="阅读这个仓库,修复 failing tests",
        options=options,
    ):
        print(message)

asyncio.run(main())

Claude Agent SDK 的价值在于"把 Claude 当作能操作工程环境的 Agent",不是把任意业务流程都塞进代码 Agent。生产使用时尤其要控制:

  • 工具白名单Bash、文件编辑、网络访问都要最小授权。
  • 工作区隔离:每个任务最好有独立 workspace,避免跨用户/跨仓库污染。
  • 审批点:写文件、执行破坏性命令、创建 PR、访问 secret 前要有策略或人工确认。
  • 审计与回放:记录工具调用、diff、命令输出摘要和最终变更,方便复盘。

适用场景:代码库理解、测试修复、代码迁移、内部开发助手、CI 失败自动诊断;不适合拿来做纯客服、内容生成、强业务状态流转等通用 Agent 编排。

smolagents(HuggingFace)

定位:HuggingFace 出品的轻量级 Agent 框架,主打极简和快速原型。截至 2026.05 [9]。

smolagents 有一个独特的 CodeAgent:它不是把动作表示为 JSON 工具调用,而是让模型直接写 Python 代码来操作工具。Python 代码的组合能力(嵌套函数、循环、条件)比 JSON 工具调用更灵活:

python
from smolagents import CodeAgent, InferenceClientModel, DuckDuckGoSearchTool, PythonInterpreterTool

model = InferenceClientModel(model_id="meta-llama/Llama-3.3-70B-Instruct")

# CodeAgent:动作以 Python 代码形式表达
agent = CodeAgent(
    tools=[DuckDuckGoSearchTool(), PythonInterpreterTool()],
    model=model,
    max_steps=10,
)

# Agent 会生成并执行 Python 代码来完成任务
result = agent.run("查找 LangGraph 的 GitHub star 数量,然后计算它和 CrewAI star 数的比值")
print(result)

CodeAgent 的优势:代码可以自然地表达"先做 A,根据 A 的结果再决定做 B 还是 C",不需要框架提供特殊的条件路由机制。代价是安全风险更高——模型生成的代码需要在沙箱里执行(smolagents 支持 Modal、E2B 等沙箱)。

适用场景:研究和原型阶段,希望最快速地验证 Agent 想法;需要模型自主编写复杂计算逻辑;需要与 HuggingFace Hub 深度集成(从 Hub 加载模型、工具、Agent)。


框架能力横向对比

截至 2026.05,各框架核心能力对比如下。注意这张表是选型线索,不是采购清单:框架能力变化很快,真正选型前要用你的工具、权限模型、部署方式和 eval 套件做一次小型 PoC。

维度LangGraphCrewAIOpenAI Agents SDKGoogle ADKMicrosoft Agent FrameworkClaude Agent SDKsmolagents
核心抽象有状态图角色 + 任务 + FlowAgent + Handoff + GuardrailsLLM Agent + Workflow AgentAgent + WorkflowClaude Code 式执行循环CodeAgent
学习曲线平缓平缓极平缓
状态控制粒度极高中(Flow)/ 低(Crew)中高低到中
持久化/Checkpoint内置多后端Flow 状态SessionsSession / StateCheckpoint / session state依赖运行环境无内置
人工介入基础Guardrail / 应用层需按平台设计Workflow / middleware权限确认适配代码任务需自建
沙箱执行需自建需自建内置 Sandbox需自建需结合 Azure/容器面向工程工作区Modal / E2B
可观测性LangSmith基础日志 / EnterpriseTracingGoogle CloudAzure / OpenTelemetry工具轨迹 / 外部日志基础日志
MCP / 工具生态通过适配器或自封装文档支持原生 MCPMCP / A2A 生态MCP client内置工程工具 + MCP 扩展MCP / Hub 工具
最适合复杂流程、长任务、审批角色分工型任务OpenAI 生态内快速交付Google Cloud / GeminiAzure / .NET / 企业集成代码 Agent原型和研究

选型落地清单

真正的框架选型不要从"哪个框架最火"开始,而要从最小可验证问题开始。建议用 1-2 周做一个 thin slice:选一个真实任务、接入真实工具、跑真实权限边界,然后用第 18 章的 eval 方法看成功率和失败类型。

PoC 至少回答这些问题:

  1. 状态是否能解释清楚:失败时能不能知道卡在哪个节点、哪个 handoff、哪个工具调用?
  2. 副作用是否可控:发邮件、写数据库、改代码、扣款这类动作能否审批、幂等、回滚或补偿?
  3. 权限是否贴近生产:是否支持按用户、租户、工具、资源、时间窗口下发最小权限?
  4. 评估是否能接入 CI/CD:框架的 trace、工具调用和状态快照能否转成回归 eval 样本?
  5. 成本是否可观测:能否按任务、模型、工具、重试、缓存命中率拆分成本?
  6. 迁移成本是否可接受:业务层是否被框架 API 深度污染,是否能把工具、prompt、memory、eval 资产迁到另一个框架?

一个实用结论:如果你还说不清楚状态机,就先用简单框架或原生 API;如果你已经开始画流程图、审批点和失败恢复路径,LangGraph / Microsoft Agent Framework 这类 workflow-first 框架通常更合适;如果核心任务是改代码或操作仓库,Claude Agent SDK / OpenAI Sandbox Agent / smolagents + 沙箱才是更直接的起点。

常见误区

误区一:框架越功能强,Agent 越可靠

LangGraph 功能最强,不代表用 LangGraph 写出来的 Agent 就更可靠。可靠性来自:好的工具设计、充分的错误处理、完整的评估体系。一个用 OpenAI SDK 写的简单 Agent,如果工具描述清晰、错误处理完备、有完整的 eval,比一个用 LangGraph 写的大图但没有测试的 Agent 可靠得多。

误区二:Handoff 等于 LangGraph 的条件边

Handoff 是控制权完全转移——被 handoff 的 Agent 接管对话,之前的 Agent 不再参与。LangGraph 的条件边是执行路径的分支,所有节点都在同一张图里,共享同一个状态。前者是"转接客服",后者是"走不同流程分支"。

误区三:CrewAI 的 Crew 等于 Multi-Agent

一个 Crew 里的 Agent 是协作完成同一个整体任务,它们的角色是互补的。真正的 Multi-Agent 系统里,Agent 是独立的决策单元,可以相互委托、并行执行、有自己的上下文隔离。如果你的场景是独立任务并行,CrewAI 的 Process.hierarchical + Manager Agent 能处理,但不如 LangGraph 或 OpenAI SDK 的控制权转移模型直观。

误区四:选定框架就不能换

这是真实存在的风险,但不是无解的。诀窍是:在应用层做好抽象,不要让框架 API 直接散落在业务代码里。把 Agent 核心逻辑包在一个业务层类里,框架只出现在底层实现里。这样换框架时,改动范围局限在底层,业务逻辑不需要动。

面试高频题

问:你的项目选了哪个 Agent 框架?为什么选这个而不是其他的?

参考答案框架:

  1. 描述需求约束(任务类型、团队规模、技术栈、上线时间)
  2. 说明候选方案(至少比较两个框架)
  3. 说明决策依据(不是"哪个更强",而是"哪个更符合这个场景")
  4. 提及实际使用中遇到的问题和取舍

加分点:能说出框架的限制和踩过的坑,而不只是说优点。能讲出框架底层机制(比如 LangGraph 的 Reducer、OpenAI SDK 的 Handoff 原理)。


问:LangGraph、CrewAI、OpenAI SDK 分别适合什么场景?如果只能选一个,你选哪个?

简要框架:

  • LangGraph:复杂状态流转、循环、人工介入点、精细 Checkpoint 需求
  • CrewAI:有自然角色分工的多步任务、快速原型、非技术背景团队协作
  • OpenAI SDK:简洁的 Multi-Agent 路由、深度 OpenAI 生态、需要一等公民 Guardrails
  • "只能选一个":要反问具体场景,不存在跨场景最优解。如果真要默认选,先用 OpenAI SDK 或 CrewAI 跑通原型,如果状态控制需求超出它们的能力,再迁移到 LangGraph

问:如果不用任何框架,你怎么实现 Agent 的多步推理?框架帮你省掉了哪些代码?

回答要点:原生实现需要手写工具注册、调用循环(while stop_reason != "end_turn")、消息历史管理、错误重试、状态持久化。框架封装了这些样板代码。理解框架底层机制的标志是:能在 30 分钟内用原生 API 写出一个基础 Agent(参考第25章)。


参考资料

[1] Building Effective Agents - Anthropic (https://www.anthropic.com/engineering/building-effective-agents)

[2] LangGraph 官方文档 (https://langchain-ai.github.io/langgraph/)

[3] CrewAI 官方文档 (https://docs.crewai.com/introduction)

[4] OpenAI Agents SDK 官方文档 (https://openai.github.io/openai-agents-python/)

[5] Google ADK 官方文档 (https://adk.dev/)

[6] Microsoft Agent Framework 官方文档 (https://learn.microsoft.com/en-us/agent-framework/)

[7] Claude Agent SDK 官方文档 (https://code.claude.com/docs/en/agent-sdk)

[8] OpenAI Agents SDK Sandbox 文档 (https://openai.github.io/openai-agents-python/sandbox/guide/)

[9] smolagents 官方文档 (https://huggingface.co/docs/smolagents/en/index)