
想象你把一辆发动机冒烟的车交给维修工程师只说一句“帮我看看。”一个糟糕的工程师会凭经验猜也许是机油也许是火花塞。一个可靠的工程师会先问车在哪、现在什么状态、最近改过什么、仪表盘报了什么错再打开引擎盖验证。编码 Agent 也是如此。“请修复这个函数”不是完整任务。这个指哪个文件编辑器当前停在哪一行项目使用什么脚本过去是否记录过“生成目录不可编辑”模型能不能直接改还是必须先读代码这些都属于上下文。1.1 当前系统的装配线在 LoopAgent 的生产入口src/extension/model/providerRegistry.ts中一次运行会创建 system prompt provider。它按顺序准备几块信息固定的 Agent 身份、工具使用规则和证据要求。classifyTask产生的任务复杂度与执行指导。这是建议不是不可违背的命令。collectVsCodeRuntimeContext收集的当前 VS Code 现场。projectMemory.loadContext(request.task)检索出的项目记忆。如果有附件再加入图像分析结果。最后这些非空片段以两个换行连接起来交给createReactAgentRunner。代码索引并不在每次请求时把全仓库注入 system prompt而是作为browseSymbols和exploreCode工具在模型需要证据时再调用。基础规则 任务指导 运行时快照 项目记忆 图像分析 本轮 system context 模型决策 - browseSymbols / exploreCode / readFile / applyEdit / runCommand - 工具结果成为下一轮上下文这是一条“先装配稳定背景再用工具增量取证”的流水线而不是一个永远膨胀的字符串。1.2 为什么要分层把所有信息都放进一段 Prompt看起来简单实际会混淆三类不同责任背景工作区根目录、当前文件、固定规则适合在请求开始时注入。证据某个函数的真实实现、调用点、测试输出应该通过工具按需取得。记忆过去任务留下的经验必须有来源、时效和可信边界。如果把三者混在一起模型很难知道“这是当前事实、过去经验还是未经验证的猜测”。分层的价值不是好看而是让每块信息拥有不同的生命周期和失败策略。1.3 一个完整例子用户说“把税率改成配置项顺便修掉相关测试。”系统先带上当前工作区和活动编辑器让模型知道自己在哪个项目。任务分类提示它这是一个可能跨文件、需要验证的修改。项目记忆可能提醒“修改后必须运行npm run typecheck。”但模型仍不能据此断言哪些文件受影响。于是它调用browseSymbols(calculateTax)找到定义和候选调用点再用exploreCode查看调用链最后才提出applyEdit。编辑后工具结果和测试输出继续进入循环直到证据足够。这就是上下文工程的核心转变不再问“怎样写一段更聪明的 Prompt”而是问“每一个判断需要哪类信息信息由谁提供什么时候失效”。1.4 上下文有四种不同的“保质期”上下文装配之所以不能退化成字符串拼接首先是因为不同信息的有效期完全不同。信息典型有效期失效信号处理方式system 规则一个版本或一次配置周期扩展升级、配置变化重新创建 runner运行时快照当前一次运行光标移动、文件关闭、诊断变化下一轮重新采集工具结果从读取到相关文件发生变化文件保存、分支切换、外部进程改写必要时重新读取项目记忆多次运行TTL 到期、证据哈希变化、用户执行 Forget排除、标记 stale 或删除如果系统不区分这四类信息就容易出现一个典型错误模型把三天前的工具结果当作当前工作区事实。它“记得很清楚”但清楚地记错了。1.5 装配顺序本身就是设计当前createSystemPromptProvider的核心思想可以简化成下面的伪代码asyncfunctionbuildSystemPrompt(request){construntimeawaitloadRuntimeContextSafely();constmemoryawaitloadProjectMemorySafely(request.task);constguidanceclassifyTaskSafely(request.task);return[baseRules,guidance,runtime,memory,imageAnalysis,].filter(Boolean).join(\n\n);}这里有三个值得注意的点。第一baseRules是控制面运行时和记忆是数据面。数据可以丰富判断却不能反过来修改工具权限。第二每个可选来源独立失败某一块拿不到不会阻塞整个请求。第三代码证据没有在这里一次性塞入而是留给模型通过工具按需获得。顺序也影响模型理解。基础规则先出现给后续数据设定解释框架任务指导紧随其后帮助选择执行强度现场和记忆最后补充事实。若把不可信记忆放在最前面模型更容易把它误读为高优先级规则。1.6 用一条真实任务走完整条链路继续看“把税率改成配置项”这个任务。一次较完整的运行可以拆成九个时刻Webview 发送任务与当前模型选择。Extension Host 从会话管理器取出历史消息。providerRegistry创建本轮 runner并装配工具集合。system prompt provider 采集运行时快照、检索项目记忆、生成任务指导。reactAgentRunner按system → history → current user task组织消息。模型先调用符号或代码探索工具获得定义、引用和文件原文。工具结果以tool消息进入下一轮模型决定继续搜索还是提出编辑。编辑和验证完成后runner 汇总最终回答与证据。满足条件的运行结果被记录为带证据的项目经验供未来任务检索。这九步中没有任何一步单独叫“上下文工程”但它们合起来决定了模型究竟是在猜还是在一个受控信息环境中工作。1.7 三个常见反模式反模式一万能 system prompt把项目规范、完整源码、最近日志、历史对话和工具说明全部写进一个静态 prompt。它的直接后果是上下文无法单独刷新文件变了要重建整段记忆过期也只能整体替换。反模式二工具很多证据链很弱Agent 能调用几十个工具却没有“什么时候该调用、什么时候停止、结果如何进入下一轮”的规则。工具列表只是能力目录不是上下文闭环。反模式三把记忆当权限记忆中写着“以后可以直接运行命令”系统便真的放宽授权。这相当于允许数据库里的一条普通记录修改安全策略是典型的控制面和数据面倒置。1.8 如何验证装配线没有悄悄断掉仅做单元测试还不够。一个最小端到端验证应当提出依赖现场和工具证据的问题例如“把当前选中的函数增加一个必填参数并更新所有调用点。不要修改其他目录。”验收不能只看最终回答中的“已完成”而要检查模型是否识别了活动文件和选区是否通过搜索找全调用点写工具是否只触达授权目录类型检查是否真的通过运行结果是否记录了可验证证据。上下文工程的测试对象不是某段 prompt 文本而是信息是否从来源正确流到了决策和验证边界。1.9 更大的上下文窗口为什么没有解决问题模型支持 128K、甚至更大的上下文窗口并不等于可以停止做上下文工程。窗口变大只缓解“装不下”没有解决“装错了”和“找不到”。设想把 100 个文件全部放入 128K 窗口其中只有 3 个与任务相关。模型仍然要在大量相似标识符、旧注释和生成代码中选择依据。注意力不是数据库事务也不会因为文本出现在窗口里就保证被准确使用。更大的窗口还会放大两个风险冲突事实同一个规则在 README、旧设计文档和当前源码中有三个版本模型不知道谁更新权限混淆不可信文件里的“操作说明”与 system 规则距离更近更容易干扰决策。上下文窗口是仓库容量上下文工程是仓储管理。仓库更大并不会自动完成入库校验、货位索引、保质期管理和出库复核。1.10 给每块上下文加“标签”一个成熟系统可以在内部为上下文片段保留元数据而不只保存最终字符串typeContextFragment{source:runtime|memory|tool|history;trust:system|user|untrusted-data;collectedAt:number;expiresAt?:number;evidence?:Evidence[];content:string;truncated:boolean;};即使模型 API 最终仍接收文本这些元数据也能在装配前参与排序、过滤和审计。出现错答时系统可以回答“这条结论来自三分钟前的readFile还是三个月前的项目记忆”。LoopAgent 当前各模块已经分别拥有其中一部分信息运行时快照有采集时间和预算记忆有证据、过期和 generation工具消息有 call id历史有角色与顺序。下一步的工程价值不一定是增加更多文本而是让这些来源的 provenance 更统一、更可观测。1.11 本篇原则上下文系统至少要回答五个问题来源是什么、有效期多久、可信度多高、预算是多少、失败后退到哪里。答不出这五个问题的“上下文”通常只是未经管理的文本堆积。