上下文构建与 Token 成本控制:企业级策略

这一篇聚焦一个在企业里必定会被问到的问题:上下文怎么构造、Token 怎么控?既要让模型“看得够多”,又不能让每次调用都烧钱烧到无法落地。

在个人项目里,很容易忽略一个事实:
大模型调用的成本是和 Token 成线性关系的,而 IDE 又是一个高频交互场景:

  • 每一次补全、Inline Chat、多文件重构,背后都是一次或多次模型调用;
  • 如果不控制上下文长度和调用频率,团队级使用时成本会迅速失控;
  • 对延迟也有直接影响:prompt 越长,端到端等待时间越高。

从企业视角看,“如何做 Token 成本控制?”不只是账务问题,也是:

  • 架构设计问题:上下文如何分层、索引如何设计;
  • 产品策略问题:哪些交互应该“实时”,哪些可以“按需触发”;
  • 观测问题:如何知道是哪类功能在吃掉绝大部分 Token。

一个不错的实践是把上下文拆成三层,每层都有明确优先级:

  • 核心上下文(必须塞进去)

    • 当前文件中光标所在函数/类的完整实现;
    • 用户选中的代码块;
    • 与任务强绑定的信息(例如编译错误、测试失败堆栈)。
  • 相关上下文(预算足够时尽量塞)

    • 同一文件中和当前函数紧耦合的辅助函数;
    • 同一模块/包下被当前文件调用的关键接口实现;
    • 最近编辑过的文件里与本次改动高相关的片段。
  • 附加信息(只在需要时塞)

    • 相关文档/注释摘要;
    • 历史对话中和当前任务强相关的几轮对话;
    • 配置信息、环境说明等。

构建上下文时,可以先按优先级填核心,再用剩余 Token 预算去填相关和附加部分。

不同任务的“信息胃口”不一样,没必要一刀切:

  • 代码补全(特别是单行补全):

    • 主要依赖光标附近代码 + 当前函数/类;
    • 项目级上下文可以非常克制,甚至可以只用 LSP 补全作为约束。
  • 局部重写 / Inline Chat:

    • 需要整个函数和文件头部 imports;
    • 对跨文件信息依赖相对有限。
  • 多文件重构 / 项目级问答(RAG):

    • 需要从索引里调大量相关片段;
    • 这类调用本身可以标记成“重型请求”,通过频控和显式 UI 触发。

可以为每类任务设一个大致的 Token 上限,比如:

  • 补全:prompt ≤ 1k token;
  • Inline 重写:prompt ≤ 2–3k;
  • 项目级 RAG 问答:prompt ≤ 4–8k。

在构造上下文时,先估算各段文本长度,再按优先级填充,超出的直接截断或省略。

当 Token 预算不够时,需要有一套“谁先被踢”的规则。一个常见策略:

  1. 当前文件的目标函数/类 永远保留完整
  2. 当前文件其他函数按“调用关系距离 + 代码行数短长”排序,靠前的优先;
  3. 索引召回的其他文件按相关度排序,取 Top-K,再按 Token 预算裁;
  4. 文档/注释类上下文最后塞,如果不够就先丢弃这部分。

对于项目级 RAG,可以再细化一层:

  • 按类型优先级:
    • 优先代码,其次设计文档,再其次是其它文本;
    • 对于“修 bug”类任务,优先错误堆栈和相关代码。

这样可以保证:

  • 就算预算很紧,模型至少能看到“正在改的那块代码”和直接相关的实现;
  • 不会出现“远方的注释/文档”把近处的关键逻辑挤掉的情况。

在 IDE 里,很多操作是围绕同一段代码、多次迭代进行的,例如:

  • 连续几次对同一个函数做重写/优化;
  • 在同一组文件里多次进行测试生成/bug 修复。

这时可以利用几种缓存方式减少 Token 消耗:

  • 会话级缓存

    • 把本轮会话中已经发送过的“项目级背景”缓存成一个短摘要,在后续请求里复用;
    • 对某个函数/模块的长注释/设计文档只摘要一次。
  • 项目级缓存

    • 对整个项目的“全局说明”(例如架构概览、主要模块划分)提前做一次摘要,存在本地或服务端;
    • 后续所有请求都引用同一份短摘要,而不是每次重新压缩。
  • 模板缓存

    • 系统提示 + 通用指令部分保持固定,通过“prefix caching”减少实际发送长度(取决于模型/提供方是否支持)。

这些手段不会改变单次请求在语义上的效果,但可以显著降低平均 Token 成本。

企业级环境里,想认真回答“Token 成本怎么控”,需要先把数据打通:

  • 每次调用要打点:

    • 功能来源(补全 / Inline Chat / 多文件重构 / RAG 问答等);
    • 实际 prompt 长度 / 输出长度;
    • 使用结果(用户是否接受、是否撤销)。
  • 定期汇总:

    • 按功能、按团队、按仓库统计 Token 消耗;
    • 找出“高成本低收益”的场景优先优化。

有了这些数据之后,可以做一些策略调整,例如:

  • 对成本极高但接受率不高的功能:调低默认开启频率,改成“显式触发”;
  • 对成本不高但帮助很大的功能:可以适度放宽上下文预算或增加调用频率。

回到前面的问题:

  • 4️⃣ 如何做 Token 成本控制?

    • 通过三层上下文结构 + 动态裁剪 + 优先级拼接 + 缓存 + 观测闭环,一层层收紧;
    • 不指望一次性找到完美策略,而是靠监控数据持续调节。
  • 5️⃣ RAG 如何避免错误召回?

    • 控制召回数量和类别,只把“真的相关”的片段塞进 prompt;
    • 在上下文不足以支持高质量回答时,宁可让模型说“不确定”,也不要硬编。

对一个 IDE 工程师来说,把“上下文构建”看作一个独立子系统,并且:

  • 在接口里显式传递任务类型和 Token 预算;
  • 在实现里显式维护不同优先级的上下文队列;

会比在各个 feature 里“看心情拼 prompt”稳得多,也更容易在企业规模下迭代。