Agent是一个能够自主决策和执行任务的Agent,它可以根据用户的输入和上下文信息来规划自己的行动,并利用工具来完成任务。LangChain和MAF针对Agent采用了完全不同的设计哲学和实现方式。虽然LangChain提供了针对Agent的不同创建方式,但是通过这些方式创建的Agent本质却没有什么不同。而MAF的设计则是,为Agent定义一个基类,通过继承该基类来创建不同类型的Agent,这些Agent在执行流程、状态管理、工具调用等方面都有可能有不同的实现。

1. LangChain

总的来说,LangChain提供了如下三种Agent创建方式:

1.1 LangGraph

我们根据推理任务为Agent定义一个状态类型,并基于此状态类型创建一个StateGraph,通过添加节点和边来构建一个图结构来定义Agent的推理流程,最后通过调用StateGraph的compile方法来将这个图结构编译成一个被我们视为Agent的CompiledStateGraph。Pregel是CompiledStateGraph基类,一个Pregel对象本质上是一个具有状态的Actor模型。Agent的状态通过一系列的通道来维护,作为Actor的节点通过订阅通道的变化来驱动执行,而它执行的结果体现为针对通道的更新。StateGraph针对Pregel的编译过程中,图的节点编程Pregel的节点,图的边体现为节点针对通道更新的订阅关系。

当作为Agent的Pregel对象被执行时,用户的输入被写入对应的通道,订阅此通道的节点被收集并创建对应的任务在下一个推理步骤中并发执行。节点任务执行后,又会更新对应的通道,新的通道更新又会触发新的节点任务的执行,如此循环往复直到没有新的节点被触发了,或者达到设定的步骤上限,此时整个推理过程就结束了。这个所谓的推理步骤被称为超步(Superstep),整个推进机制被称为BSP(Bulk Synchronous Parallel)机制。具体的执行流程可以参阅我的文章“驱动Node执行的原力”。

1.2 create_agent函数

我们将create_agent函数作为工厂,通过指定模型组件、工具列表、系统执行、状态和静态上下文Schema类型、结构化输出Schema类型、Checkpointer、存储和缓存以及中间件等参数来创建一个Agent对象。create_agent函数根据指定的模型和工具集创建的Agent,依然是采用上面这道机制创建的。默认状态类型为AgentStatemessages作为核心成员,承载了模型、用户和工具三者之间的对话历史create_agent函数依然会先创建作为Builder的StateGraph,并为其添加两个节点,一个用于封装模型(节点默认名称为model),另一个承载所有的工具(节点默认名称为tools)。

模型节点与工具节点之间具有一条动态条件边,当LLM返回的AIMessage包含工具调用时,被激活的这条边会路由到tools节点完成工具调用,反之则意味着AIMessage携带的就是最终的结果,整个推理过程就此结束。工具节点与模型节点有一条静态边,所以工具执行后会再次回到模型节点,后者在新的状态下完成下一步推理。

这种AgentState + 双节点的结构虽然简单,但却能满足常规的推理任务。对于更复杂的推理任务,一方面我们可以扩展AgentState,提供更多的状态成员来承载更多的上下文信息;另一方面我们也可以通过注册AgentMiddleware来添加更多的节点来完善工作流。具体来说,注册的AgentMiddleware提供了如下的功能:

  • 添加状态字段:如果AgentMiddleware涉及到针对状态更新,对应的状态字段会定义在state_schema字段返回的状态类型中。此状态状态类型通常是AgentState的子类,定义其中的字段最终会转换成通道;
  • 用于注册工具:当Middleware被注册到创建的Agent上时,存储在其tools字段中的工具会自动注册到Agent上。这相当于提供了一种模块化的工具开发和注册的方式;
  • 添加节点:当Middleware重写了before_agent/abefore_agentbefore_model/abefore_modelafter_model/aafter_modelafter_agent/aafter_agent方法,都会在状态图中相应的位置添加一个节点。Middleware相当于利用此方式完善了Agent的工作流;
  • 包装模型和工具调用:Middleware利用重写的wrap_model_call/awrap_model_callwrap_tool_call/awrap_tool_call方法对模型和工具的调用进行包装,将AOP引入到模型和工具的调用中,使得在调用前后添加一些额外的操作变得非常简单。比如很多Middleware都具有各自的系统提示词,它们基本上都是利用重写的wrap_model_call/awrap_model_call方法的方式实现针对系统提示词的注入;

