Agent 框架的上下文管理:四种实现,殊途同归
摘要
Pi、OpenClaw、Claude Code、Letta 在文件读取、会话压缩、子 Agent 隔离上做了不同的选择,但仔细比对会发现,它们正在收敛到同一套设计模式。Arize 创始人 Aparna Dhinakaran 把四个代码库摊在一起做了对照。
本文翻译自 Arize AI 创始人 Aparna Dhinakaran 在 X 上发布的长文 Context Management in Agent Harnesses(2026 年 4 月 26 日)。原文从源码层面比较了 Pi、OpenClaw、Claude Code、Letta 四个 Agent 框架在上下文管理上的设计选择。
译者注:原文发出后,Letta 团队的 @sarahwooders 和 @just_cameron 在评论区指出文中关于 Letta 的若干描述并不准确 - 文章对 Letta 的分析是从 3 月底开始的,期间 Letta 这边变化很快:文里详述的 Filesystem(vector store +
open_files/grep_files/semantic_search_files)已被官方文档标记为弃用;Letta Code 早在 Claude Code 之前就同时支持隔离与 fork 两种子 Agent 模式(且支持把任意带状态的专用 Agent 当作子 Agent 调用);压缩策略除了滑动窗口之外还有 compact all 模式;另外还有 Dream agents(后台重写 prompt / skill / 外部记忆)、消息默认存储 + 混合检索(含内建 search agent)、超大工具结果被截断并存为可访问文件等机制没有覆盖到。Aparna 已在评论区回复会更新原文。下文 Letta 相关的段落请结合这些指正一起读。

每一个 Agent 框架最后都会撞到同一道墙:上下文窗口太小,装不下模型可能想记住的所有东西。会话越拉越长,文件读取越积越厚,子 Agent 不断 fork,工具输出层层堆叠 - 框架必须决定,什么留在工作集里,什么压缩,什么之后再去取。
过去两年,我们一直在打磨 Arize 自家的产品级 Agent - Alyx,几乎每一种形式的"上下文不够用"都踩过:会话长到模型忘了任务在干嘛,文件读取里一半窗口塞满了样板代码,工具结果把真正的对话挤到了角落。
所以现在真正重要的问题已经不是"prompt 里放什么",而是"框架如何长时间地管理上下文"。最好的系统不会把上下文窗口当成一条被动的 transcript 缓冲区,而是主动经营它:高价值的状态留在近端,按需翻页拉取数据,建索引去找需要的东西(grep 就是干这个的),并以一种暗示"还有更多可访问"的方式做截断。

Pi、OpenClaw、Claude Code、Letta 在这件事上做了不同的选择,但它们正在收敛到一个相似的底层模式。上下文不再是 transcript 里恰好能塞下的那一段,而是系统必须主动管理的对象。真正的设计问题是:这部分管理工作,多少由框架来做,多少留给模型自己来做。
押注:相信模型能管好自己的上下文
每一个上下文管理上的决策,背后都是对模型行为的某种假设。核心问题是 - 框架要主动卡死上下文用量,还是相信模型会自己把这部分预算用对。

