☰
智能体上下文管理:删记录比堆窗口更能跑赢长任务
2026/10/7 4:56:51 网站建设 项目流程

1. 从不迷信“无限上下文”说起

这两年大模型厂商一个比一个卷,模型上下文窗口从 32K 一路干到 1M,恨不得把“全书”都塞进一次对话里。1M 上下文是什么意思?直观一点说,大概相当于一次性读完整套《三体》三部曲还有富余。但是做过真实业务的人都知道,窗口大不等于能用好,更不等于长任务就能稳定跑到底。

我踩过的坑很典型:把一个十几步的长链任务交给智能体,开头几步一切正常,步骤越多脑子越糊涂,到后面要么答非所问,要么干脆把前面已经确认过的结论又推翻重来。很多人第一反应是“上下文不够了,换更大窗口的模型”,但真正的问题往往不是窗口不够,而是上下文里塞了太多不该塞的东西,模型在前面的信息里被反复拉扯。这时候就算换 1M 窗口的模型,该乱还是乱。

说白了,上下文就像一个办公室桌面。桌面再大,你把所有文件、邮件、便签全部摊开,找东西照样困难。真正高效的工作方式是随时把没用的垃圾扔掉,只保留当前这步需要的那几张纸。智能体也一样,与其追求无限大的上下文,不如学会自己删记录、自己整理工作台。这就是今天要聊的核心话题:给智能体装上“上下文管理”的能力,让它跑长任务时始终保持头脑清醒。

这篇文章适合正在用 Dify、Coze、LangChain 这类平台或框架搭智能体的开发者,也适合被“上下文超长”折磨的业务人员。我会结合自己实际跑过的一个长任务案例,从原理拆解到可落地的工程方案,一步步说明白为什么删记录比堆窗口重要,以及具体该怎么实现。

2. 上下文失控才是长任务的真正瓶颈

2.1 你以为的“上下文”和模型看到的“上下文”不是一回事

先说个容易被忽略的问题:上下文窗口到底装的是什么?很多人以为就是用户聊天记录,其实在智能体里,上下文远比这复杂。它至少包含四类东西:

  • 系统提示词里写的角色设定、任务规则、输出格式要求;
  • 用户本轮以及历史轮次的输入;
  • 智能体调用工具后的返回结果、查询到的外部数据;
  • 智能体自己的思考过程、中间结论、决策记录。

这些信息全部拼接在一起,才构成一次请求里模型实际看到的内容。问题就在这里:智能体每执行一个步骤,就会往上下文里追加一段内容;步骤多了,历史累加,即使每条信息都很短,累积起来也会迅速逼近窗口上限。

我遇到过最夸张的一次,某个工作流只跑了 6 步,上下文里就堆了 3.2 万 token,里头一大半是中间过程的完整 JSON 输出,比如某个搜索接口返回的 20 条结果原文。模型每走下一步都要重新读一遍这些内容,既浪费 token,又制造大量无关干扰。等到第 7 步需要真正做决策时,关键信息已经被淹没了。

所以判断上下文是否够用,不能只看“总 Token 数 < 窗口大小”,更要看“有效信息密度”。上下文失控的本质不是容量不够,而是信噪比太低。

2.2 长任务为什么会“越跑越笨”

长任务跑崩,通常不是突然崩的,而是逐步劣化的。我总结过它的典型过程,基本符合这条曲线:

前几步:模型记忆清晰,每步执行很果断,工具调用准确。

中间几步:上下文里开始夹杂大量之前步骤的中间结果。模型需要花更多注意力去分辨哪些是有用的。

后段:上下文里已经堆了太多不同步骤的片段,模型开始前后矛盾,表现为重复询问已给过的问题、重新生成之前已经确定的表结构、输出与任务目标明显偏离。

最后:完全失控。要么吐出一堆无意义内容,要么直接报错超过上下文限制。

