概览

构建智能体(或任何 LLM 应用)的困难部分是使它们足够可靠。虽然它们可能适用于原型,但在现实用例中经常会失败。

智能体为何失败?

当智能体失败时,通常是因为智能体内部的 LLM 调用采取了错误的操作/没有执行我们预期的操作。LLM 因以下两个原因之一失败:
  1. 底层 LLM 能力不够
  2. “正确”的上下文没有传递给 LLM
通常情况下,这实际上是导致智能体不可靠的第二个原因。 上下文工程以正确的格式提供正确的信息和工具,以便 LLM 能够完成任务。这是人工智能工程师的首要工作。缺乏“正确”的上下文是更可靠智能体的首要障碍,而 LangChain 的智能体抽象经过独特设计,可以促进上下文工程。
上下文工程新手?从概念概述了解不同类型的上下文以及何时使用它们。

智能体循环

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

你可以控制什么

要构建可靠的智能体,您需要控制智能体循环的每个步骤以及步骤之间发生的情况。
上下文类型 你控制什么 短暂或持续
模型上下文 模型调用的内容(说明、消息历史记录、工具、响应格式) 瞬态
工具上下文 哪些工具可以访问和生成(读/写状态、存储、运行时上下文) 持久
生命周期上下文 模型和工具调用之间会发生什么(摘要、护栏、日志记录等) 持久

瞬态上下文

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

持久上下文

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

数据来源

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

它是如何运作的

LangChain中间件是使上下文工程对于使用 LangChain 的开发人员来说变得实用的底层机制。 中间件允许您连接到智能体生命周期中的任何步骤,并且:
  • 更新上下文
  • 跳转到智能体生命周期中的不同步骤
在本指南中,你会看到中间件 API 被频繁用于上下文工程。

模型上下文

控制每个模型调用的内容 - 指令、可用工具、使用哪个模型以及输出格式。这些决策直接影响可靠性和成本。 所有这些类型的模型上下文都可以借鉴状态(短期记忆),存储 (Store)(长期记忆),或运行时上下文(静态配置)。

系统提示

系统提示设置 LLM 的行为和能力。不同的用户、上下文或对话阶段需要不同的指令。成功的智能体利用记忆、偏好和配置来为当前对话状态提供正确的指令。
从状态访问消息计数或对话上下文:
import { createAgent } from "langchain";

const agent = createAgent({
  model: "gpt-4o",
  tools: [...],
  middleware: [
    dynamicSystemPromptMiddleware((state) => {
      // Read from State: check conversation length
      const messageCount = state.messages.length;

      let base = "You are a helpful assistant.";

      if (messageCount > 10) {
        base += "\nThis is a long conversation - be extra concise.";
      }

      return base;
    }),
  ],
});

消息

消息构成发送给 LLM 的提示。 管理消息内容至关重要,以确保 LLM 拥有正确的信息来做出良好的回应。
当与当前查询相关时,从 State 注入上传的文件上下文:
import { createMiddleware } from "langchain";

const injectFileContext = createMiddleware({
  name: "InjectFileContext",
  wrapModelCall: (request, handler) => {
    // request.state is a shortcut for request.state.messages
    const uploadedFiles = request.state.uploadedFiles || [];  

    if (uploadedFiles.length > 0) {
      // Build context about available files
      const fileDescriptions = uploadedFiles.map(file =>
        `- ${file.name} (${file.type}): ${file.summary}`
      );

      const fileContext = `Files you have access to in this conversation:
${fileDescriptions.join("\n")}

Reference these files when answering questions.`;

      // Inject file context before recent messages
      const messages = [  
        ...request.messages,  // Rest of conversation
        { role: "user", content: fileContext }
      ];
      request = request.override({ messages });  
    }

    return handler(request);
  },
});

const agent = createAgent({
  model: "gpt-4o",
  tools: [...],
  middleware: [injectFileContext],
});
瞬时消息更新与持久消息更新: 上面的例子使用 wrap_model_call 使瞬态更新 - 修改单次调用发送到模型的消息,而不更改状态中保存的内容。 对于需要持久修改状态的更新(例如 生命周期上下文 中的摘要示例),请使用生命周期钩子,例如 before_model 或者 after_model 永久更新对话历史记录。请参阅中间件文档了解更多详情。