拿文件读取来看就最具体了。当模型要读一个塞不进上下文的大文件时,必须有人决定保留哪部分。四个框架都支持 offset 和 limit 来分页。
Pi(pi-mono)
Pi 读文件时设了硬上限 - 2,000 行或 50KB,先到先生效,哪怕模型没指定切片范围。内容从尾部截断,工具输出后会附加一句明确的"继续读取"提示:[Showing lines 1-2000 of 50000. Use offset=2001 to continue.]。工具描述里也强化了这一点:"输出截断到 2000 行或 50KB。大文件请用 offset/limit。"
Pi 的思路是框架先行 - 框架先保护你,然后再教模型去翻页。
OpenClaw
OpenClaw 直接继承了 Pi 的读文件工具,连 2K 行 / 50KB 的截断逻辑也照搬过来。在普通文件读取上行为完全一致。在这之上,它叠了额外的限额,专门管"启动文件",也就是会话开始时一次性加载的上下文文件 - 单文件 12,000 字符,总量 60,000 字符。当某个启动文件超过预算时,会按 75% 头 + 25% 尾切:你看到开头和结尾,中间被砍掉。
工具结果有独立预算 - 16,000 字符或上下文窗口的 30%,取小者。当结尾看起来"重要"时(错误信息、JSON 闭合括号、summary 这类关键字),会切到头+尾模式;不然就只保留开头。
OpenClaw 走的是纵深防御 - 第一层 Pi 的截断,第二层启动文件的额外限额,第三层工具结果的预算。
Claude Code
Claude Code 在文件读取上叠了两层防线。第一道闸是 256KB 字节上限,在文件被打开之前就通过 stat 调用检查 - 如果超了,读取直接被拒绝,错误信息里会指引模型改用 offset/limit 或 grep。第二道闸在读取之后跑:输出按 25,000 token 预算做 token 计数,把那些字节数没超但 token 密度极高的文件拦下来。这两个上限都通过 GrowthBook feature flag 远程可调,Anthropic 不需要发新版本就能动它们。
哪怕文件没超限,工具默认也只返回前 2,000 行,单行超过 2,000 字符的部分会被截掉。模型必须显式带上 offset 和 limit 参数才能拿更多。
工具描述本身就是一段完整的多段提示词,把分页讲清楚,提到大小限制,覆盖图像 / PDF / notebook 的支持,并且鼓励对多个文件并行读取。offset 和 limit 参数有自己的描述,告诉模型这是为"一次读不完的大文件"准备的。还有一段条件性指令,会根据 feature flag 把 256KB 的限制直接铺到 prompt 里。
文件去重机制也值得一提。如果模型在同一个范围里重复读同一个文件,且时间没变,Claude Code 会返回一个占位 stub 而不是完整内容,避免在上下文里消耗重复 token。
Claude Code 的思路是框架先行 + 远程可调 - 读取前的字节闸、读取后的 token 闸、行数和行长默认值、可执行的错误信息、丰富的工具 prompt、读取去重,加上让 Anthropic 在服务端就能动所有参数的 feature flag。
Letta
Letta 走了一条根本不一样的路。每个上传的文件都会被解析、切块、向量化进 vector store,所以 Agent 同时拥有精确搜索和语义搜索两种能力。这给了 Agent 三个文件工具 - open_files 直接查看(读原文)、grep_files 做精确模式匹配(也读原文)、semantic_search_files 在嵌入后的片段里做语义检索。
当一个文件在 Agent 上下文里"打开"时,可见内容会被截断到一个按模型上下文窗口分档的字符上限:8K 上下文档 5,000 字符,32K 档 15,000,128K 档 25,000,200K+ 档 40,000。同时打开的文件数也跟着分档伸缩 - 小模型 3 个,超大模型最多 15 个,默认值是 5。超出限制时用 LRU 策略把最少访问的文件挤出去。
Letta 的思路是记忆先行 - 文件同时以原文和向量切块的形式存在,上下文窗口里只展示一份被管理的视图,模型通过工具去访问更多。
真正的工程难点:会话裁剪
会话越长,每个框架都得决定保留什么、丢掉什么。这恰恰是设计差异最显著的地方,因为压缩策略直接决定了一个长生命周期的 Agent 能保持连贯,还是会缓慢退化。

