当前位置: 首页 > news >正文

Context Engineering 实战:别再往 context 里塞东西了

Context Engineering 实战:别再往 context 里塞东西了

为什么 token 塞满反而让 LLM 变蠢?四种核心策略 + Python 代码实现


Agent 跑到第 15 步,突然开始做蠢事。

它把已经检查过的文件又检查了一遍,给出了和第 3 步完全矛盾的结论,还重复调用了两次同一个工具。我以为是模型问题,换了更贵的模型,没用。以为是 prompt 问题,把 system prompt 改得更详细,更没用。

最后发现根本原因:context 里塞了太多东西。

那 15 步里积累的工具调用历史、中间状态、重复输出——把真正重要的信息彻底淹没了。这就是 context rot,一个你在教程里很少听到但在生产环境里天天遇到的问题。

本文说的就是怎么系统性地解决它。


一、Context 不是暂存区,是注意力预算

很多工程师把 context window 当暂存区用——把所有可能有用的信息都塞进去,让模型自己判断哪个重要。这个直觉是错的。

1.1 为什么 context 越长模型越蠢

Transformer 的注意力机制计算的是每个 token 对其他所有 token 的关系,复杂度是 O(n²)。这意味着当 context 变长时,模型需要在更多的 token pair 之间分配有限的注意力。

Chroma 做了一个实测:对 18 个主流 LLM(含 GPT-4.1、Claude 3.7、Gemini 2.5)做了 context 长度对准确率的影响测试。结论:所有模型在 context 增长时性能都会下降,没有例外。GPT-4.1 在 1K token 的 context 里准确率约 95%,放大到 128K 时降到约 60%。

这不是模型的 bug,是注意力机制的数学决定的。

1.2 Lost in the Middle:中间位置最危险

Stanford 的研究发现了一个规律:模型对 context 开头和结尾的信息记忆更准,中间部分最容易丢失。这个现象叫 “lost in the middle”。

实际影响:如果你把最重要的约束或状态放在对话历史的中间,模型在长 context 下大概率会忽视它。

1.3 四种典型的 context 失败模式

Context Poisoning:错误信息进入了 context。最危险的是工具调用返回了错误结果,Agent 把它当作事实用于后续推理,越跑越偏。

Context Distraction:过多的历史信息让 Agent 倾向于重复历史模式,而不是根据当前状态重新推理。你会看到 Agent 在第 10 步"记起"第 2 步的做法并重复,即使当时的状态已经完全不同了。

Context Confusion:塞了太多工具 schema 或文档内容,模型在选工具时开始犹豫,选错率上升。

Context Clash:context 里存在互相矛盾的信息(比如第 3 步的结论和第 9 步的结论相反),模型行为变得不可预测。


二、四种 Context 管理策略

LangChain 的 context engineering 博客里把策略分成四类,我觉得这个框架非常实用:Write、Select、Compress、Isolate。每种策略解决不同类型的问题。

2.1 Write:控制写进 context 的内容质量

System prompt 的 altitude 问题

Anthropic 的工程师用了一个好词:altitude(高度)。太低了——system prompt 列了一堆具体规则,Agent 在规则没覆盖到的边缘场景完全不知道怎么办。太高了——只写了"你是一个有用的助手",Agent 行为过于随意。

工具返回结果的控制

工具调用是 context 膨胀的最大来源。一次 API 调用可能返回 5000 字,但 Agent 真正需要的可能只有 3 个字段。

