LangChain 详解 Deep Agents 上下文工程:如何不让全量上下文塞满窗口实现记忆
Context Engineering with Deep Agents: How To Remember Without Keeping Everything In Context
LangChain 介绍了开源框架 Deep Agents 应对智能体长任务“上下文退化”的工程策略与中间件设计。框架通过超大工具结果与消息卸载至文件系统、上下文占满 85% 时触发自动摘要归档、技能渐进披露加载、子智能体隔离噪音以及 Markdown 文件持久化跨会话记忆,使模型主窗口仅保留必要上下文。所有核心机制均由预置中间件驱动,支持切换不同底层大语言模型时复用同一份记忆存储。
原文系统梳理了卸载、摘要、技能按需加载和子智能体等上下文工程机制,可为长任务智能体避免上下文退化提供可迁移的架构参考。
如果你曾经在考前熬夜突击,一定很熟悉这种感觉:书越往后背,前面学过的内容就越容易忘掉。
智能体也会面临同样的困扰。随着处理的任务越来越长,它们吸收的信息也越积越多(包括指令、工具调用结果、文件等);最终,这些信息变成了远超模型上下文窗口负荷的庞大上下文。模型背负的历史记录、噪声和工具输出越多,其准确度和推理能力往往就越差。
在智能体工程领域,这种现象被称为上下文腐化。本文将探讨管理智能体上下文窗口的各种技术,并介绍 Deep Agents 框架如何利用“中间件”概念将这些技术无缝嵌入到智能体的运行流程中。
深入理解上下文工程技术
简单来说,智能体本质上就是一个被外围框架包裹的模型。

智能体用来应对上下文腐化的上下文工程技术,主要可以分为两大类:
- 阻止不必要的信息进入上下文窗口
- 管理并过滤已存在于上下文窗口中的内容
框架会灵活运用这些方法,根据当前任务在恰当的时机为模型提供恰当的上下文(即检索)。
不过,搭建一个能把这件事做好的框架说来容易做来难,背后需要大量工程投入(如果你恰好是那种习惯考前突击的人,这个工作量可能就有点吃不消了)。
Deep Agents 是 LangChain 推出的开源框架。它能为你选定的模型提供一套封装方案,把上下文从活跃窗口中移出(转存至文件、摘要或子智能体),让主智能体只保留必要的信息(后续若有需要,也可以随时恢复原始数据):
- 转存卸载(Offloading):将体积庞大的工具执行结果和用户消息保存到文件系统,模型仅读取简略预览。
- 提炼总结(Summarization):当对话接近上下文上限时,将较早的消息浓缩为摘要。
- 技能按需加载(Skills):仅在模型判定与当前任务相关时,才载入对应的专业操作流程。
- 子智能体(Subagents):将噪声较多的繁琐任务放到独立的上下文窗口中运行,最终只传回关键结果。
- 长期记忆(Long-term memory):将智能体积累的经验保存为文件,延续并复用到后续对话中。
上下文工程在常见场景中的应用
我们来看看上述技术在实际业务中是如何落地的。以竞品分析智能体为例:每周一,它都要调研 5 家竞争对手,并按照团队现有的模板撰写分析报告。单次执行流程大致如下:
- 抓取 A 公司的市场数据导出表(包含数万行数据)
- 检索相关新闻,阅读几篇报道并整理要点
- 对 B 公司到 E 公司重复上述步骤
- 起草报告初稿,根据人工反馈进行修改,并调整排版格式
等执行到第 4 步时,智能体在每次调用模型时,都会连带背负 5 份原始导出数据、数十条检索结果以及两版废弃草稿。而对最后的报告排版而言,其中绝大部分内容都毫无意义。
虽然检索方案(如 RAG 或文件系统搜索)有助于智能体仅引入真正需要的信息,但这仍然只解决了一半的问题。即使定位再精准的智能体,也难免会堆积多余的消息、工具输出以及中间过程产物,因此框架还必须对上下文窗口内部及其周边的内容进行有效管控。
落地这些技术的核心利器:中间件
智能体的运行机制本质上是一个循环:模型决定下一步操作、调用工具、读取返回结果,如此往复,直至任务完成。中间件则是插入到这个循环特定节点的代码:比如在智能体启动前、每次模型调用前后,或每次工具调用前后。
- 在每个节点上,中间件都可以读取上下文,并在执行下一步之前对其进行修改。
- Every context engineering technique in this post is a piece of middleware hooked into one of those points.
- 这些机制大多已内置开箱即用,因此无需自行编写中间件即可直接使用这些技术(除非需要进行特定定制)。
卸载机制:避免数据挤占上下文窗口
智能体并不需要随时查看每次工具调用或人工消息返回的所有数据,数据量庞大时更是如此。
例如,每次调用 pull_market_report 工具都会返回一份完整的数据导出(包含数千行);但智能体其实只需要其中的几个数字,因此完全可以限制模型只查看这些关键行。

