Skip to main content

概述

构建智能体(或任何 LLM 应用)的难点在于使其足够可靠。虽然它们可能适用于原型,但在实际用例中常常失败。

智能体为什么会失败?

当智能体失败时,通常是因为智能体内部的 LLM 调用采取了错误的行动/没有按预期执行。LLM 失败出于以下两种原因之一:
  1. 底层的 LLM 能力不足
  2. “正确”的上下文未被传递给 LLM
更多时候 - 实际上是第二个原因导致智能体不可靠。 上下文工程指的是以正确的格式提供正确的信息和工具,以便 LLM 能够完成任务。这是 AI 工程师的首要工作。这种“正确”上下文的缺失是构建更可靠的智能体的首要障碍,而 LangChain 的智能体抽象在设计上独具特色,旨在促进上下文工程。
刚接触上下文工程吗?从概念概述开始,来了解不同类型的上下文以及何时使用它们。

智能体循环

一个典型的智能体循环包含两个主要步骤:
  1. 模型调用 - 使用提示和可用工具调用LLM,返回响应或执行工具的请求
  2. 工具执行 - 执行LLM请求的工具,返回工具结果
核心智能体循环图
这个循环会一直持续,直到 LLM 决定结束。

你可以控制的内容

要构建可靠的智能体,你需要控制智能体循环每一步发生的情况,以及步骤之间发生的情况。
上下文类型您控制的内容临时的或持久的
模型上下文模型调用的输入(指令、消息历史、工具、响应格式)临时的
工具上下文工具可以访问和生成的内容(状态的读写、存储、运行时上下文)持久的
生命周期上下文模型和工具调用之间发生的事情(摘要、护栏、日志记录等)持久的

Transient context

LLM 在单次调用中看到的内容。您可以修改消息、工具或提示,而不会改变保存在状态中的内容。

Persistent context

在多个回合中保存在状态中的内容。生命周期钩子和工具写入会永久修改此内容。

数据源

在整个过程中,你的智能体访问(读取/写入)不同的数据源:
数据源又称范围示例
运行时上下文静态配置会话范围用户 ID、API 密钥、数据库连接、权限、环境设置
状态短期记忆会话范围当前消息、上传的文件、身份验证状态、工具结果
存储长期记忆跨会话用户偏好、提取的洞察、记忆、历史数据

工作原理

LangChain 中间件 是底层机制,它使上下文工程对使用 LangChain 的开发者来说变得实用。 中间件允许您钩入智能体生命周期中的任何步骤,并:
  • 更新上下文
  • 跳转到智能体生命周期中的不同步骤
在本指南中,你会看到频繁使用中间件 API 作为实现上下文工程目标的手段。

模型上下文

控制每个模型调用的输入内容 - 指令、可用工具、使用的模型以及输出格式。这些决策直接影响可靠性和成本。

System Prompt

开发者给 LLM 的基础指令。

Messages

发送给 LLM 的消息完整列表(对话历史)。

Tools

智能体可以访问的、用于执行操作的工具。

Model

将被调用的实际模型(包括配置)。

Response Format

模型最终响应的模式规范。
所有这些类型的模型上下文都可以从状态(短期记忆)、存储(长期记忆)或运行时上下文(静态配置)中获取。

系统提示词

系统提示设定 LLM 的行为和能力。不同的用户、上下文或对话阶段需要不同的指令。成功的智能体根据记忆、偏好和配置,为当前对话状态提供合适的指令。
从状态中访问消息数量或对话上下文:
from langchain.agents import create_agent
from langchain.agents.middleware import dynamic_prompt, ModelRequest

@dynamic_prompt
def state_aware_prompt(request: ModelRequest) -> str:
    # request.messages is a shortcut for request.state["messages"]
    message_count = len(request.messages)

    base = "You are a helpful assistant."

    if message_count > 10:
        base += "\nThis is a long conversation - be extra concise."

    return base

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

消息

消息构成了发送给 LLM 的提示。 管理消息的内容至关重要,以确保 LLM 拥有正确的信息,从而能够很好地回应。
当与当前查询相关时,从 State 中注入已上传文件的上下文:
from langchain.agents import create_agent
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
from typing import Callable

@wrap_model_call
def inject_file_context(
    request: ModelRequest,
    handler: Callable[[ModelRequest], ModelResponse]
) -> ModelResponse:
    """Inject context about files user has uploaded this session."""
    # Read from State: get uploaded files metadata
    uploaded_files = request.state.get("uploaded_files", [])  

    if uploaded_files:
        # Build context about available files
        file_descriptions = []
        for file in uploaded_files:
            file_descriptions.append(
                f"- {file['name']} ({file['type']}): {file['summary']}"
            )

        file_context = f"""Files you have access to in this conversation:
{chr(10).join(file_descriptions)}

Reference these files when answering questions."""

        # Inject file context before recent messages
        messages = [  
            *request.messages
            {"role": "user", "content": file_context},
        ]
        request = request.override(messages=messages)  

    return handler(request)

