LangChainV1.0中Agent开发的核心组件

1、Agent(智能体)的定义

LangChain本质上是一个具备动态决策能力的智能执行框架 ,它通过**大型语言模型(LLM)**作为"大脑",协调和调度各种工具来完成复杂任务。与传统的预定义流程软件不同,Agent能够根据实际情况自主做出决策,将单一的工具调用转化为灵活的问题解决能力。

LLM Agent在循环中运行工具以实现目标。代理运行直到满足停止条件 ------即,当模型发出最终输出或达到迭代限制时 停止迭代。LangChain中 Agent的架构由多个精密协作的组件构成,形成了一个完整的决策-执行-反馈循环系统。

Agent的核心价值在于其三重核心能力 :动态任务路由、生态化工具集成和全周期记忆管理。具体来说,动态任务路由使Agent能够根据输入内容自动规划执行路径,灵活切换工具调用逻辑;生态化工具集成让Agent可以访问300+预置工具接口,覆盖搜索引擎、数据计算、数据库交互等多领域;而全周期记忆管理则使Agent能同时维护短期对话上下文与长期知识存储,支持复杂任务的跨轮次协作。这种架构使得Agent不像传统程序那样按预定流程执行,而是更像一位智能项目经理,能够接收任务需求后拆解目标(Planner角色)、根据任务类型匹配最优工具(Router角色)、调度工具按步骤执行(Executor角色),最后整合结果生成交付物。

核心组件详解

  • 模型(Model) :Model是Agent的"大脑",负责推理和决策过程。在LangChain 1.0中,Model被抽象为统一的接口,可以与多种后端LLM提供商协同工作,包括OpenAI、Anthropic、Google等。Model的核心职责是分析当前状态和用户请求,决定是否需要调用工具以及调用哪些工具,并解析工具返回结果以形成最终响应。Model的推理过程通常基于思维链(Chain-of-Thought) 模式,将复杂问题分解为多个思考步骤。
  • 工具(Tools) :Tools是Agent与外部世界交互的能力扩展接口,每个工具封装了一个特定功能,如搜索引擎查询、数据库操作、代码执行等。在LangChain中,工具既有大量预置选项(如搜索引擎集成、数学计算、文档检索等),也支持开发者自定义扩展。一个良好设计的工具应遵循功能单一性、输入验证和异常处理等原则。从架构角度看,工具将Agent的认知能力与具体执行能力分离,使得Agent可以专注于决策而非实现细节。
  • 记忆(Memory) :Memory为Agent提供上下文感知能力,使其能够记住之前的交互历史并基于上下文做出决策。LangChain中的记忆系统分为短期记忆(维护当前对话的上下文)和长期记忆(支持跨对话会话的知识持久化)。在LangChain 1.0中,记忆系统与LangGraph的状态管理深度融合,支持更复杂和结构化的状态保持。
  • AgentExecutor :在LangChain 1.0的架构中,AgentExecutor继续扮演执行协调器的关键角色,负责迭代运行代理直至满足停止条件。新版本中,AgentExecutor基于LangGraph构建,获得了更强大的流程控制能力,包括循环控制(通过max_iterations参数防止无限循环)、异常处理(可配置的解析错误处理)和可观测性(通过verbose参数输出详细执行日志)

Agent的工作流程与决策循环

具体流程如下:

  • 输入解析:Agent首先接收用户输入,解析其意图和关键参数。输入解析器负责标准化用户请求,提取可供工具使用的结构化参数。
  • LLM推理:解析后的输入与当前状态(包括记忆和历史记录)一起传递给LLM进行推理。LLM基于预设的提示词模板分析情况,决定下一步行动------直接回答还是调用工具。
  • 工具调用:如果LLM决定调用工具,AgentExecutor会执行相应的工具函数,并获取执行结果。工具调用可能涉及外部API访问、数据库查询或计算操作等。
  • 观察与迭代:工具返回的结果作为"观察"被反馈给Agent,这些信息与之前的状态一起形成新的上下文,传递给LLM进行下一轮推理。这个循环持续进行,直到LLM认为已获得足够信息来生成最终答案。
  • 结果整合:当循环结束时,Agent整合所有获取的信息,生成格式化的最终答案返回给用户。同时,重要的交互信息可能被存储在记忆系统中,供未来会话使用

2、LLM模型组件

LangChain 支持所有主要模型提供商,包括 OpenAI、Anthropic、Google、Azure、AWS Bedrock 等。每个提供商都提供具有不同功能的各种模型。

