一个把每一步的工具结果都原样堆进上下文的 AI Agent,跑到几十步后要么报 token 超限、要么忘了最初的任务:一次 Agent 上下文管理的深度复盘

Agent 处理简单任务很好,一遇到要几十步的复杂任务就出两种诡异故障:要么跑到一半报 context length exceeded,要么跑着跑着就跑偏、忘了最初的任务目标。根因是每步都把工具返回的完整结果原样追加进上下文、把越来越长的全部历史再喂给模型——工具结果动辄上万字,几十步累积撑爆上下文窗口;即使没爆,最初的目标也被淹没在海量细节里(lost in the middle)导致失忆跑偏。本文讲透上下文窗口是有限稀缺资源,给出大结果外置、历史摘要压缩、关键信息 pinning、监控 token、结构化记忆的正解,梳理 Agent 上下文常见坑,最后落到'更多信息不等于更好决策、追求信噪比而非信息量、上下文工程是核心技能'的认知。

一个把每一步的工具结果都原样堆进上下文的 AI Agent,跑到几十步后要么报 token 超限、要么"忘"了最初的任务:一次 Agent 上下文管理的深度复盘

那个问题是 Agent 处理复杂任务时暴露的:我们的 Agent 能调用一堆工具(查数据库、读文档、调接口)来一步步完成任务,简单任务三五步就搞定、表现很好。可一旦遇到需要几十步的复杂任务,就开始出两种诡异故障:要么跑到一半直接报错——context length exceeded(超出最大上下文长度);要么更邪门,它跑着跑着就"跑偏"了——明明任务是"统计上个月华东区的销售额",它跑到第三十几步时,却开始查起华南区、甚至忘了要"统计销售额"这回事,答非所问。我排查了大半天,把每一步喂给模型的完整上下文打出来一看,才明白根源,后背发凉:我们的 Agent,每执行一步,都把"这一步调用的工具、以及工具返回的完整结果"原原本本地追加进对话上下文,然后把越来越长的整个历史全部再喂给模型去想下一步。有的工具(比如查数据库、读文档)一次就返回几千上万字;几十步累积下来,上下文飞快地膨胀,很快就撑爆了模型的上下文窗口(于是报 token 超限);而即使没爆,过长的上下文里,最初的任务目标和关键约束,被淹没在了海量的、大部分已经无关的工具结果里——模型的注意力被中间那些冗长的细节带跑了,于是""了它最初到底要干什么(这也是所谓"大海捞针"问题,长上下文里的关键信息容易被忽略)。这篇就把这次"Agent 上下文无限膨胀、失控又失忆"的坑,从头到尾复盘一遍。

故障现场:每步都把完整工具结果堆进上下文

问题代码,是一个对上下文"只增不管"的 Agent 循环:

# ✗ 出问题的 Agent: 每步都把完整工具结果追加进上下文, 上下文只增不裁
def run_agent(task: str):
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},      # 系统指令(含任务约束)
        {"role": "user", "content": task},                 # 最初的任务
    ]
    for step in range(MAX_STEPS):
        resp = llm.chat(messages, tools=TOOLS)             # ✗ 每次都喂【全部】历史
        if not resp.tool_calls:
            return resp.content
        for call in resp.tool_calls:
            result = execute_tool(call)                     # 工具返回, 可能几千上万字!
            messages.append({"role": "assistant", "tool_calls": [call]})
            messages.append({"role": "tool", "content": result})  # ✗ 原样追加完整结果
        # → messages 越来越长, 每一步都把这越来越长的东西全部喂给模型

# 两种故障:
# 故障A(token超限): 工具结果大(查库/读文档返回上万字)+ 步数多(几十步)→
#   messages的总token飞速增长 → 超过模型上下文窗口(如128k)→ 报错 context length exceeded。
# 故障B(失忆/跑偏): 即使没超限, 上下文长到几万token时:
#   - 最初的"任务目标"(在最前面)被埋在海量工具结果中间;
#   - 模型对"长上下文中间部分"的注意力较弱(lost in the middle / 大海捞针);
#   - → 模型被近期那些冗长的工具结果带偏, "忘"了最初的目标 → 跑偏、答非所问。

