LangChain v1 Agent核心能力详解

LangChain v1 Agent 核心能力详解:从模型调用到可控执行

大语言模型擅长理解和生成文本,但一个真正可用的智能体还需要具备更多能力:根据任务选择合适的模型,调用外部工具,维护会话状态,在高风险操作前请求人工确认,并将执行过程实时反馈给用户。

LangChain v1 将这些能力统一到了 create_agent 接口及中间件体系中。开发者可以先用少量代码创建一个基本 Agent,再通过模型路由、动态工具、结构化输出、自定义状态、人机协作和流式传输逐步增强它。

本文将围绕这些核心能力展开,并给出相应的实现方式与工程建议。

从一个最小 Agent 开始

一个 Agent 通常由三部分组成:

  • 模型:负责理解、推理与决策。
  • 工具:负责搜索、查询、计算、写入等具体操作。
  • 系统提示词:定义角色、目标和行为边界。
python 复制代码
from langchain.agents import create_agent
from langchain.tools import tool


@tool
def get_weather(city: str) -> str:
    """查询指定城市的天气。"""
    return f"{city}天气晴朗。"


agent = create_agent(
    model="openai:gpt-4o-mini",
    tools=[get_weather],
    system_prompt="你是一名简洁、准确的天气助手。",
)

result = agent.invoke(
    {"messages": [{"role": "user", "content": "上海天气如何?"}]}
)

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

这个例子虽然简单,却已经形成了完整的 Agent 工作链路:模型理解问题,判断是否需要调用工具,读取工具结果,再组织最终回答。

模型配置:从固定调用到动态路由

模型是 Agent 的推理引擎。实际项目既可以固定使用一个模型,也可以根据任务复杂度、用户等级、成本预算或对话状态动态选择模型。

静态模型

最直接的方式是在创建 Agent 时指定模型标识符:

python 复制代码
agent = create_agent(
    model="openai:gpt-4o-mini",
    tools=[get_weather],
)

如果需要控制温度、最大输出长度和超时时间,可以传入模型实例:

python 复制代码
from langchain_openai import ChatOpenAI


model = ChatOpenAI(
    model="gpt-4o-mini",
    temperature=0.1,
    max_tokens=1000,
    timeout=30,
)

agent = create_agent(model=model, tools=[get_weather])

静态模型配置简单、行为稳定,适合任务类型较单一的应用。

动态模型

当简单任务和复杂任务共用一个 Agent 时,可以通过 @wrap_model_call 在运行时替换模型。下面的示例根据消息数量进行路由:短对话使用轻量模型,长对话切换到能力更强的模型。

python 复制代码
from langchain.agents.middleware import (
    ModelRequest,
    ModelResponse,
    wrap_model_call,
)
from langchain_openai import ChatOpenAI


fast_model = ChatOpenAI(model="gpt-4o-mini")
strong_model = ChatOpenAI(model="gpt-4o")


@wrap_model_call
def select_model(request: ModelRequest, handler) -> ModelResponse:
    message_count = len(request.state["messages"])
    selected = strong_model if message_count > 4 else fast_model
    return handler(request.override(model=selected))


agent = create_agent(
    model=fast_model,
    tools=[get_weather],
    middleware=[select_model],
)

动态模型的价值不只是"自动切换更强模型",而是建立一套可观测的路由规则。例如:

  • 普通问答使用低成本模型,复杂分析使用高能力模型。
  • 免费用户和付费用户使用不同模型。
  • 接近上下文限制时切换到支持更长上下文的模型。
  • 高风险任务使用稳定性更高的模型,并降低随机性。

路由条件应尽量明确、可测试。仅根据消息数量判断复杂度适合演示,生产环境更适合结合任务类型、Token 数量、权限和历史执行结果。

工具体系:让 Agent 从"会说"变成"会做"

工具赋予 Agent 与外部世界交互的能力。它可以查询数据库、调用业务接口、搜索资料、发送邮件或执行计算。LangChain Agent 支持串行调用、并行调用、按前序结果继续选择工具,以及在多次工具调用之间保持状态。

静态注册工具

对于工具集合固定的应用,可以直接在创建 Agent 时注册:

python 复制代码
from langchain.tools import tool


@tool
def search_product(keyword: str) -> str:
    """按照关键词搜索商品。"""
    return f"已找到与 {keyword} 相关的商品。"


@tool
def check_inventory(product_id: str) -> str:
    """查询指定商品的库存。"""
    return f"商品 {product_id} 当前库存为 10。"


