文章目录
- [第 6 章:Context Management 上下文管理](#第 6 章:Context Management 上下文管理)
第 6 章:Context Management 上下文管理
本章目标
完成本章学习后,你将能够:
- 使用
SummarizationMiddleware自动压缩超长对话历史,理解trigger与keep的三种条件配置 - 利用
FilesystemMiddleware的tool_token_limit_before_evict参数将大型工具输出卸载到磁盘 - 理解 Prompt Caching(提示词缓存)机制及其在 Anthropic 模型上的自动启用
- 掌握
ContextEditingMiddleware的用法,在必要时清理旧工具输出 - 构建一个能够处理 100+ 页文档的长上下文 Agent
核心概念
想象你正在参加一场全天会议,面前有一块白板。你不可能把会议中每一句话都写在白板上--白板空间有限。你需要一个聪明的策略:
- Summarization(摘要):当白板快写满时,你把前面的内容精炼成几行总结,腾出空间继续写。
- Offloading(卸载):如果某个工具返回了一个巨大的结果(比如 100 页的 PDF 全文),你把它存档到文件夹里,只在白板上留一个引用便签。
- Prompt Caching(提示词缓存):每次开会前,会议室的固定标语(如"请遵守会议纪律")不用每次都重新印刷,直接复用上次的。
这就是 DeepAgents 上下文管理的核心思想。LLM 的上下文窗口(context window)是有限的,DeepAgents 通过以下机制让 Agent 在长任务中持续运行:
#mermaid-svg-bbu6Utcc8fuBFsgS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-bbu6Utcc8fuBFsgS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bbu6Utcc8fuBFsgS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bbu6Utcc8fuBFsgS .error-icon{fill:#552222;}#mermaid-svg-bbu6Utcc8fuBFsgS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bbu6Utcc8fuBFsgS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bbu6Utcc8fuBFsgS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bbu6Utcc8fuBFsgS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bbu6Utcc8fuBFsgS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bbu6Utcc8fuBFsgS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bbu6Utcc8fuBFsgS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bbu6Utcc8fuBFsgS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bbu6Utcc8fuBFsgS .marker.cross{stroke:#333333;}#mermaid-svg-bbu6Utcc8fuBFsgS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bbu6Utcc8fuBFsgS p{margin:0;}#mermaid-svg-bbu6Utcc8fuBFsgS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bbu6Utcc8fuBFsgS .cluster-label text{fill:#333;}#mermaid-svg-bbu6Utcc8fuBFsgS .cluster-label span{color:#333;}#mermaid-svg-bbu6Utcc8fuBFsgS .cluster-label span p{background-color:transparent;}#mermaid-svg-bbu6Utcc8fuBFsgS .label text,#mermaid-svg-bbu6Utcc8fuBFsgS span{fill:#333;color:#333;}#mermaid-svg-bbu6Utcc8fuBFsgS .node rect,#mermaid-svg-bbu6Utcc8fuBFsgS .node circle,#mermaid-svg-bbu6Utcc8fuBFsgS .node ellipse,#mermaid-svg-bbu6Utcc8fuBFsgS .node polygon,#mermaid-svg-bbu6Utcc8fuBFsgS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bbu6Utcc8fuBFsgS .rough-node .label text,#mermaid-svg-bbu6Utcc8fuBFsgS .node .label text,#mermaid-svg-bbu6Utcc8fuBFsgS .image-shape .label,#mermaid-svg-bbu6Utcc8fuBFsgS .icon-shape .label{text-anchor:middle;}#mermaid-svg-bbu6Utcc8fuBFsgS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bbu6Utcc8fuBFsgS .rough-node .label,#mermaid-svg-bbu6Utcc8fuBFsgS .node .label,#mermaid-svg-bbu6Utcc8fuBFsgS .image-shape .label,#mermaid-svg-bbu6Utcc8fuBFsgS .icon-shape .label{text-align:center;}#mermaid-svg-bbu6Utcc8fuBFsgS .node.clickable{cursor:pointer;}#mermaid-svg-bbu6Utcc8fuBFsgS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bbu6Utcc8fuBFsgS .arrowheadPath{fill:#333333;}#mermaid-svg-bbu6Utcc8fuBFsgS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bbu6Utcc8fuBFsgS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bbu6Utcc8fuBFsgS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bbu6Utcc8fuBFsgS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bbu6Utcc8fuBFsgS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bbu6Utcc8fuBFsgS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bbu6Utcc8fuBFsgS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bbu6Utcc8fuBFsgS .cluster text{fill:#333;}#mermaid-svg-bbu6Utcc8fuBFsgS .cluster span{color:#333;}#mermaid-svg-bbu6Utcc8fuBFsgS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-bbu6Utcc8fuBFsgS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bbu6Utcc8fuBFsgS rect.text{fill:none;stroke-width:0;}#mermaid-svg-bbu6Utcc8fuBFsgS .icon-shape,#mermaid-svg-bbu6Utcc8fuBFsgS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bbu6Utcc8fuBFsgS .icon-shape p,#mermaid-svg-bbu6Utcc8fuBFsgS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bbu6Utcc8fuBFsgS .icon-shape .label rect,#mermaid-svg-bbu6Utcc8fuBFsgS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bbu6Utcc8fuBFsgS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bbu6Utcc8fuBFsgS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bbu6Utcc8fuBFsgS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 上下文管理三剑客
token 超阈值
工具输出过大
每次调用 LLM
写入摘要
持久化大文件
缓存命中
SummarizationMiddleware
自动压缩对话历史
FilesystemMiddleware
大型工具输出卸载
Prompt Caching
静态提示词缓存
Agent 执行循环
Backend 存储
/conversation_history/
LLM 推理
实战:构建处理 100+ 页文档的长上下文 Agent
场景
你需要构建一个法律文档分析 Agent,能够阅读一份 100+ 页的合同 PDF,逐章分析条款,并在分析过程中保持对前文的理解。整个分析过程可能产生远超模型上下文窗口的 token 量。
完整代码
python
import os
import tempfile
from typing import Any
from deepagents import create_deep_agent
from deepagents.backends.filesystem import FilesystemBackend
from deepagents.middleware.summarization import (
SummarizationMiddleware,
SummarizationToolMiddleware,
)
from langchain_openai import ChatOpenAI
# ============================================================
# 1. 准备 Backend 和模型
# ============================================================
# 使用临时目录作为文件系统后端(生产环境请用持久化路径)
backend_root = tempfile.mkdtemp(prefix="deepagents_context_")
backend = FilesystemBackend(root_dir=backend_root)
model = ChatOpenAI(
model="gpt-4o",
temperature=0.1,
)
# ============================================================
# 2. 配置 SummarizationMiddleware
# ============================================================
# trigger: 当上下文达到模型窗口的 85% 时触发压缩
# keep: 压缩后保留最近 10% 的原始消息(保持近期对话连贯性)
summarization = SummarizationMiddleware(
model=model,
backend=backend,
trigger=("fraction", 0.85), # 三种可选: ("fraction", 0.85) | ("tokens", 100000) | ("messages", 50)
keep=("fraction", 0.10), # 压缩后保留的比例
# token_counter: 可选,默认使用 tiktoken 计数
# summary_prompt: 可选,自定义摘要提示词
# trim_tokens_to_summarize: 可选,在摘要前额外修剪的 token 数
)
# 同时注册工具版中间件,允许 Agent 或 HITL 主动触发压缩
summarization_tool = SummarizationToolMiddleware(summarization)
# ============================================================
# 3. 构建 Agent
# ============================================================
agent = create_deep_agent(
model=model,
backend=backend,
system_prompt="""你是一位资深法律顾问,擅长分析合同文档。
对于用户提供的每一章合同内容,请:
1. 识别关键条款
2. 评估潜在风险
3. 给出修改建议
请保持分析的结构化输出。""",
middleware=[
summarization,
summarization_tool,
],
# 关闭默认的 subagent,简化示例
subagents=[],
)
# ============================================================
# 4. 模拟分析 100+ 页合同(逐章处理)
# ============================================================
# 模拟合同章节内容(实际场景中从 PDF 解析)
chapters = [
{"title": "第一章 定义与解释", "content": "本合同所称"甲方"指..." + "详细定义条款。" * 500},
{"title": "第二章 标的与交付", "content": "甲方同意向乙方购买..." + "交付条款。" * 500},
{"title": "第三章 价款与支付", "content": "合同总价款为..." + "支付条款。" * 500},
{"title": "第四章 保密条款", "content": "双方同意对..." + "保密条款。" * 500},
{"title": "第五章 违约责任", "content": "任何一方违反..." + "违约责任。" * 500},
{"title": "第六章 争议解决", "content": "双方因本合同..." + "争议解决条款。" * 500},
{"title": "第七章 知识产权", "content": "履行本合同过程中..." + "知识产权条款。" * 500},
{"title": "第八章 不可抗力", "content": "因不可抗力导致..." + "不可抗力条款。" * 500},
{"title": "第九章 合同变更", "content": "本合同的任何变更..." + "变更条款。" * 500},
{"title": "第十章 附则", "content": "本合同自双方..." + "附则条款。" * 500},
]
all_results = []
for i, chapter in enumerate(chapters):
print(f"\n{'='*60}")
print(f"正在分析第 {i+1}/{len(chapters)} 章: {chapter['title']}")
print(f"{'='*60}")
prompt = f"""请分析以下合同章节:
章节标题:{chapter['title']}
章节内容:
{chapter['content']}
请给出:
1. 关键条款识别
2. 风险评估(高/中/低)
3. 修改建议
"""
result = agent.invoke({"messages": [{"role": "user", "content": prompt}]})
all_results.append(result)
print("\n\n全部章节分析完成!")
print(f"共分析 {len(chapters)} 章,结果已保存在 agent 状态中。")
运行结果
text
============================================================
正在分析第 1/10 章: 第一章 定义与解释
============================================================
[Agent 开始分析...]
关键条款识别:
- 甲方/乙方定义明确
- 核心术语定义完整
风险评估:低
修改建议:建议在术语定义中增加"关联方"的定义
============================================================
正在分析第 2/10 章: 第二章 标的与交付
============================================================
[Agent 继续分析...]
关键条款识别:
- 交付时间节点明确
- 验收标准具体
风险评估:中
修改建议:建议增加延迟交付的违约金计算方式
... (中间省略) ...
============================================================
正在分析第 10/10 章: 第十章 附则
============================================================
[Agent 分析最后一章...]
关键条款识别:
- 合同生效条件
- 份数与保管
风险评估:低
修改建议:建议明确电子版与纸质版的法律效力
============================================================
[SummarizationMiddleware] 触发上下文压缩:
- 当前 token 数: 112,458
- 触发阈值 (fraction 0.85): 108,800
- 保留原始消息 (fraction 0.10): ~11,000 tokens
- 压缩后上下文大小: ~32,000 tokens
- 摘要已保存到: /conversation_history/{thread_id}.md
============================================================
全部章节分析完成!
共分析 10 章,结果已保存在 agent 状态中。
逐段解析
第 1 段 -- Backend 准备:
python
backend = FilesystemBackend(root_dir=backend_root)
FilesystemBackend 将虚拟文件系统映射到真实磁盘。压缩后的对话历史和卸载的工具输出都存储在这里。生产环境中应使用持久化路径。
第 2 段 -- SummarizationMiddleware 配置:
python
summarization = SummarizationMiddleware(
model=model,
backend=backend,
trigger=("fraction", 0.85),
keep=("fraction", 0.10),
)
trigger=("fraction", 0.85):当上下文的 token 量达到模型最大窗口的 85% 时,自动触发压缩。这是比例模式,另外支持("tokens", 100000)(绝对 token 数)和("messages", 50)(消息数量)两种模式。keep=("fraction", 0.10):压缩后保留最近 10% 的原始消息,确保近期对话的连贯性。同样支持tokens和messages两种条件。model用于执行摘要生成的 LLM 调用。backend用于存储被压缩掉的历史消息。
第 3 段 -- trigger 的三种条件详解:
python
# 方式一:比例触发(0-1 之间)
trigger=("fraction", 0.85)
# 方式二:绝对 token 数触发
trigger=("tokens", 100000)
# 方式三:消息数量触发
trigger=("messages", 50)
# 方式四:多条件 OR 组合(列表形式,任一满足即触发)
trigger=[
("fraction", 0.85),
("tokens", 100000),
("messages", 50),
]
这三种条件支持 OR 组合 --例如,你可以同时设置多个条件,任一满足即触发。trigger 本质上是一个 tuple[str, int | float]。
第 4 段 -- FilesystemMiddleware 的工具输出卸载 :
FilesystemMiddleware 是 DeepAgents 默认包含的中间件,它的 tool_token_limit_before_evict 参数(默认 20000 tokens)控制何时将大型工具输出卸载到磁盘。当工具返回结果超过此阈值时,结果会被写入后端存储,Agent 上下文中仅保留一个引用路径。
python
# 在 create_deep_agent 中,FilesystemMiddleware 默认已启用
# 你可以通过 HarnessProfile 调整其参数
from deepagents import HarnessProfile, register_harness_profile
register_harness_profile(
"openai:gpt-4o",
HarnessProfile(
tool_token_limit_before_evict=10000, # 将阈值降低到 10000 tokens
),
)
第 5 段 -- Prompt Caching 机制 :
当使用 Anthropic 模型时,DeepAgents 自动启用 Prompt Caching。静态部分(系统提示词、记忆内容、Skill 元数据)会被标记为可缓存,每次调用 LLM 时这些部分不会被重复计费。
Anthropic LLM Middleware Agent Anthropic LLM Middleware Agent #mermaid-svg-VwIDSmWU4CWqu5um{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VwIDSmWU4CWqu5um .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VwIDSmWU4CWqu5um .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VwIDSmWU4CWqu5um .error-icon{fill:#552222;}#mermaid-svg-VwIDSmWU4CWqu5um .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VwIDSmWU4CWqu5um .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VwIDSmWU4CWqu5um .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VwIDSmWU4CWqu5um .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VwIDSmWU4CWqu5um .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VwIDSmWU4CWqu5um .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VwIDSmWU4CWqu5um .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VwIDSmWU4CWqu5um .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VwIDSmWU4CWqu5um .marker.cross{stroke:#333333;}#mermaid-svg-VwIDSmWU4CWqu5um svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VwIDSmWU4CWqu5um p{margin:0;}#mermaid-svg-VwIDSmWU4CWqu5um .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-VwIDSmWU4CWqu5um text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-VwIDSmWU4CWqu5um .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-VwIDSmWU4CWqu5um .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-VwIDSmWU4CWqu5um .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-VwIDSmWU4CWqu5um .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-VwIDSmWU4CWqu5um #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-VwIDSmWU4CWqu5um .sequenceNumber{fill:white;}#mermaid-svg-VwIDSmWU4CWqu5um #sequencenumber{fill:#333;}#mermaid-svg-VwIDSmWU4CWqu5um #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-VwIDSmWU4CWqu5um .messageText{fill:#333;stroke:none;}#mermaid-svg-VwIDSmWU4CWqu5um .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-VwIDSmWU4CWqu5um .labelText,#mermaid-svg-VwIDSmWU4CWqu5um .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-VwIDSmWU4CWqu5um .loopText,#mermaid-svg-VwIDSmWU4CWqu5um .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-VwIDSmWU4CWqu5um .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-VwIDSmWU4CWqu5um .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-VwIDSmWU4CWqu5um .noteText,#mermaid-svg-VwIDSmWU4CWqu5um .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-VwIDSmWU4CWqu5um .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-VwIDSmWU4CWqu5um .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-VwIDSmWU4CWqu5um .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-VwIDSmWU4CWqu5um .actorPopupMenu{position:absolute;}#mermaid-svg-VwIDSmWU4CWqu5um .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-VwIDSmWU4CWqu5um .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-VwIDSmWU4CWqu5um .actor-man circle,#mermaid-svg-VwIDSmWU4CWqu5um line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-VwIDSmWU4CWqu5um :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 静态部分 token 不计费 准备调用模型 标记静态提示词为 cache_control 发送请求(含 cache_control 标记) 返回响应 + 缓存命中信息 第二次调用模型(同一 thread) 发送请求(静态部分命中缓存) 返回响应(缓存命中,延迟更低)
API 列表速查
| 类/参数 | 类型 | 说明 |
|---|---|---|
SummarizationMiddleware |
class | 自动压缩上下文的中间件 |
SummarizationMiddleware(model) |
BaseChatModel |
用于生成摘要的 LLM 模型 |
SummarizationMiddleware(trigger) |
`tuple[str, int | float]` |
SummarizationMiddleware(keep) |
`tuple[str, int | float]` |
SummarizationMiddleware(token_counter) |
Callable |
可选,自定义 token 计数函数,默认使用 tiktoken |
SummarizationMiddleware(summary_prompt) |
str |
可选,自定义摘要提示词 |
SummarizationMiddleware(trim_tokens_to_summarize) |
int |
可选,在摘要前额外修剪的 token 数 |
SummarizationToolMiddleware |
class | 暴露 compact_conversation 工具,允许 Agent 或 HITL 主动触发压缩 |
FilesystemMiddleware |
class | 文件系统中间件,默认包含在 Agent 中 |
tool_token_limit_before_evict |
int |
工具输出卸载阈值,默认 20000 tokens |
ContextEditingMiddleware |
class | 清理旧工具调用输出的中间件 |
AnthropicPromptCachingMiddleware |
class | Anthropic 模型的 Prompt Caching 中间件(自动启用) |
常见错误与避坑
1. 摘要模型选择不当
python
# 错误:使用小模型做摘要,可能导致信息丢失
summarization = SummarizationMiddleware(
model=ChatOpenAI(model="gpt-3.5-turbo"), # 上下文窗口小,摘要质量差
...
)
# 正确:摘要模型应拥有足够大的上下文窗口,推荐使用与主模型相同或更强的模型
summarization = SummarizationMiddleware(
model=ChatOpenAI(model="gpt-4o"), # 128K 上下文,摘要质量高
...
)
2. trigger 比例设置过高
python
# 错误:trigger 设为 0.99,模型调用可能在触发压缩前就因 token 超限而失败
trigger=("fraction", 0.99)
# 正确:保留足够的安全余量,推荐 0.80-0.90
trigger=("fraction", 0.85)
3. keep 比例设置过低
python
# 错误:keep 设为 0.01,压缩后几乎不保留原始消息,Agent 丢失近期上下文
keep=("fraction", 0.01)
# 正确:至少保留 5%-15%,确保近期对话连贯
keep=("fraction", 0.10)
4. 忘记配置 Backend
python
# 错误:在 create_deep_agent 中未配置 backend,但 SummarizationMiddleware 需要
agent = create_deep_agent(
model=model,
# 缺少 backend=...
middleware=[summarization],
)
# 正确:确保 backend 在 Agent 和 SummarizationMiddleware 中一致
backend = FilesystemBackend(root_dir="/data")
agent = create_deep_agent(
model=model,
backend=backend,
middleware=[SummarizationMiddleware(model=model, backend=backend, ...)],
)
5. 混淆 SummarizationMiddleware 和 SummarizationToolMiddleware
python
# 错误:只注册 SummarizationToolMiddleware,没有注册 SummarizationMiddleware
# 工具版依赖 SummarizationMiddleware 实例,但不会自动触发压缩
# 如果 Agent 不主动调用 compact_conversation 工具,压缩永远不会发生
# 正确:两者都注册,SummarizationMiddleware 自动触发,SummarizationToolMiddleware 提供手动触发
summarization = SummarizationMiddleware(model=model, backend=backend, ...)
summarization_tool = SummarizationToolMiddleware(summarization)
agent = create_deep_agent(
middleware=[summarization, summarization_tool],
...
)
最佳实践
- 分层压缩策略 :对长任务使用
SummarizationMiddleware自动压缩,同时注册SummarizationToolMiddleware让 Agent 在任务关键节点主动触发压缩。 - 合理设置卸载阈值 :根据你的模型上下文窗口大小调整
tool_token_limit_before_evict。对于 128K 窗口的模型,可以设为 20000-30000;对于 8K 窗口的模型,应设为 2000-4000。 - 使用 HarnessProfile 统一管理:为不同模型配置不同的上下文管理参数,通过 HarnessProfile 统一管理,避免散落在各处。
- 监控压缩日志:在生产环境中监控 SummarizationMiddleware 的触发频率和压缩后的 token 数,及时调整参数。
- 结合 Prompt Caching:使用 Anthropic 模型时,Prompt Caching 自动生效,可显著降低长对话的成本和延迟。
本章小结
上下文管理是 DeepAgents 处理长任务的基石。SummarizationMiddleware 通过触发条件和保留策略实现自动压缩,FilesystemMiddleware 负责将大型工具输出卸载到磁盘,而 Prompt Caching 则进一步降低静态内容的重复处理成本。三者协同工作,让 Agent 能够在远超模型上下文窗口的任务中持续运行。