LangChain V1.0 简介
终于,万众瞩目的 LangChain V1.0 版本正式发布了!
那对于最新的 V1.0 版本而言,其最显著的代码层面变化集中在 create_agent() 方法。如果你之前没有用过旧版本的LangChain,那么以下关于更新内容的介绍可以跳过不看。
首先,create_agent() 变成了构建 agent 的标准入口,它封装了底层 LangGraph 的 graph 执行机制,将“模型调用 → 决策工具 → 执行工具 → 返回结果”这一闭环流程封装在一个高阶接口中。所以们在文档中也可以看到这样一句话:
create_agent is the standard way to build agents in LangChain 1.0
其次,原来在早期版本中需要手动配置 prompts、 hook 、状态定义等细节,在 V1.0 中这些被抽象为 middleware 机制(如 before_model、wrap_tool_call 等),开发者可以通过传 middleware 数组把通用逻辑拼入,而不需改原 agent 核心逻辑。这大大的方便了开发者能够打造真正应用于生产的智能体(过去很多时候用 LangChain 的 create_react_agent 往往打造的只是一个“玩具”而已)。
再者,参数命名和入口路径也发生了迁移:如 prompt 变为 system_prompt。这意味着我们不需要再从 hub (已经移出核心框架迁移至 langchain-classic )里去输入大段的提示词了,只需要传入系统提示词就够了,LangChain 后台会自动帮我们组合成完整的提示词引导模型的行为。
除此之外,旧的 create_react_agent() 和 AgentExecutor 被替换并迁移至 langchain-classic 。同时过去的传统链(比如 LLMChain )和传统记忆方式(比如 CoversationBufferMemory) 也被迁移至 langchain-classic 。
这些改动意味着:如果你之前基于较早版本开发,迁移到 V1.0 将需要重构部分 import 路径、参数名称、状态定义方式、 agent 构建方式等。并且最好将其升级为最新的链(LCEL)和记忆(RunnableWithMessageHistory() 或 InMemorySaver())等方法。
总的来说,LangChain V1.0 在代码使用层面表现为“更统一、更可控、更生产就绪” —— 以 create_agent() 为核心入口,以 middleware 为扩展机制,以标准 message 块与统一接口为基础,为构建现代 LLM agent 系统提供了清晰、稳定的框架。所以下面的课程里,我们就一起来看看,如何基于最新版的 Agent 创建机制实现智能体的快速搭建吧!
前期准备
在正式开始之前,我们需要准备几个事情。第一个就是安装最新版的 LangChain。
复制然后就是到阿里云获取一下 API_Key,并且最好将其设置到环境变量之中(DASHSCOPE_API_KEY)。
那获取到了密钥后,只要能够成功运行以下代码就代表以准备完成:
复制快速搭建智能体
一、智能体简介
首先我们可以在官方文档看到 create_agent 的 graph 形式:
 图片