复制代码
pip install -U "langchain[openai]"  langchain-deepseek  dotenv

一、各种供应商的大模型调用

国内外的各种大模型都可以直接使用ChatOpenAI类来负责调用。

在构建ChatModels时,我们有一些标准化参数:

  • model: 模型名称

  • temperature: 采样温度

  • timeout: 请求超时

  • max_tokens: 生成的最大令牌数

  • max_retries: 请求重试的最大次数

  • api_key: 大模型供应商的API密钥

  • base_url: 发送请求的端点

    from langchain_openai import ChatOpenAI

    llm = ChatOpenAI(
    model="gpt-5",
    # stream_usage=True,
    # temperature=None,
    # max_tokens=None,
    # timeout=None,
    # reasoning_effort="low",
    # max_retries=2,
    # api_key="...", # if you prefer to pass api key in directly instead of using env vars
    # base_url="...",
    # organization="...",
    # other params...
    )

    model = ChatDeepSeek(
    model="...",
    api_key="...",
    api_base="https://openrouter.ai/api/v1",
    extra_body={"reasoning": {"enabled": True}},
    )

二、流式输出

大多数模型都可以在生成输出内容时流式传输其输出内容。通过逐步显示输出,流媒体显著改善了用户体验,特别是对于较长的响应。

stream()invoke() 相反,invoke() 在模型生成完完整响应后返回单个 AIMessage,返回多个 AIMessageChunk 对象,每个对象包含输出文本的一部分。重要的是,流中的每个块都设计为通过求和收集到完整消息中:

复制代码
full = None  # None | AIMessageChunk
for chunk in model.stream("你的的问题"):
    full = chunk if full is None else full + chunk
    print(full.text)

# The
# The sky
# The sky is
# The sky is typically
# The sky is typically blue
# ...

print(full.content_blocks)
# [{"type": "text", "text": "The sky is typically blue..."}]

for chunk in model.stream("你的的问题"):
    reasoning_steps = [r for r in chunk.content_blocks if r["type"] == "reasoning"]
    print(reasoning_steps if reasoning_steps else chunk.text)

AIMessage消息对象的属性

|--------------------|------------------------------------------------------------------------------------------------------|
| 属性 | 描述 |
| tool_calls | 如果存在,则与消息关联的工具调用。类型:list [ToolCall] |
| invalid_tool_calls | 如果存在,则工具调用具有与消息关联的解析错误。类型:list[InvalidToolCall] |
| usage_metadata | 如果存在,则消息的使用元数据,例如令牌计数。类型:UsageMetadata | None |
| type | 消息的类型(用于反序列化)。类型:Literal['ai'] |
| lc_attributes | 要序列化的属性。类型:dict |
| content_blocks | 从消息中返回标准的、键值对的 ContentBlock 字典。类型:list[ContentBlock] |

三、速率限制

许多聊天模型提供程序对给定时间段内可以进行的调用次数施加限制。如果达到速率限制,通常会收到来自提供商的速率限制错误响应,并且需要等待才能发出更多请求。

为了帮助管理速率限制,聊天模型集成接受可在初始化期间提供的参数,以控制发出请求的速率。rate_limiter参数来设置。

LangChain 内置 InMemoryRateLimiter。此限制器是线程安全的,可以由同一进程中的多个线程共享。

复制代码
from langchain_core.rate_limiters import InMemoryRateLimiter

rate_limiter = InMemoryRateLimiter(
    requests_per_second=0.1,  # 1 request every 10s
    check_every_n_seconds=0.1,  # Check every 100ms whether allowed to make a request
    max_bucket_size=10,  # Controls the maximum burst size.
)

model = init_chat_model(
    model="gpt-5",
    model_provider="openai",
    rate_limiter=rate_limiter  
)

四、模型的输出格式化

大型语言模型能够生成任意文本。这使得模型能够适当地响应广泛的 输入范围,但对于某些用例,限制大型语言模型的输出 为特定格式或结构是有用的。这被称为结构化输出

例如,如果输出要存储在关系数据库中, 如果模型生成遵循定义的模式或格式的输出,将会容易得多。 最常见的输出格式将是JSON, 尽管其他格式如YAML也可能很有用。

.with_structured_output()

