我为什么又开始手写Agent框架了?从CrewAI和LangGraph的局限谈起


                                                                                                                                                <p><img src="https://oscimg.oschina.net/oscnet/up-5f5c51faee69514add46b5e416f4af48500.png" alt=""></p> 

一、 引言:被”框架”困住的我们

嗨,大家好,我是技术老金。

最近,我发现自己陷入了一个有趣的困境。

当我想快速搭建一个多智能体(Multi-Agent)应用时,我首先想到了CrewAI。它就像一个精装修的公寓,角色、任务、流程都替你定义好了,拎包入住,很快就能跑起来。但只要我想稍微改动一下”房型”,比如让两个Agent先碰个头,或者根据某个工具的执行结果,动态决定下一步谁来接手,我就会发现自己被困在了这精美的”枷锁”里,动弹不得。

于是,我转向了LangGraph。它就像一个堆满了顶级建材(节点、边、状态)的”毛坯房”,给了我无限的自由。我可以随心所-欲地设计任何我想要的流程,循环、分支、判断,无所不能。但很快,新的问题来了:我发现我不仅要设计流程,还要亲自设计状态管理、消息传递、工具调用、错误处理……我需要从零开始,为这个毛坯房设计一整套”水电煤”系统。

我们似乎被困在了两难的境地:要么选择一个僵化的”应用级”框架,要么选择一个过于灵活的”引擎级”工具。

有没有一条中间道路?

在反复挣扎和实践后,我得出了一个结论:有。那就是在LangGraph这种强大的”引擎”之上,构建一层我们自己的、轻量级的、符合自己团队心智模型的”协作层”。

今天,我就来聊聊,我为什么放弃了直接使用现成的框架,又开始”手写”这薄薄的一层,以及这一层到底解决了什么核心问题。

二、 CrewAI的”美丽枷锁”:当规范大于灵活

CrewAI的初衷是好的,它试图将构建Agent的过程,标准化为”角色(Agent)-任务(Task)-流程(Process)-船员(Crew)”这套模型。对于很多标准场景,这套模型非常高效。

但它的核心问题在于,它是一个**”强干预”**的框架。它为你做了太多决策,而这些决策,往往与你真实的、复杂的业务逻辑相悖。

它的主要局限在于:

  1. 僵化的线性流程:默认情况下,CrewAI的任务是串行执行的。你想实现一个”A和B并行,然后结果汇总给C”的流程,会非常别扭。
  2. 模糊的状态管理:Agent之间的数据传递,很大程度上依赖于一个全局的、非结构化的”上下文黑盒”。你很难精确地控制,在任务的某个阶段,哪些信息是可见的,哪些是不可见的。
  3. 控制权的缺失:整个流程的调度,是由CrewAI的内部机制黑盒管理的。你无法在流程中途,根据一个外部事件或一个工具的执行结果,来动态地改变整个”剧本”。

总而言之,CrewAI更像一个**”内置了固定协作模式的Agent应用”,而不是一个让你设计协作模式的“框架”**。

三、 LangGraph的”自由与混沌”:当引擎只是引擎

LangGraph则走向了另一个极端。它极其强大,也极其”无情”。

它给了你构建任何复杂图形(Graph)的能力,但它对”什么是Agent”、”Agent之间如何对话”这些核心问题,不提供任何默认的解决方案

使用LangGraph,你很快就会意识到:

  1. 你需要自己定义”状态”:这个状态(State)里,应该包含哪些字段?当前轮到哪个Agent?消息列表应该是什么格式?这些都需要你自己来设计。
  2. 你需要自己实现”调度”:当一个Agent完成了它的任务,下一个应该由谁来接手?这个核心的调度逻辑,LangGraph把它作为一个普通的”条件边”(Conditional Edge)交给了你,你需要自己编写一个复杂的函数来实现。
  3. 你需要自己管理”工具调用”:Agent调用工具的请求、工具执行的结果,如何优雅地整合进你的状态流转中?LangGraph提供了模式,但没有现成的实现。

LangGraph是一个顶级的**”工作流引擎”,但它本身,并不是一个“多智能体协作框架”**。它给了你造车的顶级发动机和变速箱,但车架、底盘、方向盘,都得你自己来造。

四、 破局之道:构建我们自己的”协作层”

现在,我的思路清晰了:我们真正需要的,不是另一个庞大的框架,而是在LangGraph这个坚实底座上,构建一层薄薄的、但至关重要的**”协作层”**。

