芥末
发布于 2026-01-08 / 0 阅读
0
0

Cursor 动态上下文发现:让代码 Agent 按需读取工具、历史和终端输出

代码 Agent 的能力不只取决于模型本身,还取决于它拿到的上下文是否合适。上下文给少了,Agent 不知道项目结构、工具能力和历史决策;上下文给多了,又会挤爆窗口,增加 token 成本,还可能把无关甚至互相冲突的信息塞给模型。

Cursor 的动态上下文发现(dynamic context discovery)解决的就是这个问题:不要一开始把所有细节都塞进提示词,而是先给 Agent 一个很小的索引,让它知道“有哪些信息可以查”,需要时再通过文件、搜索、tail、语义检索等方式按需读取。

这套思路可以概括成一句话:

静态上下文负责告诉 Agent “信息在哪里”,动态上下文负责让 Agent “只取当前任务真正需要的信息”。

Cursor 在使用多个 MCP 服务器时,通过这种方式把会调用 MCP 工具的运行总 token 消耗降低了 46.9%。这个数字不是因为模型少干了活,而是因为大量从未使用的工具描述、长日志、长 JSON、历史对话细节不再默认进入上下文窗口。

静态上下文的问题:越“完整”,越容易变重

传统做法喜欢把信息提前塞进上下文窗口,例如:

  • 所有系统规则;
  • 所有工具定义和工具描述;
  • 所有 MCP 工具说明;
  • 代码搜索结果;
  • shell 命令完整输出;
  • 历史聊天记录;
  • 项目规则、技能文档和使用示例。

这种方式看起来稳妥,因为模型似乎“什么都知道”。但在代码 Agent 场景里,它很快会遇到几个问题。

问题具体表现
token 成本高每次调用模型都带上大量不相关信息,即使任务只用到其中一小部分
上下文窗口被挤占长 JSON、日志、工具说明占用空间,真正的代码和任务细节反而放不下
信息噪声增加模型可能被无关规则、旧输出、相似工具描述干扰
截断导致信息丢失shell 输出或 MCP 结果太长时被截断,关键错误可能刚好被裁掉
摘要有损上下文满了之后压缩成摘要,细节可能被省略,后续 Agent 无法恢复

代码 Agent 和普通聊天助手的差异在于:代码任务经常需要“查证”。它不一定需要一开始记住所有内容,但必须能在需要时找到准确来源。动态上下文发现就是把“提前全部注入”改成“按需检索”。

动态上下文发现的核心机制

Cursor 的做法不是把上下文管理变复杂,而是把很多长内容统一转换成文件。文件是一种非常适合 Agent 使用的接口:可以按路径读取,可以 grep,可以 tail,可以分段查看,也可以交给语义搜索。

整体流程可以这样理解:

flowchart LR
    A[用户任务] --> B[少量静态上下文]
    B --> C[代码 Agent 判断需要什么信息]

    C --> D{需要更多细节吗}
    D -- 不需要 --> E[直接修改代码或回答]
    D -- 需要 --> F[检索上下文文件]

    F --> G[(工具输出文件)]
    F --> H[(聊天历史文件)]
    F --> I[(Agent Skills 文件)]
    F --> J[(MCP 工具描述文件)]
    F --> K[(终端会话文件)]

    G --> L[只读取相关片段]
    H --> L
    I --> L
    J --> L
    K --> L

    L --> C

这里的关键不是“文件”本身,而是文件带来的访问模式变化:

  • 以前:所有内容默认进入上下文;
  • 现在:上下文里只放索引、名称、路径、简短描述;
  • 以前:长内容只能截断或摘要;
  • 现在:长内容可以保留在文件中,Agent 自己决定读哪一段;
  • 以前:摘要丢失细节后很难恢复;
  • 现在:摘要可以引用历史文件,缺什么再回查。

1. 长工具响应不要直接塞进上下文,而是写成文件

工具调用最容易造成上下文膨胀。尤其是第三方工具返回的内容,经常是大段 JSON、大量日志或完整命令输出。

例如,一个查询生产日志的 MCP 工具可能返回几千行 JSON:

{
  "events": [
    {
      "timestamp": "2026-01-08T10:01:00Z",
      "level": "INFO",
      "message": "request started",
      "requestId": "..."
    },
    {
      "timestamp": "2026-01-08T10:01:02Z",
      "level": "ERROR",
      "message": "database timeout",
      "stack": "..."
    }
  ]
}

如果这些内容全部进入模型上下文,每一步推理都会重复携带它们。更糟的是,很多 Agent 框架会选择截断长输出,导致尾部错误、堆栈、统计结果被丢掉。

