这篇适合谁、读完能做到什么
适合已经能让大模型回答问题、现在想把智能体做得更稳定的开发者。读完你应该能说清智能体的每一步会把什么信息交给模型,知道哪些内容放进提示词、哪些放进工具、哪些在两次调用之间处理,并能用 LangChain 的中间件把其中一两项落到代码里。
上下文工程是什么
智能体出错时,多数情况不是模型不够聪明,而是模型调用时没有拿到它需要的上下文。LangChain 把两种原因分得很清楚:一是底层模型能力不够,二是「正确的」上下文没有传给模型,而后一种更常见,也是智能体不可靠的主要原因[1]。
所谓上下文工程,就是用正确的格式,把完成任务所需的信息和工具交给模型[1]。这一项工作被认为是 AI 工程师的首要工作[1]。
智能体循环:先看清每一步
一个典型的智能体循环分两步[1]:
- 模型调用:把提示词和可用工具交给模型,模型返回一个回答,或者一个执行工具的请求。
- 工具执行:运行模型请求的工具,把结果返回。
循环一直进行,直到模型决定结束[1]。要让智能体可靠,就要控制每一步发生了什么,也要控制两步之间发生了什么。
三类上下文:临时的和持久的
LangChain 把要控制的内容分成三类[1]:
- 模型上下文:每次模型调用的内容,包括指令、消息历史、工具和输出格式。这类修改是临时的,只影响这一次调用。
- 工具上下文:工具能读写什么。工具读取状态、存储和运行时上下文,也可以把结果写回状态或存储。这类修改是持久的。
- 生命周期上下文:模型和工具之间发生的事,例如摘要、护栏和日志。这类修改同样是持久的[1]。
数据来源也分三层[1]。运行时上下文是会话内的静态配置,比如用户 ID、API 密钥、数据库连接、权限和环境设置。状态是会话内的短期记忆,比如当前消息、上传的文件、登录状态和工具结果。存储是跨会话的长期记忆,比如用户偏好、提炼出的洞察和历史数据[1]。
上手:给工具写清楚的描述
工具是模型和外部系统之间的接口。每个工具都需要清晰的名称、描述、参数名和参数说明,这些信息会指导模型判断什么时候用这个工具、怎么传参数[1]。下面的 Python 例子定义了一个按状态查询订单的工具:
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这里的 docstring 写了工具的用途,Args 部分写了每个参数的含义。工具上下文分读和写两部分:读的时候,工具可以访问状态、存储和运行时上下文;写的时候,工具返回的结果既能直接交给模型,也能更新智能体的记忆,让后面的步骤用得上[1]。模型看到的正是这些描述,所以它们要写得具体,比如把可选的状态值直接列出来。
工具太多,模型反而会乱
工具太多,会淹没模型的上下文,增加出错率;工具太少,又限制了智能体能做的事[1]。解决办法是动态选工具:根据认证状态、用户权限、功能开关或对话阶段,决定这一次模型调用能看到哪些工具[1]。
示例给出了两种做法。一是未登录时只保留名字以 public_ 开头的工具。二是对话消息少于 5 条时,先隐藏 advanced_search,等对话深入后再开放[1]。工具也可以写回状态,例如密码校验通过后返回 Command(update={"authenticated": True}),把登录状态记下来[1]。
临时修改与持久修改
wrap_model_call 做的修改只影响这一次模型调用,不会改变保存在状态里的内容。要让修改持久化,在 Python 中可以从 wrap_model_call 返回带 Command 的响应,或者在 before_model、after_model、wrap_tool_call 这类生命周期钩子里更新消息历史。TypeScript 版本可以直接从 wrapModelCall 返回 Command[1]。
注入上下文时,LangChain 的建议是把内容追加在消息末尾,因为模型更关注最后的消息[1]。
长对话怎么办:用摘要中间件
对话越长,消息越多,token 消耗和延迟都会上升。LangChain 提供的 SummarizationMiddleware 会在对话超过 token 上限时,用一次单独的 LLM 调用总结较早的消息,把它们替换成一条摘要消息,并写入状态;最近的消息则原样保留[1]。示例里的参数是这样的:
from langchain.agents import create_agent
from langchain.agents.middleware import SummarizationMiddleware
agent = create_agent(
model="gpt-6.1-sol",
tools=[...],
middleware=[
SummarizationMiddleware(
model="gpt-5.4-mini",
trigger={"tokens": 4000},
keep=("messages", 20),
),
],
)这段代码的 trigger 设为 4000 个 token 时触发摘要,keep 保留最近 20 条消息。摘要是持久的:之后的每一轮,模型看到的是摘要,而不是被替换掉的原始消息[1]。所以阈值和保留条数要按你的业务来定,不能照搬示例。这两个数字只是示范,要结合你的消息长度和成本预算一起考虑。
限制和常见问题
- 摘要一旦发生,被替换的原始消息就不再发给模型。如果后面的问题依赖早期的具体细节,要么调高阈值,要么把这类信息放进存储[1]。
- 示例中的存储使用的是
InMemoryStore,它来自langgraph.store.memory[1]。它适合试验,进程结束数据就没了;要长期保存,需要换成持久化的存储。 - LangChain 建议从静态提示词和工具开始,每次只加一项上下文功能,并监控模型调用次数、token 用量和延迟[1]。
下一步
想看全部内置中间件和钩子的细节,可以接着读 LangChain 的中间件文档,其中还包括 SummarizationMiddleware 和 LLMToolSelectorMiddleware 等内置中间件[1]。示例同时提供 Python 和 TypeScript 两个版本,TypeScript 使用的是 createAgent、createMiddleware 和 initChatModel[1]。
本文改编自 LangChain 官方文档中的《Context engineering in agents》一页[1],原文以 MIT 许可证发布。
来源
- 1Context engineering in agentsLangChain docs (MIT)
引用本页: LangChain 上下文工程. TellWorld, https://www.tellworld.net/zh/practice/practice-langchain-context-engineering. 更新于 2026-11-14