LangChain_v1_Agent快速开发和更新说明

LangChain v1 Agent 开发实战:从框架演进到上下文与记忆

大语言模型最初更像一个只负责生成文本的接口:输入提示词,模型返回答案。随着应用需求从"回答问题"走向"完成任务",模型还需要查询外部信息、调用业务接口、保存对话状态,并根据执行结果调整下一步行动。Agent 正是在这一背景下成为 LLM 应用的重要形态。

LangChain v1 对 Agent 开发方式进行了明显收敛:开发者使用统一的 create_agent 入口组织模型、工具和提示词,底层则由 LangGraph 提供状态管理、持久化、流式输出和人工审批等能力。这种分层让简单应用保持简洁,也为复杂应用保留了扩展空间。

本文将从 LangChain 的演进逻辑出发,说明 Agent 的运行原理、Agent 与 Graph 的选型方式,并通过两个示例展示如何逐步构建具备上下文、结构化输出和记忆能力的 Agent。

从"链式调用"到 Agent 应用

LangChain 最早提供的核心能力可以概括为两部分:对不同大语言模型的统一抽象,以及将多个步骤串联起来的 Chain。以 RAG 为例,系统通常先检索相关资料,再将检索结果交给模型生成答案。这类任务的执行路径由开发者预先确定,适合流程稳定、输入输出边界清晰的场景。

随后,ReAct 思想被引入通用 Agent。模型不再只是流程中的一个处理节点,而是能够决定调用哪个工具、传入什么参数,以及是否需要根据工具结果继续行动。模型提供商陆续推出原生函数调用能力后,工具调用也从解析模型生成的 JSON,逐渐转向更可靠的结构化协议。

当 Agent 开始进入生产环境,仅有工具调用还不够。开发者还需要解决运行轨迹追踪、效果评估、状态持久化、流式响应、人工审批和故障恢复等问题。LangGraph 因此承担起底层编排职责,而 LangChain 则继续提供更易用的高层开发接口。

LangChain v1 的整体方向可以概括为三个关键词:

  • 统一 :使用 create_agent 作为构建 Agent 的标准入口。
  • 精简:核心命名空间集中保留模型、消息、工具和 Agent 等基础模块。
  • 可扩展:通过 LangGraph 和中间件支持持久化、流式输出、状态管理与上下文工程。

对于仍依赖旧版 Chain、检索器、索引接口或 Hub 模块的项目,可以使用 langchain-classic 作为兼容方案;新项目则更适合围绕 v1 的 Agent 接口组织代码。

LangChain v1 带来了什么

统一的 Agent 构建入口

create_agent 将模型、工具、系统提示词、上下文、响应格式和记忆组件汇集到一个入口。它的底层运行逻辑并不神秘:

text 复制代码
接收用户消息
    ↓
调用模型进行判断
    ↓
模型是否请求调用工具?
    ├─ 是:执行工具 → 将结果加入消息 → 再次调用模型
    └─ 否:返回最终回答

这个循环为简单 Agent 提供了足够清晰的心智模型。开发者负责提供能力边界,模型负责在边界内做决策。

基于 LangGraph 的运行能力

通过 LangGraph,create_agent 创建的 Agent 可以获得多项工程能力:

  • 使用检查点保存对话状态,使会话能够持续进行;
  • 流式返回模型生成内容、工具调用和执行进度;
  • 在转账、删除、发送消息等敏感操作前暂停,等待人工确认;
  • 回到已有状态并从新的输入继续执行,用于调试或探索其他路径。

这意味着开发者可以先用高层 API 快速完成业务,再按需要逐步引入更细粒度的控制,而不必一开始就手写完整状态图。

跨模型的标准消息内容

不同模型提供商可能返回文本、推理内容、工具调用、引用或多模态数据。v1 使用 content_blocks 提供统一访问方式,让上层业务尽量少依赖某一家模型的原始响应结构。

python 复制代码
response = model.invoke("What's the capital of France?")

for block in response.content_blocks:
    if block["type"] == "reasoning":
        print("Reasoning:", block["reasoning"])
    elif block["type"] == "text":
        print("Answer:", block["text"])
    elif block["type"] == "tool_call":
        print("Tool:", block["name"], block["args"])

统一内容格式的价值不只在于代码更短,更重要的是减少模型切换和多模型协同时的适配成本。

Agent 的核心:推理、行动与反馈

一个可执行任务的 Agent 通常由三部分组成:

text 复制代码
Agent = LLM 推理引擎 + Tools 执行能力 + 自主决策循环

LLM 理解用户意图并决定下一步,Tool 负责读取数据或改变外部系统,控制循环则把工具返回结果重新交给模型,直到任务完成或达到迭代上限。