工具

工具允许模型与数据库、API 和外部系统交互。如何定义和选择工具直接影响模型能否有效完成任务。

定义工具

每个工具都需要一个清晰的名称、描述、参数名称和参数描述。这些不仅仅是元数据,它们指导模型关于何时以及如何使用该工具的推理。
import { tool } from "@langchain/core/tools";
import { z } from "zod";

const searchOrders = tool(
  async ({ userId, status, limit }) => {
    // Implementation here
  },
  {
    name: "search_orders",
    description: `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.`,
    schema: z.object({
      userId: z.string().describe("Unique identifier for the user"),
      status: z.enum(["pending", "shipped", "delivered"]).describe("Order status to filter by"),
      limit: z.number().default(10).describe("Maximum number of results to return"),
    }),
  }
);

选择工具

并非每种工具都适合每种情况。太多的工具可能会压垮模型(超载上下文)并增加错误;太少限制了能力。动态工具选择根据身份验证状态、用户权限、功能标志或对话阶段来调整可用的工具集。
仅在某些对话里程碑后启用高级工具:
import { createMiddleware } from "langchain";

const stateBasedTools = createMiddleware({
  name: "StateBasedTools",
  wrapModelCall: (request, handler) => {
    // Read from State: check authentication and conversation length
    const state = request.state;  
    const isAuthenticated = state.authenticated || false;  
    const messageCount = state.messages.length;

    let filteredTools = request.tools;

    // Only enable sensitive tools after authentication
    if (!isAuthenticated) {
      filteredTools = request.tools.filter(t => t.name.startsWith("public_"));  
    } else if (messageCount < 5) {
      filteredTools = request.tools.filter(t => t.name !== "advanced_search");  
    }

    return handler({ ...request, tools: filteredTools });  
  },
});
参见动态选择工具以了解更多示例。

模型

不同的模型有不同的优势、成本和上下文窗口。为手头的任务选择正确的模型, 在智能体运行期间可能会发生变化。
根据州的对话长度使用不同的模型:
import { createMiddleware, initChatModel } from "langchain";

// Initialize models once outside the middleware
const largeModel = initChatModel("claude-sonnet-4-5-20250929");
const standardModel = initChatModel("gpt-4o");
const efficientModel = initChatModel("gpt-4o-mini");

const stateBasedModel = createMiddleware({
  name: "StateBasedModel",
  wrapModelCall: (request, handler) => {
    // request.messages is a shortcut for request.state.messages
    const messageCount = request.messages.length;  
    let model;

    if (messageCount > 20) {
      model = largeModel;
    } else if (messageCount > 10) {
      model = standardModel;
    } else {
      model = efficientModel;
    }

    return handler({ ...request, model });  
  },
});
参见动态模型以了解更多示例。

响应格式

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

定义格式

模式定义指导模型。字段名称、类型和描述准确指定输出应遵循的格式。
import { z } from "zod";

const customerSupportTicket = z.object({
  category: z.enum(["billing", "technical", "account", "product"]).describe(
    "Issue category"
  ),
  priority: z.enum(["low", "medium", "high", "critical"]).describe(
    "Urgency level"
  ),
  summary: z.string().describe(
    "One-sentence summary of the customer's issue"
  ),
  customerSentiment: z.enum(["frustrated", "neutral", "satisfied"]).describe(
    "Customer's emotional tone"
  ),
}).describe("Structured ticket information extracted from customer message");

选择格式

动态响应格式选择根据用户偏好、对话阶段或角色来调整模式——尽早返回简单格式,并随着复杂性的增加而返回详细格式。
根据对话状态配置结构化输出:
import { createMiddleware } from "langchain";
import { z } from "zod";

const simpleResponse = z.object({
  answer: z.string().describe("A brief answer"),
});

const detailedResponse = z.object({
  answer: z.string().describe("A detailed answer"),
  reasoning: z.string().describe("Explanation of reasoning"),
  confidence: z.number().describe("Confidence score 0-1"),
});

