前言

上篇分享LangChain1.0速通指南(二)——LangChain1.0 create_agent api 基础知识笔者带大家学习了 create_agent API 的基本构建要素、消息机制与流式输出等核心操作。然而,create_agent 作为 LangChain 1.0 中新一代智能体构建的标准 API,其能力远不止于此。从工具调用的精细化控制,到记忆机制的灵活运用,再到细粒度的逻辑控制,它都提供了丰富而强大的高阶功能。本文作为该系列第三篇,将继续深入 create_agent 的相关知识体系,重点解析其在MCP接入,记忆管理和中间件机制的进阶用法,帮助大家真正掌握这一核心 API 的高级操作技巧,从而在实际项目中构建更智能、更可靠的 AI 应用。

本系列内容适合所有对 LangChain 感兴趣的学习者,无论之前是否接触过 LangChain。当然,如果大家已经学习过我的专栏《深入浅出LangChain&LangGraph AI Agent 智能体开发》,相信可以更快上手。该专栏基于笔者在实际项目中的深度使用经验,系统讲解了使用LangChain/LangGraph如何开发智能体,目前已更新 23 讲,并持续补充实战与拓展内容。欢迎感兴趣的同学关注笔者的CSDN账号与专栏,也可关注笔者的同名微信公众号 大模型真好玩,每期分享涉及的代码均可在公众号私信: LangChain智能体开发免费获取。

一、Tools With MCP

上篇文章笔者使用@tool装饰器自定义工具函数并将其接入 create_agent API。除了这种方法,LangChain 还支持集成当前备受关注的 MCP(Model Context Protocol)协议

1.1 认识 MCP 协议

MCP 是一个开放协议,它在大模型和外部应用程序之间定义了一套标准化的接口规范。通过 MCP,开发者可以将自己编写的功能封装成 MCP 服务并分享,其他开发者则可以快速集成和复用这些功能,实现了工具的“即插即用”。

LangChain 作为领先的大模型开发框架,对 MCP 提供了良好的支持。其核心集成流程与笔者之前介绍的工具函数接入方式相似:模型仍然会获得工具函数的详细使用说明(类似于自定义函数的文档字符串),但这些描述信息是通过与 MCP 服务器的协议通信获取的。不同的是,工具的执行不再发生在本地函数节点,而是在代理请求时由 MCP 服务器执行并返回结果。

在这里插入图片描述

1.2 实战:构建高德地图智能规划助手

下面笔者通过使用 create_agent 接入高德地图 MCP 服务的完整案例,来学习如何在 LangChain 中使用 MCP。

  1. 环境安装与配置:首先确保 LangChain 环境已激活,然后安装 MCP 适配器依赖:
conda activate langchainenvnew
pip install langchain-mcp-adapters

在这里插入图片描述

  1. 安装 Node.js:由于高德地图 MCP 服务基于 Node.js 开发,你需要在本地安装 Node.js 运行时环境。详细的安装配置流程可参考笔者的文章《不写一行代码! VsCode+Cline+高德地图MCP Server 帮你搞定和女友的出行规划(附原理解析)》。
  2. 代码实现与解析: 引入相关依赖并编写连接代码。通过 MultiServerMCPClient 类初始化 MCP 客户端,配置高德地图 MCP 服务连接参数,然后使用 get_tools 方法获取服务端提供的所有工具函数。特别注意:与 MCP 服务器的交互都是异步的,需要使用 Python 的 asyncio 库来异步获取服务器方法。对 Python 协程不熟悉的读者推荐先学习 廖雪峰的协程教程
import asyncio

from langchain.chat_models import init_chat_model
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import create_agent

model = init_chat_model(
    model="deepseek-chat",
    base_url="https://api.deepseek.com",
    api_key="你注册的deepseek api key"
)

mcp_client = MultiServerMCPClient(
    {
        "amap-maps": {
              "command": "cmd",
              "args": [
                "/c",
                "npx",
                "-y",
                "@amap/amap-maps-mcp-server"
              ],
              "env": {
                "AMAP_MAPS_API_KEY": "你注册的高德地图api key"
              },
              'transport': 'stdio'
            }
    }
)

async def get_server_tools():
    tools = await mcp_client.get_tools()
    print(f"加载了{len(tools)}: {[t.name for t in tools]}")



asyncio.run(get_server_tools())

运行上述代码,你会看到终端输出类似以下内容,表明已成功连接 MCP 服务。