为了方便,一些LangChain聊天模型支持.with_structured_output()方法。 该方法只需要一个模式作为输入,并返回一个字典或Pydantic对象。 通常,这个方法仅在支持下面描述的更高级方法的模型上存在, 并将在内部使用其中一种。它负责导入合适的输出解析器并 将模式格式化为模型所需的正确格式。

复制代码
class Movie(BaseModel):
    """电影详情。"""
    title: str = Field(..., description="电影标题")
    year: int = Field(..., description="电影发行年份")
    director: str = Field(..., description="电影导演")
    rating: float = Field(..., description="电影评分(满分10分)")

model_with_structure = llm.with_structured_output(Movie)
response = model_with_structure.invoke("提供电影《盗梦空间》的详细信息")
print(response)
SimpleJsonOutputParser
复制代码
# 创建聊天提示模板,要求模型以特定格式回答问题
prompt = ChatPromptTemplate.from_template(
    "尽你所能回答用户的问题。"  # 基本指令
    '你必须始终输出一个包含"title","year","director","rating"键的JSON对象。其中"title"代表:电影标题;"year"代表:电影发行年份;"director"代表:电影导演的名字;"rating"代表:电影评分(满分10分)。'
    "{question}"  # 用户问题占位符
)


chain = prompt | llm | SimpleJsonOutputParser()
resp = chain.invoke({"question": "提供电影《盗梦空间》的详细信息?"})
print(resp)

3、创建Agent项目和本地测试环境

langchain给我们提供了Studio+ LangSmith集成的测试环境,使用 Studio 在本地可视化、交互和调试您的Agent。

Studio 是一个专门的Agent IDE,支持对实现智能体服务器 API 协议的代理系统进行可视化、交互和调试。Studio 还集成了跟踪评估提示工程

一、 安装 LangGraph CLI
复制代码
# Python >= 3.11 is required.
pip install --upgrade "langgraph-cli[inmem]"
二、配置LangSmith的环境变量

在项目的根目录中创建一个文件并填写必要的 API 密钥。我们需要将环境变量设置为您从 LangSmith 获得的 API 密钥。.envLANGSMITH_API_KEY

复制代码
LANGSMITH_API_KEY=lsv2...
三、创建 LangGraph 配置文件

在应用程序的目录中,创建一个配置文件:langgraph.json

复制代码
{
  "dependencies": ["."],
  "graphs": {
    "agent": "./src/agent.py:agent"
  },
  "env": ".env"
}

我们的项目结构是这样的:

复制代码
my-app/
├── src
│   └── agent.py
├── .env
└── langgraph.json
四、编写你的智能体项目代码
复制代码
from langchain.agents import create_agent

def send_email(to: str, subject: str, body: str):
    """发送邮件"""
    email = {
        "to": to,
        "subject": subject,
        "body": body
    }
    # ... 邮件发送逻辑

    return f"邮件已发送至 {to}"

agent = create_agent(
    "gpt-4o",
    tools=[send_email],
    system_prompt="你是一个邮件助手。请始终使用 send_email 工具。",
)
五、安装依赖项

在新 LangGraph 应用的根目录中,安装依赖项:

复制代码
先拷贝pyproject.toml到项目目录下
pip install -e .
六、在 Studio 中启动和运行您的agent

启动本地的Agent服务器:

复制代码
langgraph dev

您的智能体程序将通过Studio UI 访问:http://127.0.0.1``:2024``https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024

4、工具的定义

工具是Agent调用以执行作的组件。它们通过让模型通过定义明确的输入和输出与世界交互来扩展模型功能。

在构建Agent 时,您需要为其提供一个它可以使用的 工具 列表。除了实际调用的函数之外,工具还包括几个组件:

注意: 如果工具具有精心选择的名称、描述和args_schema,模型将表现得更好。

LangChain 支持从以下几种方式创建工具

  1. Tool装饰器的函数 --- 这是最常见的。
  2. 通过从 BaseTool 子类化 -- 这是最灵活的方法,它提供了最大的控制程度,但代价是需要付出更多的努力和编写更多的代码。
  3. 从MCP的服务端获得工具

一、@Tool装饰器定义工具