agent = create_agent(
    model="openai:gpt-4o-mini",
    tools=[search_product, check_inventory],
)

如果传入空工具列表,Agent 会退化为只有模型节点的对话应用,不再具备外部操作能力。

根据状态动态筛选工具

工具并不是越多越好。大量无关工具会增加模型选择难度,也可能让未授权用户获得不应使用的能力。一个常见做法是在运行时根据认证状态或角色筛选工具。

python 复制代码
from langchain.agents import AgentState
from langchain.agents.middleware import ModelRequest, ModelResponse, wrap_model_call
from langchain.tools import tool


@tool
def public_search(query: str) -> str:
    """查询公开信息。"""
    return f"公开结果:{query}"


@tool
def private_search(query: str) -> str:
    """查询仅限认证用户访问的数据。"""
    return f"私有结果:{query}"


class AuthState(AgentState):
    authenticated: bool


@wrap_model_call(state_schema=AuthState)
def filter_tools(request: ModelRequest, handler) -> ModelResponse:
    if not request.state.get("authenticated", False):
        allowed = [
            tool for tool in request.tools
            if tool.name.startswith("public_")
        ]
        request = request.override(tools=allowed)

    return handler(request)


agent = create_agent(
    model="openai:gpt-4o-mini",
    tools=[public_search, private_search],
    middleware=[filter_tools],
)

筛选条件除了来自 state,还可以来自运行时 context 或持久化 store。因此,权限、租户、功能开关和业务阶段都可以参与工具选择。

需要特别注意:动态隐藏工具只是一层 Agent 能力约束,不能代替后端授权。真正敏感的接口仍然必须在工具实现内部执行身份校验和权限检查。

运行时加入新工具

有些工具只在特定阶段出现,甚至不需要在创建 Agent 时注册。此时可以用类中间件同时处理模型可见工具和工具执行逻辑。

python 复制代码
from langchain.agents.middleware import (
    AgentMiddleware,
    ModelRequest,
    ToolCallRequest,
)
from langchain.tools import tool


@tool
def calculate_tip(
    bill_amount: float,
    tip_percentage: float = 20.0,
) -> str:
    """计算账单小费与总金额。"""
    tip = bill_amount * tip_percentage / 100
    total = bill_amount + tip
    return f"小费 {tip:.2f} 元,总计 {total:.2f} 元。"


class DynamicToolMiddleware(AgentMiddleware):
    def wrap_model_call(self, request: ModelRequest, handler):
        updated = request.override(
            tools=[*request.tools, calculate_tip]
        )
        return handler(updated)

    def wrap_tool_call(self, request: ToolCallRequest, handler):
        if request.tool_call["name"] == "calculate_tip":
            request = request.override(tool=calculate_tip)
        return handler(request)

动态加入工具时,必须同时解决两个问题:模型需要看到工具定义,执行阶段也需要找到对应工具。只完成前者会导致模型生成了工具调用,但运行时无法执行。

工具错误处理

外部接口超时、参数不合法或服务不可用都可能导致工具失败。不要让异常直接终止整个 Agent,可以将错误转换为 ToolMessage 返回给模型,让模型修正参数、选择替代工具或向用户解释问题。

python 复制代码
from langchain.agents.middleware import wrap_tool_call
from langchain.messages import ToolMessage


@wrap_tool_call
def handle_tool_errors(request, handler):
    try:
        return handler(request)
    except Exception as exc:
        return ToolMessage(
            content=f"工具执行失败,请检查输入后重试:{exc}",
            tool_call_id=request.tool_call["id"],
        )

生产环境不建议把完整异常、堆栈或内部地址直接暴露给模型和用户。更稳妥的方式是返回经过分类的错误码与简洁说明,同时将完整异常写入服务端日志。

ReAct:Agent 如何连续解决任务

Agent 调用工具不是一次性的函数跳转,而是一个"推理、行动、观察、再推理"的循环,也就是 ReAct 模式。

以"找出热门无线耳机并确认库存"为例,执行过程可能是:

  1. 模型判断商品热度属于实时信息,调用商品搜索工具。
  2. 搜索工具返回排名最高的商品型号。
  3. 模型发现还缺少库存信息,继续调用库存查询工具。
  4. 工具返回库存数量,模型据此生成最终答复。

这种循环让 Agent 可以把复杂目标拆成多个可执行步骤,并根据每一步的观察结果决定下一步动作。与此同时,它也带来了新的工程问题:如何限制最大循环次数、如何处理工具失败、如何避免重复调用,以及哪些动作必须由人工确认。