在这里插入图片描述

  1. 现在利用 create_agent 构建集成高德地图能力的智能体。同样使用异步方法 ainvoke 来调用代理,改写代码如下:
async def get_server_tools():
    mcp_tools = await mcp_client.get_tools()
    print(f"加载了{len(mcp_tools)}: {[t.name for t in mcp_tools]}")
    agent_with_mcp = create_agent(
        model,
        tools=mcp_tools,
        system_prompt = "你是一个高德地图规划助手,能帮我规划形成和获得地图基本信息"
    )
    result = await agent_with_mcp.ainvoke(
        {
            "messages":{
                "role": 'user',
                "content": "请告诉我北京圆明园到北京西北旺地铁站距离"
            }
        }
    )
    for msg in result['messages']:
        msg.pretty_print()


asyncio.run(get_server_tools())

执行结果会显示完整的推理过程:

  • 首先调用 maps_geo 工具获取北京圆明园和西北旺地铁站的经纬度坐标
  • 然后调用 maps_distance 工具计算两地之间的距离
  • 最终输出自然语言形式的回答

在这里插入图片描述

上述示例的工作原理是在本地使用 npx 启动高德地图 MCP 服务端进程,该服务端封装了调用高德地图 API 的方法。LangChain 客户端作为 MCP 协议的客户端,通过标准输入输出(stdio)与本地服务端通信,获取并调用这些方法。关于 MCP 协议的更多技术细节和应用场景,大家可参考笔者的专栏《MCP怎么玩?》。同样的大家能够发现LangChain 集成 MCP 服务的过程非常简单高效。大家可以访问 魔搭社区 MCP 广场 探索更多有趣的 MCP 服务,并尝试用 LangChain 集成使用,这个就当作课后小练习啦!

二、结构化输出

在《LangChain 1.0 速通指南(一)——LangChain 1.0 核心升级》中曾提到,create_agent API 的一个重要特性是支持结构化输出。这一功能极大地方便了后续的数据处理与系统集成。本节将基于前面高德地图 MCP 的示例,演示如何让智能体返回标准化的距离信息,格式如下:

{
    loc1: 地址1
    loc2: 地址2
    distance: 两地址间距离
}

2.1 实战:LangChain结构化输出

1.定义输出数据结构: 首先引入必要的依赖,并使用 pydantic 库定义期望的输出格式。这里笔者创建一个 Result 类,明确指定需要返回的字段名称和类型。关于pydantic的使用大家可以参考笔者的文章 深入浅出LangGraph AI Agent智能体开发教程(六)—LangGraph 底层API入门

from langchain.agents.structured_output import AutoStrategy
from pydantic import BaseModel

class Result(BaseModel):
    loc1: str
    loc2: str
    distance: float

2.配置智能体的结构化输出: 修改 create_agent 的调用参数,通过 AutoStrategy 包装定义的 Result 类,并将其传递给 response_format 参数,智能体在获得原始结果后,会自动按照 Result 中定义的格式进行转换和封装。

agent_with_mcp = create_agent(
    model,
    tools=mcp_tools,
    system_prompt = "你是一个高德地图规划助手,能帮我规划形成和获得地图基本信息",
    response_format=AutoStrategy(Result)
)

3.查看执行结果: 运行代码后,可以看到智能体成功返回了符合预定结构的数据:

在这里插入图片描述

2.2 结构化输出策略解析

LangChain 提供了三种主要的结构化输出策略,各自适用于不同的场景:

  • ToolStrategy:利用模型本身的任务分解与工具调用能力来生成结构化输出。这种方法通用性强,适用于任何支持工具调用的模型,但依赖于模型自身的推理能力。
  • ProviderStrategy:直接使用模型提供商(如 OpenAI)原生的结构化输出功能。这种方法更加稳定可靠,但仅限于支持该特性的模型提供商。
  • AutoStrategy:智能选择最合适的结构化策略。它会自动检测当前使用的模型能力,优先选择 ProviderStrategy(如果可用),否则回退到 ToolStrategy,为开发者提供了最佳的兼容性和易用性。

在实际开发中,推荐优先使用 AutoStrategy,它能够在保证功能的前提下,最大化地简化配置流程,让开发者更专注于业务逻辑的实现。

三、Memory记忆管理

现在一起来探讨一个关键问题:当我们在同一个程序中连续向大模型提问两次,模型能够记住之前的对话内容吗?让我们通过一个简单的实验来验证

3.1 问题验证:智能体是否拥有记忆?

首先编写以下测试代码:

from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langchain.chat_models import init_chat_model

