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

news2026/5/21 15:16:42
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 Clashcontext 里存在互相矛盾的信息比如第 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_namesearch_files:# 只保留文件路径和关键摘要丢弃原始内容ifisinstance(result,list):return\n.join([f-{item[path]}:{item.get(summary,item.get(content,)[:100])}foriteminresult[:10]# 最多 10 条])iftool_namerun_code:# 只保留最后 50 行输出截断中间linesstr(result).split(\n)iflen(lines)50:returnf[前{len(lines)-50}行已截断]\n\n.join(lines[-50:])returnstr(result)iftool_nameread_file:# 大文件只返回前后各 500 字符 行数contentstr(result)iflen(content)2000:return(f[文件共{len(content)}字符]\nf[前 500 字符]\n{content[:500]}\nf...\nf[后 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:strall-MiniLM-L6-v2):self.examplesexamples self.modelSentenceTransformer(model_name)# 预计算所有例子的 embeddingtexts[ex[input]forexinexamples]self.embeddingsself.model.encode(texts)defselect(self,query:str,top_k:int3)-list[dict]:选择与 query 最相关的 top_k 个例子query_embeddingself.model.encode([query])similaritiescosine_similarity(query_embedding,self.embeddings)[0]# 按相似度排序top_indicesnp.argsort(similarities)[::-1][:top_k]return[self.examples[i]foriintop_indices]defformat_for_context(self,query:str,top_k:int3)-str:直接返回格式化好的 few-shot 文本selectedself.select(query,top_k)parts[]forexinselected:parts.append(fInput:{ex[input]}\nOutput:{ex[output]})return\n\n.join(parts) 实际上大多数场景用不到复杂的 embeddingBM25 检索已经够用了。关键是不要用静态的 few-shot 列表。### 2.3 Compress压缩现有 context这是实际工程里用得最多的策略。三种主要手段-**Summarization**用 LLM 把对话历史摘要成更短的文本--**Truncation**直接丢弃最早的消息最简单但会丢失重要状态--**Distillation**提取关键事实丢弃推理过程最精确成本最高 关键问题不是选哪种而是**什么时候触发压缩到多长**。应该基于 context 使用率触发而不是每 N 轮触发一次。 pythonimportanthropicfromdataclassesimportdataclassdataclassclassContextConfig:max_tokens:int100_000# context window 上限compress_threshold:float0.65# 超过 65% 时触发压缩target_ratio:float0.40# 压缩后目标是 40%summary_max_tokens:int400# summary 本身不超过 400 tokenpreserve_last_n:int6# 最近 N 条消息不压缩classContextManager: 生产可用的 Context 管理器 基于 token 使用率自动触发压缩 def__init__(self,config:ContextConfigNone):self.configconfigorContextConfig()self.clientanthropic.Anthropic()self.messages:list[dict][]self.current_token_count:int0defadd_message(self,role:str,content:str)-None:self.messages.append({role:role,content:content})# 粗估每个字符约 0.5 tokenself.current_token_countint(len(content)*0.5)# 检查是否需要压缩usage_ratioself.current_token_count/self.config.max_tokensifusage_ratioself.config.compress_threshold:self._compress()def_compress(self)-None:执行 sliding window summarizationiflen(self.messages)self.config.preserve_last_n:return# 分离出需要压缩的历史和需要保留的最近消息split_pointlen(self.messages)-self.config.preserve_last_n to_compressself.messages[:split_point]to_keepself.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])responseself.client.messages.create(modelclaude-3-5-haiku-20241022,# 用便宜的模型做压缩max_tokensself.config.summary_max_tokens,messages[{role:user,content:compress_prompthistory_text}])summaryresponse.content[0].text# 重建 messagessummary 最近 N 条self.messages[{role:system,content:f[对话历史摘要]\n{summary}},*to_keep]# 重新估算 token 数total_content .join([m[content]forminself.messages])self.current_token_countint(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:listNone):self.clientanthropic.Anthropic()self.tasktask_description self.toolstoolsor[]# 子 Agent 有自己干净的 contextself.messages:list[dict][]asyncdefrun(self,max_steps:int10)-str: 运行子任务返回结果摘要 子 Agent 的全部历史不会泄漏到父 Agent self.messages[{role:user,content:self.task}]forstepinrange(max_steps):responseself.client.messages.create(modelclaude-opus-4-5,max_tokens4096,toolsself.tools,messagesself.messages)ifresponse.stop_reasonend_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_descriptionf任务背景{parent_context}\n\n具体任务{task})fortaskinsubtasks]resultsawaitasyncio.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 在不同阶段只激活当前阶段需要的工具而不是一次性注册所有工具。坑 3sub-agent 的 context 隔离破了常见错误父 Agent 把整个self.messages列表直接传给子 Agent。子 Agent 带着父 Agent 几千 token 的历史跑隔离完全没有意义。正确做法只传任务描述必要的输入数据任务描述用自然语言写清楚不要把对话历史序列化进去。坑 4Long-term Memory 的写时机错了很多实现是在每轮对话结束后都往 long-term memory 里写一遍导致 memory 里全是重复和噪音。写入时机应该是有新的、值得记住的状态变化时才写。比如发现了一个新的 API 端点、“确认了文件结构”而不是每次对话都写。小结Context Engineering 这个词最近很流行但它说的不是什么新技术本质是一种工程习惯像管理内存一样管理 context。具体到行动Write工具返回结果在进 context 前先过滤不要裸str(result)Selectfew-shot 用动态选择不用静态列表RAG 召回数量保守点Compress在 65% 使用率触发用便宜模型做 summarizesummary 保留 150-400 tokenIsolate子任务用独立 context只传任务描述不传历史最后一点不要等到 context 爆了再处理。context rot 是渐进的模型不会突然失效它会越来越蠢而你可能以为是别的问题。主动监控 context 健康度是 LLM 应用生产化里最容易被忽视的一个环节。参考资料Anthropic: Effective context engineering for AI agentsLangChain: Context Engineering for AgentsWeaviate: Context EngineeringJetBrains Research: Efficient Context Management (NeurIPS 2025)Karpathy, A. (2025). “Context Engineering is the new Prompt Engineering.” [Twitter/X]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2631875.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…