# 缺失: 没有任何"上下文管理"——不裁剪、不摘要、不把大结果外置, 任由它无限膨胀。

# 关键: 上下文窗口是有限的、宝贵的资源; Agent每步堆完整工具结果会让它快速耗尽,
#       既可能token超限崩溃, 又会因关键信息被淹没而"失忆跑偏"。

第一次理清这两种故障时,我恍然又无奈:"我光想着把所有信息都给模型、生怕它信息不够,却没想到'信息太多、太杂'反而会害了它。"这个坑最容易被忽视的地方在于:它在短任务时完全正常——三五步、上下文没多长,既不会超限、关键信息也还突出;它只在长任务(多步+大工具结果)时才暴露,而且暴露成两副面孔(硬性的 token 超限崩溃、和软性的失忆跑偏),后者尤其隐蔽——它不报错,只是默默地变笨、跑偏下面就来拆解,上下文为什么是有限资源、该怎么管理。

第一件事:搞懂上下文窗口是有限资源,以及它怎么影响 Agent

我顺着这次事故,把"上下文窗口"这个 Agent 的核心约束彻底理清了。

上下文窗口(context window): Agent 必须精打细算的有限资源

【核心: 上下文窗口大小有限; Agent每步都带着全部历史, 它会膨胀; 既有"硬上限(超限崩)", 也有"软衰减(太长变笨)"】

1. 什么是上下文窗口:
   - 大模型一次能"看到"的输入+输出的最大token数(如8k/32k/128k...);
   - Agent每一步, 都要把【到目前为止的全部对话历史 + 工具结果】作为输入喂给模型;
   - → 这个输入不能超过上下文窗口的大小, 否则报错(硬上限)。

2. Agent为什么会快速撑爆它:
   - Agent是"多步循环", 每步都会往历史里追加内容(模型的思考、工具调用、工具结果);
   - 尤其工具结果常常很大(查库返回上万行、读文档返回全文);
   - → 历史"只增不减", 几十步后轻松达到几万、十几万token, 撑爆窗口。

3. 两种危害(一硬一软):
   - 硬: token超过窗口上限 → 直接报错, Agent崩溃, 任务失败。
   - 软: 即使没超限, 上下文过长时模型表现会下降——
     "lost in the middle": 模型对长上下文【中间部分】的信息利用较弱;
     → 最初的任务目标/约束(在开头)、和关键信息, 容易被淹没在海量细节里 → 失忆、跑偏。

4. 所以: 上下文是"有限且宝贵"的, 要【精打细算地管理】, 而不是无脑全塞:
   - 不是"给模型的信息越多越好", 而是"给它【恰好够用、且突出重点】的信息最好";
   - 塞进太多无关/冗长的内容, 既浪费窗口、又稀释了关键信息、还增加成本和延迟。

类比: 上下文窗口像一个人的"工作记忆(短期记忆)", 容量有限;
   你不停地往他脑子里塞各种细节, 他要么"记不下了"(超限), 要么"被细节淹没、忘了正事"(跑偏);
   高效的做法是: 只让他记住"当前最相关的关键信息", 其余的放到"外部笔记"里需要时再查。

一句话: 上下文窗口有限且宝贵; Agent每步带全部历史会让它膨胀, 既可能token超限崩溃、
   又会因过长而"失忆跑偏"(lost in the middle); 必须精打细算地管理上下文, 而非无脑全塞。