model = init_chat_model(
    model="deepseek-chat",
    base_url="https://api.deepseek.com",
    api_key="你注册的deepseek api key"
)

agent = create_agent(
    model=model,
)

result = agent.invoke(
    {
        "messages": "你好我叫苍进空?"
    }
)

for msg in result['messages']:
    msg.pretty_print()

result = agent.invoke(
    {
        "messages": "你好我叫什么名字?"
    }
)

for msg in result['messages']:
    msg.pretty_print()

执行结果如下:在这里插入图片描述

大家可能已经注意到了,在使用智能体时,默认情况下,智能体不会记住过去的请求。第二次提问时,模型完全忘记了之前提到的姓名信息。如何解决这个问题呢?这就需要用到create_agentmemory机制了。

3.2 记忆解决方案:langgraph记忆管理

要解决这个问题,我们需要利用 create_agentmemory 机制。create_agent 创建的智能体会将所有消息保存在一个 messages 列表中,可以通过将对话历史存储到系统内存中来维持对话的连续性(在打印的时候通过result['messages']取出列表中的数据)。

存储对话历史到内存中需要利用langchain的InMemorySaver()功能,它通过线程 ID(thread_id)来区分不同的对话会话。这类似于我们在使用 DeepSeek 等聊天服务时创建"新对话"的功能,本质上就是开启了一个新的线程会话。因此在传递参数时必须通过 configurable 参数明确指定 thread_id,这样智能体才能找到对应的历史对话记录。

编写如下代码赋予智能体管理历史记录的能力:

from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langchain.chat_models import init_chat_model
from langgraph.checkpoint.memory import InMemorySaver

model = init_chat_model(
    model="deepseek-chat",
    base_url="https://api.deepseek.com",
    api_key="你注册的deepseek api key"
)

agent = create_agent(
    model=model,
    checkpointer=InMemorySaver()
)

result = agent.invoke(
    {
        "messages": "你好我叫苍进空?"
    },
    {
        "configurable": {
            "thread_id": "1"
        }
    }
)

for msg in result['messages']:
    msg.pretty_print()

result = agent.invoke(
    {
        "messages": "你好我叫什么名字?"
    },
{
        "configurable": {
            "thread_id": "1"
        }
    }
)

for msg in result['messages']:
    msg.pretty_print()

执行结果如下:

在这里插入图片描述

现在智能体能够准确记住用户的名字是"苍进空",实现了真正的多轮对话能力。

3.3 LangChain记忆管理架构解析

从导入的包 langgraph.checkpoint.memory 可以看出,LangChain 1.0 的记忆管理实际上是基于 LangGraph 的内置功能实现的。这种设计体现了 LangChain 架构的模块化思想,将核心能力委托给专门的组件处理。

关于 LangGraph 记忆管理的完整技术解析,读者可以参考笔者的文章《深入浅出 LangGraph AI Agent 智能体开发教程(九)— LangGraph 长短期记忆管理》。本节内容主要聚焦于短期记忆的内存会话临时存储,这是构建流畅对话体验的基础能力。

多轮对话历史对于用户体验至关重要。只有当智能体能够记住之前的对话状态时,用户才能与之进行自然、连贯的互动,这也是构建真正智能对话系统的核心要素之一。

四、中间件机制

中间件机制是 create_agent API 的核心特性之一,它为开发者提供了在智能体执行关键节点进行干预和定制的能力。智能体在执行过程中会经历多个关键生命周期阶段,LangChain 在这些关键节点为开发者提供了高度定制化的入口。

通过中间件,开发者可以实现动态提示词控制、对话历史摘要、选择性工具调用、状态管理及安全护栏等重要功能,从而大幅提升智能体的功能上限和可靠性

在这里插入图片描述

钩子函数 触发时机 应用场景
before_agent 在调用代理之前 加载记忆数据、验证输入
before_model 在每次大模型调用之前 更新提示词、精简消息历史
wrap_model_call 围绕每次大模型调用(可拦截) 拦截并修改请求/响应
wrap_tool_call 围绕每次工具调用(可拦截) 拦截并修改工具执行过程
after_model 在每次大模型返回响应之后 验证输出内容、应用安全护栏
after_agent 在代理完成运行之后 保存结果、执行清理操作

4.1 LangChain预置中间件

LangChain为常见场景提供了以下预置中间件:

  • PIIMiddleware:在发送至模型前自动屏蔽敏感信息
  • SummarizationMiddleware:当对话历史过长时自动进行内容浓缩
  • HumanInTheLoopMiddleware:敏感工具调用需经人工审批