agent = create_agent(
    model="openai:gpt-4o",
    tools=[...],
    middleware=[inject_file_context]
)
临时的与持久的消息更新:上面的示例使用 wrap_model_call 进行临时更新——修改发送给模型的消息用于单次调用,而不改变保存在状态中的内容。对于修改状态的持久化更新(例如 生命周期上下文 中的摘要示例),请使用像 before_modelafter_model 这样的生命周期钩子来永久更新对话历史。请参阅 中间件文档 了解更多详情。

工具

工具使模型能够与数据库、APIs 和外部系统进行交互。如何定义和选择工具,直接影响模型能否有效完成任务。

定义工具

每个工具都需要有清晰的名称、描述、参数名称和参数描述。这些不仅仅是元数据——它们指导模型推理何时以及如何使用该工具。
from langchain.tools import tool

@tool(parse_docstring=True)
def search_orders(
    user_id: str,
    status: str,
    limit: int = 10
) -> str:
    """Search for user orders by status.

    Use this when the user asks about order history or wants to check
    order status. Always filter by the provided status.

    Args:
        user_id: Unique identifier for the user
        status: Order status: 'pending', 'shipped', or 'delivered'
        limit: Maximum number of results to return
    """
    # Implementation here
    pass

选择工具

并非所有工具都适用于所有场景。工具过多可能会使模型不堪重负(导致上下文过载)并增加错误;工具过少则会限制其能力。动态工具选择会根据认证状态、用户权限、功能开关或对话阶段来调整可用的工具集。
仅在达到特定对话里程碑后启用高级工具:
from langchain.agents import create_agent
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
from typing import Callable

@wrap_model_call
def state_based_tools(
    request: ModelRequest,
    handler: Callable[[ModelRequest], ModelResponse]
) -> ModelResponse:
    """Filter tools based on conversation State."""
    # Read from State: check if user has authenticated
    state = request.state  
    is_authenticated = state.get("authenticated", False)  
    message_count = len(state["messages"])

    # Only enable sensitive tools after authentication
    if not is_authenticated:
        tools = [t for t in request.tools if t.name.startswith("public_")]
        request = request.override(tools=tools)  
    elif message_count < 5:
        # Limit tools early in conversation
        tools = [t for t in request.tools if t.name != "advanced_search"]
        request = request.override(tools=tools)  

    return handler(request)

agent = create_agent(
    model="openai:gpt-4o",
    tools=[public_search, private_search, advanced_search],
    middleware=[state_based_tools]
)
请参阅 动态选择工具 以获取更多示例。

模型

不同的模型有不同的优势、成本和上下文窗口。为当前任务选择合适的模型,这在智能体运行过程中可能会发生变化。
根据 State 中的对话长度使用不同的模型:
from langchain.agents import create_agent
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
from langchain.chat_models import init_chat_model
from typing import Callable

# Initialize models once outside the middleware
large_model = init_chat_model("anthropic:claude-sonnet-4-5")
standard_model = init_chat_model("openai:gpt-4o")
efficient_model = init_chat_model("openai:gpt-4o-mini")

@wrap_model_call
def state_based_model(
    request: ModelRequest,
    handler: Callable[[ModelRequest], ModelResponse]
) -> ModelResponse:
    """Select model based on State conversation length."""
    # request.messages is a shortcut for request.state["messages"]
    message_count = len(request.messages)  

    if message_count > 20:
        # Long conversation - use model with larger context window
        model = large_model
    elif message_count > 10:
        # Medium conversation
        model = standard_model
    else:
        # Short conversation - use efficient model
        model = efficient_model

    request = request.override(model=model)  

    return handler(request)

agent = create_agent(
    model="openai:gpt-4o-mini",
    tools=[...],
    middleware=[state_based_model]
)
请参阅动态模型以获取更多示例。

响应格式

结构化输出将非结构化文本转换为已验证的结构化数据。在提取特定字段或为下游系统返回数据时,自由格式文本是不够的。 工作原理: 当您提供一个模式作为响应格式时,模型的最终响应保证符合该模式。智能体运行模型/工具调用循环,直到模型完成工具调用,然后将最终响应强制转换为提供的格式。

定义格式

模式定义指导模型。字段名、类型和描述精确指定了输出应遵循的格式。
from pydantic import BaseModel, Field

class CustomerSupportTicket(BaseModel):
    """Structured ticket information extracted from customer message."""

    category: str = Field(
        description="Issue category: 'billing', 'technical', 'account', or 'product'"
    )
    priority: str = Field(
        description="Urgency level: 'low', 'medium', 'high', or 'critical'"
    )
    summary: str = Field(
        description="One-sentence summary of the customer's issue"
    )
    customer_sentiment: str = Field(
        description="Customer's emotional tone: 'frustrated', 'neutral', or 'satisfied'"
    )

选择格式

动态响应格式选择可根据用户偏好、对话阶段或角色调整模式——初期返回简单格式,随着复杂度增加则返回详细格式。
基于对话状态配置结构化输出:
from langchain.agents import create_agent
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
from pydantic import BaseModel, Field
from typing import Callable

class SimpleResponse(BaseModel):
    """Simple response for early conversation."""
    answer: str = Field(description="A brief answer")