复制代码
@tool('calculate', parse_docstring=True)
def calculate4(
        a: float,
        b: float,
        operation: str) -> float:
    """工具函数:计算两个数字的运算结果

    Args:
        a: 第一个需要输入的数字。
        b: 第二个需要输入的数字。
        operation: 运算类型,只能是add、subtract、multiply和divide中的任意一个。

    Returns:
        返回两个输入数字的运算结果。

    """
    print(f"调用 calculate 工具,第一个数字:{a}, 第二个数字:{b}, 运算类型:{operation}")

    result = 0.0
    match operation:
        case "add":
            result = a + b
        case "subtract":
            result = a - b
        case "multiply":
            result = a * b
        case "divide":
            if b != 0:
                result = a / b
            else:
                raise ValueError("除数不能为零")

    return result

二、继承BaseTool定义工具

复制代码
class SearchArgs(BaseModel):
    query: str = Field(description="需要进行网络搜索的信息。")


# 网络搜索的工具
class MySearchTool(BaseTool):
    # 工具名字
    name: str = "search_tool"

    description: str = '搜索互联网上公开内容的工具'

    return_direct: bool = False

    args_schema: Type[BaseModel] = SearchArgs

    def _run(self, query) -> str:
        try:
            # print("执行我的Python中的工具,输入的参数为:", query)
            response = zhipuai_client.web_search.web_search(
                search_engine="search_pro",
                search_query=query
            )
            # print(response)
            if response.search_result:
                return "\n\n".join([d.content for d in response.search_result])
            return '没有搜索到任何内容!'
        except Exception as e:
            print(e)
            return '没有搜索到任何内容!'

三、从MCP服务器端获得工具

这里不多赘述了

5、Agent的消息和记忆存储

一、四种消息类型

  • SystemMessage 表示一组初始指令,用于启动模型的行为。您可以使用系统消息来设置基调、定义模型的角色并建立响应指南。

    system_msg = SystemMessage("You are a helpful coding assistant.")

    messages = [
    system_msg,
    HumanMessage("How do I create a REST API?")
    ]
    response = model.invoke(messages)

  • HumanMessage 表示用户输入和交互。它们可以包含文本、图像、音频、文件和任何其他数量的多模态内容。

    response = model.invoke([
    HumanMessage("What is machine learning?")
    ])

  • AIMessage 表示模型调用的输出 。它们可以包括多模式数据、工具调用和您以后可以访问的特定于提供商的元数据。AIMessage 对象在调用时由模型返回,其中包含响应中的所有关联元数据。当模型进行工具调用 时,它们包含在 AIMessage 中;在流式传输期间,您将收到可以组合成完整消息对象的 AIMessageChunk 对象.

    from langchain.chat_models import init_chat_model

    model = init_chat_model("gpt-5-nano")

    def get_weather(location: str) -> str:
    """Get the weather at a location."""
    ...

    model_with_tools = model.bind_tools([get_weather])
    response = model_with_tools.invoke("What's the weather in Paris?")

    for tool_call in response.tool_calls:
    print(f"Tool: {tool_call['name']}")
    print(f"Args: {tool_call['args']}")
    print(f"ID: {tool_call['id']}")

  • ToolMessage 表示单个工具执行的结果

    ai_message = AIMessage(
    content=[],
    tool_calls=[{
    "name": "get_weather",
    "args": {"location": "San Francisco"},
    "id": "call_123"
    }]
    )

    Execute tool and create result message

    weather_result = "Sunny, 72°F"
    tool_message = ToolMessage(
    content=weather_result,
    tool_call_id="call_123" # Must match the call ID
    )

二、定义Agent的状态

AgentState(可变上下文):状态在运行期间充当Agent的记忆,可以短期存储也可以长期存储。它保存可在执行期间演变的动态数据,例如从工具或 LLM 输出派生的值。

你可以延长AgentState以添加额外的字段。自定义状态模式传递给create_agent使用state_schema参数。

复制代码
class CustomAgentState(AgentState):  
    user_id: str
    preferences: dict

注意:还有一个context_schema来设置不可变状态,默认是没有的

三、Agent中的记忆存储

短期存储:线程级存储(会话级)

短期存储使Agent能够跟踪多轮对话。要使用它,您必须:

  1. 在创建代理时提供checkpointercheckpointer可以实现代理状态的持久性。
  2. 在运行代理时在配置中提供thread_idthread_id是对话会话的唯一标识符。
  3. 使用ToolRuntime.store函数从工具或提示中访问存储。

线程在特定时间点的状态称为checkpointer (检查点)。检查点是每个超级步骤保存的图状态快照,并由具有以下关键属性的对象表示:StateSnapshot

  • config: 配置与该检查点关联。
  • metadata:与该检查点相关的元数据。
  • values:此时状态信道的值。
  • next图中下一组节点命名的元组。
  • tasks:一组包含下一个要执行任务信息的对象元组。如果该步骤之前尝试过,则会包含错误信息。如果图被中断PregelTask动态在节点内部,任务还会包含与中断相关的额外数据。