4.1.1 HumanInTheLoopMiddleware实战演示

在某些关键业务场景中,智能体需要人工干预来确保操作的安全性。例如,在执行数据库写入操作前必须经过人工确认,否则可能因误操作导致数据丢失或系统崩溃。

下面笔者基于上篇内容中的天气助手案例,演示如何使用 HumanInTheLoopMiddleware 实现人工审批机制。

  1. 环境初始化: 首先引入相关依赖,定义工具函数、模型和系统提示词三件套
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langchain.chat_models import init_chat_model
from langchain.tools import tool
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import Command


@tool
def get_weather(loc:str)->str:
    """
    根据地点参数可以返回该地点的天气情况
    """
    return f"{loc} 天气是晴!气温23°"

SYSTEM_PROMPT = "你是一个天气助手,具备调用get_weather天气函数获取指定地点天气的能力"

model = init_chat_model(
    model="deepseek-chat",
    base_url="https://api.deepseek.com",
    api_key="你注册的deepseek api key"
)
  1. 配置人工审批中间件: 在 create_agent 中传入 HumanInTheLoopMiddleware,配置在执行 get_weather 函数前需要用户确认。用户输入 approve 表示允许执行,输入 reject 表示拒绝执行。(HumanInTheLoopMiddleWare需要记忆管理参与,毕竟中间会中断执行,相当于多轮对话)
agent = create_agent(
    model=model,
    tools=[get_weather],
    system_prompt=SYSTEM_PROMPT,
    middleware=[HumanInTheLoopMiddleware(
        interrupt_on={
            "get_weather": {
                "allowed_decisions": ["approve", "reject"]
            }
        }
    )],
    checkpointer=InMemorySaver()
)
  1. 测试审批通过场景: 传入线程id, 如果检测到__interrupt__表示程序在这里阻塞了,需要人类确认结果,这里使用Command指令通过resume模拟人类反馈结果为同意,查看输出:

config = {
    "configurable": {
        "thread_id": "1"
    }
}

result = agent.invoke(
    {
        "messages": "今天北京天气怎么样?"
    },
    config
)

if "__interrupt__" in result:
    result = agent.invoke(
        Command(
            resume={"decisions": [{"type": "approve"}]}
        ),
        config
    )

for msg in result['messages']:
    msg.pretty_print()

执行结果显示工具函数成功执行,返回了北京的天气信息。

在这里插入图片描述

  1. 测试审批拒绝场景: 现在测试用户拒绝工具执行的情况,并向消息流中添加拒绝说明
result = agent.invoke(
    {
        "messages": "今天北京天气怎么样?"
    },
    config
)

if "__interrupt__" in result:
    result = agent.invoke(
        Command(
            resume={"decisions": [{"type": "reject", "message": "用户拒绝执行"}]}
        ),
        config
    )

for msg in result['messages']:
    msg.pretty_print()

执行结果显示工具调用被拒绝,智能体根据拒绝信息生成了相应的回复。

在这里插入图片描述

4.1.2 HumanInTheLoopMiddleware中间件原理

通过查看 HumanInTheLoopMiddleware 中间件的源码,我们发现它继承了 AgentMiddleware 基类。主要通过重写 after_model 方法来实现审批机制。其核心工作原理是:

  1. 调用拦截:在模型输出信息后,工具执行前进行拦截,暂停智能体的正常执行流程
  2. 状态挂起:将当前执行状态保存到检查点,等待外部输入
  3. 决策处理:根据用户的审批决定(批准/拒绝)决定是否执行工具
  4. 流程恢复:基于审批结果恢复执行流程,并注入相应的上下文信息

这种机制确保了关键操作始终在人工监督下进行,为构建安全可靠的AI应用提供了重要保障。

在这里插入图片描述

4.2 自定义中间件

理解了预置中间件的实现逻辑后,笔者现在来动手实现一个自定义中间件。随着智能体处理任务的复杂度不断提升不仅需要在初始阶段设置模型的总体角色,还需要在任务执行的不同阶段、步骤和异常情况下动态调整模型行为。

接下来笔者完成一个实用示例:基于用户专业级别的动态模型选择。如果用户被识别为专业人员,我们使用 Deepseek 模型;否则使用 Qwen3-8b模型。这种策略可以在保证服务质量的同时优化资源使用效率。

