LangChain Blog · 2026/10/15 01:11:43

LangChain 开源 Deep Agents:五种上下文外移技术解决长程智能体 Context Rot 难题

原标题:Context Engineering with Deep Agents: How To Remember Without Keeping Everything In Context
68AI 研判分
核心综述

LangChain 开源智能体框架 Deep Agents 提出五种“上下文外移”技术,旨在解决长程任务中因信息堆积导致的模型准确率下降(Context Rot)问题。该方案将智能体记忆以纯文件形式存储于用户可控的外部存储中,而非全部塞入模型上下文窗口,从而在保持推理精度的同时实现跨模型、跨供应商的知识持久化与迁移。

报道全文原始报道全文

本文目录13 个章节

核心要点

  • “上下文腐烂”(Context rot)发生在长时间运行的智能体积累了超出模型承载能力的上下文时,导致准确率下降。
  • Deep Agents 是 LangChain 推出的开源智能体框架(harness),它采用五种技术将上下文移出活动窗口(而非删除),以防止上下文腐烂。
  • 在 Deep Agents 中,智能体的记忆以普通文件的形式存储在你控制的存储空间中,因此你可以切换模型或服务商,同时保留其已学习到的所有内容。

如果你曾在考试前一晚临时抱佛脚,你一定熟悉这种感觉:复习的内容越多,早期章节的记忆就越容易模糊。

智能体也面临同样的问题。随着它们处理更长的任务,会摄入越来越多的信息(指令、工具结果、文件等);最终,这些信息量远超模型上下文窗口所能舒适容纳的范围 context。模型携带的历史记录、噪声和工具输出越多,其准确率和推理能力往往越差。

在智能体工程领域,我们将这种现象称为 上下文腐烂(context rot)。本博客将介绍管理智能体上下文窗口的技术,以及 Deep Agents 框架如何利用名为“中间件”(middleware)的概念将这些技术注入到智能体的运行过程中。

‍

理解上下文工程技术

简单来说,智能体就是一个模型加上围绕它的框架(harness)。

框架 = 包裹模型(LLM)的外围结构

智能体可用于解决上下文腐烂问题的上下文工程技术主要分为两类:

框架策略性地应用这些方法,以便为给定任务向模型提供正确时间点的正确上下文。

然而,要构建一个能出色执行此任务的框架并非易事,需要大量工作(如果你是那种考前才突击复习的人,这可能意味着工作量过大)。Deep Agents 是 LangChain 的开源框架。它将你选择的模型包裹起来,并运用各种技术将上下文移出活动窗口(存入文件、摘要或独立的智能体中),从而使主智能体仅携带其所需的信息(并在需要时可恢复原始数据):

  1. 卸载(Offloading): 大型工具结果和用户消息会保存到文件系统中,模型仅查看预览内容。
  2. 摘要(Summarization): 当对话接近上下文限制时,较早的消息会被压缩成摘要。
  3. 技能(Skills): 任务专业知识仅在模型判断其相关时才加载。
  4. 子代理(Subagents): 嘈杂的工作在独立的上下文窗口中运行,仅返回最终结果。
  5. 长期记忆(Long-term memory): 智能体学到的内容会保存到一个文件中,并延续到下一次对话。

‍

将上下文工程应用于常见用例

让我们看看上述技术如何应用于实际场景:一个竞争分析智能体。每周一,该智能体会研究五个竞争对手,并按照团队模板撰写报告。单次运行流程大致如下:

  1. 拉取公司 A 的市场数据导出文件(数万行数据)
  2. 搜索新闻,阅读几篇文章并做笔记
  3. 对公司 B 至 E 重复上述步骤
  4. 起草报告,根据你的反馈进行修改,并完成格式化

到了第 4 步,智能体已经在每次模型调用中携带了五份原始导出数据、数十条搜索结果以及两份过时的草稿。其中大部分内容与最终报告的格式化无关。

检索方法(RAG 或文件系统搜索)有助于智能体仅获取所需信息,但这只解决了一半的问题。即使是一个精准的 Agent,也会累积不必要的消息、工具结果和中间工作产物,因此框架(harness)还必须管理已经存在于上下文窗口内及其周边的内容。

‍