Pi(pi-mono)
Pi 用的是 compaction - 由 token 阈值触发的 LLM 摘要。
- 触发条件:估算的上下文 token 超过
contextWindow - reserveTokens(reserve 默认 16,384 token) - 保留什么:从对话末尾向前走,保留最近 约 20,000 token 的消息(keepRecentTokens)
- 摘要什么:更早的内容全部交给 LLM 做摘要
- 摘要去哪:变成一条合成的用户消息,拼接到保留的尾部前面
- 工具调用安全:永远不会切在一个孤立的 tool result 上,会沿着边界走,确保 tool-call / tool-result 成对完整
OpenClaw
OpenClaw 在 Pi 的 compaction 之上跑两套不同的上下文管理机制。
- 触发条件:历史消息超过上下文窗口的 50%(maxHistoryShare,默认 0.5)
- 保留什么:历史按 token 等量切成多个 chunk,最旧的一块被丢弃,其余保留,并修复 tool-call / result 配对
- 摘要什么:被丢弃的内容走一个分阶段的多轮 LLM 摘要,最后还有一步 merge
- 摘要去哪:和 Pi 一样 - 合成消息拼接到保留尾部前
- 工具调用安全:
repairToolUseResultPairing会修复 chunk 丢弃后产生的孤立 tool result,splitMessagesByTokenShare避免在 tool-call / result 对内切开 - 压缩前 flush:一次静默的 agentic 轮次让 Agent 在历史消失之前把状态持久化到 memory 文件
- 第二层:对工具结果做非破坏性的内存内裁剪(先软裁、再硬清),缓存 TTL 5 分钟,在保住持久会话的前提下为当前请求腾出上下文
Claude Code
Claude Code 的上下文管理走两路 - 查询前的优化 + LLM 驱动的 compaction。
- 触发条件:估算 token 超过"有效上下文窗口减去 13,000 token 缓冲"(200K 上下文的模型大约在 167K token 处触发)
- 摘要什么:整段对话被发给模型,配一份 9 段结构化 prompt - primary request、关键技术概念、文件与代码、错误与修复、问题求解、所有用户消息、待办任务、当前工作,以及可选的 next step
- 摘要去哪:变成一条用户消息,告诉模型"这是从一个因为上下文耗尽而中断的会话续上来的"
- 压缩后恢复:压缩之后会把最近读过的最多 5 个文件重新塞回上下文,受 token 预算约束
- 摘要器安全:模型分别输出一个 analysis 草稿块和一个 final summary 块。草稿在 summary 进入上下文前被剥离 - 既提升了质量,又不让结果膨胀
- prompt-too-long 兜底:如果 compaction 调用本身撞上下文限制,会用一个确定性的 head-drop 把最旧的若干 API 轮次组掉(20% 的组数,或刚好够把 token 缺口补上)
查询前优化(每次 API 调用都跑,跟有没有上下文压力无关):在每次模型调用之前,Claude Code 走一个 pipeline,专门管工具结果,对话文本不动。超大的工具结果被持久化到磁盘,替换成 2KB 预览,单工具上限 50,000 字符,单消息总量上限 200,000 字符 - 所以一个 60KB 的 grep 结果在新会话的第一轮就被卸到磁盘上。
Letta
Letta 用多种 compaction 策略管上下文,主路径溢出时还有一个两阶段摘要器兜底。
- 触发条件:估算上下文用量超过窗口的 90%
- 滑动窗口驱逐:起点是 30% 的消息(不是 10%),每次迭代再加 10%,直到 token 用量降到目标以下。保留最近的,赶走最旧的
- self-compact 模式:用 Agent 自己的模型做摘要 - 不用单独配置摘要器,也不会多花一份调用费用
- 摘要器溢出的两阶段兜底:先把工具返回值钳到 5,000 字符重试;如果还是溢出,对 transcript 做中段截断,保留 30% 头 + 30% 尾,砍掉中间
- 告警阈值:单独设了一道 75% 内存告警,比 90% 压缩触发更早
子 Agent 的上下文管理
在我们看的这些框架里,子 Agent 通常是和父会话隔离的。这里没有任何一个例子会把父会话的完整对话历史塞给子 Agent。问题在于,子 Agent 继承哪些工作区上下文。