fromtypingimportAnydefcompress_tool_result(result:Any,tool_name:str)->str:""" 把工具返回结果压缩成 Agent 真正需要的内容 不要把原始 JSON 塞进 context """iftool_name=="search_files":# 只保留文件路径和关键摘要,丢弃原始内容ifisinstance(result,list):return"\n".join([f"-{item['path']}:{item.get('summary',item.get('content','')[:100])}"foriteminresult[:10]# 最多 10 条])iftool_name=="run_code":# 只保留最后 50 行输出,截断中间lines=str(result).split("\n")iflen(lines)>50:returnf"[前{len(lines)-50}行已截断]\n"+"\n".join(lines[-50:])returnstr(result)iftool_name=="read_file":# 大文件只返回前后各 500 字符 + 行数content=str(result)iflen(content)>2000:return(f"[文件共{len(content)}字符]\n"f"[前 500 字符]\n{content[:500]}\n"f"...\n"f"[后 500 字符]\n{content[-500:]}")returncontent# 默认:字符串化并截断returnstr(result)[:2000]``` 这个函数做的事很简单:在工具结果进入 context 之前先过滤一遍。大多数教程里的 Agent 代码直接 `str(tool_result)` 塞进去,这是 context 膨胀最常见的原因。### 2.2 Select:决定什么放进 context不是所有历史对话都有用,不是所有 few-shot 例子都需要。**动态 few-shot 选择器**静态 few-shot 的问题:你放了5个例子,实际上对当前请求有用的可能只有1个,另外4个在白占 token。 ```pythonimportnumpyasnpfromsentence_transformersimportSentenceTransformerfromsklearn.metrics.pairwiseimportcosine_similarityclassDynamicFewShotSelector:""" 根据当前请求动态选择最相关的 few-shot 例子 用 embedding 相似度替代静态选择 """def__init__(self,examples:list[dict],model_name:str="all-MiniLM-L6-v2"):self.examples=examples self.model=SentenceTransformer(model_name)# 预计算所有例子的 embeddingtexts=[ex["input"]forexinexamples]self.embeddings=self.model.encode(texts)defselect(self,query:str,top_k:int=3)->list[dict]:"""选择与 query 最相关的 top_k 个例子"""query_embedding=self.model.encode([query])similarities=cosine_similarity(query_embedding,self.embeddings)[0]# 按相似度排序top_indices=np.argsort(similarities)[::-1][:top_k]return[self.examples[i]foriintop_indices]defformat_for_context(self,query:str,top_k:int=3)->str:"""直接返回格式化好的 few-shot 文本"""selected=self.select(query,top_k)parts=[]forexinselected:parts.append(f"Input:{ex['input']}\nOutput:{ex['output']}")return"\n\n".join(parts)``` 实际上大多数场景用不到复杂的 embedding,BM25 检索已经够用了。关键是不要用静态的 few-shot 列表。### 2.3 Compress:压缩现有 context这是实际工程里用得最多的策略。三种主要手段:-**Summarization**:用 LLM 把对话历史摘要成更短的文本--**Truncation**:直接丢弃最早的消息(最简单,但会丢失重要状态)--**Distillation**:提取关键事实,丢弃推理过程(最精确,成本最高) 关键问题不是选哪种,而是**什么时候触发,压缩到多长**。应该基于 context 使用率触发,而不是每 N 轮触发一次。 ```pythonimportanthropicfromdataclassesimportdataclass@dataclassclassContextConfig:max_tokens:int=100_000# context window 上限compress_threshold:float=0.65# 超过 65% 时触发压缩target_ratio:float=0.40# 压缩后目标是 40%summary_max_tokens:int=400# summary 本身不超过 400 tokenpreserve_last_n:int=6# 最近 N 条消息不压缩classContextManager:""" 生产可用的 Context 管理器 基于 token 使用率自动触发压缩 """def__init__(self,config:ContextConfig=None):self.config=configorContextConfig()self.client=anthropic.Anthropic()self.messages:list[dict]=[]self.current_token_count:int=0defadd_message(self,role:str,content:str)->None:self.messages.append({"role":role,"content":content})# 粗估:每个字符约 0.5 tokenself.current_token_count+=int(len(content)*0.5)# 检查是否需要压缩usage_ratio=self.current_token_count/self.config.max_tokensifusage_ratio>self.config.compress_threshold:self._compress()def_compress(self)->None:"""执行 sliding window summarization"""iflen(self.messages)<=self.config.preserve_last_n:return# 分离出需要压缩的历史和需要保留的最近消息split_point=len(self.messages)-self.config.preserve_last_n to_compress=self.messages[:split_point]to_keep=self.messages[split_point:]# 调用 LLM 生成摘要(用便宜的模型)compress_prompt=("以下是对话历史,请提取关键事实、决策和状态,"f"压缩成不超过{self.config.summary_max_tokens}token 的摘要。""只保留后续推理必需的信息,丢弃推理过程。\n\n")history_text="\n".join([f"{m['role']}:{m['content']}"forminto_compress])response=self.client.messages.create(model="claude-3-5-haiku-20241022",# 用便宜的模型做压缩max_tokens=self.config.summary_max_tokens,messages=[{"role":"user","content":compress_prompt+history_text}])summary=response.content[0].text# 重建 messages:summary + 最近 N 条self.messages=[{"role":"system","content":f"[对话历史摘要]\n{summary}"},*to_keep]# 重新估算 token 数total_content=" ".join([m["content"]forminself.messages])self.current_token_count=int(len(total_content)*0.5)defget_messages(self)->list[dict]:returnself.messages ``` 有几个细节值得注意:1.**用便宜模型做压缩**。`claude-3-5-haiku` 做 summarization 完全够用,不需要用 Opus。2.2.**summary 的 token 上限**要控制严格,否则你会发现压缩完了 summary 比原始内容还长。3.3.**最近 N 条不压缩**。刚发生的对话对当前推理最重要,不要动它。### 2.4 Isolate:给子任务一个干净的 context当 Agent 需要处理相对独立的子任务时,最好给它一个全新的 context,而不是带着父 Agent 的全部历史。 ```pythonimportasyncioimportanthropicclassIsolatedSubAgent:""" 每个子任务运行在独立的 context 中 父 Agent 只传递必要的任务描述,不传递完整历史 """def__init__(self,task_description:str,tools:list=None):self.client=anthropic.Anthropic()self.task=task_description self.tools=toolsor[]# 子 Agent 有自己干净的 contextself.messages:list[dict]=[]asyncdefrun(self,max_steps:int=10)->str:""" 运行子任务,返回结果摘要 子 Agent 的全部历史不会泄漏到父 Agent """self.messages=[{"role":"user","content":self.task}]forstepinrange(max_steps):response=self.client.messages.create(model="claude-opus-4-5",max_tokens=4096,tools=self.tools,messages=self.messages)ifresponse.stop_reason=="end_turn":# 只返回最终结果,不是整个对话历史returnself._extract_result(response)# 处理工具调用...(简化示意)self.messages.append({"role":"assistant","content":response.content})return"子任务达到步数上限"def_extract_result(self,response)->str:"""从响应中提取关键结果,丢弃推理过程"""text_blocks=[b.textforbinresponse.contentifhasattr(b,"text")]return"\n".join(text_blocks)asyncdefrun_parallel_subtasks(parent_context:str,subtasks:list[str])->list[str]:""" 并行运行多个子任务,每个都有独立的 context 父 Agent 的 context 不会传给子任务,只传任务描述 """agents=[IsolatedSubAgent(task_description=f"任务背景:{parent_context}\n\n具体任务:{task}")fortaskinsubtasks]results=awaitasyncio.gather(*[agent.run()foragentinagents])returnlist(results)``` Isolate 策略的核心思想:子任务不需要知道父 Agent 的全部历史,只需要知道它要做什么。把父 Agent 的全部 context 传给子 Agent 是最常见的架构错误之一。---## 三、一个决策树:什么时候用哪种策略理论讲完了,实际工程里你面对的问题是:**现在应该做什么**