如何实现这些技术:中间件(Middleware)

Agent 在一个循环中运行:模型决定做什么,调用工具,读取结果,然后重复此过程直到任务完成。中间件是插入该循环特定节点的代码:例如在 Agent 启动前、围绕每次模型调用或围绕每次工具调用。

  • 在每个节点,它都可以读取上下文并在下一步运行前对其进行修改。

  • 本文提到的每一项上下文工程技术,都是挂钩到其中一个节点的中间件组件。

  • 大多数功能已内置,因此你无需编写任何中间件即可使用这些技术(除非你想自定义某些特定功能)。

‍

卸载:将其保留在上下文窗口之外

我们的智能体并不总是需要看到每次工具调用或人类消息返回的所有数据,尤其是在数据量很大的情况下。

例如,每个 pull_market_report 工具调用都会返回一份完整的数据导出文件(包含数千行);然而,智能体只需要其中的几个数值,因此可以限制模型仅查看这些特定的行。

工具结果卸载过程(动画演示)

每当调用工具时,FilesystemMiddleware 都会执行以下操作:

  1. 当工具结果超过大约 20,000 个 token 时,在结果进入对话之前,完整的结果会被写入 /large_tool_results/{tool_call_id}。
  2. 模型只能看到一个简短的预览(开头和结尾的几行)以及文件路径。
  3. 当智能体后续需要查询公司 B 的季度营收时,它会通过 read_file 分页读取该文件。

超大的用户/人类消息(如粘贴的通话记录)也会受到类似处理,但阈值更高(50,000 个 token),并被保存到 /conversation_history/。

‍

摘要生成:压缩已有内容

尽管我们尽力卸载冗长的工具调用,但在长时间运行过程中(例如涉及五家公司的搜索、笔记和草稿),上下文会逐渐堆积。我们可以使用摘要生成功能将智能体的整个上下文窗口压缩为迄今为止发生情况的总结,以便其继续执行研究任务。

卸载机制阻止了小幅度的峰值增长,而摘要生成则“重置”了整体的上升趋势

  • 峰值是由大型工具结果/人类消息到达引起的;卸载机制在模型看到它们之前,将每一个替换为简短的预览,因此上下文仅少量增长。
  • 摘要生成处理缓慢的累积上升:一旦使用率达到 85%,较旧的消息会被压缩成摘要,从而使上下文占用量回落。

SummarizationMiddleware 会在每次模型调用前检查 token 使用情况:

  1. 一旦使用率超过阈值(默认为模型上下文窗口的 85%),一个独立的 LLM 调用会将较旧的消息压缩为一个单独的摘要。
  2. 较旧的消息会从上下文窗口中移除,而最近的消息则保持原样保留。
  3. 模型现在首先看到摘要,然后是最近的消息(因此不会改写任何近期的内容)。

两个重要细节:

  • 可恢复。 摘要后的消息会写入 /conversation_history/{session_id}.md。如果 Agent 在起草时需要查阅 Company A 的研究细节,它可以重新读取该文件。
  • 循环迭代。 随着运行继续,Token 用量再次攀升,下一次摘要将包含之前的摘要内容。

你也可以在话题切换时进行摘要,而不仅仅是在达到特定 Token 阈值时。自定义摘要提示词(prompt)通常很有帮助。

‍

OffloadingSummarization
拦截输入,防止其进入上下文作用于已在上下文窗口中的内容

我们关于 Deep Agents 的上下文管理的文章对这两者进行了更详细的探讨。

Skills:仅在需要时加载信息

假设你的销售团队有特定的季度业务回顾(QBR)格式。Agent 每季度只需要这些指令一次,因此将其保留在系统提示词中意味着每次调用都要为此付出代价(包括 Token 成本和响应时间)。相反,你可以将这些信息打包为一个 Skill。

Skill 是一个包含指令的文件夹(可选包含脚本和参考文档),它为 Agent 提供执行专门任务所需的信息。

Skill 文件夹的结构

Agent 首先看到简短的描述,只有当 QBR 任务出现时,它才会加载 qbr-report 的完整指令(其他任务永远不会携带这些指令)。

