1. 长程 Agent 的记忆衰减:为什么做得越多忘得越快
如果你正在做 Deep Research、Agentic Coding 或者任何需要连续跑几十轮工具调用的智能体,大概率遇到过这个场景:前 10 轮还挺聪明,到第 30 轮开始答非所问,第 50 轮直接把自己最初的任务目标搞丢了。这不是模型变笨了,而是上下文膨胀把关键信息淹没了。
我先把问题拆开看。一个典型的 ReAct 循环长这样:思考 → 调工具 → 读结果 → 再思考。每一轮,框架都会把之前所有轨迹重新塞回模型。短任务没问题,但一旦涉及搜索网页、读源码、跑终端、看错误日志,单条工具返回就可能几千 token。几十轮下来,上下文轻松冲到 100K 以上。
这里有两个独立的恶化机制。第一是信噪比崩塌:窗口还没满,但真正相关的信息(比如最初的任务约束、某个关键数字)已经被大量中间过程稀释,注意力权重被摊薄。第二是成本非线性上升:KV-cache 随上下文线性增长,注意力计算随长度平方增长,延迟和费用一起飙升。
传统解法有两种,都不太理想。一种是"快满了就总结",但总结点往往落在推理中途,会把还没结束的思考链压断。另一种是预先设计多个专职 Agent 分工,工作流靠人工编排,难以端到端优化,任务一变就得重写。
Context Folding 的思路不一样:它不在任意位置压缩,而是在明确的子任务边界上折叠。Agent 可以临时开一条分支去干脏活累活,干完只把结论带回主线,中间的海量过程被"折"掉。更关键的是,"何时开分支、何时返回、返回什么"是 Agent 自己学出来的,不是硬编码的。
这篇就按这个思路,给你一套能直接跑起来的折叠工作记忆配置。我会用 TaoToken 作为模型接入层来演示,因为长程任务对模型调用的稳定性和多模型切换有实际需求。下面从接入配置讲到折叠策略参数,再到多轮任务的记忆保持验证,每一步都能复制。
2. TaoToken 接入前置:给长程 Agent 准备稳定的模型通道
长程 Agent 和普通问答最大的区别是调用量大、持续时间长、经常需要在不同模型间切换(规划用强模型、执行用快模型)。这就要求接入层足够稳、足够灵活。TaoToken 在这里扮演的是统一 API 网关的角色,你用一个 Key 就能访问多种模型,Base URL 和 OpenAI 兼容格式一致,改配置的成本很低。
先说清楚它适合谁:如果你在本地或服务器上跑 Agent 框架(LangGraph、AutoGen、自研 ReAct 循环都行),需要长时间连续调用模型,又不想为每个模型单独维护一套 SDK 和鉴权,那这套接入方式能省不少事。它不替代你的编辑器或 Agent 框架,只是把模型调用这一层标准化。
接入前你需要准备三样东西,我称之为"三件套",缺一不可:
- Base URL:
https://taotoken.net/api,所有兼容 OpenAI 协议的客户端都填这个 - API Key:在控制台生成,形如
sk-开头的一串字符 - Model ID:具体调用的模型标识,比如
gpt-4o、claude-3-5-sonnet这类,以控制台实际列表为准
获取 Key 的路径是:登录后进入控制台,找到 API Keys 页面新建一个。建议给长程 Agent 单独建一个 Key,方便按项目统计用量和限额。控制台地址是https://taotoken.net/console,API Keys 页面是https://taotoken.net/api-keys。
这里有个容易被忽略的点:长程任务的失败往往不是模型能力问题,而是接入层超时或限流。所以配置时要把超时设长一点(建议 120 秒以上),并开启重试。下面这段是通用的环境变量配置,任何框架都能读:
export TAOTOKEN_BASE_URL="https://taotoken.net/api" export TAOTOKEN_API_KEY="sk-你的实际Key" export TAOTOKEN_MODEL="gpt-4o"如果你用的是 Claude Code 这类工具,它的配置走的是 Anthropic 协议,Base URL 要填对应的 Anthropic 兼容端点,Key 和 Model ID 同样从控制台取。Cline 或 Roo Code 这类 VS Code 插件则在设置里选 OpenAI Compatible,把上面三件套填进去即可。Codex 用户改的是~/.codex/auth.json,里面同样需要 Base URL、Key、Model ID 三项对齐。
配置完先别急着跑长任务,用一条最简单的请求验证通道是否通。这一步能帮你把接入问题和 Agent 逻辑问题分开,后面排障会省很多时间。
3. 可复制的折叠策略配置:JSON 参数与运行时骨架
这一节是核心。我给你一套可以直接落地的折叠配置,包含两部分:一份声明式的策略 JSON(控制折叠行为),和一段运行时骨架(真正执行折叠)。两者配合,才能让 Agent 在长程任务里稳定保留关键信息。
先看策略配置。这份 JSON 定义了主线程预算、分支上限、返回摘要的必填字段,以及触发折叠的阈值。你可以直接存成folding_policy.json:
{ "main_thread_budget_tokens": 8000, "fold_trigger_ratio": 0.5, "max_branches": 10, "branch_budget_tokens": 32000, "allow_nested_branch": false, "return_schema": { "required_fields": ["conclusion", "evidence_ref", "confidence"], "max_summary_tokens": 300 }, "penalties": { "unfolded_token_penalty": true, "out_of_scope_penalty": true, "failure_penalty": true }, "model_routing": { "planning": "gpt-4o", "execution": "gpt-4o-mini" } }逐项解释一下关键参数,这些数字不是拍脑袋来的,是我按长程任务的实测经验调的:
main_thread_budget_tokens设 8000,意思是主线程只保留目标、约束、关键决策和已验证结论。这个值太小会导致主线信息不足,太大就失去折叠意义。8000 是个比较稳的起点。
fold_trigger_ratio设 0.5,对应论文里的 Unfolded Token Penalty 思路:主线程用到预算一半还没折叠,就该提醒 Agent 开分支了。这个阈值让折叠发生在"还来得及"的时候,而不是等爆了才补救。
max_branches设 10,配合 32K 的分支预算,理论总预算能到 320K,但峰值活跃上下文始终压在 8K 左右。这就是折叠的核心收益:降低峰值,而不是降低总量。
return_schema是最容易被忽视但最重要的部分。它强制每个分支返回时带上conclusion(结论)、evidence_ref(证据引用)、confidence(置信度)。没有这个约束,Agent 返回的摘要要么太短丢信息,要么太长等于没折叠。
model_routing把规划和执行分开:规划用强模型保证决策质量,执行用快模型控制成本。长程任务里这个分离能省下可观的费用。
再看运行时骨架。下面这段 Python 同时维护"完整审计日志"和"精简模型上下文",这是折叠能落地的关键——折叠的是喂给模型的工作上下文,不是删除历史:
from dataclasses import dataclass, field import json @dataclass class Branch: description: str prompt: str events: list = field(default_factory=list) class FoldingContext: def __init__(self, task: str, policy_path: str = "folding_policy.json"): with open(policy_path, encoding="utf-8") as f: self.policy = json.load(f) self.task = task self.main_thread = [f"TASK: {task}"] self.audit_log = [f"TASK: {task}"] self.active_branch = None self.branch_count = 0 def branch(self, description: str, prompt: str): if self.active_branch is not None: raise RuntimeError("nested branch disabled by policy") if self.branch_count >= self.policy["max_branches"]: raise RuntimeError("branch budget exhausted") self.active_branch = Branch(description, prompt) self.branch_count += 1 self.audit_log.append(f"BRANCH: {description} | {prompt}") def observe(self, event: str): if self.active_branch is None: self.main_thread.append(event) else: self.active_branch.events.append(event) self.audit_log.append(event) def return_to_main(self, conclusion: str, evidence_ref: str, confidence: float): if self.active_branch is None: raise RuntimeError("no active branch to fold") folded = len(self.active_branch.events) summary = ( f"FOLDED[{self.active_branch.description}, {folded} events] " f"conclusion={conclusion} | evidence={evidence_ref} | conf={confidence}" ) self.main_thread.append(summary) self.audit_log.append(f"RETURN: {summary}") self.active_branch = None def model_context(self) -> str: ctx = list(self.main_thread) if self.active_branch is not None: ctx.extend([ f"ACTIVE BRANCH: {self.active_branch.description}", f"BRANCH PROMPT: {self.active_branch.prompt}", *self.active_branch.events, ]) return "\n".join(ctx)注意return_to_main强制三个参数,对应策略里的return_schema。这样每次折叠都保证带回结构化结论,而不是一句模糊的"我查完了"。model_context()是唯一喂给模型的东西,审计日志则完整保留在外部,需要追溯时随时能查。
把这两部分接进你的 Agent 循环:每轮开始前检查主线程 token 数,超过budget * trigger_ratio就提示模型开分支;分支内的高 token 操作(搜索、读文件、跑命令)全部走observe;分支结束调return_to_main。这样主线程永远只看到结论,看不到过程。
4. 验证请求与记忆保持:多轮任务下确认关键信息没丢
配置写完必须验证,否则你不知道折叠到底有没有生效、关键信息有没有在折叠中丢失。这一节给你两个验证动作:一个是接入通道的连通性验证,一个是多轮任务的记忆保持验证。
先验证通道。用 curl 发一条最小请求,确认 Base URL、Key、Model ID 三件套对齐:
curl https://taotoken.net/api/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "reply with OK only"}], "max_tokens": 10 }'正常返回里会有choices[0].message.content字段,内容是OK。如果这一步就失败,先别往下走,去第 5 节对照报错排查。通道通了,再验证折叠逻辑。
记忆保持验证的核心思路:设计一个多轮任务,在早期埋入一个关键约束,然后跑足够多的轮次让上下文膨胀,最后检查 Agent 是否还记得那个约束。我用的测试任务是"比较三篇论文并推荐一篇,要求必须记录每篇的代码是否开源",其中"代码是否开源"就是埋入的关键约束。
验证脚本这样写:
ctx = FoldingContext("Compare 3 papers, must record code availability for each") # 第 1 轮:埋入关键约束 ctx.observe("CONSTRAINT: for each paper, must record whether code is open-sourced") # 模拟 20 轮高 token 分支操作 for i in range(3): ctx.branch(f"paper_{i}", "collect method, dataset, code availability") for j in range(8): ctx.observe(f"search result chunk {j} for paper {i}: " + "x" * 200) ctx.return_to_main( conclusion=f"paper {i} method verified", evidence_ref=f"https://example.com/paper{i}", confidence=0.9 ) # 检查主线程是否还保留关键约束 final_ctx = ctx.model_context() assert "code is open-sourced" in final_ctx, "关键约束丢失!" print("主线程 token 估算:", len(final_ctx) // 4) print("审计日志事件数:", len(ctx.audit_log))跑下来你会看到两个关键指标:主线程 token 数应该稳定在 8000 以内(尽管审计日志有 20 多条事件),且code is open-sourced这个约束始终在model_context()里。这就是折叠生效的证据——过程被折掉了,约束留下了。
如果断言失败,说明折叠策略有问题,通常是return_schema没强制保留约束类信息。解决办法是在return_to_main里加一个校验:返回前检查主线程里的关键约束词是否还在,不在就拒绝折叠并让分支补充。
再补一个更严格的验证:让 Agent 在最后一步回答"哪篇论文代码开源",看它能否基于折叠后的上下文正确作答。这一步把记忆保持从"字符串还在"升级到"语义可用",更接近真实场景。
5. 常见报错排查:401、local proxy failed 与 choices 读取失败
长程 Agent 跑起来后,报错基本集中在接入层和折叠逻辑两类。这一节按真实报错给你对照排查,每条都给出定位方法和修复动作。
401 Unauthorized。这是最常见的。原因通常是 Key 没生效、Key 复制时带了空格、或者环境变量没被进程读到。排查顺序:先echo $TAOTOKEN_API_KEY确认变量有值且无多余空白;再用第 4 节的 curl 直接测,绕开框架;如果 curl 通但框架报 401,说明框架读的是另一套配置,检查它有没有自己的 config 文件覆盖了环境变量。修复就是让三件套在同一个地方对齐。
local proxy failed / connection refused。这个报错说明请求根本没发出去,卡在本地网络层。常见原因是 Base URL 写错(比如漏了/api或多了斜杠)、本地有残留的代理环境变量干扰、或者防火墙拦了出站。排查:curl -v https://taotoken.net/api看握手是否成功;检查HTTP_PROXY、HTTPS_PROXY是否被设成了无效值,有就清掉;确认 Base URL 精确等于https://taotoken.net/api。注意这里不要引入任何网络加速工具,问题基本都在配置本身。
reading 'choices' of undefined。这是解析层报错,意思是返回体里没有choices字段。根因通常是请求被网关拦截返回了错误 JSON,或者模型 ID 写错导致返回了非预期结构。排查:把原始返回打印出来看,print(response.text)而不是直接.json()["choices"];确认 Model ID 在控制台列表里存在;检查请求体是不是漏了messages字段。修复后加一层防御:解析前先判断choices是否存在,不存在就抛出带原始返回的错误,方便下次定位。
OAuth / token expired。如果你用的是 Claude Code 或 Codex 这类带 OAuth 的工具,报这个说明它的登录态过期了,和 API Key 是两套机制。修复:重新走一遍工具的登录流程,或者在配置里改用 API Key 模式(填 Base URL + Key + Model ID 三件套)。长程任务建议直接用 Key 模式,避免跑到一半 OAuth 过期。
分支预算耗尽(branch budget exhausted)。这是折叠逻辑自己的报错,说明max_branches用完了。要么调大上限,要么检查是不是有分支没正常return就卡住了。后者更常见——分支超时没返回,导致计数只增不减。修复:给每个分支加超时,超时后强制return_to_main一个"证据不足"的结论,让主线程决定重试还是放弃。
折叠后信息丢失。这个不报错,但结果不对。表现是 Agent 最后答不出早期埋的约束。根因是return_schema太宽松,分支返回的摘要没覆盖关键信息。修复:在return_to_main里加约束校验,返回前扫描主线程的关键词,缺失就拒绝折叠。同时把max_summary_tokens适当调大,给结论留足空间。
排查时记住一个原则:先分清是接入问题还是折叠问题。接入问题用 curl 能复现,折叠问题只在多轮任务里出现。分开定位,效率高很多。
6. 把折叠工作记忆接进你的 Agent:从配置到长期运行
到这里,配置、验证、排障都齐了,最后说怎么把它变成长期稳定运行的能力。长程 Agent 的竞争,正在从"模型一次能读多少 token"转向"系统能否让模型在正确的时刻看到正确的信息"。折叠工作记忆就是后者的一个具体实现。
落地时我建议按这个顺序推进。第一步,先用第 2 节的三件套把通道跑通,用第 4 节的 curl 确认。第二步,把第 3 节的策略 JSON 和运行时骨架接进你现有的 Agent 循环,先不追求效果,只确认折叠动作能正常触发和返回。第三步,用第 4 节的多轮验证脚本测记忆保持,把断言跑绿。第四步,再上真实的长任务,观察主线程 token 是否稳定、关键约束是否保留。
几个实战经验值得记一下。主线程预算不要一上来就压到 4000,容易丢信息,从 8000 起步更稳。分支返回的evidence_ref一定要存,长程任务出问题时,能顺着引用回到原始证据,排查成本会低很多。规划模型和执行模型分开路由,长期跑下来费用差别很明显。还有,折叠质量要单独评估,不能只看最终任务成功与否——一个任务成功了但中间丢了关键约束,下次换个任务就会翻车。
如果你想让 Agent 在长程编码或 Agent 场景里持续跑,可以考虑用 Coding Plan 这类面向长期任务的方案,配合折叠策略一起用。需要切换不同模型做规划/执行分离时,模型对话页面能直接对比效果。接入文档里有各框架的完整配置示例,照着填三件套即可。
真正难的部分从来不是创建一个子线程,而是让 Agent 学会生成高质量的返回摘要:足够短以节省上下文,又足够完整以支撑后续决策。这件事没有银弹,只能靠策略约束加持续验证慢慢调。但方向是清楚的——未来的 Agent 不只要会推理和调用工具,还要学会整理自己的工作记忆。把折叠机制接进去,你的长程 Agent 就多了一层可操作、可压缩、可审计的记忆结构,而不是一段无限膨胀的聊天记录。