当前 context 使用率 > 65%?
├── 否 → 继续,监控
└── 是 → 最近 N 步是否有重复/冗余工具调用?
├── 是 → Write 策略:改进 compress_tool_result,减少单次写入
└── 否 → 任务是否可以分解成独立子任务?
├── 是 → Isolate 策略:用 IsolatedSubAgent
└── 否 → 历史消息是否超过 20 条?
├── 是 → Compress 策略:触发 summarization
└── 否 → Select 策略:减少 few-shot / RAG 召回数量
```
65% 这个阈值不是拍脑袋的——留 35% 的余量是为了给模型的推理和输出留空间。很多工程师等到 90% 才触发压缩,这时候 context rot 已经发生了。


四、生产环境的几个工程坑

坑 1:把压缩 token 当压缩信息

Summary 写得太短会丢失关键状态。一个 Agent 在 30 步任务里可能有很多中间决策(“决定不用方案 A 因为 X”、“发现文件 Y 已存在”),如果 summary 只有 50 字,这些状态全丢了。

经验值:每 10 条对话历史,summary 保留 150-300 token。少于这个,信息损失太大。

坑 2:忘记工具 schema 也占 token

很多教程只统计对话消息的 token,但工具定义本身也会占用大量 context。如果你注册了 20 个工具,每个 schema 200 token,光工具定义就占了 4000 token。

做法:动态注册工具。Agent 在不同阶段只激活当前阶段需要的工具,而不是一次性注册所有工具。

坑 3:sub-agent 的 context 隔离破了

常见错误:父 Agent 把整个self.messages列表直接传给子 Agent。子 Agent 带着父 Agent 几千 token 的历史跑,隔离完全没有意义。

正确做法:只传任务描述+必要的输入数据,任务描述用自然语言写清楚,不要把对话历史序列化进去。

坑 4:Long-term Memory 的写时机错了

很多实现是在每轮对话结束后都往 long-term memory 里写一遍,导致 memory 里全是重复和噪音。

写入时机应该是:有新的、值得记住的状态变化时才写。比如"发现了一个新的 API 端点"、“确认了文件结构”,而不是每次对话都写。


小结

Context Engineering 这个词最近很流行,但它说的不是什么新技术,本质是一种工程习惯:像管理内存一样管理 context

具体到行动:

  1. Write:工具返回结果在进 context 前先过滤,不要裸str(result)
    1. Select:few-shot 用动态选择,不用静态列表;RAG 召回数量保守点
    1. Compress:在 65% 使用率触发,用便宜模型做 summarize,summary 保留 150-400 token
    1. Isolate:子任务用独立 context,只传任务描述不传历史
      最后一点:不要等到 context 爆了再处理。context rot 是渐进的,模型不会突然失效,它会越来越蠢,而你可能以为是别的问题。主动监控 context 健康度,是 LLM 应用生产化里最容易被忽视的一个环节。

参考资料

  • Anthropic: Effective context engineering for AI agents
    • LangChain: Context Engineering for Agents
    • Weaviate: Context Engineering
    • JetBrains Research: Efficient Context Management (NeurIPS 2025)
    • Karpathy, A. (2025). “Context Engineering is the new Prompt Engineering.” [Twitter/X]
http://www.jsqmd.com/news/858678/

相关文章:

  • 专访驭势科技CEO吴甘沙:我们选择在巨头射程之外的战场竞争
  • GEO 优化源码(多行业适用)
  • 图片怎么转PDF?2026手机在线方法大全 - 软件小管家
  • QuickLook.Plugin.FolderViewer:如何用空格键实现Windows文件夹零秒预览?
  • 多iTOF相机联合标定:原理、方法与实践
  • 北京雅思培训机构大揭秘:哪家才是你的“上分”神器? - 品牌测评鉴赏家
  • 2026南京旅游亲子游必去博物馆盘点:遛娃看展两不误的宝藏场馆 - 深度智识库
  • 哪家管道清淤检测做得好?优质知名品牌精选 - 品牌推荐大师
  • 降AI率天花板!AI率92%暴降至5%!实测10款降AIGC工具!10款工具深度解析!
  • ISO14229-1 DTC核心参数深度解析|全网独家拆解,FDC故障检测计数器篇|量产实战版|深入剖析原理、防抖机制与ECU代码落地,助力刷写故障精准排查、提升诊断可靠性
  • 2026企业微信怎么开通?相关问题咨询电话汇总 - 品牌2025
  • 黑苹果终极指南:如何用OpCore-Simplify在10分钟内完成OpenCore自动化配置
  • 毛发质感始终“塑料感”?5类生物毛发纹理映射表,含真实显微扫描数据+对应--s、--style、--stylize配置
  • 5分钟快速解锁VMware:在普通电脑上运行macOS的终极指南
  • 2026年长春搬家公司怎么选?老兵搬家与头部品牌深度横评避坑指南 - 精选优质企业推荐官
  • Google Gemini Omni:突破物理世界理解边界的原生多模态世界模型
  • 长春搬家公司选择困难?5大品牌深度对比与企业级搬迁避坑指南 - 精选优质企业推荐官
  • 2026 邯郸装修公司综合实力榜单|靠谱一站式装修深度测评 - GEO排行榜
  • 2026上海保鲜冷库安装公司推荐:水果蔬菜肉类一站式服务 - 品牌2025
  • Python初学者项目练习12--找出年龄最大者
  • GMR 工程实践笔记:把自己的机器人接入动作重定向流程
  • PhanTap硬件探针原理与红队流量感知实战
  • 红黑树详解
  • 北京雅思写作提分机构排名!2026靠谱榜单出炉 - 品牌测评鉴赏家
  • 2026年5月最新 环保监测十大口碑品牌排名 - 水质仪表品牌排行榜
  • 2026上海健身教练培训机构推荐:5家高性价比靠谱机构全解析 - 品牌2025
  • 2026年常州热缩管源头厂家深度横评:从新能源防护到军工定制化解决方案全景对标 - 精选优质企业推荐官
  • 一句话看懂 vibing-steampunk
  • 如何快速管理Android设备:Fastboot Enhance完整指南
  • 一张图看懂中画幅vs全画幅AI渲染差异:2024 Q2 Midjourney V6.1 GPU显存占用热力图实测对比