2. 第一现场:VS Code 运行时上下文

发布时间:2026/8/22 16:26:54
2. 第一现场:VS Code 运行时上下文 “这个函数为什么报错”是 IDE 里最自然的一句话也是最考验上下文的一句话。人类同事看到你把光标放在src/auth/token.ts:42通常能立刻理解“这个函数”。模型没有眼睛必须有人把现场翻译给它。2.1 现场快照包含什么src/extension/runtime/vscodeRuntimeContext.ts从 VS Code API 读取当前状态再交给collectCodeRuntimeContext做规范化。最终上下文可以包含workspace 名称和根目录active editor 的路径、语言、行数、脏状态光标位置和用户选区光标附近有限行数的代码片段visible editors 与 open tabspackage.json、配置文件和文档等项目文件摘要当前诊断信息包括级别、行号和消息。contextPrompt.ts再把这些字段渲染成带标题的 Markdown 块。路径、语言、行号和截断状态都被显式写出模型不必从一团代码里猜“这段来自哪里”。2.2 预算不是附加功能运行时上下文有一个字符预算。每次budget.take(text)都先计算剩余空间超出后截断并在最终 prompt 中写出usedChars/maxChars和“是否截断”。这很像急诊室的白板空间有限必须先写病人的姓名和生命体征再写病史摘要不能把整本病历贴上去。预算还让模型知道自己看到的是摘要而不是完整文件。优先级示例 当前文件路径与选区 光标附近代码 诊断信息 打开的标签 项目文件摘要这里的优先级不是为了“省 token”这么简单而是为了让上下文在被截断时仍保留决策所需的最小现场。2.3 “当前文件”为什么不能等于“整个文件”用户的注意力通常集中在一个很小的范围。将整个 3000 行文件注入可能把真正相关的 20 行淹没还会挤掉工具规则和历史结论。LoopAgent 默认收集选区或光标附近片段需要更多范围时模型可以调用readFile精确读取。这形成了两阶段策略快照阶段低成本告诉模型“现场在哪里”。取证阶段通过工具读取“现场发生了什么”。2.4 失败时不能拖住主循环providerRegistry对运行时上下文采用 best-effort 策略收集失败时吞掉异常继续进入模型和工具循环。原因很现实编辑器状态偶尔变化、文档可能已关闭、诊断 API 也可能暂时不可用。一个辅助上下文源不应该让整个 Agent 无法回答。2.5 从 VS Code 对象到稳定数据结构直接把 VS Code API 对象交给模型并不可行。它们包含 URI、事件对象、可变编辑器状态和大量模型不需要的字段。LoopAgent 因此把采集拆成两层VS Code API - createVsCodeRuntimeContextSource() - CodeRuntimeContextSource宿主无关的原始结构 - collectCodeRuntimeContext() - CodeRuntimeContext预算化、相对路径化、截断后的快照 - renderCodeRuntimeContextPrompt()这个分层带来两个工程收益。采集层可以独立处理 VS Code 的 API 细节规范化层不依赖真实 Extension Host可以用普通 TypeScript 测试各种边界。未来即使数据源换成其他编辑器只要能生成同样的 source 结构后半段逻辑仍可复用。2.6 默认预算背后的优先级当前实现给运行时文本设置了几个明确上限总字符预算默认12_000光标附近默认向前、向后各取80行最多保留20个打开标签最多保留20条诊断workspace intelligence 还会单独限制文件数量和单文件大小。这些数字不是模型上下文窗口的上限而是“运行时快照愿意占用多少空间”的局部预算。system 规则、对话历史、工具结果仍需要空间因此运行时快照不能独占全部窗口。预算的消费顺序同样重要。活动编辑器和选区先于项目文件摘要进入意味着容量不足时用户正在看的内容更有机会被保留。这是一种基于交互意图的排序而不是平均分配。2.7 选区、光标和脏文件的细节编辑器里存在三个容易混淆的事实磁盘文件、内存文档和用户选区。用户可能已经修改代码但还没保存。此时磁盘上的readFile与编辑器里的 document text 不同。运行时快照会携带isDirty提示模型当前视图可能领先于磁盘。可靠的 Agent 不能一边引用未保存选区一边声称磁盘测试已经覆盖了它。有选区时选区通常比光标附近文本更能表达用户意图没有选区时再退到以光标为中心的片段。片段记录起止行和是否截断让模型能说“我看到第 35–68 行”而不是含糊地说“在上面的代码里”。2.8 路径为什么要相对化toWorkspaceRelativePath会尽量把绝对路径变成工作区相对路径。相对路径有三点好处输出更短减少重复的磁盘前缀回答更容易在项目内复现降低无意义暴露本机用户名和目录结构的机会。多根工作区会让相对化更复杂同一个src/index.ts可能属于不同根目录。因此实现必须结合 workspace roots 判断而不能简单删除字符串前缀。2.9 Prompt 渲染也要防“格式逃逸”运行时片段会被包进 Markdown 代码围栏。如果源码本身包含三反引号它可能提前关闭围栏让后续内容看起来像新的 prompt 指令。sanitizeCodeBlock会替换这类围栏标记保持内容仍被解释为代码数据。这不是完整的 prompt injection 防御但它修复了一个明确的结构问题数据不能通过伪造格式边界改变自己在 prompt 中的角色。2.10 一个可复现的现场测试可以构造以下场景验证运行时上下文打开src/auth/token.ts在第 42 行选中refreshToken。在内存中把函数参数改掉但暂不保存。制造一条 TypeScript error diagnostic。提问“这个函数为什么报错先只分析。”理想输出应体现当前相对路径、TypeScript 语言、选区范围、脏状态、错误位置和消息模型随后若需要文件其他部分再调用读取工具而不是假装快照已经包含完整文件。测试还应覆盖空工作区、无活动编辑器、选择跨越超长文本、标签超过 20 个、诊断超过 20 条和项目文件读取失败。只有空状态也能工作快照系统才算真正稳健。2.11 诊断信息是线索不是判决VS Code diagnostics 可能来自 TypeScript language server、ESLint、Java 插件或其他扩展。它们的生命周期和准确性不同有的在敲键盘后立即更新有的需要保存或重新构建。因此诊断上下文应包含 provider 能提供的路径、级别、位置和原始消息却不应该被渲染成“已确认根因”。例如Cannot find name config可能是变量真的不存在也可能是语言服务尚未看到刚生成的类型文件。模型合理的下一步是读取附近代码、检查导入和项目配置如果需要证明修复成功再运行对应 typecheck。诊断负责把注意力引向现场编译或测试负责作最终判定。2.12 运行时上下文的隐私边界IDE 现场可能包含用户尚未准备共享的内容未保存草稿、环境配置、打开但无关的密钥文件、诊断消息中的绝对路径。采集器不能因为“当前可见”就默认全部适合发送给外部模型。工程上至少需要考虑文件类型和排除规则特别是.env、凭据和大型日志将绝对路径相对化只收集必要片段不默认读取所有可见编辑器全文在模型请求日志中避免打印完整 prompt对图片附件和文本上下文使用相同的数据边界说明。上下文相关性和数据最小化在这里方向一致少发无关内容既提高质量也降低泄露面。2.13 一个“现场与磁盘冲突”的故事用户把timeout 30改成timeout 300尚未保存然后问“为什么还是 30 秒超时”运行时快照看到脏文件中的 300磁盘上的测试和运行进程仍读取 30。如果模型只看编辑器它会怀疑运行时缓存只看磁盘它会说用户没有修改。正确回答必须同时承认两个事实编辑器中的未保存状态是 300进程可见的磁盘状态仍是 30。这个例子说明上下文不仅要记录值还要记录值属于哪个世界。isDirty只有一个布尔字段却能阻止一次非常典型的错误归因。2.14 本篇原则运行时上下文的目标不是“尽可能多”而是“让模型知道当前现场并能沿着工具继续调查”。快照负责定位工具负责证明预算负责防止定位信息被淹没。

相关新闻