Skills 分层加载上下文,因此在相关内容实际出现之前,上下文窗口保持较小。这被称为渐进式披露(progressive disclosure):模型首先看到一个“菜单”,并在需要时拉取完整的“食谱”,因此十个 Skills 消耗的上下文大约相当于十行简短描述。

渐进式披露(动画演示)

你指定 Agent 使用你的 Skills,SkillsMiddleware 会使用两个钩子(hooks):

  1. 在 Agent 启动前,它扫描 Skills 文件夹并仅读取每个 SKILL.md 文件的头部。
  2. 在每次模型调用时,它将那些名称和描述添加到系统提示词中。

只有当模型决定某个 Skill 相关时(并通过 read_file 工具读取它),完整的指令才会进入上下文,因此我们的 Agent 在进行研究时无需携带模板,只有在需要格式化 QBR 时才加载它。

‍

Subagents:隔离嘈杂的工作

Offloading 能有效应对结果过大的问题,但对于那些你已知会产生大量噪声的工作(例如通过数十次搜索和页面浏览来调研一家公司),subagents 是一个更好的设计选择。Subagent 会在其独立的上下文窗口中完成这些工作,并仅返回最终结果,从而确保主 agent 永远不会看到“噪声”。

SubAgentMiddleware 允许我们自定义 subagents 的系统提示词(system prompt)和可用工具。

PYTHON
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])

在我们的案例中,主 agent 将每家公司交给 market-researcher subagent 处理。

该 researcher subagent 在其自身的上下文中读取完整报告,并仅返回三个要点,因此主 agent 只能看到这三个要点(甚至看不到原始报告的预览)。此外,由于 subagent 的任务通常较小且范围明确,它们往往可以在更便宜的模型上并行运行。

‍

长期记忆:跨对话持久化

到结束时,你的 agent 已经学到了很多东西。其中一些是有用的,另一些则不然。

假设你告诉你的 agent:“在摘要中使用项目符号,而不是段落。”如果没有记忆功能,你可能每周都要重复这一指令。在 Deep Agents 中,长期记忆就是一个 Markdown 文件。你只需通过一个参数指向该文件,MemoryMiddleware 就会处理其余部分:

  1. 加载(Load): 在每次运行开始时读取该文件。
  2. 注入(Inject): 将其内容添加到每次模型调用的系统提示词中。
  3. 更新(Update): 当 agent 学到值得保留的内容时(例如你的格式偏好),它会更新该文件。

‍

更换模型,保留记忆

Deep Agents 与提供商无关(provider agnostic),这一特性也延伸到了记忆功能。因为记忆是存储在你自己后端中的普通文件(而非由提供商持有的数据),你可以让同一个后端对接不同的模型:

PYTHON
agent = create_deep_agent(
    model="openai:gpt-6",  # different provider, same memory
    memory=["/memories/AGENTS.md"],
    backend=backend,
    store=store,
)

下周,另一个模型可以读取同一个文件,并依然以项目符号的形式编写摘要。

值得关注的上下文工程策略

无论你是在 Deep Agents 上构建、使用其他框架,还是自行搭建,以下几点都值得关注:

  • 大型输入应卸载而非截断。 截断是有损的;卸载则能保持其可检索性。
  • 压缩应是可恢复的。 在摘要之后,智能体仍应能够获取所需的细节信息。
  • 专业知识应按需加载。 将每个任务的指令都放入系统提示词中,会在每次调用时消耗上下文窗口。
  • 嘈杂的工作负载应被隔离。 拥有独立上下文的子智能体能让主智能体保持专注。
  • 你应当掌控记忆。 存储在你控制范围内的记忆,能够在模型或提供商切换后依然存续。
  • 默认设置应可定制。 适用于某一任务的阈值,很少能适用于所有任务。

试一试

SHELL
uv add deepagents

‍

想从现场讲师那里了解更多内容吗?我将于 11 月 3 日涵盖此主题:请在此注册!

‍

👉

要更深入地了解上下文工程,请查看我们在 LangChain Academy 上提供的免费自学课程 Deep Agents course on LangChain Academy。

看看你的智能体究竟在做什么

LangSmith 是我们的智能体工程平台,帮助开发者调试每一个智能体决策、评估变更,并实现一键部署。

Try LangSmithGet a demo