Cursor 的处理方式是把长响应写入文件,再给 Agent 读取文件的能力。Agent 可以先查看末尾,再按需搜索或读取局部内容。

可以把这种模式理解为:

# 先看最后一段,判断命令是否成功、错误在哪里
tail -n 80 tool-output-123.log

# 如果发现错误关键字,再定位具体行
grep -n "ERROR\|Exception\|timeout" tool-output-123.log

# 只读取相关区间,而不是把整个文件塞进上下文
sed -n '240,320p' tool-output-123.log

这种方式带来两个直接好处:

  1. 不丢数据:完整输出仍然保存在文件里,不需要为了节省上下文强行截断。
  2. 少读无关内容:Agent 只把相关片段带回上下文窗口,减少 token 消耗。

长工具响应的处理流程如下:

sequenceDiagram
    participant Agent as 代码 Agent
    participant Tool as 工具 / MCP / Shell
    participant FS as 上下文文件
    participant Model as 模型上下文

    Agent->>Tool: 调用工具
    Tool-->>FS: 长响应写入文件
    FS-->>Agent: 返回文件引用和简短摘要
    Agent->>FS: tail / grep / 分段读取
    FS-->>Model: 只注入相关片段

2. 摘要不再只靠压缩,而是引用完整聊天历史

大语言模型(LLM,Large Language Model)的上下文窗口不是无限的。会话变长后,常见做法是生成摘要,把之前的对话压缩成一段较短的文字,然后用新的上下文窗口继续工作。

问题在于,摘要一定是有损压缩。它可能保留了主线,却丢掉了变量名、文件路径、用户约束、失败尝试、某个工具返回的关键细节。对于代码 Agent 来说,这类细节经常决定任务能不能继续推进。

Cursor 的改进是:摘要阶段不只给 Agent 一段压缩结果,还把聊天历史作为文件提供。摘要相当于目录和路线图,历史文件则是可回查的完整记录。

flowchart TD
    A[上下文窗口接近上限] --> B[生成当前任务摘要]
    B --> C[开启新的上下文窗口]
    C --> D[摘要进入新上下文]
    C --> E[聊天历史文件引用进入新上下文]

    D --> F{摘要信息够用吗}
    E --> F

    F -- 够用 --> G[继续执行任务]
    F -- 不够 --> H[搜索历史文件]
    H --> I[找回遗漏细节]
    I --> G

这种机制可以缓解两类常见问题:

场景只有摘要时的问题引用历史文件后的处理方式
用户指定过细粒度约束摘要可能只保留大意Agent 可以搜索历史中的关键词
曾经尝试过失败方案摘要可能省略失败原因Agent 可以回查失败命令和报错
多轮讨论中出现文件路径摘要可能写得不完整Agent 可以定位具体路径和上下文
用户手动触发摘要压缩粒度不一定适合后续任务后续可按需补读历史片段

这不是取消摘要,而是把摘要从“唯一记忆”改成“入口索引”。Agent 先靠摘要快速恢复状态,缺细节时再去历史里找。

3. Agent Skills 用文件扩展专用能力

Agent Skills 是一种给编码 Agent 扩展专用能力的开放标准。它可以理解为一组面向特定任务的说明文件,告诉 Agent 遇到某类工作时应该怎么做。

例如,一个团队可以定义:

  • 如何运行前端测试;
  • 如何发布某个内部包;
  • 如何生成数据库迁移;
  • 如何调用内部代码生成脚本;
  • 如何检查安全规则;
  • 如何按照团队规范修改 API。

Skills 通常包含名称、描述和具体说明。名称和描述可以作为较短的静态上下文出现,让 Agent 知道“可能存在这些能力”。真正的细节则保存在文件里,由 Agent 在需要时读取。

flowchart LR
    A[系统提示词中的 Skill 名称和描述] --> B[Agent 判断当前任务类型]
    B --> C{需要某个 Skill 吗}
    C -- 是 --> D[搜索 Skill 文件]
    D --> E[读取说明、脚本、示例]
    E --> F[按 Skill 执行任务]
    C -- 否 --> G[忽略 Skill 细节]

这种设计比把所有 Skill 说明全部塞进上下文更稳。一个项目可能有很多专用规则,但当前任务只需要其中一两个。动态发现可以保留能力扩展的灵活性,又不会让上下文被规则文档填满。

Skills 还可以携带脚本或可执行文件。由于它们本质上仍是文件,Agent 可以通过文件系统找到相关内容,而不是依赖提示词一次性描述清楚所有细节。