图片
从本质上来说,create_agent 打造的就是一个基础的 ReAct Agent 的形式,就是我们把问题传给大模型,然后让大模型思考需要使用什么工具(action),然后调用完工具获取到的结果(observation)返回给大模型。接着大模型再去不断的重复这个循环直到其认为已经搜集到足够的信息或者完成指定的任务后,就会退出这个循环(finish)并给出最终的回复。
那在这个场景下,create_agent 需要我们准备以下几个基本的组件:
- 模型(也就是前面的 ChatTongyi )
- 工具库(内置工具或自建工具)
- 记忆(帮助模型直到当前进行到哪一步)
- 提示词(引导模型)
那我们下面就来一个个的看看如何创建这些基本的组件并快速搭建一个智能体!
导入模型
那首先我们还是像前面一样准备好模型:
复制这里的模型我们选择 ChatTongyi 的主要原因不仅仅只是我们这个课程一直都用, 还因为在 LangChain 里其实就属通义千问的适配性最好,而且其还支持工具调用,具体后面我们会提到。
二、导入工具
前面提到 LangChain 里主要有两种方式导入工具,一种是内置工具,另外一种是自行创建,那我们分别进行阐述一下。
(1)内置工具
根据LangChain的官方文档[1],目前 LangChain 提供支持的有以下几类工具:
- 搜索工具:用于在线搜索,包括 Bing、Google、DuckDuckGo 等搜索接口。返回内容一般包括:URL、标题、摘要等。
- 代码辅助工具:支持 Python、JavaScript 等语言的代码执行环境,可用于复杂计算或文件处理。
- 生产力工具:用于对接 Gmail、Office365、Slack、Jira 等办公/协作平台,实现任务自动化。
- 网页浏览:用于浏览网页、交互操作、信息抓取(如Hyperbrowser 等)。
- 数据库:支持与数据库交互,包括 SQL、Spark SQL 等数据库的读取与操作。
- 其他常用工具:维基百科、ArXiv、Python REPL。
由于网络的原因,其实大部分的内置工具我们是用不了的,只有一些特定的工具我们能够使用,包括论文查询工具 arxiv、计算器工具 llm-math、维基百科 wikipedia 以及执行 python 代码的 python_repl 。
假如我们要导入这些内置工具的话,我们需要使用 langchain_community 里的 load_tools 方法进行载入。
复制假如我们需要导入多个工具的话,我们可以在后面不断添加(部分依赖语言模型来执行计算或生成代码需要传入一个 LLM 实例)。例如:
复制除此之外,load_tools这个方法还有一个实参是 allow_dangerous_tools,具体是在本地执行代码、读写文件等(如python_repl工具)是需要开启,因为运行 python 代码可能会涉及删除文件等操作。例如:
复制(2)自定义工具
自定义工具其实本质上就是定义一个函数,比如现在我有一个基于 eval() 的计算器工具函数 calculate:
复制假如我希望将其改造为一个 LangChain 所支持的工具的话我需要在函数上方加上一个 @tool 装饰器:
复制然后呢!还需要加上介绍函数相关信息的文档字符串以及输入输出参数说明:
复制当然其实我们除了函数的文档字符串以外,还可以添加输入输出参数的具体信息,比如我们可以通过 Pydantic 添加了一个输入参数的介绍信息,然后通过 @tool 中的 args_schema 将这部分信息载入:
复制这样的话这个工具在载入到 Agent 的时候就会创建成 Function Calling 的格式将这些信息传给 LLM 进行参考使用:
复制(3)工具的组合
在定义好多个自定义工具后,我们可以将其组装在一起传给 Agent ,比如:
复制但是假如我们想要的 LangChain 的内置工具一起传入的话,我们的写法需要变为:
复制那在后续的内容里,为了简化整个流程,我们这里就选用 arxiv 作为智能体的工具进行演示(需要先安装 arxiv,这在前期准备中已经完成):
复制三、构建短期记忆
在前面讲 LCEL 的内容里我们提到了,为了能够包裹一个 runnable 的组件,我们所使用的是 RunnableWithMessageHistory 的记忆方法。但是由于现在智能体的创建是基于 LangGraph 实现的,因此我们所使用的记忆方法就不再是这个了,而是使用 InMemorySaver() 的方法实现短期记忆的存储。
所谓的短期记忆呢,其实指的是只保存的是保存当前对话轮次、临时上下文,用于连续对话或局部推理。那 InMemorySaver() 的作用是在图(Graph)执行的过程中,保存 agent 的对话、状态、工具调用等信息,让系统在一次会话(session)中能“记住”先前的输入输出。
短期记忆的信息只存在与内存中(RAM),一旦程序关闭、刷新或重启,数据就会消失。假如我们需要构建长期的记忆,我们需要把记忆存储到数据库中,在后面的进阶内容中我们会更进一步的讲述到。
那创建这个记忆的方法也非常的简单,就是像之前的 CoversationBufferMemory 一样创建后就会自动的保存所有的信息了:
复制那在这个短期记忆里会怎么保存我们的记忆呢?其实和之前的类似,比如我们在与大模型对话的过程中常见的就有几类信息:
- 系统提示词(通常是第一条信息,用于引导模型整体的行为)
- 用户提示词(用户发出的问题)
- 模型提示词(模型对于用户问题的回复或调用工具的指令)
- 工具提示词(在模型发出执行工具的指令后返回的信息)
那这个短期记忆就会把这些所有的信息都保存到 AgentState 当中,这个 AgentState 其实一个用于管理 Agent 当前状态的结构(Schema),其默认格式大概是这样的:
复制那这里面最重要的其实就是 "messages" (其他都是一些辅助的元数据),这里就保存了智能体在每个时间点上需要记住的所有上下文信息。比如像这样简单的一问一答(当然这里面有很多的元数据也会保存起来):
复制那后续我们讲到记忆的剪裁以及删除等高级操作其实本质上都是对这个 AgentState 进行操作。
四、提示词模版及智能体组装
在前面的 LangChain 课程中我们是提到了两种提示词模版,一种是 PromptTemplate ,另一种是 ChatPromptTemplate 。前者简单直接,适合单轮指令或单输入;而后者支持多角色消息(System / Human / AI / Tool),并且可嵌入进 LCEL 表达式链,是 v1.0 的推荐写法。
那在 create_agent 中,其实有一个和前面 create_react_agent 最大的不同点是我们不需要使用一大段的提示词模版去引导模型进行回复了。比如说之前我们需要传入一个像 LangChain Hub 上 hwchase17/react-chat 这样的提示词了:
复制在这个提示词里我们可以看到其主要划分为五部分:
- 系统角色与能力设定
- 工具介绍
- 工具调用方式(ReAct 核心)
- 无需工具时的结束协议
- 运行时上下文注入(形成 ReAct 闭环)
那模型在运行时就会学着这里面的实现方式,比如在需要工具的时候输出:
复制然后 LangChain 通过正则表达式的解析去执行对应的工具返回结果写到 Observation 里:
复制但是这样的方法有两个问题,一个是假如某些模型的指令跟随能力相对比较弱一些,可能没有办法在多轮对话中一直保持这样输出的模式。一旦模版输出错误了,那就可能没有办法去解析了导致报错。另一个问题是其实这种模版有很多,其实也不通用,所以整体也不太方便。
因此 LangChain V0.3 开始就重构了 Agent 层,从而让开发者只描述能力,而不是再造 prompt。所以我们只需要输入模型、工具列表、记忆以及系统提示词(会在每一次传入给模型时才传入,不会放入记忆中)。然后在调用的时候传入用户提示词,这样 LangChain 会自动帮你构建完整的提示词上下文。
那假如我们没有直接在提示词里告诉大模型具有有哪些工具,每个工具怎么使用的话,那大模型怎么知道要用什么呢?其实这个时候就要讲到的是 LangChain 中的 bind_tools 方法。这个方法要求模型支持 OpenAI 的工具调用格式 Function Calling。在国内的大模型厂家中,明确说了模型支持 Function Calling 也就只有通义千问。这也是为什么在 LangChain 的所有课程中我们都是用 ChatTongyi 来进行演示的。
那什么是 Function Calling 呢?其实很简单,Function Calling 是大模型在对话过程中调用外部工具函数的能力,它允许模型在无法直接回答问题时,返回一个函数调用请求(结构化的JSON)。然后开发者需要手动解析这个请求,执行对应的函数,并将结果传回给模型,以生成最终答案。并在最后把结果返回给模型得到最终的回复。
比如说在 OpenAI 官方的这张图就很好的解释了 Function Calling 的运行过程:
- 第一步:把工具的信息以及问题传给模型
- 第二步:由于模型支持工具调用,就会返回一个调用请求
- 第三步:开发者解析调用请求并执行
- 第四步:将调用请求的结果返回给大模型
- 第五步:返回最终的回复