Pi 为每个被委派的任务起一个新进程,配一个内存里的会话。子进程拿到的唯一一条用户消息就是任务字符串本身。父对话历史不传。
OpenClaw 默认给子 Agent 一个全新的隔离会话 - 不带父 transcript。但它有一个 fork 模式,会把父 transcript 复制进子 Agent,限制是只对同类型 Agent 之间的 spawn 生效。工作区上下文被过滤成一个最小白名单(AGENTS.md、TOOLS.md、SOUL.md)。
Claude Code 有两条路。默认的 typed-agent 路径起的是一个空白对话 - 委派出去的 prompt 就是唯一的用户消息,没有父历史。一个更新的 fork 路径会把父消息历史完整传给子 Agent,目的是让 prompt cache 能复用,外加一条合成的 assistant 消息和占位的 tool result。工具会以子 worker 自己的权限模式重新构建;async Agent 拿到一份显式白名单(Read、Grep、Glob、Shell、Edit、Write、WebSearch 等)。Agent 定义里引用的 Skill 会被预先加载 - skill 的完整内容作为用户消息注入到初始对话里,不是按需加载。
Letta 在普通工具执行里压根不 fork - 工具就在主 Agent 循环里跑。历史上下文通过专门的搜索工具去访问 - conversation search 拿 recall memory,archival memory search 查嵌入存储。
设计正在收敛的地方
把这四个代码库摆在一起,最意外的不是它们多么不同,而是它们多么一致。
四个框架都对文件读取设了硬上限。四个都支持 offset / limit 分页。四个都给工具结果上限。四个都隔离子 Agent 会话。四个都跑 token 阈值触发的 LLM compaction。四个都估算上下文用量、检测压力。这些不是巧合,是同一个工程问题的收敛解 - 一个固定大小的工作集,要让人感觉无限。
收敛不止停留在"功能相同"。具体的设计选择都在押同一个韵脚。Pi 和 OpenClaw 都在文件读取里做尾截断 + 续读提示。Claude Code 和 OpenClaw 都把超大工具结果持久化到磁盘。Pi、OpenClaw、Claude Code 都在 compaction 里强制 tool-call / result 边界安全。四个里面有三个支持把父 transcript fork 进子 Agent。这些框架在独立地走向同一组答案。
而且这些模式不只属于编码 Agent。Arize 自家的 Alyx 助手 - 一个为数据探索而非代码编辑设计的产品 - 独立走到了同一套设计上。Alyx 给工具结果设了 10,000 token 预算,用二分搜索去找能塞下的最大数据切片。它对幂等的工具调用做去重 - 把对话历史里的重复预览裁掉,只保留最新一次。它把大 JSON 拆成两份 - 一份压缩过的、给 LLM 看的预览,一份完整的、放在服务端的副本,模型可以通过 jq 钻进去 - 这正是 Pi、OpenClaw、Claude Code 在文件读取里用的"把超大结果存在上下文外"的同一种模式。它对长 cell 值做头+尾截断,并附带回指完整内容的引用。它用 char/4 启发式估算 token 压力,会话过 50,000 token 时强制做一次 checkpoint - 模型在历史被裁剪之前自己写一份状态摘要 - 把 Claude Code 的确定性触发器和 OpenClaw 的"压缩前 flush"组合在了一起。它的子 Agent 用的是四个框架都在用的隔离模式。一个为完全不同领域而生的产品,收敛到了同一份上下文管理 playbook。

50 年的计算机历史告诉我们一件事 - 最好的内存管理,是程序压根不去想它的那种。寄存器、cache line、页表、swap,每一层都由系统管,对上一层不可见。程序只管跑。
Agent 框架正在朝同一个方向走。目标不是把所有东西都摊给模型看,而是 - 在合适的时刻给它合适的工作集,让它自己动态地决定如何管理上下文。
相关文章
2026年7月26日
Claude 5 时代,上下文工程的新规则 - Anthropic 为什么删掉了 80% 的系统提示词
Anthropic 的 Claude Code 团队复盘了 Claude 5 时代的上下文工程新规则:他们把系统提示词删掉 80% 以上,编码评测却没有可测的下降。这篇读完来分享六组“过去 → 现在”的变化,以及我照着新规则改自己开源 Skills 的两处实践。
2026年7月15日
【AI早读 0715】Agent 从实验走向生产的基础设施建设
过去 24 小时的信号都指向 Agent 从演示走向生产 - Context Layer 被点名为缺失的基础设施层,Martin Fowler 网站主张用 DSL 约束 LLM 输出,而 Codex 半年内用户从 70 万涨到 700 万。
2026年6月4日
强强联手!把 Codex 接入任何智能体(Claude Code, OpenClaw, Hermes, ...)
Claude Code 写代码、Codex 出图、Hermes 跑研究,与其在三个窗口间切换,不如让一个 agent 直接喊另一个上场。聊聊我做的 codex-cli skill:用 codex exec 把 Codex CLI 包成可装能力,任何 agent 装上就能委托并把它藏起来的图找回来。
最近一封 · Sample
Claude 5 时代,上下文工程的新规则 - Anthropic 为什么删掉了 80% 的系统提示词
“Anthropic 的 Claude Code 团队复盘了 Claude 5 时代的上下文工程新规则:他们把系统提示词删掉 80% 以上,编码评测却没有可测的下降。这篇读完来分享六组“过去 → 现在”的变化,以及我照着新规则改自己开源 Skills 的两处实践。”
—— william
来信
里面装的是
- 新文章 — 写完一篇就寄一封,不攒货
- 这周读到的、看到的、好用的工具
- 正在折腾的实验,附带翻车记录
约莫 1–2 周一封 · 随时退订
合作伙伴
CompeteMap — 英国及爱尔兰学生竞赛一站式搜索
数学、编程、科学、写作等各类竞赛信息汇总,支持按年龄和科目筛选,再也不错过报名截止日。