class DetailedResponse(BaseModel):
    """Detailed response for established conversation."""
    answer: str = Field(description="A detailed answer")
    reasoning: str = Field(description="Explanation of reasoning")
    confidence: float = Field(description="Confidence score 0-1")

@wrap_model_call
def state_based_output(
    request: ModelRequest,
    handler: Callable[[ModelRequest], ModelResponse]
) -> ModelResponse:
    """Select output format based on State."""
    # request.messages is a shortcut for request.state["messages"]
    message_count = len(request.messages)  

    if message_count < 3:
        # Early conversation - use simple format
        request = request.override(response_format=SimpleResponse)  
    else:
        # Established conversation - use detailed format
        request = request.override(response_format=DetailedResponse)  

    return handler(request)

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

工具上下文

工具的特殊之处在于它们既能读取也能写入上下文。 在最基本的情况下,当一个工具执行时,它会接收 LLM 的请求参数并返回一个工具消息。该工具完成其工作并产生一个结果。 工具也可以为模型获取用于执行和完成任务的重要信息。

读取

大多数现实世界中的工具需要的不仅仅是 LLM 的参数。它们需要用于数据库查询的用户 ID、用于外部服务的 API 密钥,或用于做出决策的当前会话状态。工具通过读取状态、存储和运行时上下文来访问这些信息。
从状态中读取以检查当前会话信息:
from langchain.tools import tool, ToolRuntime
from langchain.agents import create_agent

@tool
def check_authentication(
    runtime: ToolRuntime
) -> str:
    """Check if user is authenticated."""
    # Read from State: check current auth status
    current_state = runtime.state
    is_authenticated = current_state.get("authenticated", False)

    if is_authenticated:
        return "User is authenticated"
    else:
        return "User is not authenticated"

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

写入

工具结果可用于帮助智能体完成给定任务。工具既可以直接将结果返回给模型,也可以更新智能体的记忆,从而为后续步骤提供重要上下文。
使用命令写入状态来跟踪会话特定信息:
from langchain.tools import tool, ToolRuntime
from langchain.agents import create_agent
from langgraph.types import Command

@tool
def authenticate_user(
    password: str,
    runtime: ToolRuntime
) -> Command:
    """Authenticate user and update State."""
    # Perform authentication (simplified)
    if password == "correct":
        # Write to State: mark as authenticated using Command
        return Command(
            update={"authenticated": True},
        )
    else:
        return Command(update={"authenticated": False})

agent = create_agent(
    model="openai:gpt-4o",
    tools=[authenticate_user]
)
请参阅 Tools,了解在工具中访问状态、存储和运行时上下文的全面示例。

生命周期上下文

控制核心智能体步骤之间发生的事情 - 拦截数据流来实现横切关注点,例如摘要、护栏和日志记录。 正如您在 模型上下文工具上下文 中所见,中间件 是使上下文工程变得可行的机制。中间件允许您钩入智能体生命周期的任何步骤,并执行以下任一操作:
  1. 更新上下文 - 修改状态和存储,以持久化更改、更新对话历史或保存洞察
  2. 生命周期跳转 - 根据上下文移动到智能体周期中的不同步骤(例如,如果满足条件则跳过工具执行,使用修改后的上下文重复模型调用)
智能体循环中的中间件钩子

示例:摘要

最常见的生命周期模式之一是在对话历史过长时自动压缩。与模型上下文中显示的临时消息修剪不同,摘要会持久化更新状态 - 用一个为所有未来轮次保存的摘要永久替换旧消息。 LangChain 为此提供了内置的中间件:
from langchain.agents import create_agent
from langchain.agents.middleware import SummarizationMiddleware

agent = create_agent(
    model="openai:gpt-4o",
    tools=[...],
    middleware=[
        SummarizationMiddleware(
            model="openai:gpt-4o-mini",
            max_tokens_before_summary=4000,  # Trigger summarization at 4000 tokens
            messages_to_keep=20,  # Keep last 20 messages after summary
        ),
    ],
)
当对话超过 token 限制时,SummarizationMiddleware 会自动:
  1. 使用单独的 LLM 调用总结较早的消息
  2. 将它们在 State 中永久地替换为一条摘要消息
  3. 保持最近的消息完整,以提供上下文
对话历史的摘要会被永久更新 - 未来的对话轮次将看到摘要而不是原始消息。
有关内置中间件的完整列表、可用的钩子以及如何创建自定义中间件,请参阅中间件文档

最佳实践

  1. 从简单开始 - 从静态提示和工具入手,仅在需要时添加动态内容
  2. 逐步测试 - 一次只添加一个上下文工程特性
  3. 监控性能 - 跟踪模型调用、Token 使用情况和延迟
  4. 使用内置中间件 - 利用 SummarizationMiddlewareLLMToolSelectorMiddleware 等。
  5. 记录你的上下文策略 - 明确说明传递了哪些上下文以及为什么传递
  6. 理解瞬时与持久:模型上下文的变化是瞬时的(每次调用),而生命周期上下文的变化会持久化到状态中

相关资源