这一层的核心,只解决三个问题:

  1. 一个统一的”世界状态” (World State):定义一个所有Agent都认可的数据结构,用于记录全局信息、消息历史和任务状态。
  2. 一个中心化的”调度器” (Dispatcher):它本身是LangGraph中的一个特殊节点,负责读取”世界状态”,并决定下一个应该被唤醒的Agent。
  3. 一套标准的”交互协议” (Interaction Protocol):定义Agent如何向状态里写入消息,以及调度器如何解析这些消息。

我们来看一个极简的伪代码实现,你马上就能明白:

from typing import TypedDict, List, Literal
from langgraph.graph import StateGraph, END
from operator import itemgetter

# 1. 定义我们自己的"世界状态"
class AgentWorldState(TypedDict):
    task: str
    messages: List[tuple[str, str]] 
    next_agent: Literal["Researcher", "Writer", "FINISH"]

# 2. 定义Agent节点,展示它如何与"世界状态"交互
def researcher_agent_node(state: AgentWorldState):
    task = state['task']
    print(f"--- [Agent: 研究员] 开始工作,任务: {task} ---")
    research_result = f"这是关于'{task}'的研究成果。"
    # 注意:这里返回的是一个包含元组的列表,以支持状态的累加
    return {"messages": [("Researcher", research_result)]}

def writer_agent_node(state: AgentWorldState):
    messages = state['messages']
    print(f"--- [Agent: 作家] 开始工作 ---")
    writing_result = f"基于以下研究成果:\n{messages[-1][1]}\n\n我完成了最终报告。"
    return {"messages": [("Writer", writing_result)]}

# 3. 定义我们的核心"调度器"节点
def dispatcher_node(state: AgentWorldState):
    last_message_sender = state['messages'][-1][0] if state['messages'] else "START"

    if last_message_sender == "Researcher":
        return {"next_agent": "Writer"}
    elif last_message_sender == "Writer":
        return {"next_agent": "FINISH"}
    else: # START
        return {"next_agent": "Researcher"}

# 4. 在LangGraph中组装
workflow = StateGraph(AgentWorldState)
workflow.add_node("researcher", researcher_agent_node)
workflow.add_node("writer", writer_agent_node)
workflow.add_node("dispatcher", dispatcher_node) 

workflow.set_entry_point("dispatcher")

workflow.add_conditional_edges(
    "dispatcher",
    itemgetter('next_agent'),
    {
        "Researcher": "researcher",
        "Writer": "writer",
        "FINISH": END
    }
)

workflow.add_edge("researcher", "dispatcher")
workflow.add_edge("writer", "dispatcher")

# 编译
app = workflow.compile()

# 运行
inputs = {"task": "AI在软件开发中的作用", "messages": []}
for s in app.stream(inputs, {"recursion_limit": 10}):
    print(s)
    print("----")

看明白了吗?通过这层薄薄的封装,我们做到了:

  • 解耦 :Agent之间不直接对话,而是通过向messages列表追加消息来通信。
  • 中心化控制 :所有的流程走向,都由dispatcher这个唯一的”大脑”来决定。
  • 无限的扩展性 :我们可以让dispatcher变得无比智能,它可以调用一个LLM来做决策,也可以集成复杂的业务规则。

五、 老金总结:从”框架使用者”到”架构设计者”

我们之所以要”手写”这一层,不是为了重复造轮子,而是为了夺回控制权

CrewAI这样的框架,让你成为了一个熟练的”司机”,但它限制了你能走的路。LangGraph给了你最强的”引擎”,但它要求你成为一个”机械师”,从零件开始造车。

而我们今天探讨的思路,是让你成为一个**”汽车设计师”**。你使用最专业的引擎(LangGraph),但你亲自设计汽车的底盘和传动系统(我们的协作层),最终组装出一辆最适合在你的业务道路上飞驰的、独一-无二的赛车。

这,或许才是AI时代,我们架构师真正的价值所在——不满足于使用工具,而是有能力、有思想地去组合与驾驭工具。

觉得有用,别忘了给老金点个赞,关注一下!


[版权声明]

本文由”技术老金”原创首发于个人博客及微信公众号『技术老金』,转载请注明出处。

                                                                                </div>



Source link

未经允许不得转载:紫竹林-程序员中文网 » 我为什么又开始手写Agent框架了?从CrewAI和LangGraph的局限谈起

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
关于我们 免责申明 意见反馈 隐私政策
程序员中文网:公益在线网站,帮助学习者快速成长!
关注微信 技术交流
推荐文章
每天精选资源文章推送
推荐文章
随时随地碎片化学习
推荐文章
发现有趣的