拿我调试一个销售线索清洗智能体来说,任务本身不复杂:读取原始线索表 → 去重 → 补全企业信息 → 按行业打分 → 输出分级结果。前 3 步跑得非常漂亮,到第 4 步开始发疯——把已经完成去重的结论又拿出来重新判断,然后怀疑原始数据有问题,竟然又去调了一次查询接口。我打开调试日志一看,第 4 步发起请求时,携带的历史消息里包含了前面 3 步所有工具返回的完整 JSON,里面有用信息不超过 20%,剩下全是无关字段和过程数据。模型不是不聪明,是实在被噪声淹没得没法聪明了。

2.3 “大窗口”为什么救不了你

有人会觉得,那换个大窗口模型不就行了吗?比如从 32K 换到 128K,所有历史都塞得下,模型总能看到完整过程了吧?

实际情况是,模型对上下文不同位置的关注度是不均匀的。业界做过很多实验也验证了这一点:距离当前时间越早的内容,注意力越淡;特别长的输入中段,往往是模型最不擅长的区域。窗口越大,这个问题越明显——不是模型不想看,是注意力机制决定了它很难同等对待每一段输入。有人把这种情况叫“迷失在中间”。

更现实的问题是成本。大窗口模型价格更高,响应更慢。一个 10 步的长任务,每一步都要重新把所有历史发给模型,Token 消耗随窗口增长近乎线性上升。窗口开 128K,每跑一趟完整任务光输入可能就吃掉几万 Token,跑一次成本可能够小模型跑几十次了。

所以我的观点一直很明确:大窗口是兜底能力,不是日常方案。真正支撑长任务可靠运行的核心能力,是智能体是否具备根据任务阶段动态裁剪、压缩、重组上下文的能力。

3. 给智能体装上“记忆管理”机制

3.1 删记录的核心思想:从“全量记忆”变成“按需取用”

要解决上下文失控,思路得先扭转过来。人类完成长任务靠的不是把所有信息背下来,而是把每个阶段的关键产出记录在合适的地方,到下一步时只拿出需要的那部分。智能体也应该这样。

具体来说,一个合格的上下文管理机制应该做三件事:

  1. 及时清零:每一步执行完毕后,把已经用过的原始数据、不再需要的中间结果从上下文里移除。
  2. 精简保留:只保留对后续步骤有影响的关键信息,比如最终结论、筛选条件、确认过的决策。
  3. 按需取回:后续步骤需要某条信息时,从外部存储里定向取回,而不是天然带着所有历史走。

做到这三点,上下文里就永远只装“当前这步该做的事”和“全局必须记住的少量结论”。Token 占用会大幅下降,模型注意力也能集中在当下最关键的内容上。

3.2 三种记忆类型,对应不同删除策略

要把这套机制落地,首先要对记忆做分类。我在实践中通常把智能体的记忆分成三种:短期工作记忆、长期项目记忆、静态规则记忆。三类信息的生命周期完全不同,处理策略也不同。

短期工作记忆是任务执行过程中产生的临时数据,比如上一步的工具返回结果、临时循环变量、中间计算值。这类信息的特点是“用完即弃”,应该在下一步开始前主动清理。典型例子是:一个查询接口返回了 50 行商品数据,你把其中符合条件的产品 ID 提取出来,那 50 行原始数据就没有保留价值了,直接从消息列表里删掉。

长期项目记忆是整个任务生命周期内必须稳定的关键信息,比如用户的核心需求、已经确定的目标函数、已产出的阶段性结论。这类信息必须保留,但保留的形式不是原始长文本,而是提炼后的摘要或结构化字段。比如“用户要求所有价格单位按美元计算”这种全局约束,就应该以一条规则的形式固定下来,而不是留在某段历史对话里自然延续。

静态规则记忆是角色设定、输出格式、安全限制这类内容,它们从头到尾不能变。这类记忆不参与任务过程的增删管理,但要注意它们占用的 Token 数,规则太长的要压缩表达,避免每次请求都携带一大坨无用的设定文本。

3.3 没有外部记忆的“删记录”是耍流氓

如果删完就彻底扔掉,那后面的步骤需要前面的信息时怎么办?所以实现删记录的前提,是同时具备“外部记忆存储”能力。简单来说,就是把关键信息写到外部存储里(数据库、向量库、Redis 都行),上下文里只留一个轻量引用或关键摘要。