这套认知,是整个坑的根。上下文窗口是大模型一次能"看到"的最大 token 数(8k/32k/128k…),Agent 每步都要把到目前为止的全部历史+工具结果喂给模型,这个输入不能超过窗口(否则报错)。Agent 为什么快速撑爆?它是多步循环、每步往历史追加内容(思考、工具调用、工具结果),尤其工具结果常常很大,历史只增不减、几十步后轻松几万十几万 token两种危害:——token 超窗口上限直接报错崩溃;——即使没超限,上下文过长时模型表现下降("lost in the middle":对长上下文中间部分利用弱),最初的任务目标和关键信息被淹没→失忆跑偏。所以上下文是有限且宝贵的,要精打细算地管理而非无脑全塞:不是给的信息越多越好,而是给它"恰好够用、突出重点"的信息最好。就像上下文像人的工作记忆、容量有限,不停塞细节他要么记不下、要么被淹没忘了正事;高效做法是只让他记住当前最相关的关键信息、其余放外部笔记需要时再查一句话:上下文窗口有限且宝贵;Agent 每步带全部历史会膨胀,既可能 token 超限崩溃、又会因过长失忆跑偏;必须精打细算管理上下文而非无脑全塞。

第二件事:正解——裁剪/摘要历史、外置大结果、固定保留关键信息

搞懂了原理,正解就清晰了:主动管理上下文——把大工具结果外置(只在上下文放摘要/引用)、对久远的历史做摘要压缩、始终把任务目标等关键信息固定保留在显眼处

# ✓ 正解: 给 Agent 加上下文管理
def run_agent(task: str):
    system = {"role": "system", "content": SYSTEM_PROMPT}
    goal = {"role": "user", "content": f"【核心任务, 始终牢记】: {task}"}  # ★ 关键目标
    history = []           # 中间过程的历史(会被管理)

    for step in range(MAX_STEPS):
        # ★ 每步构造上下文: 系统 + 固定的核心任务 + (被管理过的)历史
        messages = [system, goal] + manage_context(history)
        resp = llm.chat(messages, tools=TOOLS)
        if not resp.tool_calls:
            return resp.content
        for call in resp.tool_calls:
            result = execute_tool(call)
            # ★ 大结果外置: 太大就存外部, 上下文里只放摘要+引用
            stored = store_if_large(result)   # 存到外部(文件/KV), 返回引用或摘要
            history.append({"role": "assistant", "tool_calls": [call]})
            history.append({"role": "tool", "content": stored})

def manage_context(history):
    # 策略: 保留最近K步的完整内容; 更早的历史摘要压缩成几句话
    if total_tokens(history) < THRESHOLD:
        return history
    recent = history[-RECENT_K:]                      # 最近K步保留细节
    old_summary = summarize(history[:-RECENT_K])      # 早期历史用模型摘要成简短要点
    return [{"role": "user", "content": f"【早期步骤摘要】: {old_summary}"}] + recent

def store_if_large(result, max_inline=1000):
    if len(result) <= max_inline:
        return result                                 # 小结果直接放
    ref = save_to_store(result)                        # 大结果存外部
    return f"[结果较大已存储, 摘要: {summarize(result)[:500]} | 需完整内容可用引用 {ref} 再查]"
# 上下文管理的几个核心策略

# 1. 大工具结果外置(offloading):
#    工具返回的大数据(查库结果/文档全文)别全塞上下文; 存到外部(文件/向量库/KV),
#    上下文里只放"摘要 + 引用ID", 模型需要细节时再用工具按引用去取。

# 2. 历史摘要/压缩(summarization):
#    保留最近K步的完整细节(近期最相关); 更早的历史用模型摘要成简短要点, 节省token又保留脉络。

# 3. 关键信息固定保留(pinning):
#    把"核心任务目标、关键约束、已确定的中间结论"固定放在上下文显眼位置(如紧跟system),
#    不让它被裁剪掉、不被淹没 → 防止"失忆跑偏"。

# 4. 滑动窗口 + 监控token:
#    实时统计上下文token, 接近阈值就触发裁剪/摘要; 给上下文用量留足余量。

# 5. 结构化记忆(进阶):
#    把Agent的"记忆"分为短期(当前上下文)和长期(外部存储/向量库); 按需检索相关记忆进上下文。