我们将使用硅基流动的 Qwen3-8B 模型和 DeepSeek 模型进行演示。

  1. 环境初始化与上下文格式定义: 首先引入相关依赖,定义两个大模型和上下文格式。使用dataclasses定义的Context 类格式可以理解为智能体执行时的全局变量容器。在智能体执行流程中,工具函数、中间件等组件可以通过 .context.变量名 的方式访问这些全局变量。下面代码定义了一个包含 user_level 字段的上下文结构。
from dataclasses import dataclass
from typing import Callable

from langchain.agents import create_agent
from langchain.agents.middleware import AgentMiddleware, ModelRequest, ModelResponse
from langchain.chat_models import init_chat_model

@dataclass
class Context:
    user_level: str = "expert"

deepseek_model = init_chat_model(
    model="deepseek-reasoner",
    base_url="https://api.deepseek.com",
    api_key="你注册的deepseek api key"
)

Qwen3_model = init_chat_model(
    model="Qwen/Qwen3-8B",
    model_provider="openai",
    base_url="https://api.siliconflow.cn/v1/",
    api_key="你注册的硅基流动 api key",
)
  1. 实现自定义中间件:定义 ExpertiseBasedToolMiddleware 中间件,它继承自 AgentMiddleware 类,并重写 wrap_model_call 方法。该中间件在模型调用前根据上下文中的用户级别信息动态选择模型:
class ExpertiseBasedToolMiddleware(AgentMiddleware):
    def wrap_model_call(
        self,
        request: ModelRequest,
        handler: Callable[[ModelRequest], ModelResponse]
    ) -> ModelResponse:
        user_level = request.runtime.context.user_level

        if user_level == "expert":
            model = deepseek_model
            tools = []
        else:
            # Less powerful model
            model = Qwen3_model
            tools = []

        request.model = model
        request.tools = tools
        return handler(request)

agent = create_agent(
    model=Qwen3_model,
    tools=[],
    middleware=[ExpertiseBasedToolMiddleware()],
    context_schema=Context,
)
  1. 测试专家用户场景:首先测试 user_level 为专业用户的情况,传入上下文全局变量,首先尝试user_level为专业用户,查看执行结果
question = "你好请问你是?"

for step in agent.stream(
    {"messages": {'role':'user', 'content':question}},
    context=Context(user_level='expert'),
    stream_mode="values",
):
    step['messages'][-1].pretty_print()

在这里插入图片描述

执行结果显示智能体使用了 DeepSeek 模型进行响应,这表明中间件成功根据用户级别动态选择了模型。

  1. 测试普通用户场景: 现在测试非专业用户场景,修改 user_level 为普通用户:
for step in agent.stream(
    {"messages": {'role':'user', 'content':question}},
    context=Context(user_level='student'),
    stream_mode="values",
):
    step['messages'][-1].pretty_print()

在这里插入图片描述

执行结果显示智能体使用了 Qwen3-8B 模型进行响应,这表明中间件成功根据用户级别动态选择了模型。

以上就是自定义中间件的简单实现,是不是很强大,更多中间件的使用技巧笔者这里不再赘述,大家详细参考官方文档满血教程~

五、总结

本篇内容深入分享了 LangChain 1.0 create_agent API 的高阶功能,涵盖 MCP 协议工具集成、结构化输出、记忆管理和中间件机制四大核心能力。通过高德地图 MCP 接入、动态模型选择等实战案例,展示了如何构建具备外部工具调用、记忆保持和流程可控的智能体应用。

这两期内容系统讲解了 create_agent API 的核心功能体系,为掌握 LangChain 1.0 智能体开发奠定了坚实基础。LangChain1.0 核心组件和生态真正的熟练运用仍需在实际项目中不断实践。接下来,笔者将推出基于 LangChain 1.0 构建的多模态知识库系统实战,该系统采用前后端分离架构,基本复刻了当前企业级多模态知识库的完整技术方案,大家敬请期待!

《深入浅出LangChain&LangGraph AI Agent 智能体开发》专栏内容源自笔者在实际学习和工作中对 LangChain 与 LangGraph 的深度使用经验,旨在帮助大家系统性地、高效地掌握 AI Agent 的开发方法,在各大技术平台获得了不少关注与支持。目前已更新24讲,正在更新实战篇和LangChain1.0速通指南速通指南篇,并随时补充笔者在实际工作中总结的拓展知识点。如果大家感兴趣,欢迎关注笔者的CSDN账号与专栏,也可关注笔者的同名微信公众号 大模型真好玩,每期分享涉及的代码均可在公众号私信: LangChain智能体开发免费获取。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