
GSD别让 Agent 在脏上下文里写代码说实话Agent写代码翻车这件事我一开始是没有认真对待的。直到我连续三天看着同一个Coding Agent在我自己的项目里反复横跳一会儿觉得这是个Python项目一会儿又开始改package.json刚跟它确认过用户需求是“导出报表”它转头就在代码里写了一个导出PDF的逻辑。我当时的第一反应是“模型不行”第二反应是“Prompt没写好”直到我把Agent启动时携带的上下文全部导出来翻了一遍才意识到问题根本不是模型而是它在进入工作状态之前就已经泡在了一团浆糊里。这个项目我给它起名叫GSD——最直白的理解就是“Get Stuff Done”但实际上它是“帮Agent把代码写好”这件事本身的一场底层自省。GSD要解决的核心问题不是让Agent变聪明而是别再让它在脏上下文里写代码。脏上下文是什么就是Agent每次调用时看到的那些系统提示、历史记录、检索资料、工具回写日志拼接而成的那一大堆文本。它们看似无害实际上决定了Agent的所有后续行为。这个内容适合所有正在用AI辅助写代码的人看尤其是那些已经发现“Agent偶尔能用、但经常不可控”的人以及正在给自己搭建Agent工作流的开发者。本文我会把GSD的核心设计思路、上下文污染的几个主要来源、我做的一次完整清洗实操还有排查过程中踩过的坑全部摊开来讲。1. 脏上下文到底“脏”在哪先理解Agent眼里看到的到底是什么1.1 Agent每一次写代码之前都被迫“读”了一遍垃圾很多人在调Agent时以为它只是根据你最新的那条指令去写代码其实不是。Agent在真正生成代码之前它的模型上下文里已经被打包了一堆东西系统提示词、任务目标、历史对话摘要、工具返回结果、目录结构甚至上一次会话遗留下来的总结。这些东西合起来就是Agent的“工作台面”。如果这个台面上堆满了过期的需求、重叠的指令和互相打架的资料Agent写得越认真错得越离谱。我用GSD做性能对比的时候有一个特别典型的案例同一个代码生成任务在干净上下文里的通过率接近八成一旦把旧任务的工作日志、三条定向检索摘要、还有一长串工具调用历史都塞进去通过率直接跌破两成。而且最神奇的是Agent自己并不觉得有问题它会很自信地把错误方案写出来甚至还会在注释里写“根据上下文中的要求”。它对自己看到的垃圾没有任何判断力这就是脏上下文的危险之处。1.2 三种最常见的“脏形态”冗余、冲突、过期我把脏上下文归纳成三种形态每一种的成因和处理方式都不一样。第一种是冗余。最典型的就是把整个历史对话从头到尾都塞进上下文或者系统提示里同时保留了好几套互相对齐但已经不需要的旧版规则。冗余的坏处不只是浪费Token而是它会把模型对任务目标的注意力给稀释掉。理论上模型有“长程注意力”能力但它在真正长上下文里还是会漏而且漏得很有规律越靠中间的指令越容易被忽略。第二种是冲突。比如知识库里检索出来的三份文档一份说方案A一份说方案B还有一份改了需求要说方案C。Agent拿到这些彼此矛盾的资料之后最可能的处理方式不是停下来问你而是自己“平均”出一个四不像的方案。这种问题在有RAG增强的Agent里特别常见。第三种是过期。上一轮任务的目标、中间产物、临时方案如果是同一套上下文机制被带进了本轮任务过期信息就会像旧地图一样明明标的是一条已经修好的路Agent却以为那是施工中的路。过期信息最麻烦因为它不会报错它只会让Agent非常确定地走向错误方向。1.3 为什么Agent自己察觉不到脏这个问题值得展开说。模型在做生成时本质上是基于全部上下文信息做概率预测。它没有“主动校验”这个步骤不会问自己“这段历史和当前任务到底有没有关系”所以即便是GPT级别的主流模型也不会主动识别出上下文中的垃圾。它们更像一个超高速的阅读者读到什么就理解到什么理解了就顺着往下写。如果读到的信息是错的AI最多只是输出不连贯但在逻辑表面上它往往会显得极其自洽。这也是GSD项目最核心的认知与其改模型不如改模型看到的东西。上下文干净模型正确率自然提高上下文脏模型再强也拦不住它自己编。很多人把希望寄托在更强的新模型上但在我做过十几组对比之后发现模型能力提升带来的收益远远不如上下文清洗带来的收益。模型你换一个可能进步10%但上下文洗干净稳定度可能提升50%以上。2. 上下文污染的四个主要来源把“脏”给拆开看2.1 会话历史最大、最难防的一坨会话历史是所有Agent上下文里最天然存在的部分。不管是ChatGPT还是Claude Code这类工具当你跟Agent聊了二十轮之后前面的对话已经被整体编码进了上下文。这里有个很容易忽略的问题对话里既包含了你的真实需求也包含了大量试错过程中的中间产物。比如你可能说过“先试试这个方法”后来自己发现方法不对改成“还是用另一个方案吧”。但对Agent来讲最初的“先试试”依然占着一个权重很高的位置。我的经验是在代码生成场景里历史对话携带的“噪音/信号比”会随轮数指数上升。第1轮到第5轮上下文里基本都是有效信息第5轮往后修正、反悔、补充说明和无关闲聊的比例会迅速增加。GSD的做法很粗暴把任何超过十轮的会话都打上“怀疑”标记进新任务时要么重开话题要么强制生成结构化的任务摘要而不是直接把原文全带过去。2.2 检索结果来源多不等于信息好带RAG检索的Agent是最容易把自己搞乱的。知识库检索出来的东西在质量上是参差不齐的有些是官方文档有些是内网同事笔记有些是已经被废弃的API文档。当这些互相矛盾的内容同时出现在上下文里时Agent就进入了我们常说的“检索打架”状态。我做过一个小实验把同一个问题分别用一份高质量文档和三份低质量文档喂给Agent结果模型明显倾向于生成包含所有文档观点的大杂烩。它不会主动做来源可信度排名。所以在GSD的流程里检索结果在进入上下文之前必须经过一道“归并”工序相同主题、相同结论的内容先合并过期或冲突的内容要么标记优先级要么直接排除。检索这一步质量永远比数量重要。2.3 工具返回日志和报错记录并不是给模型看的东西这个问题在函数调用型Agent里尤其严重。很多工具返回值都是面向人类开发者的大量的DEBUG日志、堆栈信息、JSON字段甚至还有控制台彩色编码标记。这些东西如果被原样塞进上下文模型会误以为这些是必须遵循的代码风格或业务逻辑。我自己踩过的最蠢的一个坑一个Python编码Agent在完成任务后把一次正常执行时打印的WARNING信息当成了“用户要求解决的问题”开始主动修复一个不存在的Bug。为什么因为WARNING信息在上下文里紧挨着用户指令模型分不清哪部分是工具噪音、哪部分是核心目标。后来GSD在工具返回层做了一道净化把工具日志中的运行时信息默认丢弃只保留下层状态变更和结构化返回值这个奇怪行为就消失了。2.4 系统提示词不是写得越多越好系统提示词是很多人最喜欢堆东西的地方。今天加一条“你必须遵守编码规范”明天加一条“记得重视安全性”后天再加一段“回答时请考虑部署环境”。大家都在疯狂往系统提示里塞规则觉得规则越多越安全。但实际情况是规则堆积到一定数量之后Agent对每一条的敏感性会显著下降它不会每条都严格执行只会抓住那些它认为更重要的内容。我在GSD里做了一个值得参考的尝试把系统提示词拆成“省流版”和“详细版”两级。省流版只有五到八条核心规则按重要程度排序放在最前面详细版则外置成一个文件只在Agent主动查询时才进入上下文。对比下来这个方法在很大程度上解决了规则太多导致的“规则稀释”。3. GSD怎么做一套给Agent做“上下文体检”的完整流程3.1 第一步把Agent看到的全部上下文导出并盘点我建议开发Agent工作流的人都至少做一次彻底的上下文盘点。方法是给Agent加一个环境变量像这样export GSD_DUMP_CONTEXT1然后跑两三轮正常的任务把每一轮启动时的上下文原始内容都dump下来。注意这里不是让你看转化后的模型输入而是看最底层的拼接结果——系统提示、会话历史、工具返回等所有东西按顺序排列之后才是Agent真正看到的内容。导出之后拿一个文本分析工具做基础统计有哪些Token被反复提及哪些冲突词汇出现频次很高哪些段落和当前任务完全不相关这一步的目的是“看见”为后续清洗提供依据。我见过不少团队做了这个动作之后才意识到自己以为很干净的Agent实际上下文中有接近一半都是在重复上一次的任务内容。3.2 第二步给上下文分“区”从源头切脏真正可落地的上下文管理不能靠模型自觉而是靠结构隔离。我的做法是把Agent的上下文分为四个独立区域第一区是“系统前缀区”只放当前任务的最终目标、核心约束、交付格式。第二区是“实时工作区”放当前会话内新出现的信息包括最新指令、工具返回、用户反馈。第三区是“历史摘要区”只放对历史过程的压缩摘要原始日志必须清走。第四区是“外部引用区”放RAG检索到的文档但这个区在进入上下文之前必须经过格式净化和冲突消解。这四个区在物理上要是独立的不然又会被模型当成一整锅粥。你可以用一个简单的JSON结构来标记分区{ prefix: ..., workspace: ..., summary: ..., references: [...] }然后在把上下文交给模型之前做一次拼接并在每个分区起始处强制加一个“分区标记符”。这种处理看起来笨但效果立竿见影。分区能很大程度防止不同来源的信息交叉污染。3.3 第三步编写清洗规则建立“白名单”和“黑名单”清洗规则是GSD的核心资产。我整理出来的规则很简单但每条都来自实际踩坑黑名单规则包括所有DEBUG/INFO级别的日志所有历史任务的中间产物所有工具调用的原始参数头所有超过一个月且未被用户重新确认的业务规则所有来自低优先级知识库的冲突文档。白名单规则包括用户最近一次明确表达的最终目标经过结构化后的工具返回值当前任务的文件变更清单由高优先级文档生成的归并摘要。清洗规则最好是配置化的不要硬编码在代码里。不要一上来就写Python类来处理我是用一个YAML或JSON文件管理规则Agent的每次上下文构建都会先去读取规则文件。配成这样filters: - type: drop_prefix pattern: DEBUG|INFO - type: drop_expired ttl: 30d - type: merge_duplicate field: topic这样做的好处是你可以随时调整规则而不需要重新发布整个Agent框架。而且当团队成员对“什么该进上下文”有争议时配置化可以把争议从代码评审变成配置文件评审效率高很多。3.4 第四步在Agent运行中动态“扫尘”不只是一次性清理一次性清洗是不够的。Agent在对话过程中还会持续产生新信息这些新信息如果不加约束又会重新制造一个新的脏上下文。所以GSD里我加了一个“动态卫生检查器”每次Agent调用工具之后、进入下一轮生成之前都会自动执行一次短暂的上下文扫描。这个扫描器做三件事第一检查是否存在需要过期淘汰的历史消息第二检查工具返回中是否有超过预设大小的原始日志有就截断或剔除第三检查当前上下文总量是否超过设定阈值一旦超过就触发一次强制摘要过程把早先的对话内容压缩成几百字的结构化摘要替换掉原始长文。我特意把扫描器设计成“每次激活时间限制在几百毫秒以内”不能成为性能瓶颈。它更像是一个定时清垃圾的保洁员而不是一个复杂的数据处理引擎。实测下来动态扫尘在长会话场景里的收益非常明显Agent错误的次数大幅下降。4. 工具选型和场景适配不是所有Agent都要同一套清洗方案4.1 编码Agent和问答Agent清洗策略完全不同我在GSD实践过程中发现一个容易被忽视的事实编码类Agent和问答/分析类Agent它们对上下文质量的敏感点完全不同。编码类Agent最怕的是“历史记忆混乱”。因为代码任务的每一个步骤都强依赖前一步的状态如果Agent记混了变量名或者搞错了项目结构后面的代码几乎全部作废。所以编码Agent的清洗重点是工作区隔离和历史摘要。问答类Agent最怕的是“检索内容冲突”。因为它的主要任务是综合信息并给结论如果参考文档互相矛盾它既可能夹带私货也可能左右摇摆。对这种Agent清洗重点放在来源优先级和冲突标识上尽量把互斥信息提前标记清楚或者在进入上下文前就定好“以文档A为准”。4.2 长会话场景别让上下文总长度把你拖垮很多人在设计Agent时会担心上下文窗口不够用。但我观察到大部分项目真正的问题不是“窗口太小”而是“窗口里装的东西很多都是垃圾”。与其想着换一个超长窗口模型不如先解决垃圾囤积问题。我在GSD里做了一个很糙但有效的指标有效上下文密度。计算方法是“当前任务真正需要的信息量 / 总Token数”。把这个密度调高比什么优化都管用。用数学语言描述就是有效上下文密度 核心任务相关Token数 / 总Token数理想情况下这个密度应该维持在0.5以上低于这个值就要触发强制摘要。这里的“核心任务相关Token数”不需要精确计算粗略估计就行因为它的作用是提供一个触发阈值而不是做精准测量。4.3 轻量级方案不依赖重型框架也能做上下文卫生有人一听“上下文管理”就觉得要上LangChain、LlamaIndex这些重型框架。其实不需要。GSD的核心理念是轻量级、可以在已有的任意Agent框架外面套一层。哪怕你只是在Cursor、Codex这类工具外面写一层临时脚本也可以实现。以Codex CLI类工具为例你可以写一个简单的包装脚本#!/bin/bash # gsd-wrapped.sh: 轻量级包装器 export CODEX_CONTEXT_FILE/tmp/gsd_context.json python3 gsd_preprocess.py --input $1 --output /tmp/gsd_clean.md codex --context /tmp/gsd_clean.md这样你就能在进入Agent之前先把输入上下文过一遍GSD清洗脚本。这原本是不需要改动Agent底层实现的。我个人觉得先跑通这种轻量方案比一头扎进复杂框架更靠谱因为你先建立了对上下文的感知才谈得上优化。5. 实操实录一场“脏上下文vs干净上下文”的对照实验5.1 实验设计同一任务两种输入环境为了验证GSD的方案是否有效我做了一个比较简单的对照实验。实验对象是同一款开源编码Agent任务是用Python写一个支持重试机制的HTTP请求封装函数。要求模块化设计并包含简单单元测试。对照组直接把Agent拉起来就跑不做任何上下文清洗。实验组则先经过GSD的预处理流程将系统提示简洁化、历史记录压缩为摘要、工具返回信息过滤掉日志。两边使用同样的模型版本、同样的温度参数和同样的生成轮次上限。5.2 结果对比差距比我预想的还要大对照组的表现是灾难级的。Agent在函数定义里莫名引入了两个与本任务无关的依赖还注释了一行“根据上下文分析这个模块可能还需要支持异步”——没人让它做异步。更离谱的是它写的单元测试里有三个断言是基于一个根本不存在的配置项。很明显它从脏上下文的旧代码片段中“学到”了一些不存在的约定。实验组的表现非常干净。输出代码完全符合任务描述没有多余依赖测试用例也全部基于真实存在的函数接口。唯一的小问题是它在重试逻辑里默认采用了指数退避但没把这个策略写成可配置参数属于灵活度不够不算错误。5.3 这个实验说明了什么说明一个很简单但有分量的道理Agent写不出好代码很多时候不是它能力不行而是它看到的世界太烂。你给模型一坨脏数据它回你一堆“脏逻辑”你给它干净边界它就能稳定发挥。这不是猜测这是我做了多次同类的不同任务试验之后看到的稳定规律。很多时候我们抱怨AI写代码不靠谱但真正该优化的是我们喂给它的数据线。6. 常见问题与排查技巧现实中踩过的坑和建议的修法6.1 清洗过头了上下文太“薄”导致Agent失忆有人看完我的方案后容易走极端把所有历史记录都删掉结果Agent进入“失忆”状态连续对话逻辑全断。这是真实存在的副作用。我的解法是给历史压缩设定一个底线核心决策点必须保留。比如用户明确否决过的方案、已经确定的技术选型、命名约定这些绝对不能从上下文里删除。你可以写一个决策点列表在清洗时凡是命中决策点的内容一律保留其余才走摘要流程。6.2 工具返回值被误删导致Agent拿不到关键字段工具返回净化这一层容易误伤。最开始我设定的是把超过一定大小的返回直接截断结果一不小心把一些重要的数据字段也给截掉了。后来我把规则改成“按字段重要性保留”先对工具返回做字段名校验保留业务字段丢弃日志类和状态码类的冗余信息。建议所有做工具返回清洗的人都先花半天时间把项目中所有工具的真实返回结构梳理一遍做一个“字段优先级映射表”。这个表格看起来费事但能避免很多莫名其妙的线上问题。6.3 动态扫尘在超大上下文时延迟变高动态扫尘的机制虽然轻量但在上下文真的非常大的情况下每次扫尘的时间也还是会累积。我的经验是给扫尘设一个触发频率上限比如最多每三轮对话扫一次不要每次工具调用后都扫。垃圾清理可以晚一点但不能因为清理垃圾把正常流程拖垮。6.4 常见问题速查表现象可能原因建议处置Agent反复确认已确认过的需求历史摘要缺失决策点补回决策点保留规则Agent采用了过期的API用法检索文档未做优先级排序对知识库来源打权重标记Agent写了很多无关依赖历史代码片段混入工作区对工具返回做字段级过滤生成结果越来越长但质量下降上下文冗余密度过高触发强制摘要压缩历史多轮后规则遵守度明显下降系统提示词规则过多拆分为省流版和详细版两级7. 让GSD方案持续生效把它变成你的开发习惯最后说点实际的。GSD不是一次性项目它是一种持续性的开发习惯。光做一次上下文清洗没有用因为Agent每天的输入都在变知识库在更新项目的代码结构也在演进。你如果不在Agent工作流里建立起一套常态化的上下文治理机制过两周它又会重新脏起来。我个人的做法是给每个Agent项目建一个“上下文卫生清单”每周五下午花半小时检查一遍本周新增了哪些工具哪些历史记录已经过期系统提示词有没有被不合适地膨胀这个例行检查看起来不起眼但它帮我挡住了很多原本会浪费一整天才发现的诡异Agent问题。上下文管理这个东西你说它是技术也行说它是习惯也行但比起换个更强的新模型它对写代码这件事的帮助其实更实在——至少在GSD这个项目上我是实打实地体会到了这一点。