那在 LangChain 里的话,当我们的工具载入到了 create_agent 中时,其实就会自动的将工具注册为 Function Calling 的格式,并且每次调用的时候都会传给模型让其选择。然后假如模型决定要调用工具的时候,就会发出类似下面的这段请求:
复制我们可以看到这里的 content 是空的,代表模型其实并没有回复任何的内容,只不过是发出了一个 “tool_calls” ,然后里面写入了对应函数的名称,传入的参数以及一些元数据。然后在 create_agent 内部就会自动的去解析这部分的内容。
假如模型已经获取到了必要的信息,那其回复的内容应该就是下面这样的:
复制这样的方式就能够让模型更加准确的表达其执行的内容。而且 JSON 格式的内容也是更加的通用,所以 Function Calling 这个类似于 HTTPS 协议一样的工具也被大量的推行。
好了,扯得好像有点远,那既然我们不需要输入完整的提示词了,我们只需要输入系统提示词就足够了,那我们要在哪里去写入呢?其实当我们准备好前面的模型、工具及记忆以后,我们就在创建 create_agent 中写入就行,比如像下面这样:
复制这样我们就把我们想要传入的系统提示词给写入进去了。
五、调用智能体
那我们组装好了一个智能体了以后,下一步我们就是去调用智能体并且获取回复了。那本质上就是我们发送了一个用户提示词给到整体,比如像下面这样:
复制这里我们就定义了一个字典,然后往 messages 信息里传入了一个列表,这个列表里目前就是一条用户提问的信息。当然我们也可以往这个列表里面添加更多的信息进去,这个就是看大家具体的需求了。
除了传入用户提问的问题以外呢,还有一个需要额外指定一个 记忆定位标识。这就好比我们在使用 conda 创建虚拟环境时,会为每个项目单独建立一个环境,以避免依赖冲突。同样地,在对话系统中,我们也希望不同用户或不同任务的记忆彼此独立,不会相互干扰。为此,我们可以通过传入一个唯一的 thread_id(线程 ID)来区分不同的会话上下文。例如,下面的配置中,我们指定了 "user_1" 作为当前对话的标识:
复制这样,系统就会在内存中为 "user_1" 维护一份独立的对话记忆。如果下一次调用时仍然使用相同的 thread_id,模型就能“接着上次聊”;而如果换成一个新的 ID(例如 "user_2"),那么之前的记忆不会被继承,相当于开启了一场全新的对话。
准备好了这两个以后呢,我们就可以使用 LangChain 中最常用的调用方法 .invoke() 去对智能体进行调用了,具体的方法如下:
复制这样我们就可以成功的对模型进行调用了,调用的结果如下:
复制这里其实就把整个 agent 的运行过程都给打印出来了,包括:
- 用户输入:
- 模型调用工具:
- 工具执行并返回结果:
- 模型整合工具结果并生成回复:
- 最后输出 finish_reasnotallow='stop’ 表示任务完成。
这其实也是 ReAct Agent 的一个基本流程,假如我们要直接取最后一条信息作为输出的话,我们可以在打印的时候进行指定:
复制这个时候就只会回复下面的内容:
复制六、完整代码
完整的代码如下所示:
复制总结
总的来说,这节课我们完整讲解了 LangChain V1.0 智能体(Agent)创建的全新机制。新版的核心在于 create_agent() 方法,它取代了旧版的 create_react_agent() 和 AgentExecutor,成为构建智能体的标准入口。其底层基于 LangGraph 执行机制,将“模型调用 → 工具决策 → 工具执行 → 结果整合”这一完整闭环封装成统一流程,大幅提升了代码的简洁性与可维护性。
可以看到,LangChain V1.0 在设计上更加“工程化”和“生产可用”,让智能体的构建更加轻量、结构更清晰。那在下一节课中,我们将基于本节内容进一步优化智能体的交互能力——包括引入多工具协作、增强记忆管理与上下文控制,打造一个更接近真实应用的多轮智能体。
参考资料
[1] LangChain的官方文档: https://python.langchain.com/docs/integrations/tools/
 
                     
                 
                