4. MCP 工具描述只加载必要部分

MCP(Model Context Protocol,模型上下文协议)让 Agent 能访问外部系统,例如:

  • 生产环境日志;
  • 设计稿;
  • 企业知识库;
  • 内部文档;
  • OAuth 保护的第三方资源;
  • 数据库或监控系统。

MCP 的问题在于:一个 MCP 服务器可能暴露很多工具,每个工具还有很长的描述、参数说明和使用示例。如果多个 MCP 服务器同时启用,工具描述会迅速占满上下文窗口。

传统做法是把所有 MCP 工具定义都放进提示词:

flowchart LR
    A[多个 MCP 服务器] --> B[所有工具描述]
    B --> C[全部注入系统提示词]
    C --> D[模型上下文变大]
    D --> E[大部分工具没有被使用]

Cursor 的做法是把 MCP 工具描述同步到文件夹里。Agent 默认只看到一小段静态信息,例如工具名称或简短索引;当任务真的需要某个工具时,再去查找完整描述和参数。

flowchart LR
    A[多个 MCP 服务器] --> B[工具描述同步为文件]
    B --> C[系统提示词只保留工具索引]
    C --> D[Agent 根据任务检索工具说明]
    D --> E[读取目标工具参数]
    E --> F[调用需要的 MCP 工具]

对比一下两种策略:

策略上下文内容优点代价
全量注入 MCP 工具描述所有工具名、描述、参数、示例模型一开始就知道全部细节token 占用高,多个 MCP 服务器时膨胀明显
动态加载 MCP 工具描述少量工具索引 + 文件中的完整描述只读取当前任务相关工具,token 更少Agent 需要多一步检索工具说明

在 Cursor 的 A/B 测试中,对于会调用 MCP 工具的运行,这种策略让总 token 消耗减少了 46.9%。这个收益会受已安装 MCP 服务器数量影响:工具越多、描述越长,动态加载越容易节省上下文。

基于文件的 MCP 工具描述还有一个额外好处:工具状态也可以被表达出来。比如某个 MCP 服务器需要重新认证,以前 Agent 可能像“忘了这些工具存在”一样无法解释原因;现在它可以看到相关状态,并提示用户完成认证。

5. 终端会话也应该作为可检索文件

代码开发离不开终端输出。测试失败、构建失败、服务启动异常、端口占用、依赖冲突,通常都藏在终端日志里。

过去用户经常需要把终端输出复制给 Agent:

为什么这个命令失败了?

[粘贴几十行日志]

如果日志很长,比如本地开发服务器已经运行了半小时,复制全部内容不现实;只复制一小段,又可能漏掉真正的错误位置。

Cursor 把集成终端会话同步到本地文件系统后,Agent 可以像处理普通日志一样处理终端历史:

# 查找最近的错误
grep -n "ERROR\|Error\|Exception\|failed" terminal-session.log

# 查看服务最后输出
tail -n 120 terminal-session.log

# 定位某个测试文件相关日志
grep -n "user.service.test.ts" terminal-session.log

这让“为什么我的命令失败了?”变成一个可定位的问题。Agent 不需要用户手动搬运日志,而是自己去终端会话文件中搜索相关片段。

对于长期运行的进程,这个设计尤其有用:

进程类型终端输出特点动态读取方式
本地开发服务器输出持续增长,错误可能出现在中间grep 错误关键字,再读取附近行
测试命令失败信息通常在末尾或某个测试块tail 或搜索测试文件名
构建命令依赖、类型、打包错误混杂搜索 failedCannot findType error
后端服务日志量大,可能夹杂请求记录搜索 requestId、异常名、状态码

这和 CLI 形态的代码 Agent 有相似之处:它们都能访问之前的 shell 输出。差异在于,Cursor 不是把所有终端历史静态注入上下文,而是把终端历史变成可动态检索的文件。

文件为什么适合作为 Agent 上下文接口

动态上下文发现把很多东西都统一成文件:

  • 工具输出是文件;
  • 聊天历史是文件;
  • Skill 说明是文件;
  • MCP 工具描述是文件;
  • 终端会话是文件。

文件看起来朴素,但它有几个适合 Agent 的特性。

文件能力对 Agent 的意义
路径定位可以引用具体来源,不必把内容复制进提示词
分段读取长内容可以按行、按区间、按尾部读取
文本搜索可以用 grep 快速定位关键词
语义搜索可以在大型项目或文档中找相关内容
状态持久化摘要后仍然可以回查完整历史
工具通用shell、编辑器、索引器、搜索工具都能处理文件