1.3 create_deep_agent函数

Deep Agents提供create_deep_agent函数创建一个深度Agent,能够将复杂的规划、文件管理和多Agent协作能力一键集成到你的应用中。它其实最终也会调用create_agent函数来创建Agent,它仅仅在调用此方法时利用注册的Middleware为Agent添加了更多的功能,如任务规划、用于上下文管理的文件系统、Sub-Agent生成和长期记忆等功能。

从LangGraph和LangChain创建Agent的方式可以看出,它们相当于是自助餐和套餐的差别。LangGraph赋予完全的自由度,我们可以根据具体的推理任务对状态图进行DIY。虽然灵活自由,实则对用户提出了更高的要求。相比之下,LangChain利用create_agent函数创建的Agent具有固定结构,却能满足大部分的推理任务。它相当于提供了一款符合大众口味的基础套餐。在点餐的时候,我们可以选择这个基础套餐,也可以在此基础上添加、替换和剔除一些配菜来满足个性化的需求,这就是Middleware赋予的定制能力。

LangChain和Deep Agents的关系就像是基础套餐和升级套餐的关系。LangChain利用create_agent函数构建的Agent只具有基础的功能,任务定制的功能都需要通过注册Middleware来提供。企业级Agent来往往具有一些公共的功能需求,如任务规划、用于上下文管理的文件系统、Sub-Agent生成和长期记忆等功能。如果任何一个功能都要求用户通过注册Middleware来实现的话,无疑也会增加用户的使用门槛。于是Deep Agents对套餐进行了升级,在通过create_deep_agent函数创建Agent的时候,根据提供的配置自动注册了对应的Middleware。

综上所述,LangChain采用万法归一的设计哲学,create_agentcreate_deep_agent函数的背后依然是通过LangGraph的方式来创建Agent。所以整个LangChain平台所谓的Agent本质上就是一个Pregel对象,所以Agent在任何地方的执行方式都是一致的。这是认为LangChain在设计上优于MAF的一个主要的原因。

2. MAF

于LangChain类似,MAF提供Workflow来定义Agent的推理流程,Workflow可以视为MAF的LangGraph,不仅功能一致,背后的设计思路也几乎一样:

  • 都是基于同一篇文章(Pregel: a system for large-scale graph processing)来设计的;
  • 都是基于基于Superstep的BSP(Bulk Synchronous Parallel)机制来驱动Agent的执行;
  • 最终都体现为一个具有状态的Actor模型;

虽然LangChain提供了不同的Agent创建方式,由于最终的Agent都是利用LangGraph来创建的,所以从执行层面看Agent只有一种唯一的形式。但是对于MAF来说,这仅仅是Workflow的设计而且,它对应MAF众多Agent类型的一种。在它之上,MAF定义了一个抽象的AIAgent,然后提供了一系列不同类型的Agent实现。

LangChain和MAF都提供都通过消息关联的方式来在用户和Agent之间保持一种连续的对话状态。LangChain将维持对话的上下文称为Thread,而MAF则称之为Session,并通过抽象类AgentSession来表示。我们通过在每次调用关联一个AgentSession对象的方式当前调用纳入某个Session。作为基类的AIAgent定义了创建AgentSession的方法CreateSessionAsyncAgentSession对象自身就是一个承载可序列化会话状态的容器,所以AIAgent还定义了用来对它进行序列化和反序列化的SerializeSessionAsyncDeserializeSessionAsync方法。