这一过程常用 ReAct 来描述,即 Reasoning、Acting 和 Observation 的循环。以"上海明天天气如何"为例,Agent 的内部工作过程可以抽象为:

  1. 判断问题需要实时天气数据,现有上下文不足以直接回答。
  2. 调用天气工具,并传入城市和日期参数。
  3. 读取工具返回的天气情况。
  4. 判断信息是否足够;如果足够,则组织最终答案。
  5. 如果工具报告参数错误,则分析原因、修正参数并重新调用。

与固定流程相比,ReAct 的优势在于它能够根据反馈调整行动。不过,自主性也会带来不确定性,因此生产系统仍然需要明确的工具描述、参数校验、迭代上限、超时策略和敏感操作审批。

Agent 与 Graph 应该怎么选

Agent 和 Graph 不是互相替代的两条路线,它们解决的是不同问题。

对比维度 Agent Graph
核心特征 自主决策 确定流程
执行路径 运行时由模型动态选择 开发时预先定义
典型入口 对话、自然语言任务 按钮、API、定时任务
适合场景 开放式问答、多步骤问题解决 转录、审核、生成报表等固定流程
主要风险 行为不确定、成本难预测 灵活性有限、分支需要显式维护

可以用三个问题完成初步选型:

  • 任务路径完全确定吗?如果确定,优先使用 Graph。
  • 任务需要多轮对话和动态决策吗?如果需要,使用 Agent。
  • 整体目标开放,但其中包含必须稳定执行的子流程吗?使用 Agent 与 Graph 的组合。

例如,"生成上周销售周报"可以由 Agent 理解用户意图和时间范围,再调用一个固定的 Graph。Graph 内部依次完成拉取数据、分析指标、生成图表和排版输出。这样既保留了自然语言交互的灵活性,又保证关键业务流程具有可预测性。

两者通常有两种组合方式:把 Agent 作为 Graph 中的决策节点,或者把完整 Graph 封装成 Tool 交给 Agent 调用。实践中,后者很适合复用已经稳定运行的业务流程。

构建一个最小可用 Agent

先安装核心包和对应的模型集成:

bash 复制代码
pip install -U langchain langgraph langchain-openai

同时按照模型提供商的要求配置 API Key。下面使用一个模拟天气工具展示最小结构:

python 复制代码
from langchain.agents import create_agent
from langchain.tools import tool


@tool
def get_weather_for_location(city: str) -> str:
    """获取指定城市的天气信息。"""
    return f"{city}今天晴朗。"


agent = create_agent(
    model="gpt-5-mini",
    tools=[get_weather_for_location],
    system_prompt="你是一位乐于助人的天气助手。",
)

response = agent.invoke(
    {
        "messages": [
            {"role": "user", "content": "北京的天气如何?"}
        ]
    }
)

print(response["messages"][-1].content)

这个示例虽然很小,却包含了 Agent 的三个基本构件:

  • model 是决策核心;
  • tools 定义可以执行的外部能力;
  • system_prompt 规定角色、目标和行为边界。

运行时,模型先判断是否需要工具。如果需要,它会生成结构化工具调用;框架执行工具并把结果写回消息列表;模型读取结果后再生成最终回答。整个过程中,Agent 状态会维护完整的消息轨迹。

需要注意的是,示例工具返回的是模拟数据。真实项目应在工具内部调用可靠的数据源,并补充超时、异常处理和返回值校验。

加入上下文、结构化输出与记忆

真实应用往往还需要回答三个问题:当前用户是谁、输出应该遵循什么格式、系统应该记住哪些信息。下面在基础示例上加入这些能力。

python 复制代码
from dataclasses import dataclass

from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.tools import ToolRuntime, tool
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.store.memory import InMemoryStore


SYSTEM_PROMPT = """你是一位天气助手。
你可以使用以下工具:
- get_weather_for_location:查询指定城市的天气
- get_user_location:根据当前用户获取所在城市

当用户询问天气却没有说明地点时,先调用 get_user_location,
再调用 get_weather_for_location。不要猜测用户位置。
"""


@dataclass
class Context:
    user_id: str


@dataclass
class WeatherResponse:
    answer: str
    weather_conditions: str | None = None


@tool
def get_weather_for_location(city: str) -> str:
    """获取指定城市的天气信息。"""
    return f"{city}今天晴朗。"


@tool
def get_user_location(runtime: ToolRuntime[Context]) -> str:
    """根据当前用户 ID 获取所在城市。"""
    user_id = runtime.context.user_id
    store = runtime.store

    profile = store.get(("users",), user_id)
    if profile is None:
        default_city = "北京" if user_id == "1" else "上海"
        store.put(("users",), user_id, {"city": default_city})
        return default_city

    return profile.value["city"]


model = init_chat_model("gpt-5-mini", temperature=0)
checkpointer = InMemorySaver()
store = InMemoryStore()

agent = create_agent(
    model=model,
    name="weather_agent",
    system_prompt=SYSTEM_PROMPT,
    tools=[get_user_location, get_weather_for_location],
    context_schema=Context,
    response_format=WeatherResponse,
    checkpointer=checkpointer,
    store=store,
)