提示词:静态规则与动态上下文

系统提示词定义 Agent 的角色、表达方式和决策边界。

静态系统提示词

python 复制代码
agent = create_agent(
    model="openai:gpt-4o-mini",
    tools=[get_weather],
    system_prompt=(
        "你是一名企业客服助手。回答应简洁、准确;"
        "无法确认的信息必须明确说明,不得编造。"
    ),
)

静态提示词适合全局不变的规则,例如品牌语气、安全边界和固定输出要求。

动态系统提示词

同一个问题面对初学者和专家时,回答方式应当不同。@dynamic_prompt 可以根据运行时上下文生成提示词:

python 复制代码
from typing import TypedDict

from langchain.agents.middleware import ModelRequest, dynamic_prompt


class UserContext(TypedDict):
    user_role: str


@dynamic_prompt
def role_prompt(request: ModelRequest) -> str:
    role = request.runtime.context.get("user_role", "初学者")
    base = "你是一名严谨的技术助手。"

    if role == "专家":
        return f"{base} 给出技术细节、限制条件和工程权衡。"
    return f"{base} 使用易懂语言解释,减少不必要的术语。"


agent = create_agent(
    model="openai:gpt-4o-mini",
    tools=[],
    middleware=[role_prompt],
    context_schema=UserContext,
)

动态提示词适合处理用户角色、地区、语言、业务阶段和个性化偏好,但不应塞入大量与当前任务无关的信息。上下文越长,成本越高,关键规则也越容易被稀释。

结构化输出:让结果直接进入业务流程

自然语言适合展示给人,但系统间协作更需要稳定的数据结构。LangChain 可以通过 response_format 将 Agent 输出约束为 Pydantic 模型。

python 复制代码
from pydantic import BaseModel


class ContactInfo(BaseModel):
    name: str
    email: str
    phone: str

结构化输出主要有两种策略:

策略 实现方式 适用场景
ProviderStrategy 使用模型提供商的原生结构化输出 模型原生支持时优先使用,可靠且高效
ToolStrategy 用工具调用参数承载结构化数据 适用于支持工具调用但缺少原生结构化输出的模型

使用 ToolStrategy

python 复制代码
from langchain.agents.structured_output import ToolStrategy


agent = create_agent(
    model="openai:gpt-4o-mini",
    tools=[],
    response_format=ToolStrategy(ContactInfo),
)

使用 ProviderStrategy

python 复制代码
from langchain.agents.structured_output import ProviderStrategy


agent = create_agent(
    model="openai:gpt-4o",
    tools=[],
    response_format=ProviderStrategy(ContactInfo),
)

LangChain v1 还支持直接传入 Pydantic 模型:

python 复制代码
agent = create_agent(
    model="openai:gpt-4.1",
    response_format=ContactInfo,
)

此时框架会优先尝试原生结构化输出,不支持时再回退到工具策略。最终结果可从 result["structured_response"] 中读取。

需要注意,预先执行过 bind_tools 的模型不能直接与这种结构化输出方式组合。动态模型路由也应传入尚未预绑定工具的模型实例。

自定义 State:为当前会话增加短期记忆

Agent 默认会在 messages 中维护对话历史。但用户偏好、临时标志、中间结果和认证状态并不适合全部塞入聊天消息,此时可以扩展 State。

自定义状态需要继承 AgentState,并使用 TypedDict 风格声明。在 LangChain v1 中,推荐通过中间件绑定状态结构,使状态与相关逻辑保持在同一作用域。

python 复制代码
from typing import Any

from langchain.agents import AgentState, create_agent
from langchain.agents.middleware import AgentMiddleware


class PreferenceState(AgentState):
    user_preferences: dict


class PreferenceMiddleware(AgentMiddleware):
    state_schema = PreferenceState

    def before_model(
        self,
        state: PreferenceState,
        runtime,
    ) -> dict[str, Any] | None:
        preferences = state.get("user_preferences", {})
        print(f"当前偏好:{preferences}")
        return None


agent = create_agent(
    model="openai:gpt-4o-mini",
    tools=[],
    middleware=[PreferenceMiddleware()],
)

result = agent.invoke(
    {
        "messages": [{"role": "user", "content": "解释什么是大模型"}],
        "user_preferences": {
            "style": "技术性",
            "verbosity": "详细",
        },
    }
)

也可以通过 state_schema 参数直接传入状态类型,但这种方式的作用域更宽,主要用于简单场景或兼容既有代码。新项目中,将状态定义放到对应中间件通常更清晰。