public abstract class AIAgent
{    
    public ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
    => this.CreateSessionCoreAsync(cancellationToken);
    public ValueTask<JsonElement> SerializeSessionAsync(
        AgentSession session, 
        JsonSerializerOptions? jsonSerializerOptions = null, 
        CancellationToken cancellationToken = default)
    => this.SerializeSessionCoreAsync(session, jsonSerializerOptions, cancellationToken);
    public ValueTask<AgentSession> DeserializeSessionAsync(
        JsonElement serializedState, 
        JsonSerializerOptions? jsonSerializerOptions = null, 
        CancellationToken cancellationToken = default)
}

LangChain中并为将状态封装在一个容器对象中进行传递,会话状态总是以Checkpoint的形式基于thread_id进行存储,所以调用时只需要指定对应的thread_id就可以了,Agent会自动加载对应的Checkpoint来恢复状态。

针对AIAgent的调用分两种:

  • 阻塞式调用:通过如下这些重载的RunCoreAsync方法来完成,整个执行流程结束之后返回一个AgentResponse作为响应内容;
  • 流式响应:对应RunStreamingAsync方法,在执行过程中不断地响应中间结果。方法返回一个IAsyncEnumerable<AgentResponseUpdate>对象,AgentResponseUpdate代表了Agent在执行过程中的一个更新,可以是一个新的响应、一个工具调用的结果或者一个状态更新等。
public abstract class AIAgent
{        
    public Task<AgentResponse> RunAsync(
        IEnumerable<ChatMessage> messages,
        AgentSession? session = null,
        AgentRunOptions? options = null,
        CancellationToken cancellationToken = default);
    public async IAsyncEnumerable<AgentResponseUpdate> RunStreamingAsync(
        IEnumerable<ChatMessage> messages,
        AgentSession? session = null,
        AgentRunOptions? options = null,
        CancellationToken cancellationToken = default)
}

LangChain中的Agent类型都是继承自Pregel的CompiledStateGraph类型,因为都是通过StateGraph编译而成。但是MAF中的Agent可就多了,比如:

  • ChatClientAgent:这是MAF中最通用的Agent类型。它直接基于推理服务构建,可以适配任何实现了IChatClient接口的服务(如Azure OpenAI、OpenAI、Anthropic等)。支持函数调用(Function Calling)、多轮对话上下文管理以及结构化输出;
  • WorkflowHostAgent:用于在Agent体系中Host并运行完整的MAF Workflow。它将一个复杂的、基于图(Graph)的执行逻辑封装成一个普通的Agent接口。外部看来它只是一个Agent,但内部其实运行着一个包含多个Superstep的Pregel计算图。用于实现复杂任务的拆解、循环重试以及需要严格逻辑顺序的业务流程。这就是我们上面说得与LangChain Agent采用相同设计,基于MAF Workflowd的Agent类型;
  • A2AAgent:这是实现跨进程/跨网络协作的Agent。这是一种远程Agent,基于A2A(Agent-to-Agent)通信协议。它不一定在本地运行,可以代表运行在另一个服务、容器甚至另一个地理位置的Agent。它负责消息的序列化、传输和响应挂钩。在构建分布式的Multi-Agent系统,实现Agent之间的远程调用;
  • DurableAIAgent:DurableAIAgent是基于Durable Task Framework (DTF) 构建的有状态Agent。它利用与Azure Functions相同的状态重放机制,实现任务的持久化与断点续传。它支持长周期任务,即使进程重启也能恢复进度,且不依赖特定的云环境,适合高可靠性的企业级业务编排;
  • CopilotStudioAgent:这是低代码平台集成Agent。用于连接和调用Microsoft Copilot Studio中定义的机器人。它充当了MAF与Copilot Studio之间的桥梁,允许开发者在代码流中直接触发低代码平台配置好的对话路径、知识库查询或插件。利用它可以将业务人员在UI界面配置的机器人逻辑无缝整合到后端代码架构中;
  • GitHubCopilotAgent:这是开发生态集成Agent。专门用于接入GitHub Copilot扩展生态的Agent。它遵循GitHub Copilot Chat的协议规范,能够处理与代码生成、代码解释或 GitHub 生态相关的特定上下文。在 IDE扩展开发或自动化代码审查流程中,可以直接利用GitHub Copilot的专业模型能力;
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