【Bug已解决】This model‘s maximum context length is X tokens. However, you requested Y tokens 解决方案
【Bug已解决】This models maximum context length is X tokens. However, you requested Y tokens 解决方案1. 问题描述在自己搭建 Agent Harness、调用大模型 API 时随着对话轮次增多、工具调用结果不断累积很多人会在某一次请求突然收到这样的报错{ error: { message: This models maximum context length is 131072 tokens. However, you requested 296261 tokens (296261 in the messages, 0 in the completion). Please reduce the length of the messages., type: invalid_request_error, code: context_length_exceeded } }Anthropic 系接口的报错文案略有不同Error: prompt is too long: 215043 tokens 200000 maximum用 LangChain/LangGraph 构建的自定义 Harness 里这类报错通常会被封装成一个更通用的异常抛出来langchain_core.exceptions.OutputParserException: ... openai.BadRequestError: Error code: 400 - {error: {message: This models maximum context length is 128000 tokens...这个问题在自己动手搭建 Agent Harness而不是用现成的 Claude Code/Cursor 这类成品工具、长时间运行的多轮对话没有做上下文裁剪、工具调用结果比如整个文件内容、大段日志被原样塞进消息历史这几种场景下特别常见。很多人第一反应是是不是这次问题太复杂了反复精简当前这一句提问的措辞但问题往往根本不在最后一句话上——真正超限的是整个对话历史的累积长度而不是某一句话本身。2. 原因分析大模型 API 的每一次请求实际发送给模型的不是你这一句话而是完整的消息历史Messages加上模型即将生成的补全内容Completion两者之和不能超过模型设定的上下文窗口上限。这正是这类报错里(215043 in the messages, 0 in the completion)这种细分统计的意义——它明确告诉你超限的部分几乎全部来自于历史消息的累积而不是你当前这次请求本身要求生成的内容。在手工搭建 Harness 的场景下上下文迅速膨胀的常见原因可以归纳为原因分类具体表现多轮对话未做裁剪每一轮都把完整的历史对话原样拼接随着轮次增加线性增长工具调用结果未经压缩读文件、执行命令、搜索结果等工具输出被原样整段塞入消息历史系统提示词过长项目级配置文件如 AGENTS.md、工具定义 Schema 本身占用了大量固定的基础开销重复的历史工具调用Agent 反复调用同一个工具、结果不断累积追加而没有去重或替换旧结果多 Agent 协作场景的上下文传递主 Agent 把子 Agent 的完整中间过程原样传递下去而不是只传递最终结论用一张流程图梳理触发链路每一轮对话结束 ↓ 新增内容用户输入 模型回复 工具调用与结果追加进消息历史 ↓ 下一轮请求把累积的完整消息历史一起发送给模型 ↓ 消息历史总token数 预期生成长度 是否超过模型上下文窗口上限 ├─ 未超过 → 正常处理 └─ 超过 → 返回 context_length_exceeded / prompt is too long这正是六层 Harness 架构里上下文管理层要解决的核心问题——这一层的设计质量直接决定了一个 Agent 能不能支撑长任务、多轮对话而不会在某个时刻突然爆窗。3. 解决方案方案一对历史对话做滑动窗口裁剪最基础最先做最简单直接的方式是只保留最近 N 轮对话超出窗口的历史直接丢弃def truncate_messages(messages, max_turns10): system_msgs [m for m in messages if m[role] system] other_msgs [m for m in messages if m[role] ! system] return system_msgs other_msgs[-max_turns * 2:] # 每轮约含userassistant两条这种方式简单粗暴但也有明显的代价——较早的上下文信息会被完全丢弃如果任务依赖早期的关键决策可能会导致 Agent 失忆。适合对历史依赖不强的短任务场景。方案二对早期对话做摘要压缩保留关键信息更推荐比直接丢弃更优雅的做法是当上下文接近窗口上限时用模型自己把早期的对话内容压缩成一段摘要替换掉原始的详细记录def compress_history(messages, model_client, threshold_tokens100000): total_tokens count_tokens(messages) if total_tokens threshold_tokens: return messages old_part messages[:len(messages)//2] recent_part messages[len(messages)//2:] summary_prompt 请将以下对话历史压缩成一段简洁摘要保留关键决策和结论\n format_messages(old_part) summary model_client.generate(summary_prompt) return [{role: system, content: f此前对话摘要{summary}}] recent_part这种方式能在大幅降低 token 消耗的同时尽量保留任务推进所需的关键上下文是目前业内做长会话 Agent 最常见的处理方式之一。方案三对工具调用结果做裁剪而不是整段塞入历史很多超限问题的根源并不是对话本身而是某一次工具调用返回了一大段内容比如读取了一个几千行的日志文件、搜索返回了几十条结果被原样塞进了消息历史。针对这种情况应该在工具调用结果返回给模型之前先做处理def truncate_tool_result(result: str, max_chars3000): if len(result) max_chars: return result return result[:max_chars] f\n...(内容过长已截断完整内容共{len(result)}字符)更进一步可以设计摘要型工具——比如读文件工具默认只返回文件的关键片段或结构化摘要而不是整个文件内容需要细节时再让 Agent 显式请求特定的行范围。方案四动态调整历史保留策略区分结论和过程对于多 Agent 协作或者 ReAct 循环这类场景一个更精细的策略是把中间推理过程和最终结论分开对待。中间的思考步骤、失败的尝试、被放弃的方案在任务推进到后续阶段后往往不再需要完整保留只需要保留最终采纳的结论def keep_only_conclusions(messages): filtered [] for m in messages: if m.get(is_intermediate_reasoning): continue # 跳过标记为中间过程的内容 filtered.append(m) return filtered这需要在设计 Harness 时提前给消息打上是否为中间过程这样的标记属于更系统性的架构设计但长期来看能大幅提升上下文利用效率。方案五切换到更大上下文窗口的模型作为兜底如果任务本身确实需要依赖大量历史信息比如超长代码库的分析任务前面几种压缩类方案可能都无法完全避免信息损失这时候可以考虑切换到上下文窗口更大的模型版本作为兜底方案try: response client.chat.completions.create(modelgpt-4o, messagesmessages) except openai.BadRequestError as e: if context_length_exceeded in str(e): response client.chat.completions.create(modelgpt-4o-128k, messagesmessages)⚠️风险提示更大上下文窗口的模型通常意味着更高的调用成本且塞得进去不代表用得好——过长的上下文依然会面临Lost in the middle效应模型对中间部分信息的利用效率会下降。这个方案应该作为补充手段而不是替代前面几种精细化管理的根本方案。4. 各方案对比总结方案适用场景推荐指数滑动窗口裁剪短任务对早期历史依赖不强⭐⭐⭐摘要压缩长会话需要保留关键决策脉络⭐⭐⭐⭐⭐工具结果裁剪工具调用返回内容过长导致超限⭐⭐⭐⭐⭐区分结论与过程多Agent协作、ReAct复杂循环场景⭐⭐⭐⭐切换更大窗口模型前几种方案仍不够用时的兜底手段⭐⭐⭐5. 常见问题 FAQ5.1 为什么明明当前这一句话很短也会报这个错这是最常见的误解。报错文案里的 token 数统计的是整个消息历史的累积长度不是你当前这一句话的长度。排查时应该去看历史消息总量而不是纠结于最后一句提问是不是写得太长了。可以在发请求前先用 tokenizer 统计一下完整消息体的总 token 数直观确认问题出在哪个环节。5.2 只降低 max_tokens生成长度上限参数就够了吗通常不够。max_tokens控制的是模型这次生成回复的长度上限而超限报错的主要来源往往是输入消息历史本身的长度降低生成长度上限对已经超限的输入部分没有任何缓解作用。必须针对输入端消息历史、工具结果做裁剪或压缩才能真正解决问题。5.3 用 LangGraph 构建的 AgentState 里累积的历史要怎么统一管理LangGraph 里通常会把对话历史放在 State 的某个字段里比如messages可以在图的某个节点专门负责裁剪逻辑在每次进入下一步之前检查并处理超限的历史def trim_history_node(state): trimmed compress_history(state[messages], model_client) return {messages: trimmed}把这个节点插入到主循环里作为常规的一个处理环节而不是等到真正报错了才手忙脚乱地处理。5.4 多 Agent 系统里子 Agent 的完整过程会不会把主 Agent 的上下文撑爆会这是多 Agent 协作场景里非常典型的超限来源。正确的做法是子 Agent 完成任务后只把结构化的最终结论返回给主 Agent而不是把子 Agent 内部完整的思考过程、工具调用细节全部传递上去。可以约定一个统一的返回格式{ summary: 已完成对XX模块的代码审查发现3个潜在问题, details_ref: sub_agent_log_20260703_001 # 详细日志单独存储需要时再检索 }5.5 这个问题在生产环境和本地开发时表现会不一样吗会。本地开发时任务通常比较简单、对话轮次少很难触发这个问题但生产环境里用户可能会进行长时间、多轮次的连续对话累积的上下文量会远超本地测试场景。建议在压测阶段专门模拟长对话场景提前验证 Harness 的上下文管理策略是否足够健壮而不是等到线上用户反馈才发现问题。5.6 团队协作中如何避免不同开发者各自写各自的裁剪逻辑导致行为不一致建议把上下文管理逻辑封装成团队统一的公共模块比如一个ContextManager类所有 Agent Harness 项目统一调用这个模块而不是每个人各自实现一套裁剪/压缩策略。这样既能保证行为一致性也方便后续统一优化和维护。5.7 排查清单速查表□ 1. 先用tokenizer统计完整消息历史的总token数确认超限的具体来源 □ 2. 检查是否有单次工具调用返回了异常大的内容大文件/长日志/大量搜索结果 □ 3. 检查对话轮次是否已经积累过多是否需要引入滑动窗口或摘要压缩 □ 4. 多Agent场景检查子Agent是否把完整中间过程传递给了主Agent □ 5. 确认降低max_tokens生成长度无法解决输入侧超限问题需处理输入端 □ 6. 评估是否需要切换更大上下文窗口的模型作为兜底 □ 7. 在压测阶段模拟长对话场景提前验证上下文管理策略的健壮性 □ 8. 将裁剪/压缩逻辑封装为团队统一模块避免各自为战6. 总结context_length_exceeded类报错的本质是消息历史的累积长度超过了模型上下文窗口上限而不是某一句话写得太长。这正是 Harness Engineering 六层架构里上下文管理层要重点解决的问题核心处理思路可以浓缩成三句话超限的通常是历史累积不是当前输入——排查时先统计完整消息历史的 token 数而不是纠结最后一句话摘要压缩优于粗暴裁剪——直接丢弃早期历史会造成失忆用模型自己做摘要能更好地保留关键信息工具调用结果是常被忽视的超限大户——给工具结果设计合理的裁剪/摘要策略往往比优化对话轮次管理更立竿见影。最佳实践建议把上下文管理作为 Agent Harness 设计阶段就要考虑的核心模块而不是等线上用户报错了才临时补救——这也是为什么上下文管理层会被列为六层架构里最基础的一层。

相关新闻