State 表示当前会话生命周期内的短期状态。如果需要跨会话长期保存用户信息,应使用持久化存储,而不是把所有数据长期堆积在 State 中。

Human-in-the-loop:在关键操作前加入人工决策

Agent 一旦拥有写文件、执行 SQL、发送邮件或修改业务数据的能力,就不能只依赖模型自行判断。Human-in-the-loop,简称 HITL,可以在敏感工具执行前暂停流程,让人工审核调用参数。

人工通常可以做出三类决策:

  • approve:按原参数执行。
  • edit:修改工具名称或参数后执行。
  • reject:拒绝操作,并将原因反馈给 Agent。

下面的配置对写文件操作开放全部决策,对 SQL 操作只允许批准或拒绝,而只读工具自动放行。

python 复制代码
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import InMemorySaver


agent = create_agent(
    model="openai:gpt-4.1",
    tools=[write_file, execute_sql, read_data],
    middleware=[
        HumanInTheLoopMiddleware(
            interrupt_on={
                "write_file": True,
                "execute_sql": {
                    "allowed_decisions": ["approve", "reject"]
                },
                "read_data": False,
            },
            description_prefix="该操作需要人工确认",
        )
    ],
    checkpointer=InMemorySaver(),
)

HITL 依赖检查点保存和恢复执行状态。调用时还需要提供稳定的 thread_id

python 复制代码
config = {"configurable": {"thread_id": "task-123"}}

result = agent.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "删除 data 表中 id 为 1 的记录",
            }
        ]
    },
    config=config,
    version="v2",
)

print(result.interrupts)

审核通过后,使用相同的 thread_id 恢复:

python 复制代码
from langgraph.types import Command


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

拒绝操作时可以附带原因,编辑操作时可以提供新的工具参数。多个工具同时进入审核时,决策顺序必须与中断请求中的动作顺序一致。

内存检查点适合本地开发。生产环境应使用数据库支持的持久化检查点,保证服务重启后仍能恢复等待审核的任务。

流式传输:让执行过程可见

Agent 可能经历模型推理、工具调用、人工审核和再次推理。若用户只能等待最终结果,体验会显得迟钝,也难以判断系统当前状态。LangChain 提供三种主要流模式:

模式 输出内容 典型用途
updates 每个 Agent 步骤结束后的状态更新 展示模型、工具和中断节点的进度
messages 模型生成的增量消息块 实现逐字输出、显示工具参数片段
custom 应用主动发送的自定义数据 展示查询进度、中间计算和业务状态

使用 updates 展示执行步骤

python 复制代码
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": "上海天气如何?"}]},
    stream_mode="updates",
    version="v2",
):
    if chunk["type"] == "updates":
        for step, data in chunk["data"].items():
            print("步骤:", step)
            print("内容:", data["messages"][-1].content_blocks)

输出通常会依次出现模型发起工具调用、工具返回结果、模型生成最终答案等状态。

使用 messages 实现 Token 级输出

python 复制代码
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": "上海天气如何?"}]},
    stream_mode="messages",
    version="v2",
):
    if chunk["type"] == "messages":
        token, metadata = chunk["data"]
        print(metadata["langgraph_node"], token.content_blocks)

messages 不只会输出最终文本,也可能包含工具调用参数的增量 JSON 片段。前端渲染时应根据内容块类型分别处理文本、推理信息和工具调用数据。

使用 custom 汇报业务进度

工具内部可以通过 get_stream_writer() 主动发送进度:

python 复制代码
from langgraph.config import get_stream_writer


def get_weather(city: str) -> str:
    """查询天气并报告进度。"""
    writer = get_stream_writer()
    writer(f"正在连接 {city} 的天气服务...")
    writer(f"已经获取 {city} 的天气数据。")
    return f"{city}天气晴朗。"

消费自定义数据:

python 复制代码
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": "上海天气如何?"}]},
    stream_mode="custom",
    version="v2",
):
    if chunk["type"] == "custom":
        print(chunk["data"])

组合多种流模式

一个完整界面通常同时需要步骤进度、模型文本和业务通知,可以把多种模式一起传入:

python 复制代码
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": "上海天气如何?"}]},
    stream_mode=["messages", "updates", "custom"],
    version="v2",
):
    mode = chunk["type"]
    payload = chunk["data"]
    print(mode, payload)

version="v2" 提供统一的 typedata 和命名空间格式,需要 LangGraph 1.1 或更高版本。旧格式在组合模式下通常返回 (mode, data) 元组,消费代码更容易分支混乱。