FilesystemMiddleware 在每次工具被调用时都会运行:
- 当工具返回的结果超过约 20,000 个 token 时,完整内容会在进入对话之前被写入 /large_tool_results/{tool_call_id}。
- 模型只能看到简短的预览内容(开头和结尾的几行)以及该文件路径。
- 后续如果智能体需要 B 公司的季度营收数据,可以通过 read_file 进行分页查阅。
超大的人工/用户消息(例如粘贴进来的电话会议记录)也会享受同样的待遇,只是阈值更高(50,000 个 token),并会被保存至 /conversation_history/。
摘要压缩:精简既有上下文
即便竭力卸载冗长的工具调用结果,随着运行时间拉长,上下文依然会逐渐堆积(比如对五家公司的搜索记录、笔记和草稿)。此时可以利用摘要压缩技术,将智能体当前的全部上下文浓缩为一份过往进程摘要,让它得以继续推进调研任务。

- 这里的激增指的是大体积工具结果或人工消息的涌入;卸载机制会在模型接收前将其替换为简短预览,让上下文只增加微量内容。
- 摘要压缩则负责应对缓慢的累积增长:一旦占用率达到 85%,较早的历史消息就会被压缩成一段摘要,上下文占用随即大幅回落。
SummarizationMiddleware 会在每次模型调用前即刻检查 token 占用情况:
- 一旦占用量突破设定的阈值(默认为模型上下文窗口的 85%),系统就会发起一次单独的 LLM 调用,将较早的消息浓缩为一段摘要
- 较早的消息会从上下文窗口中移除,而最新的消息则会原样保留。
- 此时模型首先看到的是摘要,随后是最近的消息(因此最新内容不会因意译而失真)。
两处关键细节:
- 内容可追溯。被压缩的消息会被写入 /conversation_history/{session_id}.md。如果智能体在起草内容时需要 A 公司调研中的某个细节,可以直接回溯读取。
- 循环运作。随着任务继续进行,token 占用会再次上升,而下一次生成的摘要将包含上一次的摘要内容。
除了根据 token 阈值触发外,也可以在话题切换时进行摘要。定制摘要提示词往往能带来不错的效果。

我们关于深度智能体上下文管理的文章对此有更详细的介绍。
技能(Skills):按需动态加载信息
假设你的销售团队有一套固定的季度业务复盘(QBR)格式。智能体每个季度只需要用到一次这些指令,但如果把它们一直放在系统提示词里,就意味着每次调用都要为此承担 token 和响应时间的开销。与其这样,不如把这套信息打包成一项“技能”(Skill)。
技能是一个包含指令(还可选配脚本和参考文档)的文件夹,能为智能体提供完成专项任务所需的一切资源。

智能体平时只能看到简要说明,只有在遇到 QBR 任务时,才会加载 qbr-report 的完整指令(其他任务根本不会带上这些内容)。
技能支持分层加载上下文,只有在真正相关时才会扩大上下文窗口。这种机制被称为“渐进式展开”(progressive disclosure):模型预先看到的是一份“菜单”,只在需要时才拉取完整的“食谱”。这样一来,加载十项技能消耗的上下文,差不多只相当于十句一句话简介。