config = {"configurable": {"thread_id": "conversation-001"}}

response = agent.invoke(
    {
        "messages": [
            {"role": "user", "content": "外面的天气怎么样?"}
        ]
    },
    config=config,
    context=Context(user_id="1"),
)

print(response["structured_response"])

这段代码中有几个容易混淆但非常重要的概念。

运行时上下文

Context 保存本次调用需要使用、但不应该混入自然语言消息的数据,例如用户 ID、租户 ID、权限信息或请求来源。工具通过 ToolRuntime[Context] 读取这些信息,不需要让模型生成或转述它们。

这种设计比把用户 ID 拼进提示词更可靠,也更容易进行权限控制。

结构化输出

WeatherResponse 规定最终结果必须包含 answer,并允许附带 weather_conditions。业务代码可以直接读取字段,而不必再次解析自然语言。

结构化输出适合 API 返回、前端卡片渲染、数据入库以及后续自动化流程。字段设计应只包含业务真正需要的数据,避免为了"看起来完整"而堆积大量可选字段。

短期记忆

checkpointerthread_id 保存同一段对话的状态。继续使用相同的 thread_id 调用 Agent,系统就能恢复之前的消息,并理解"刚才那个城市""继续查询明天"等依赖会话历史的表达。

短期记忆的边界是会话。不同对话应使用不同的 thread_id,否则互不相关的消息可能被错误地串联起来。

长期记忆

store 保存跨会话复用的用户信息。示例按 user_id 存储城市,新的对话即使使用不同 thread_id,仍然可以读取该用户的资料。

长期记忆不应等同于"保存所有对话"。更合理的做法是只沉淀稳定且有业务价值的信息,例如用户偏好、常用地点或已确认的配置,并为更新、过期和删除设计明确规则。

从示例走向生产

一个能运行的 Agent 与一个可靠的 Agent 之间,通常还隔着若干工程约束。

工具要小而清晰

工具名称、参数类型和文档字符串都会影响模型的选择。一个工具最好只完成一种明确操作。对于复杂的确定性流程,可以先封装为 Graph,再作为单个 Tool 暴露给 Agent。

提示词要描述边界

系统提示词不仅要告诉模型"做什么",还要说明"何时调用哪个工具""缺少信息时怎么办"以及"哪些事情不能自行假设"。规则应具体、可验证,避免只有角色描述而没有行为约束。

记忆要按用途分层

会话消息放入检查点,跨会话资料放入 Store,临时请求信息放入 Context。三类数据用途不同,混在一起会增加上下文长度,也容易造成隐私和状态污染问题。

固定流程不要强行交给模型

身份校验、扣款、审批、归档等关键业务步骤通常需要确定性。让 Agent 负责理解意图和选择能力,让 Graph 或普通代码负责稳定执行,是更容易测试和审计的架构。

必须设置失败边界

工具调用可能超时,参数可能无效,模型也可能重复尝试。生产环境应设置最大迭代次数、超时时间、错误重试策略和降级方案。对有外部影响的操作,还应加入人工确认或明确授权。

总结

LangChain v1 将 Agent 开发收敛到更清晰的分层结构:create_agent 提供简洁入口,模型负责推理与决策,工具提供执行能力,LangGraph 承担状态和运行时编排。开发者可以从一个只有模型、工具和提示词的最小 Agent 开始,再逐步加入上下文、结构化输出、短期记忆与长期记忆。

真正重要的不是把所有流程都改造成 Agent,而是划清自主决策与确定执行的边界:开放式任务交给 Agent,固定步骤交给 Graph,复杂应用则让两者协同。沿着这一思路构建,既能发挥大语言模型的灵活性,也能保留业务系统需要的可靠性与可维护性。

相关推荐
Hyyy1 小时前
Electron多进程
前端
光影少年1 小时前
RN原生交互 & 桥接
前端·javascript·react native·react.js·前端框架
东风破_2 小时前
React 状态更新为什么不是立即赋值?从 useState、状态快照到更新队列
前端
用户526835677902 小时前
云原生运维实践:构建 Prometheus + 自定义 Webhook 的硬件声光告警闭环
javascript
GuWenyue2 小时前
踩坑无数!吃透useState 3个核心技巧,彻底解决状态旧值、性能卡顿问题
前端·javascript·react.js
无人生还2 小时前
从 Vue3 到 React · 快速上手系列第 4 篇:条件渲染与列表渲染
前端·vue.js·react.js
颜进强2 小时前
从零搭建私人 RAG 实战:用 Markdown 沉淀技术决策与业务决策
前端·后端·ai编程
咩咩啃树皮2 小时前
第46篇:Vue3 Router工程化进阶——懒加载+嵌套路由+全局守卫+权限控制
javascript·vue.js·ecmascript
触底反弹2 小时前
💡 React 父子组件通信:一个进度条教会我的 5 件事
前端·react.js·面试