我在 Dify 里跑长任务时常用的组合是:工作流内部变量保存短期关键值,外部知识库(或者直接建一张简单数据库表)保存跨步骤结论。每执行完一个阶段,把该阶段的核心产出整理成一条简洁记录存入外部存储,然后把这条信息对应的完整上下文从会话消息中移除。

这样做有双重好处:一方面上下文变干净,模型不再被噪声干扰;另一方面,外部存储里的记录是结构化、可检索的,后续步骤可以精确取用,比让模型从一大段历史里自己找要可靠得多。

4. 实战:在 Dify 工作流里实现长任务上下文瘦身

4.1 一个典型的“上下文超长”工作流案例

前段时间我帮人搭过一个内容审核工作流,任务链是这样的:抓取文章原文 → 正文清洗 → 敏感词检测 → 语义风险判断 → 生成审核报告。单篇文章正文平均 6000 字,转换成 Token 差不多 8000 左右。如果每步都把原文带在上下文里,第 1 步结束后上下文就有 9000+ Token,第 2 步结束时超过 1.8 万,到第 4 步时已经接近 3 万。虽然有上下文 32K 的模型能硬扛,但实际跑的时候经常出现检测结果前后不一致,一个明明安全的词,到了第 4 步突然被判为高风险。

问题就出在上下文堆积上。前一步的判断依据和后一步需要的信息,其实完全不同。比如敏感词检测需要的是全文文本,而语义风险判断需要的是检测命中的词列表及所在段落摘要,至于文章开头那些大段背景介绍,后面完全用不上。

改造方案很明确:每个步骤执行完,就把这步的输入原始内容从会话消息中移除,只往下游步骤传递结构化的小体积结论。

4.2 关键实现步骤:消息改写与变量传递

在 Dify 这类可视化工作流平台里,不需要改一行代码也能实现上下文瘦身。我的做法是充分用好两个核心组件:消息处理节点(Message Tool)和变量赋值节点。

以内容审核工作流为例,我的节点编排如下:

第一步:文章原文作为用户输入传入工作流,先经过文本清洗节点,输出清洗后的正文。这个正文会作为后续检测节点的输入。

第二步:把清洗后的正文存入一个临时变量,比如article_clean。紧接着用一个消息处理节点,把当前会话中的用户消息替换成一句简短摘要,比如“用户提交了一篇文章,已完成清洗,正文字数为 X”。这一步相当于直接抹掉了原文在上下文里的大段存在感。

第三步:敏感词检测节点从变量article_clean读取正文,进行检测,输出命中列表。此步结束后,再次用消息处理节点,把当前上下文中的这篇文章内容删除,同时把命中结果写入新变量word_hits。

第四步:语义风险判断节点读取word_hits和对应命中词的上下文片段,不再读取全文。

第五步:生成审核报告,使用最终的word_hits、risk_level、summary等变量输出。

这套流程跑下来,上下文里从头到尾不会出现完整正文。每一步模型实际看到的信息量很小,相当于每执行完一步就“翻篇”一次,模型永远只面对当前这一张写得清清楚楚的笔记,而不是背后那堆原始档案。

4.3 把“删”做成智能体自己的技能

上面的方案适合固定流程的工作流。但如果你的智能体是自由对话式的,任务路径不可预知,那单纯靠编排就不够了,需要让智能体自己学会“何时删、怎么删”。

我在基于 Dify Agent 节点做自由任务智能体时,采用的方式是给智能体配置一套上下文管理工具,类似这样:

  • extract_key_info(source_text, keep_fields):把一段长文本提炼成精简摘要或指定字段的结构化数据。
  • drop_history(range_or_index):移除当前会话中指定的历史消息区间。
  • write_note(key, value):把关键结论写入外部记忆存储。
  • read_note(key):从外部记忆读取指定结论。
  • clear_scratchpad():清空所有临时过程中产生的消息记录。

然后在系统提示词里明确写入使用规则,这部分规则要写得非常具体,否则模型不会主动执行。我实际使用的提示词片段是:

在执行每个任务步骤前,先检查当前会话中的历史消息。如果历史消息中存在“原始工具返回值、上一步骤的完整输出、已被提炼过的源文本”,同时它们与当前步骤无关,先调用 drop_history 清除。清除前,确保已经把必要结论通过 write_note 保存。

这套方法跑下来的效果非常明显。拿一个资料调研类智能体来说,改造前它需要让它读 20 篇网页文章,跑到第 8 篇时模型的记忆就开始混淆来源;改造后每篇文章读完只留下一句话的核心观点笔记,上下文里始终只有当前这篇文章的内容,跑完全部 20 篇依然思路清晰。

4.4 一个 Python 版本的极简实现

如果你不是用平台搭工作流,而是用代码直接调模型 API,那么上下文管理的实现更直接。核心就是自己维护 message 列表,在每次调模型前手动裁剪。

下面我把自己在 Python 里常用的一套极简实现贴出来,核心思路是:用一个NoteStore保存必须跨步骤保留的信息,每轮对话结束后把消息列表做一次瘦身。

import json from typing import Any, Dict, List class NoteStore: """轻量外部记忆,用 dict 充当存储,生产环境可换数据库/向量库""" def __init__(self): self._notes: Dict[str, Any] = {} def write(self, key: str, value: Any): self._notes[key] = value def read(self, key: str, default=None): return self._notes.get(key, default) def to_system_block(self, keys: List[str]) -> str: """把指定 key 的笔记汇总为一条精简的系统提示片段""" lines = [] for k in keys: if k in self._notes: lines.append(f"{k}: {json.dumps(self._notes[k], ensure_ascii=False)}") return "\n".join(lines) def compact_messages(messages: List[Dict], keep_last_n: int = 4, note_block: str = "") -> List[Dict]: """ 简单的上下文压缩策略: 1. 系统提示词 + 最近 keep_last_n 条消息 2. 额外附加 note_block 作为外部记忆摘要注入 """ system_msgs = [m for m in messages if m["role"] == "system"] recent = messages[-keep_last_n:] compacted = system_msgs + recent if note_block: # 把笔记摘要挂到第一条 system 消息末尾,确保模型感知全局关键信息 merged = compacted[0].copy() merged["content"] = merged["content"].rstrip() + "\n\n[外部记忆参考]\n" + note_block compacted[0] = merged return compacted def run_smart_agent(step_inputs: List[Dict]): store = NoteStore() messages = [{"role": "system", "content": "你是一个能管理自己上下文的智能体。"}] for idx, step in enumerate(step_inputs): # 每一步只追加当前步骤的输入 messages.append({"role": "user", "content": step["prompt"]}) # 模拟调用模型的返回,实际场景里这里调用 LLM API # 这里以一段简单伪代码代替 assistant_reply = f"第{idx+1}步完成,产出关键值: {step.get('key')}" messages.append({"role": "assistant", "content": assistant_reply}) # 提取关键信息存入外部记忆 if step.get("note_key"): store.write(step["note_key"], step["note_value"]) # 步骤结束后压缩消息列表,仅保留最近 4 条 # 同时注入外部记忆摘要,让模型拥有全局视野 messages = compact_messages( messages, keep_last_n=4, note_block=store.to_system_block(["rule", "confirmed_result"]) ) return messages # 使用示例 step_list = [ {"prompt": "读取原始数据文件,统计总行数。", "note_key": "line_count", "note_value": 12890}, {"prompt": "根据规则过滤无效行,规则:status=active。", "note_key": "rule", "note_value": "仅保留status=active的数据"}, {"prompt": "基于上一步结果进行分组统计,并输出各城市数量。", "note_key": "confirmed_result", "note_value": "城市数量已统计,北京3782,上海2941"}, ] final_messages = run_smart_agent(step_list) for m in final_messages: print(f"{m['role']}: {m['content'][:80]}")

这个极简版本的核心洞察是:不要让模型通过自然对话历史去记忆重要信息,而是把重要信息显式提取出来,作为系统提示的一部分注入。历史消息被裁掉之后,模型失去了“原文”,但该记住的结论都在note_block里稳稳妥妥地带着。这就既瘦身又不失忆。