# 核心: 主动管理上下文——大结果外置(只放摘要引用)、早期历史摘要压缩、关键目标固定保留、
#   监控token及时裁剪; 给模型"恰好够用、重点突出"的上下文, 而非"全部、冗长"的上下文。

修复的核心,是"主动管理上下文,给模型恰好够用、重点突出的信息"策略一:大工具结果外置——大数据存外部(文件/向量库),上下文只放"摘要+引用 ID",需要细节时再按引用取策略二:历史摘要压缩——保留最近 K 步完整细节,更早的历史摘要成简短要点策略三:关键信息固定保留(pinning)——把核心任务目标/约束固定放在显眼处(紧跟 system),不被裁剪、不被淹没,防失忆跑偏策略四:滑动窗口+监控 token(接近阈值就裁剪);策略五:结构化记忆(短期上下文+长期外部存储按需检索)。归根结底:主动管理上下文——大结果外置(只放摘要引用)、早期历史摘要压缩、关键目标固定保留、监控 token 及时裁剪;给模型"恰好够用、重点突出"的上下文,而非"全部、冗长"的上下文。

第三件事:Agent 上下文与记忆相关的其他常见坑

排查后我把 Agent 上下文/记忆相关的其他常见坑也系统梳理了一遍。

Agent 上下文 / 记忆的其他常见坑

# 1. 上下文无限膨胀(本文): 每步堆完整结果致超限/失忆。→ 外置+摘要+pinning+监控。

# 2. 大工具结果直接入上下文: 一个工具吐回上万字塞爆窗口。→ 工具结果截断/摘要/外置。

# 3. 关键约束被淹没: 任务目标在开头被海量历史挤到"中间"被忽略。→ 关键信息固定/重申。

# 4. 没有长期记忆: Agent跨会话/跨任务记不住已知信息。→ 外部记忆库+检索。

# 5. 摘要丢了关键信息: 压缩历史时把重要的中间结论也摘没了。→ 摘要要保留关键事实/决策。

# 6. 上下文里全是噪声: 塞了大量无关工具结果, 稀释了有用信息、还增成本。→ 只放相关的。

# 7. 成本/延迟随上下文暴涨: 上下文越长, 每步调用越贵越慢。→ 控制上下文大小=控制成本延迟。

# 8. 多Agent间传递整个上下文: 协作时把巨大上下文整个传来传去。→ 传摘要/结构化结果。

# 共同根源: 把"上下文窗口"当成"无限的、免费的、信息越多越好的"空间, 无脑往里堆;
#   而它是"有限、昂贵、且过载会降低模型表现"的稀缺资源, 必须当成核心约束来精心管理。

# 核心: 把上下文窗口当稀缺资源精心管理: 大结果外置、历史摘要、关键信息pinning、监控token、
#   分短期/长期记忆; 追求"信息相关且精炼", 而非"信息全且冗长"——这是Agent工程的核心能力。

排查让我把 Agent 上下文的其他坑也梳理清了。一、上下文无限膨胀(本文)。二、大工具结果直接入上下文三、关键约束被淹没(固定/重申)。四、没有长期记忆(外部记忆库+检索)。五、摘要丢了关键信息六、上下文全是噪声七、成本/延迟随上下文暴涨八、多 Agent 间传递整个上下文它们的共同根源是:把"上下文窗口"当成"无限的、免费的、信息越多越好的"空间无脑往里堆;而它是"有限、昂贵、且过载会降低模型表现"的稀缺资源,必须当核心约束来精心管理核心是:把上下文窗口当稀缺资源精心管理:大结果外置、历史摘要、关键信息 pinning、监控 token、分短期/长期记忆;追求"信息相关且精炼"而非"信息全且冗长"——这是 Agent 工程的核心能力下面这张图,是这次上下文膨胀坑的成因与解法:

第四件事:上下文管理策略速查表

这次踩坑后,我把 Agent 上下文管理的几种策略整理成一张表,按需组合使用。