const stateBasedOutput = createMiddleware({
  name: "StateBasedOutput",
  wrapModelCall: (request, handler) => {
    // request.state is a shortcut for request.state.messages
    const messageCount = request.messages.length;  

    let responseFormat;
    if (messageCount < 3) {
      // Early conversation - use simple format
      responseFormat = simpleResponse; 
    } else {
      // Established conversation - use detailed format
      responseFormat = detailedResponse; 
    }

    return handler({ ...request, responseFormat });
  },
});

工具上下文

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

大多数现实世界的工具需要的不仅仅是 LLM 的参数。它们需要用于数据库查询的用户 ID、用于外部服务的 API 密钥或当前会话状态来做出决策。工具从状态、存储和运行时上下文中读取以访问此信息。
读取State来检查当前会话信息:
import * as z from "zod";
import { createAgent, tool, type ToolRuntime } from "langchain";

const checkAuthentication = tool(
  async (_, runtime: ToolRuntime) => {
    // Read from State: check current auth status
    const currentState = runtime.state;
    const isAuthenticated = currentState.authenticated || false;

    if (isAuthenticated) {
      return "User is authenticated";
    } else {
      return "User is not authenticated";
    }
  },
  {
    name: "check_authentication",
    description: "Check if user is authenticated",
    schema: z.object({}),
  }
);

工具结果可用于帮助智能体完成给定的任务。工具都可以将结果直接返回给模型 并更新智能体的内存,以便为未来的步骤提供重要的上下文。
使用命令写入状态以跟踪特定于会话的信息:
import * as z from "zod";
import { tool } from "@langchain/core/tools";
import { createAgent } from "langchain";
import { Command } from "@langchain/langgraph";

const authenticateUser = tool(
  async ({ password }) => {
    // Perform authentication
    if (password === "correct") {
      // Write to State: mark as authenticated using Command
      return new Command({
        update: { authenticated: true },
      });
    } else {
      return new Command({ update: { authenticated: false } });
    }
  },
  {
    name: "authenticate_user",
    description: "Authenticate user and update State",
    schema: z.object({
      password: z.string(),
    }),
  }
);
参见工具以了解在工具中访问状态、存储和运行时上下文的完整示例。

生命周期上下文

控制发生的事情之间核心智能体步骤 - 拦截数据流以实现横切关注点,例如汇总、护栏和日志记录。 正如您所见模型上下文工具上下文 , 中间件是使上下文工程变得实用的机制。中间件允许您连接到智能体生命周期中的任何步骤,并且:
  1. 更新上下文- 修改状态和存储以保存更改、更新对话历史记录或保存见解
  2. 生命周期跳跃- 根据上下文移动到智能体循环中的不同步骤(例如,如果满足条件,则跳过工具执行,使用修改后的上下文重复模型调用)
Middleware hooks in the agent loop

示例:总结

最常见的生命周期模式之一是在对话历史过长时自动压缩。与模型上下文中展示的瞬态消息裁剪不同,摘要会持续更新状态——用为未来各轮保存的摘要永久替换旧消息。 LangChain为此提供了内置中间件:
import { createAgent, summarizationMiddleware } from "langchain";

const agent = createAgent({
  model: "gpt-4o",
  tools: [...],
  middleware: [
    summarizationMiddleware({
      model: "gpt-4o-mini",
      trigger: { tokens: 4000 },
      keep: { messages: 20 },
    }),
  ],
});
当会话超过令牌限制时, SummarizationMiddleware 自动地:
  1. 使用单独的 LLM 调用总结旧消息
  2. 将它们替换为状态中的摘要消息(永久)
  3. 保持最近消息的上下文完整
摘要会永久更新对话历史——后续对话看到的是摘要而非原始消息。
有关内置中间件、可用挂钩以及如何创建自定义中间件的完整列表,请参阅中间件文档 .

最佳实践

  1. 从简单开始- 从静态提示和工具开始,仅在需要时添加动态
  2. 增量测试- 一次添加一项上下文工程功能
  3. 监控性能- 跟踪模型调用、令牌使用情况和延迟
  4. 使用内置中间件- 杠杆作用 SummarizationMiddleware , LLMToolSelectorMiddleware , ETC。
  5. 记录您的情境策略- 明确正在传递的上下文以及原因
  6. 了解瞬态与持久:模型上下文更改是暂时的(每次调用),而生命周期上下文更改会持续到状态

连接这些文档通过 MCP 发送给 Claude、VSCode 等以获得实时答案。