5. 更系统的上下文管理三板斧

5.1 清理:把用完即弃的信息坚决扔出去

实际落地时,很多人会心软,觉得“万一之后还要用呢”,然后就把原始数据留在上下文里。我在自己项目中定了一个原则:没有明确说明“后续第几步要用”的数据,一律视为用完即弃。

判断一条消息是否该删,可以按这三个条件自检:

  1. 这条信息对当前步骤之后的任意已知步骤是否有影响?如果没有,删除。
  2. 这条信息是否已经被提炼成更高层的结论?如果是,删除原始内容,保留结论。
  3. 这条信息是否可以随时从外部存储重新取回?如果可以,删除,只保留引用标识。

按这个标准执行下来,通常一个 10 步任务只需要在上下文里保留不到 20% 的信息。

5.2 摘要:长资料变短结论,再喂给模型

清理不一定非要删除。对于某些还需要参考、但原始内容太长的中间产物,最适合的方法是先摘要,再让模型读摘要而不是读原文。

我在内容审核案例里用的就是摘要方案:敏感词检测步骤需要全文,但语义判断步骤只需要“命中词列表 + 命中位置所在句子”。那么在执行完检测后,我就把检测结果整理成:

命中词列表:["沟通", "安排", "搞定"] 命中位置:第2段第3句、第5段第1句 涉及上下文:……

这样原本几千字的正文就被压缩成几十字的结构化摘要。模型后续判断时拿到的信息密度非常高,决策质量反而比读全文更好。

具体实现时,可以使用单独的摘要节点,也可以用模型自己来提炼。建议摘要节点的模型用高性价比型号,比如更小的模型专门拿来压缩文本,大模型只做最终决策,成本能省不少。

5.3 引用:让模型学会“按需查档”而非“抱着一堆档案干活”

删和摘要解决的是“过去的信息”,还要解决“未来的信息”。长任务做到后面,某一步会需要早期步骤的某个具体数据,那这时候怎么办?不能靠上下文自动携带,而应该提供“查档”能力。

我在智能体技能列表里加入了一个query_memory(keyword)工具。模型在执行某一步时如果发现缺少信息,会主动调用这个工具,从外部的记忆库中检索相关记录。这个工具的底层可以是一个简单的关键词匹配,也可以直接查数据库表,甚至接向量检索。

这样做的好处是:信息存在外部,需要时才取回来;取回来的只是和当前步骤相关的那一小块。上下文里不会因为某条信息“以后可能用到”就一直占着位置。

我见过很多失败的长任务智能体,它们的共同问题就是没有“查档”意识。模型不知道信息存哪了,只能指望历史里碰运气。这个工具一加上,相当于给了模型一个明确的信息获取入口,它就不再被动依赖积累了。

6. 常见问题与排查技巧实录

6.1 删完记录后模型失忆了怎么办

这是最常遇到的问题。我用“删记录”方案改造的早期版本里,模型经常忘记全局约束。比如用户明确说过“所有输出要按中文列名”,但是跑到第五步时模型突然开始输出英文列名。

排查后发现,问题出在我把用户原始约束也当成“用完即弃”信息删掉了。而全局约束显然是长期记忆,不应该被清理。

修正方案:区分“全局固定信息”和“步骤临时信息”。全局固定信息本来就不该放在普通对话消息里,而应该放进系统提示词的固定部分;或者通过write_note写入外部记忆,然后每一步都把它注入note_block。这样即使用户消息被删,约束也不会丢。

另一个失忆来源是:模型需要某条信息,但该信息已经被清掉,并且没有写入外部记忆。例如模型在第一步自行推测出一个重要结论,但你没有显式提示它“把结论保存”,后续步骤就无法访问。解决办法是在系统提示词里强调:每个关键结论产出后,立即调用 note 相关工具保存,保存后才能继续下一步。

6.2 上下文明明不长,但任务还是乱

有朋友跑过来问我,他的工作流每步输入都很短,上下文总共才 2000 token,怎么任务还是跑不对?