策略 解决什么 怎么做
大结果外置 大工具结果撑爆窗口 存外部, 上下文放摘要+引用
历史摘要压缩 历史无限增长 早期摘要, 近期保留细节
关键信息pinning 目标被淹没/失忆 核心任务固定在显眼处
滑动窗口 token超限 只保留最近N步
结构化记忆 跨步/跨会话记忆 短期上下文+长期外部库检索
监控token 及时干预 接近阈值触发裁剪

这张表把上下文管理策略钉清了。核心是:管理上下文不是单一手段,而是一套组合——大结果外置(减体积)、历史摘要(压时间维度)、关键信息 pinning(保重点)、滑动窗口(控长度)、结构化记忆(扩容到外部)、监控 token(及时干预),按任务复杂度组合使用它给我的最大启发是:这套上下文管理,本质是在解决一个经典问题——"有限的快速存储"和"无限的数据"之间的矛盾——这和操作系统的内存管理(有限内存 vs 大量数据:换页、缓存、LRU淘汰)、CPU 的缓存层级(小而快的缓存 vs 大而慢的内存)惊人地相似;"上下文窗口"就是 Agent 的"快速但有限的工作内存",而外部存储就是它的"慢但海量的硬盘",上下文管理就是在做"把最相关的放进有限的快速区、其余放慢速区按需调入"的调度这让我看到了知识的迁移:计算机科学里"分层存储 + 把热数据放快速层 + 冷数据放慢速层 + 按需调度"这套经典思想,在 Agent 上下文管理上完美复现——理解了内存管理/缓存的人,理解 Agent 上下文管理几乎是降维的;"有限快速资源 + 无限慢速资源 + 智能调度"是一个跨越无数领域的通用模式把上下文管理看成经典的分层存储/缓存调度问题、迁移已有知识——是这个坑带给我的认知。

第五件事:Agent 工程里"上下文就是一切"

这次让我深刻体会到,Agent 的能力很大程度上取决于"你给它什么样的上下文"。我整理成表。

上下文的状态 对Agent的影响
关键信息突出 Agent抓得住重点, 不跑偏
信息相关精炼 推理准、速度快、成本低
信息冗长噪杂 被带偏、变慢、变贵
关键信息缺失 "巧妇难为无米之炊", 答不对
超出窗口 直接崩溃

这张表道出了 Agent 工程的一个核心真相。核心是:Agent 的表现,极大程度上取决于"喂给它的上下文质量"——关键信息突出、相关且精炼的上下文,让它推理准、不跑偏、又快又省;冗长噪杂或缺失关键的上下文,让它被带偏、变慢变贵、甚至答不对或崩溃;"给模型什么上下文",几乎决定了它能产出什么它给我的深刻启发是:构建 Agent/LLM 应用,有一项核心工作就是"上下文工程(context engineering)"——不只是写好 prompt,而是系统地设计"在每一步,该把哪些信息、以什么形式、放在上下文的什么位置";RAG 检索相关知识、到工具结果的取舍、到历史的摘要、到关键信息的强调——本质都是在"为模型精心组织它这一步推理所需的、恰到好处的信息";"模型能力是给定的, 你能优化的是喂给它的上下文"——上下文工程, 是 AI 应用工程师最核心的技能之一这给了我一个清晰的努力方向:做 AI 应用,要把大量心思花在"上下文的构建与管理"上——确保每次调用模型时,它拿到的是"解决当前问题所需的、最相关的、组织良好的、不超量的"信息;"把对的信息,在对的时候,以对的方式,放进有限的上下文",是驾驭大模型、做出好用 Agent 的关键功夫认清上下文质量决定 Agent 表现、把上下文工程当核心技能——是这个坑带给我的、关于 AI 工程的方向性认知。

第六件事:设计 Agent 上下文时,我现在的思考习惯

现在每当我设计一个 Agent 的上下文,我都会按这张图先想清楚:

这张图的精髓,是"长任务必须管理上下文:外置大结果、摘要历史、固定目标、监控 token"短任务简单堆历史即可(注意工具结果别太大);长多步任务必须管理:大结果外置、早期历史摘要、核心目标 pinning、监控 token、需要时加外部记忆。这套习惯,让我从"把所有信息都堆给模型"变成了"精心组织恰好够用的上下文"——核心始终是:上下文窗口是稀缺资源,长任务必须主动管理,给模型相关精炼、重点突出的上下文。

我立下的几条规矩

这场"上下文无限膨胀、Agent 失控失忆"的事故,换来了我做 Agent 时,刻进骨子里的几条铁律:

  1. 上下文窗口是有限且宝贵的资源。不是信息越多越好,要精打细算。
  2. Agent 每步带全部历史会快速膨胀。工具结果大+步数多,轻松撑爆窗口。
  3. 过长上下文既会 token 超限,也会失忆跑偏。lost in the middle。
  4. 大工具结果外置。上下文只放摘要+引用,需要细节再按引用取。
  5. 早期历史摘要压缩,核心目标固定保留。压时间维度、保重点不被淹没。
  6. 实时监控 token,接近阈值就裁剪。控上下文大小=控成本和延迟。
  7. 上下文工程是 Agent 工程的核心技能。给对的信息,以对的方式,在对的位置。

写在最后

回头看,这场由"上下文无限堆积"引发的、Agent 又崩又"失忆"的事故,真正教给我的,远不止"Agent 要做上下文管理"这一个技巧。它让我对"'给得越多越好'是一种危险的直觉,真正有价值的,往往是'恰到好处的、突出重点的少'",有了一次刻骨的体会。我栽跟头,源于一个根深蒂固的、却是错误的直觉:"信息越多越好"——我生怕模型"缺信息"而做不好决策,于是把每一步的所有细节、所有工具结果,都毫无保留地塞给它,以为"我把知道的全告诉它,它就能做得最好"。可现实恰恰相反:过量的、未经组织的信息,不仅没帮上忙,反而害了它——要么撑爆了它的容量(它根本"装不下"),要么淹没了真正重要的信息(它在海量细节里"找不到重点"、被带偏);我以为的"慷慨地给足信息",实际是"用噪声稀释了信号、用冗余挤掉了关键"这让我领悟到一个超越 Agent、关于"信息与决策"的深刻认知:"更多的信息"不等于"更好的决策"——决策质量取决于"相关信息的信噪比",而非"信息的总量";过量的信息会带来"信息过载":淹没关键、增加噪声、消耗有限的处理能力(无论是模型的上下文窗口,还是人的注意力);真正的高手, 不是"掌握信息最多"的, 而是"能从海量信息里精准地筛出、组织出当下最相关的那一点点"的这给了我一种"少即是多"的设计智慧:无论是给模型构造上下文、还是给人做汇报、还是设计接口——都要追求"精准、相关、突出重点",而非"详尽、全面、事无巨细"——主动地筛选、提炼、组织、强调,把"最相关的关键信息"清晰地呈现出来,把无关的噪声滤掉;"恰到好处的少",远胜于"不分主次的多"——这是处理一切"有限的处理能力 + 过量的信息"问题的核心智慧认清更多信息不等于更好决策、追求信噪比而非信息量、奉行少即是多——这,是我用一次 Agent 上下文失控的事故,换来的、关于 AI Agent、也关于如何处理信息与决策的、最朴素也最深刻的领悟。如果这篇复盘,能让你下次给 Agent(或给任何需要决策的对象)准备信息时,先想一句"哪些是真正关键的、能不能更精炼",而不是一股脑全塞进去,那我对着那个又崩又跑偏的 Agent 排查的这大半天,就值了。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

一个把数字枚举的值直接存进数据库的设计,在我往枚举中间插了一个新成员后,让所有历史订单的状态集体错位了一格:一次 TypeScript 枚举持久化的深度复盘

2026-6-2 16:09:01

技术教程

一段用多线程给 CPU 密集计算加速的 Python 代码,开了八个线程却比单线程还慢,我被 GIL 实实在在上了一课:一次多线程并行误区的深度复盘

2026-6-2 16:19:55

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索