流式处理与人工审核结合

在流式执行中,HITL 中断会出现在 updates 模式的 __interrupt__ 节点。应用可以暂停输出,展示待审核工具和参数,收集人工决策,再通过 Command(resume=...) 使用同一 thread_id 恢复流式执行。

这使前端可以形成完整交互:

  1. 实时显示模型准备调用哪个工具。
  2. 敏感操作出现时弹出审核界面。
  3. 用户批准、修改或拒绝。
  4. Agent 从检查点继续运行并输出结果。

流式展示工具调用

messages 模式中的 tool_call_chunks 适合实时展示参数生成过程,updates 模式中的 tool_calls 则包含模型节点完成后的完整解析结果。需要将工具调用写入日志、执行审批或驱动后续业务逻辑时,应以完整解析结果为准。

对于支持推理输出的模型,还可以从 content_blocks 中读取类型为 reasoning 的内容块。不同模型提供商的配置方式可能不同,应用应把这类内容作为可选能力,并避免假设所有模型都会返回相同格式或完整内部推理。

工程落地建议

将这些能力放在一起后,一个可靠的 Agent 系统通常应遵循以下原则。

能力最小化

只向模型暴露当前任务需要的工具。根据权限、租户和业务阶段动态筛选工具,同时在工具服务端再次执行授权校验。

高风险操作默认中断

读取类操作可以自动执行,写入、删除、转账、发送等不可逆操作应进入人工审核。允许 edit 还是只允许 approvereject,应根据操作风险决定。

错误可恢复

把工具异常转换为模型能够理解的结果,设置合理的超时和重试上限,并避免把内部异常细节直接暴露给最终用户。

状态边界清晰

对话消息、会话短期状态、运行时上下文和长期存储承担不同职责。不要用单一状态对象承载所有信息,也不要让无关中间件随意修改共享字段。

输出面向系统设计

需要进入数据库、审批流或前端组件的数据,应优先使用结构化输出。自然语言可以作为展示层,结构化对象才是更稳定的系统接口。

全流程可观测

同时记录模型选择、工具调用、耗时、错误、中断决策和最终状态。流式输出负责改善用户体验,服务端日志和链路追踪负责定位问题,两者不能互相替代。

总结

LangChain v1 的 Agent 并不只是一个"模型加工具"的封装。它提供了一套完整的运行时机制:

  • 静态或动态选择模型,在成本、速度和能力之间做路由。
  • 静态注册、动态筛选或临时加入工具,并对调用错误进行恢复。
  • 根据用户和业务上下文动态生成系统提示词。
  • 使用结构化输出把模型结果接入稳定的业务接口。
  • 通过 State 保存会话内的额外信息。
  • 在高风险工具调用前暂停执行,交由人工批准、编辑或拒绝。
  • 通过多种流模式展示模型输出、执行进度和自定义业务状态。

真正决定 Agent 是否能进入生产环境的,不只是模型回答得是否聪明,而是整个执行过程是否有边界、可恢复、可审核、可观察。围绕这些能力建立清晰的控制面,才能让 Agent 从演示代码成长为可靠的应用系统。

相关推荐
吴佳浩1 小时前
AI 核心技术解析|OPD:大模型开始复制的不再是知识,而是判断力
人工智能·深度学习·llm
数智化管理手记2 小时前
主数据重复、错漏频发?一站式主数据管理平台如何落地?
大数据·运维·数据库·人工智能·数据挖掘
thesky1234562 小时前
27届大模型岗面试准备(五):预训练全流程拆解——从数据清洗到 Tokenizer 再到 PT 的每
人工智能·面试·大模型·预训练·tokenizer
attitude.x2 小时前
2026年数据透视分析工具推荐:多维与融合对比
人工智能
就是一顿骚操作2 小时前
Agent Loop 入门到深入:从 ReAct 到 LangGraph、Agents SDK 与 MCP
人工智能·大模型·llm·论文解读·ai agent
quanjui2 小时前
【医学尝试】基于Segment Anything Model的医学图像分割研究:眼底OCT与X线胸片微调实战
人工智能·笔记·学习
YUS云生2 小时前
大模型学习·第40天:LangChain框架入门——从RAG原理到模型调用的统一接口
学习·langchain
就是一顿骚操作2 小时前
Dropout:神经网络正则化的经典解读
人工智能·深度学习·神经网络·论文解读
2401_843253702 小时前
数据隐私AI:从合规检查到隐私计算的Skill化
人工智能