我看了看他的工作流设计,发现问题的根源不是上下文太长,而是他的节点之间传递的是“模型生成的自由文本”,不是结构化数据。比如第一步让模型“提取客户名称”,返回的是自然语言“有两个客户:A公司和B公司”;第二步又让模型“比较这两个客户的规模”,它从自由文本里解析公司名,一旦表达稍有变化就找不到目标。

这不是上下文管理能解决的问题,这是数据流设计的问题。正确做法是:让工具节点输出 JSON 或结构化数据,并存到变量里,后续步骤直接用变量引用,而不是让模型从文本中二次提取。

上下文管理的目标不是把所有问题都变成“信息太长”,而是让信息在恰当的结构中以恰当的形式流动。如果你发现信息很短但模型还是乱,大概率不是删得不够多,而是信息结构没有理清。

6.3 删除后 token 降下来了,但模型变“目光短浅”了

还有一个副作用值得警惕:上下文删得太狠,模型容易变得只盯着眼前几步,失去全局规划能力。

比如一个数据统计任务,第 1 步要读取多张表,第 5 步要把这些表做 join。如果你在第 1 步结束后就把表的字段信息全删了,到第 5 步模型根本不知道原表长什么样,自然没法正确 join。

针对这种情况,我的经验是:保留“元信息”,删除“实体内容”。比如一张媒体表,实体内容可能是几千行数据,这不能放在上下文里;但表的字段名、行数、主键、与其它表的关系,这些元信息很小,却极其关键,必须长期保留。类似地,前面所有步骤的关键决策结论、已经确认过的口径、统一过的术语,也都属于元信息。

具体做法:在每步结束后,除了写note,额外写一个meta_note,里面记录“这一步处理对象的结构信息 + 这一步产出的关键决策”。模型在后续步骤需要时,可以直接在系统提示里看到所有历史的关键决策摘要,这些摘要加起来通常不超过几百 token。

我实践下来的配置是:上下文窗口如果只有 8K,那么历史消息只留最近 3 轮,但meta_note允许容纳全部步骤的关键决策,最多 1K token。这样模型既有眼前的操作信息,又有全局的结构信息,长任务跑起来非常稳定。

6.4 快速自查表:你的智能体是否需要上下文管理

如果你还不确定自己的智能体该不该做这套改造,可以参考下面的自查表,凡是有两条以上命中,就说明上下文管理已经在拖后腿了。

检查项命中说明
任务步骤超过 5 步时,模型开始重复提问或答非所问历史噪声干扰了注意力
工具返回的原始 JSON 直接暴露在后续对话中上下文信噪比低
Token 消耗比预估高 30% 以上大量无效历史被反复携带
同一个任务在不同时间跑,结果偏差严重模型注意力在不同历史区间漂移
模型会自己追溯前面步骤的中间产物,并试图修改它已经分不清当前该干什么

如果你的智能体命中了两条以上,别急着换更大窗口的模型,先做上下文管理。大概率你会发现,问题解决得比想象中快。

7. 最终的一点实战体会

从“堆窗口”到“删记录”,这个观念转变我花了挺长时间才真正想明白。刚开始做长任务智能体时,我也总担心信息删了会丢,拼命保留一切;后来被一个大参数模型在 16K 上下文里跑崩了三次之后,才下定决心彻底清理消息。结果是任务成功率反而从不到 60% 提升到了 90% 以上。

再分享一个小技巧:调试智能体时,别只看最终答案,一定要打开每次请求的消息列表,逐条看看模型实际看到了什么。很多时候你以为它看到了全貌,其实它只看到了一坨杂乱的历史。每当你发现一条历史消息对当前步骤毫无帮助,那就是该删的记录。

上下文管理不是一个高大上的算法,而是一种朴实的工程习惯。你需要的不是什么复杂的 Agent 框架,而是时刻问自己:这一段信息,模型现在真的需要吗?如果答案是否定的,就把它从工作台上拿开。长任务跑得起来,从来不是靠超大的口袋,而是靠随时腾出干净的桌面。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询