检查点是需要提供存储机制的,可用于在后续时间恢复线程状态。

复制代码
# 内存: 开发环境
checkpointer = InMemorySaver()

# 在生产环境中,使用由数据库支持的检查点
DB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable"
with PostgresSaver.from_conn_string(DB_URI) as checkpointer:
# 必须安装:pip install -U "psycopg[binary,pool]" langgraph-checkpoint-postgres

# 生产环境:Redis
# pip install -U langgraph-checkpoint-redis
DB_URI = "redis://:6379"
with RedisSaver.from_conn_string(DB_URI) as checkpointer:

长期存储:跨线程存储

使用长期内存来跨会话存储用户特定或应用程序特定的数据。这对于聊天机器人等应用程序非常有用,您可能希望记住用户偏好或其他信息。

要使用长期内存,您需要:

  1. 配置一个存储以在调用之间持久化数据。

  2. 使用ToolRuntime.store函数从工具或提示中访问存储。

    开发环境中: 内存

    store = InMemoryStore()

    在生产环境中,使用由数据库支持的存储

    DB_URI = "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"
    with (
    PostgresStore.from_conn_string(DB_URI) as store,
    PostgresSaver.from_conn_string(DB_URI) as checkpointer,
    ):
    # store.setup()
    # checkpointer.setup()

    DB_URI = "redis://:6379"
    with (
    RedisStore.from_conn_string(DB_URI) as store,
    RedisSaver.from_conn_string(DB_URI) as checkpointer,
    ):
    store.setup()
    checkpointer.setup()

注意:

  • 首次使用 Postgres 存储时,您需要调用 store.setup(),checkpointer.setup()
  • 首次使用 Redis 存储时,您需要调用 store.setup(),checkpointer.setup()

在 LangGraph 中,通过 stream函数进行流式输出时,stream_mode参数是关键,它决定了你以何种粒度接收工作流执行过程中的中间结果。不同的模式适用于不同的监控和交互场景。

下面这个表格直观地对比了主要的流式输出模式及其核心特点,你可以根据你的需求快速了解。

|-------------------|-------------------------------------|---------------------------|--------|
| 流模式 (stream_mode) | 输出内容特征 | 核心用途 | 输出粒度 |
| "values" | 每个节点执行后完整的全局状态快照 | 需要实时掌握全量状态,用于复杂逻辑判断或实时仪表盘 | 节点级 |
| "updates" | 每个节点执行后发生变更的状态字段(增量) | 轻量级前端渲染、更新进度条、仅关注状态变化 | 字段级 |
| "messages" | LLM 对话消息,通常为逐词(token)流 | 实时显示大模型生成内容,实现"打字机"效果 | Token级 |
| "custom" | 节点内通过 get_stream_writer()发送的任意自定义数据 | 发送进度、日志、内部计算结果等非状态信息 | 任意 |
| "debug" | 丰富的调试信息(如节点耗时、内存占用) | 开发阶段诊断工作流性能和行为 | 调试级 |

相关推荐
别惹CC3 小时前
OpenClaw 是如何设计提示词的?
人工智能·ai·aigc
杨浦老苏3 小时前
AI提示词管理工具AiShort
人工智能·docker·ai·群晖
Hehuyi_In4 小时前
云数据库反营销话术 checklist
ai·云数据库·宣传
玖雨y5 小时前
Agent Skills:AI的行动力
ai·llm·agent skills
vm3210 小时前
01:Agent Loop 深度剖析:ReAct 循环的工程实现
人工智能·ai·自然语言处理·开源
埃泽漫笔10 小时前
LangChainV1.0 + LangGraphV1.0介绍
ai·ai应用开发
mtouch33310 小时前
三维数字沙盘智能交互式可视化动态主界面系统
人工智能·ai·信息可视化·无人机·虚拟现实·电子沙盘·数字沙盘
TDengine (老段)11 小时前
TDengine IDMP 数据可视化——富文本
大数据·数据库·物联网·ai·时序数据库·tdengine·涛思数据
凌云拓界12 小时前
TypeWell全攻略(四):AI键位分析,让数据开口说话
前端·人工智能·后端·python·ai·交互