
1. 从一次 Agent 爆窗说起上下文窗口管理到底难在哪AI Agent 跑长对话时突然报context_length_exceeded或者明明没超限却开始胡言乱语这类问题几乎每个做 Harness Engineering 的人都遇到过。上下文窗口管理要解决的核心就一句话在有限的 token 预算里把最该让模型看到的信息放进去把不该占位的清出去。它适合正在搭多轮对话 Agent、工具调用 Agent、或者需要多模型切换的开发者。我试过最直接的做法——把历史对话全塞进去结果第三轮就爆了。后来拆开看问题出在三个地方一是历史消息无限增长二是工具返回的 JSON 动辄几千 token三是不同模型的窗口大小和计费方式不一样切换时预算没跟着变。举个具体场景一个客服 Agent 接了 20 轮对话每轮用户输入加助手回复平均 300 token历史就 6000 token中间调了 5 次订单查询工具每次返回 800 token 的 JSON又是 4000 token再加上系统提示词和当前轮输入轻松破万。如果用的是 8K 窗口的模型第三轮就崩了。上下文预算分配要解决的就是这个给系统提示词留多少、给历史留多少、给工具输出留多少、给当前轮留多少。历史压缩则是把旧对话摘要成短文本工具输出裁剪是把 JSON 里真正有用的字段抽出来。这三条主线配合起来才能让 Agent 在长对话里稳定跑下去。多模型切换又加了一层复杂度。Claude 的 200K 窗口和 GPT-4o 的 128K 窗口预算表不能共用不同模型的 tokenizer 对中文的切分也不一样同样一段中文Claude 可能算 1.0 token/字GPT-4o 算 1.3 token/字。如果 Harness 里写死了预算数字换模型就会出问题。所以工程化落地的关键不是找一个“万能窗口”而是建一套可配置的预算管理机制模型元数据里带窗口大小和 tokenizer 类型Harness 根据当前模型动态算预算超限时按优先级裁剪。下面就从接入配置开始一步步把这套机制搭起来。2. 用 TaoToken 统一 Key 接入多模型前置准备与配置多模型切换的第一个坑是 Key 管理。每个模型厂商一套 Key、一套计费、一套 SDKHarness 里要维护多套凭证和调用逻辑改起来很烦。TaoToken 的做法是提供一个统一的 API 通道用同一个 Key 和同一个 Base URL 访问不同模型Harness 只需要改 model 字段就能切换。前置准备很简单注册后拿到 API Key确认 Base URL 是https://taotoken.net/api。这个地址是 OpenAI 兼容格式所以任何支持自定义 Base URL 的客户端都能接。如果你用的是 Claude Code 这类工具它本身走 Anthropic 协议TaoToken 也提供了对应的接入点。配置的核心是三件套Base URL、API Key、Model ID。这三样在 Harness 的配置文件里写清楚后面切换模型只改 Model ID。先看一个通用的环境变量配置适合 Python 或 Node 项目export TAOTOKEN_BASE_URLhttps://taotoken.net/api export TAOTOKEN_API_KEYsk-你的Key export TAOTOKEN_DEFAULT_MODELclaude-3-5-sonnet-20241022如果你用的是 Claude Code配置方式不太一样。Claude Code 读的是 Anthropic 的协议需要在 settings 里指定 Base URL 和 Key。可以创建一个~/.claude/settings.json{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的Key, ANTHROPIC_MODEL: claude-3-5-sonnet-20241022 } }注意这里 Base URL 不要带末尾斜杠Model ID 要写完整版本号。Claude Code 启动时会读这个文件如果 Key 不对会报 401如果 Base URL 写错会报连接失败。对于 Cline 或 Roo Code 这类 VS Code 插件配置在插件的设置面板里API Provider 选 OpenAI CompatibleBase URL 填https://taotoken.net/apiAPI Key 填你的 KeyModel ID 填你要用的模型。Cline 还支持 MCP如果你要用 MCP 工具在 MCP 配置里同样走这个 Base URL。Codex 的配置在~/.codex/auth.json和~/.codex/config.toml里。auth.json 存 Key{ OPENAI_API_KEY: sk-你的Key }config.toml 里指定 Base URL 和模型model gpt-4o model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key OPENAI_API_KEY这样 Codex 启动时就会走 TaoToken 的通道。三件套里 Base URL 和 Key 是固定的Model ID 按需改。配置好之后建议先用一个最简单的请求验证通道是否通。可以用 curlcurl https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -d { model: claude-3-5-sonnet-20241022, messages: [{role: user, content: 回复 OK}], max_tokens: 10 }如果返回里有choices字段和内容说明通道正常。如果返回 401检查 Key 有没有复制错如果返回 404检查 Base URL 是不是写成了https://taotoken.net/api/v1有些客户端会自动加/v1所以 Base URL 只写到/api就行。这一步做完Harness 就有了统一的多模型入口。接下来要在这个入口上叠加上下文预算管理。3. 可复制的 Harness 配置上下文预算表与压缩策略上下文预算管理的核心是一张预算表把模型的总窗口切成几块每块设上限超了就触发对应的裁剪策略。这张表要跟模型绑定切换模型时自动换表。先定义模型元数据。在 Harness 里建一个models.yamlmodels: claude-3-5-sonnet-20241022: provider: taotoken base_url: https://taotoken.net/api context_window: 200000 max_output: 8192 tokenizer: claude budget: system_prompt: 4000 history: 60000 tool_output: 40000 current_turn: 8000 reserve: 2000 gpt-4o: provider: taotoken base_url: https://taotoken.net/api context_window: 128000 max_output: 4096 tokenizer: o200k_base budget: system_prompt: 3000 history: 40000 tool_output: 25000 current_turn: 6000 reserve: 1500这张表里context_window是模型物理上限budget是各块的软上限。reserve是留给输出和意外情况的缓冲。各块之和加 reserve 要小于 context_window 减 max_output否则还是会爆。预算分配的原则是按信息密度和重要性排。系统提示词最重要但通常固定给一个够用的值就行历史对话信息密度低给大块但要做压缩工具输出信息密度高但冗余多给中等块但要做字段裁剪当前轮必须完整保留给足。历史压缩的策略有三种按对话轮数触发第一种是滑动窗口只保留最近 N 轮。简单但会丢早期信息适合短任务 Agent。第二种是摘要压缩把超过 N 轮的旧对话用模型摘要成一段短文本。摘要提示词可以这样写SUMMARY_PROMPT 把以下对话压缩成不超过 200 字的摘要保留用户的核心诉求、已确认的事实、未解决的问题。不要加评论。 对话 {history} 摘要摘要后的文本替换掉原始历史token 数能降到原来的 10% 到 20%。第三种是结构化抽取把历史里的关键实体抽成 JSON比如用户 ID、订单号、偏好标签。这种适合业务 Agent抽取后可以精确检索。工具输出裁剪更直接。工具返回的 JSON 往往字段很多但 Agent 只需要其中几个。在 Harness 里给每个工具配一个output_schema只保留 schema 里定义的字段TOOL_OUTPUT_SCHEMA { query_order: [order_id, status, amount, created_at], search_product: [product_id, name, price, stock], }裁剪函数遍历 JSON只留白名单字段其余丢掉。一个 800 token 的订单 JSON 裁剪后可能只剩 80 token。超限回退策略是最后一道防线。当所有块都满了还是超限按优先级从低到高丢弃先丢最旧的工具输出再丢最旧的历史摘要再丢低优先级的系统提示词片段。回退逻辑要记日志方便排查为什么某轮对话信息不全。把预算表和压缩策略写进 Harness 的配置文件切换模型时只改model字段预算自动跟着换。这样多模型切换就不会因为窗口大小不同而崩。4. 验证请求跑通长对话压缩与超限回退配置写好后要验证两件事压缩是否生效回退是否按预期触发。用一个模拟长对话的脚本跑一遍最直接。先构造一个 30 轮的对话历史每轮 200 token 左右总共 6000 token。再模拟 3 次工具调用每次返回 500 token 的 JSON。总输入约 7500 token。用 gpt-4o 的预算表history 上限 40000不会触发压缩。把轮数加到 200 轮历史 40000 token就会触发摘要压缩。验证脚本的核心逻辑import tiktoken def count_tokens(text, modelgpt-4o): enc tiktoken.encoding_for_model(model) return len(enc.encode(text)) def build_context(history, tools, model_config): budget model_config[budget] # 系统提示词 system SYSTEM_PROMPT # 历史压缩 history_tokens sum(count_tokens(m[content]) for m in history) if history_tokens budget[history]: history compress_history(history, budget[history]) # 工具输出裁剪 tools [trim_tool_output(t) for t in tools] # 组装 messages [{role: system, content: system}] history tools total sum(count_tokens(m[content]) for m in messages) if total model_config[context_window] - model_config[max_output]: messages fallback_trim(messages, model_config) return messages跑一遍打印每步的 token 数。压缩前历史 40000 token压缩后应该降到 8000 以内。工具输出裁剪前 1500 token裁剪后 300 以内。总输入控制在预算内。然后发一个真实请求from openai import OpenAI client OpenAI( base_urlhttps://taotoken.net/api, api_keyos.environ[TAOTOKEN_API_KEY], ) resp client.chat.completions.create( modelgpt-4o, messagesbuild_context(history, tools, model_config), max_tokens1024, ) print(resp.choices[0].message.content)如果返回正常说明压缩后的上下文模型能理解。如果返回内容缺失关键信息说明压缩太狠要调摘要提示词或提高 history 预算。再验证超限回退。手动把 history 预算调到 1000强制触发回退。观察日志里丢弃了哪些块最终请求是否成功。成功的话回退逻辑就通了。多模型切换验证把 model 从 gpt-4o 改成 claude-3-5-sonnet-20241022预算表自动换成 200K 窗口的版本同样的对话历史不会触发压缩。再改回 gpt-4o压缩重新生效。这说明预算表跟模型绑定是有效的。验证时要注意 tokenizer 差异。同样一段中文用 tiktoken 的 o200k_base 和 Claude 的 tokenizer 算出来可能差 20%。如果 Harness 里用统一的 tokenizer 估算切换模型时预算要留足余量否则会误判。5. 常见报错排查401、local proxy failed、reading choices、OAuth接入和验证过程中会碰到几类典型报错逐个说排查方法。401 Unauthorized最常见。先检查 Key 有没有复制完整有没有多余空格。然后确认 Base URL 是不是https://taotoken.net/api不要写成https://taotoken.net/api/v1或带末尾斜杠。如果用的是 Claude Code检查~/.claude/settings.json里ANTHROPIC_API_KEY字段名对不对。如果 Key 是从环境变量读的确认环境变量在当前 shell 里生效了echo $TAOTOKEN_API_KEY能看到值。local proxy failed这个报错通常出现在客户端配置了本地代理但代理没启动或者 Base URL 指向了本地地址。检查客户端设置里有没有开代理开关关掉。确认 Base URL 是https://taotoken.net/api不是http://localhost:xxxx。如果用了系统代理确认代理规则没有拦截这个域名。reading choices 报错一般是响应格式不符合预期。可能原因有三个一是 Base URL 写成了不带/v1的路径客户端自动拼/v1/chat/completions后 404二是 Model ID 写错了服务端返回错误对象而不是 choices 数组三是请求体里messages格式不对比如 role 用了human而不是user。排查时先用 curl 发一个最小请求看返回的 JSON 结构。如果 curl 正常但客户端报错就是客户端配置问题。OAuth 相关报错Claude Code 或 Codex 这类工具有时会走 OAuth 流程如果配置了 API Key 但工具还在尝试 OAuth会报冲突。解决办法是在 settings 里显式指定用 API Key 模式关掉 OAuth。Claude Code 里确认ANTHROPIC_API_KEY有值Codex 里确认auth.json的OPENAI_API_KEY有值并且 config.toml 里env_key指向正确的环境变量名。context_length_exceeded这个不是接入问题是预算没管好。检查预算表各块之和加 reserve 是否小于context_window - max_output。检查 tokenizer 估算是否偏小中文场景建议按 1.5 token/字估算留余量。检查工具输出裁剪是否生效有没有漏掉某个工具。模型返回空内容可能是 max_tokens 设太小或者压缩后上下文丢了关键信息。先把 max_tokens 调大再把 history 预算调大看是否恢复。如果还不行检查摘要提示词是不是把关键实体摘要掉了。排查时养成先 curl 后客户端的习惯。curl 通了说明通道没问题问题在客户端配置curl 不通说明 Key 或 Base URL 有问题。这样能快速定位。6. 把上下文管理做成 Harness 的常驻能力上下文窗口管理不是一次性配置而是要嵌进 Harness 的每次请求里。建议把预算计算、历史压缩、工具裁剪、超限回退封装成一个ContextManager类在每次调模型前调用一次。这样无论上层业务怎么变上下文始终在预算内。多模型切换的场景下把模型元数据和预算表放在配置中心Harness 启动时加载运行时按当前模型查表。新增模型只需要加一条配置不用改代码。监控也要跟上。记录每轮请求的各块 token 数、压缩触发次数、回退触发次数、最终是否成功。这些指标能帮你发现预算表哪里设得不合理。比如压缩触发太频繁说明 history 预算偏小回退触发太多说明总预算偏紧。最后压缩策略要按业务调。客服 Agent 的历史摘要要保留订单号和用户诉求代码 Agent 的历史摘要要保留文件路径和函数名。通用的摘要提示词效果有限针对业务定制的抽取规则更可靠。这套机制跑顺之后Agent 在长对话里的稳定性会明显提升token 成本也能降下来。多模型切换时只要配置对预算自动适配不用每次手动调窗口。