相比重新设计一套专门给 LLM 使用的上下文存储协议,文件系统足够简单,也足够稳定。代码 Agent 本来就擅长和文件打交道,把上下文放进文件,等于把上下文管理变成了它已经会做的事情。

一个简化版实现思路

如果要在自己的 Agent 系统里复刻这种思路,可以把上下文分成两层:

  1. 静态索引层:短小,默认进入提示词。
  2. 动态内容层:完整内容放文件,按需读取。

示例结构可以设计成这样:

.agent-context/
  tool-outputs/
    2026-01-08-search-result.log
    2026-01-08-mcp-response.json

  conversations/
    current-session.md

  skills/
    run-tests/
      SKILL.md
      scripts/
        run-tests.sh

    create-migration/
      SKILL.md
      scripts/
        migration-template.sql

  mcp-tools/
    github/
      search_issues.md
      create_pull_request.md

    logs/
      query_production_logs.md

  terminals/
    terminal-1.log
    terminal-2.log

系统提示词不需要塞入所有文件内容,只需要给 Agent 一个索引:

可用上下文位于 .agent-context/:

- tool-outputs/:保存较长工具调用结果
- conversations/current-session.md:保存当前会话历史
- skills/:保存可用 Agent Skills
- mcp-tools/:保存 MCP 工具说明
- terminals/:保存集成终端输出

当需要更多细节时,优先使用 grep、tail、sed 或语义搜索读取相关片段,不要一次性读取大型文件。

Agent 执行任务时,流程可以是:

flowchart TD
    A[接收用户任务] --> B[阅读静态索引]
    B --> C[判断是否需要上下文文件]
    C -- 需要工具说明 --> D[查 mcp-tools 或 skills]
    C -- 需要历史细节 --> E[查 conversations]
    C -- 需要日志错误 --> F[查 terminals 或 tool-outputs]
    D --> G[读取相关片段]
    E --> G
    F --> G
    G --> H[执行代码修改或工具调用]
    H --> I[将新长输出写回文件]

这套机制的重点是约束 Agent 不要贪多:

读取上下文时遵循:
1. 先搜索,再读取;
2. 先 tail,再扩大范围;
3. 只把相关片段带入上下文;
4. 大文件不要整文件读取;
5. 如果摘要缺少细节,回查历史文件。

使用动态上下文时的注意事项

动态上下文发现不是简单地“把所有东西写文件”就结束了,还需要处理几个工程细节。

注意事项说明
文件命名要清晰Agent 需要从路径和文件名判断内容用途
静态索引要短但准确只保留名称、位置、简短描述,不要重新变成长提示词
长文件要可搜索日志、JSON、历史记录最好保留时间、来源、关键字段
权限状态要表达出来MCP 认证失败、工具不可用等状态也应该能被 Agent 看到
避免读入整份大文件需要在系统规则里鼓励 greptail、分段读取
摘要仍然重要摘要负责恢复任务主线,文件负责补细节,两者不是替代关系

还要注意一个边界:动态上下文发现依赖 Agent 自己判断“什么时候该查”。如果模型能力较弱,或者工具使用能力不稳定,它可能不会主动找文件,反而漏掉信息。因此这种模式更适合工具调用能力较强、能稳定执行搜索和读取动作的代码 Agent。

适合和不适合的场景

场景是否适合动态上下文发现原因
大型代码库适合项目文件、规则、搜索结果都很长,按需读取更省上下文
多 MCP 服务器很适合工具描述膨胀明显,大部分工具不会在单次任务中使用
长时间调试任务很适合终端输出和工具结果持续增长,需要回查错误片段
简短一次性问答不一定需要上下文很短时,动态检索的收益有限
模型工具调用能力弱不适合直接依赖Agent 可能不会正确搜索和读取文件
高安全隔离环境需要谨慎文件化上下文要配合权限控制和敏感信息处理

核心价值:让上下文从“堆进去”变成“找得到”

代码 Agent 不需要每一步都携带全部信息,它真正需要的是:

  • 知道有哪些信息源;
  • 能判断当前任务需要什么;
  • 能快速找到相关片段;
  • 能在摘要后恢复遗漏细节;
  • 能避免无关内容污染上下文。

Cursor 的动态上下文发现把上下文工程从“如何把更多内容塞进提示词”转向“如何让 Agent 可靠地找到必要内容”。这也是代码 Agent 继续变强时很自然的方向:模型能力越强,越不需要把所有细节预先铺好;只要信息可发现、可检索、可引用,它就能在执行过程中自己补齐上下文。


评论