只要将智能体指向技能目录,SkillsMiddleware 就会利用两个钩子来处理:
- 在智能体启动前,扫描技能文件夹,且只读取每个 SKILL.md 的头部信息。
- 在每次调用模型时,把这些技能的名称和简介注入到系统提示词中。
只有当模型判定某项技能与当前任务相关(并使用 read_file 工具读取它)时,完整指令才会进入上下文。因此,我们的智能体在调研阶段完全不带模板,直到需要排版 QBR 报告时才会将其加载进来。
子智能体:隔离嘈杂工作
卸载任务能有效解决返回结果过于臃肿的问题;但对于那些你明知会产生大量噪音的工作(比如需要翻阅几十次搜索和网页来调研一家公司),子智能体则是更理想的设计方案。子智能体会在自己的上下文窗口中完成这些繁杂的工作,最终只返回处理好的结果,主智能体从头到尾都不会受到这些“噪音”的干扰。

借助 SubAgentMiddleware,我们可以灵活定制子智能体的系统提示词和可用工具。
market_researcher = {
"name": "market-researcher",
"description": "Researches a company and returns the 3 most notable developments.",
"system_prompt": "Use pull_market_report, then return 3 short bullet points. Do not return the raw report.",
"tools": [pull_market_report],
}
agent = create_deep_agent(model=model, subagents=[market_researcher])在我们的案例中,主智能体会把每家公司交由 market-researcher 子智能体处理。
调研子智能体在自己的上下文中通读整份报告,最后只返回三条要点,因此主智能体看到的仅仅是这三条要点(连原始报告的预览都不会接触到)。此外,由于子智能体的任务通常体量较小、边界明确,往往可以使用成本更低的模型并行跑完。
长期记忆:实现跨会话持久化
随着任务推进,你的智能体会学到大量信息,其中有些很有价值,有些则无关紧要。
举个例子,你跟智能体说:“总结要用要点列表,别写成大段文字。”要是没有记忆机制,你可能每周都得重复交代一遍。在 Deep Agents 中,长期记忆其实就是一个 Markdown 文件。你只需传入一个参数将智能体指向该文件,剩下的全部交给 MemoryMiddleware 处理:
- 加载:运行伊始读取该文件。
- 注入:在每次调用模型时,将其内容添加到系统提示词中。
- 更新:当智能体学到了值得保留的新信息(比如你的排版偏好)时,就会更新这个文件。
模型随意换,记忆不丢失
Deep Agents 完全不绑定特定模型供应商,这一点在记忆机制上也体现得淋漓尽致。因为记忆只是存储在你本地的纯文本文件(而非被服务商扣住的数据),所以即便底层换成另一个模型,依然可以使用同一份记忆:
agent = create_deep_agent(
model="openai:gpt-6", # different provider, same memory
memory=["/memories/AGENTS.md"],
backend=backend,
store=store,
)哪怕下周换用另一家厂商的模型,它读到这个文件后,依然会乖乖用列表形式来写总结。
值得牢记的上下文工程策略
无论你是基于 Deep Agents 构建应用、使用其他框架,还是从零自研,以下几点都值得重点关注:
- 超长输入应当外部转存,而不是直接截断。截断是有损的,而外部转存能保证随时可检索。
- 压缩应当可恢复。即便做了摘要总结,智能体仍应能取回所需的细节。
- 专业知识应当按需加载。如果把每个任务的指令都塞进系统提示词,每次调用都会平白消耗上下文。
- 杂乱任务应当隔离处理。让子智能体拥有独立的上下文,有助于主智能体保持专注。
- 记忆必须由你自主掌控。把记忆存放在自己掌控的存储中,即便更换模型或供应商也不会丢失。
- 默认设置应当允许自定义。适用于某一任务的阈值,往往很难照搬到其他所有任务上。
亲自体验
uv add deepagents我曾在工作坊中分享过这部分内容,点击这里即可查看!
管理好上下文只是第一步,另一半关键在于从一开始就引入正确的信息。接下来不妨阅读这篇检索技术博客。
如果想进一步深入学习上下文工程,不妨了解一下我们免费的自主进度课程:LangChain Academy 上的 Deep Agents 课程。
来源:LangChain Blog · langchain.com