首页
/ VS Code Copilot NES:xtab 单池 Token 预算级联(Global Budget Cascade)设计与实现解析

VS Code Copilot NES:xtab 单池 Token 预算级联(Global Budget Cascade)设计与实现解析

2026-09-04 21:10:47作者:齐冠琰

本文围绕 VS Code 仓库中 Copilot 扩展(NES/xtab 内联编辑)的 globalBudgetCascade.md 规范文档展开,完整解读"单池 Token 预算级联"这一可选(opt-in)机制:它如何用 totalTokens / order / shares 三个旋钮把多个 prompt 片段的预算打通、让未用预算向后续片段捐赠、并保证当前文件最后裁剪时能复用级联剩余;读完你将掌握该机制的算法细节、校验规则、实验配置接入方式(chat.advanced.inlineEdits.xtabProvider.globalBudget),以及源码中 runGlobalBudgetCascadeGlobalBudgetOptions 的实际实现依据。

动机:为什么要打通各片段的独立预算上限

在默认的 legacy 路径中,xtab 的每个 prompt 片段都有各自独立的 maxTokens 上限。这些上限是按"最坏情况下组合 prompt"保守配置的,这意味着即使其他片段未使用或几乎为空,某个片段也会被自己的上限截断。级联(cascade)机制让前面片段未使用的预算"捐赠"给后面的片段,其设计原型来自 completions-core 中的 CascadingPromptFactory

该机制是 opt-in(可选启用) 的:当 PromptOptions.globalBudgetundefined 时,getUserPrompt 走 legacy 路径,各片段仍按各自的上限截断,输出与旧路径逐字节一致(生产环境默认值)。源码中这一分支位于 promptCrafting.tsopts.globalBudget !== undefined 时调用级联(或复用调用方预计算的 precomputedCascade),否则回落到 getRecentCodeSnippets 的旧逻辑。

作用范围:哪些片段参与级联

由级联渲染的片段(GlobalBudgetPart

GlobalBudgetPartorder 中列出、由级联循环依次渲染的片段,只有四个:

  • recentlyViewedDocuments(最近查看文档)
  • languageContext(语言上下文)
  • neighborFiles(邻居文件)
  • diffHistory(编辑 diff 历史)

类型定义见 xtabPromptOptions.ts

参与分配但不参与渲染的片段:currentFile

currentFile 参与 份额分配shares),但 不在渲染顺序order)中。它在级联运行完之后 最后 被裁剪——围绕光标/编辑窗口裁剪,尺寸为其池内份额 加上 级联未用完的剩余预算。具体地,级联以 0 作为种子先行运行(即当前文件不捐赠任何东西),然后当前文件被裁剪到 currentFileBudget + cascadeFinalSurplus。由于预算只单向流动(级联 → 当前文件,从不反向),当前文件"复用"级联的剩余,从而被裁掉得更少。

能拿到 shares 条目的片段集合为 GlobalBudgetSharePart = GlobalBudgetPart | 'currentFile'

片段 与池的关系
currentFile 从池中定量,但在级联 外部且之后 被裁剪:裁剪上限为 floor(totalTokens * shares.currentFile) + cascadeFinalSurplus,围绕光标/编辑窗口由 createTaggedCurrentFileContentUsingPagedClipping 执行(裁剪上限 currentFile.maxTokens 会在 xtabProvider 中被该池预算覆盖)。它 不在 order 中,级联循环从不渲染它;它吸收级联的剩余而不是向其中捐赠。当 globalBudgetundefined 时回落到自己的 currentFile.maxTokens 上限。
lintOptions 可选、独立格式化、体量小。完全排除——没有 shares 条目,保留自己的独立形状。

当前文件裁剪函数 createTaggedCurrentFileContentUsingPagedClipping 的实现见 promptCrafting.ts:它基于"分页裁剪"围绕 areaAroundEditWindowLinesRange 保留范围,超预算时返回 outOfBudget 错误。

输入参数

输入 说明
globalBudget.totalTokens 单池大小。默认 7500。通过实验 JSON 字符串 chat.advanced.inlineEdits.xtabProvider.globalBudgettotalTokens 字段设置。
globalBudget.order 被渲染 片段的有序列表。排在前面的片段先获得预算,其剩余流向后面的片段。currentFile 不出现在这里。
globalBudget.shares Record<GlobalBudgetSharePart, number>——每个被渲染片段 以及 currentFile 各占 totalTokens 的一个比例。order 各片段加 currentFile 的总和必须等于 1 ± 1e-3

默认值

GlobalBudgetOptions.DEFAULT_ORDERxtabPromptOptions.ts):

['languageContext', 'recentlyViewedDocuments', 'neighborFiles', 'diffHistory']

GlobalBudgetOptions.DEFAULT_SHARES(与现有逐片段上限"体量中性",即不增不减):

片段 份额 totalTokens = 7500 时的基础预算
currentFile 1500/7500 1500
recentlyViewedDocuments 2000/7500 2000
languageContext 2000/7500 2000
neighborFiles 1000/7500 1000
diffHistory 1000/7500 1000

GlobalBudgetOptions.DEFAULT_TOTAL_TOKENS = 7500

这些份额精确复现了现有逐片段上限:currentFile.maxTokens 1500、recentlyViewedDocuments 2000、languageContext 2000、neighborFiles 1000、diffHistory 1000。池总量(7500)是这些上限之和,因此启用默认的 global budget 既不放大也不缩小任何被渲染片段的基础分配。这与源码中 DEFAULT_OPTIONS 的各片段 maxTokens 一致(xtabPromptOptions.tscurrentFile.maxTokens: 1500recentlyViewedDocuments.maxTokens: 2000languageContext.maxTokens: 2000neighborFiles.maxTokens: 1000diffHistory.maxTokens: 1000)。

默认顺序把 languageContext 放在第一位,因为它经常被禁用或为空,其份额会捐赠给紧随其后、几乎总是启用的 recentlyViewedDocuments。级联未用完的部分进入 finalSurplus,最终交给当前文件的裁剪复用。

GlobalBudgetOptions.currentFileBudget(gb) 返回 floor(totalTokens * shares.currentFile)——这是当前文件 基础 份额的唯一事实来源(xtabPromptOptions.ts)。当前文件实际的裁剪上限 = 该基础值 + 级联的 finalSurplus

算法

surplus ← 0                          // 当前文件从不捐赠,所以级联始终从 0 开始
for part in order:
    budget   ← max(0, floor(surplus + totalTokens * shares[part]))
    consumed ← runSubBuilder(part, maxTokens: budget)   // 子构建器 ≤ budget
    surplus  ← max(0, budget - consumed)                // 只流向下一个片段
// 循环结束时的 surplus 作为 finalSurplus 返回,加到当前文件的裁剪上限上

该伪代码与源码实现 runGlobalBudgetCascade 一一对应:循环体中 budget = Math.max(0, Math.floor(surplus + globalBudget.totalTokens * share))promptCrafting.ts#L280-L283),每轮结束后 surplus = Math.max(0, budget - tokensConsumed)#L326),最终 finalSurplus: surplusCascadeResult 返回(#L331-L343)。

要点(源码级补充):

  • 级联 始终以 0 作为种子。当前文件在级联之后被裁剪,只会 接收 剩余而从不捐赠。

  • 级联为每个片段调用现有子构建器,只覆盖其 maxTokens(其余选项从 opts 继承):

    • recentlyViewedDocumentsbuildCodeSnippetsUsingPagedClipping
    • languageContextappendLanguageContextSnippets
    • neighborFilesappendNeighborFileSnippets
    • diffHistorygetEditDiffHistory

    这些子构建器分别定义在 recentFilesForPrompt.tsdiffHistoryForPrompt.ts 中。在级联的 switch 分支里(promptCrafting.ts#L285-L320),recentlyViewedDocuments 通过 { ...opts, recentlyViewedDocuments: { ...opts.recentlyViewedDocuments, maxTokens: budget } } 覆盖上限;diffHistory 通过 { ...opts.diffHistory, maxTokens: budget } 覆盖;neighborFiles 仅在 enabled && neighborSnippets.length > 0 时运行。

  • 每个子构建器行为不变。

  • 每个子构建器返回 tokensConsumed,使用的是它 做预算决策时所用的同一套内部计量(recently-viewed 用分页裁剪行成本、appender 用原始片段成本、diff 历史用逐条 diff 成本)。级联用该上报值计算 surplus,使级联与每个片段实际扣减预算的方式保持一致。

  • surplus 在级联片段之间 只向前流动。最后一个片段未用完的 token 不会丢失:它构成 finalSurplus,由当前文件的裁剪复用。

  • 防御性不变量:每个子构建器必须上报 0 ≤ tokensConsumed ≤ budget。级联对每个片段 softAssert 这一点(不会 静默地把超支截断回预算内,那会掩盖 bug),从而当前文件裁剪所依赖的"守恒论证"成立。对应源码在 promptCrafting.ts#L321-L325softAssert(tokensConsumed >= 0 && tokensConsumed <= budget, ...)

文档跟踪(docsInPrompt)

级联用当前活动文档为 docsInPrompt 播种(promptCrafting.ts#L271-L272),recentlyViewedDocuments 步骤会把其收录的文档加入集合;neighborFiles 读取该集合以避免与已有文档重复,随后 appendNeighborFileSnippets 也会把每个收录的邻居文档加入集合。正是这个依赖关系,使得 GlobalBudgetOptions.validate 拒绝 neighborFiles 排在 recentlyViewedDocuments 之前的顺序。

累积的 docsInPrompt 随后传给 getEditDiffHistory,因此当开启 diffHistory.onlyForDocsInPrompt 时,哪些文档在集合中的变化会影响 diff 选择(diff 步骤只输出文档位于 docsInPrompt 中的条目)。

输出结构

级联的输出镜像 legacy getRecentCodeSnippets 的形状,使 getUserPrompt 的其余部分保持不变,外加当前文件裁剪所用的 finalSurplus

{
    codeSnippets,              // recentlyViewed + langCtx + neighbor,用 "\n\n" 连接
    documents,                 // docsInPrompt
    neighborSnippetsResult,    // 不变遥测载荷
    editDiffHistory,
    nDiffsInPrompt,
    subsections,               // recentlyViewed/langCtx/neighbor 的字符串,用于逐小节 token 上报
    finalSurplus,              // 循环结束时的剩余,被当前文件裁剪复用
}

对应类型 CascadeResultpromptCrafting.ts#L220-L234,其 finalSurplus 字段的注释明确说明:"provider adds it to the current file's clip budget (currentFileBudget + finalSurplus) so the current file, which is clipped last, reuses whatever the cascade left unused."

保证与边界

totalTokens = T、份额为 s_i(校验保证它们有限且非负,见 校验):

  • 逐片段下限:索引 i 处的片段无论如何总能拿到至少 floor(T * s_i) 个 token,与前面片段的行为无关。
  • 逐片段上限:索引 i 处的片段最多拿到逐步取整的部分和 floor(…floor(floor(T * s_0) + T * s_1) + … + T * s_i)——即它自己的份额加上所有前面片段的捐赠,且每一步都取 floor。注意这通常小于 floor(T * (s_0 + … + s_i))
  • 当前文件下限:当前文件总能拿到至少 floor(T * shares.currentFile)(其基础份额),且 finalSurplus ≥ 0 是额外加成的。
  • 池上限:级联管理的 token 总量 ≤ 最后一个片段的上限(恒 ≤ T - floor(T * shares.currentFile))。当前文件随后取自己的基础份额加级联的 finalSurplus,因此受预算管理的片段合计消耗 ≤ T;lint 与脚手架(scaffolding)位于预算之外。
  • 级联片段之间无回流:最后一个级联片段的剩余不会重新分配给更早的片段——它作为 finalSurplus 流向当前文件。
  • 无片段内公平性:单个片段内的大条目可以吃完该片段的整份分配;级联只解决跨片段捐赠问题。

校验

GlobalBudgetOptions.validate每次 级联调用开始时运行(并且在当前文件裁剪之前于 xtabProvider 中再运行一次),配置错误时直接抛错。因为配置是运行时可调的(实验驱动),"大声失败" 优于静默的少分配/超分配。实现见 xtabPromptOptions.ts#L213-L250

规则 错误信息
totalTokens 有限且 >= 0 globalBudget.totalTokens must be a finite, non-negative number, got X
order 无重复片段 globalBudget.order contains duplicate part 'X'
order 中每个片段都有数值型 shares[part] globalBudget.shares is missing entry for 'X'
shares.currentFile 是数值 globalBudget.shares is missing entry for 'currentFile'
每个份额(order 片段 currentFile)有限且 >= 0 globalBudget.shares['X'] must be a finite, non-negative number, got Y
若两者都在,recentlyViewedDocuments 必须先于 neighborFiles globalBudget.order must place 'recentlyViewedDocuments' before 'neighborFiles'
order 各片段份额加 shares.currentFile 之和 ≈ 1(epsilon 1e-3 globalBudget.shares across order must sum to ~1, got ${sharesSum}

为什么非负规则重要:负份额仍可能通过"和 ≈ 1"检查(例如一个片段 -0.25、另一个 1.0)。分配时负份额片段会被截断到 0 预算,但它仍计入份额总和,于是其他片段超配超过池总量——这会让当前文件的 finalSurplus 超过真实剩余。在入口处拒绝负数/非有限份额,才能保证 Σ consumed ≤ totalTokens 可证明。

单元测试对这些抛错路径均有覆盖,见 promptCrafting.spec.ts#L1044-L1105(顺序错误、重复片段、份额和不等于 1、缺 diffHistory、缺 currentFile 等用例)。

配置接入:单个实验驱动的 JSON 字符串

级联由 单个实验驱动的 JSON 字符串 配置,ConfigKey.TeamInternal.InlineEditsXtabGlobalBudget,建模自 modelConfigurationString。该配置键定义在 configurationService.ts#L1036

export const InlineEditsXtabGlobalBudget = defineTeamInternalSetting<string | undefined>(
  'chat.advanced.inlineEdits.xtabProvider.globalBudget', ConfigType.ExperimentBased, undefined);

xtabProvider.tsgetGlobalBudget() 读取它并用 GlobalBudgetOptions.fromConfigString 解析(xtabProvider.ts#L1599-L1620):

private getGlobalBudget(): GlobalBudgetOptions | undefined {
    const configString = configService.getExperimentBasedConfig(InlineEditsXtabGlobalBudget, expService);
    if (!configString) {
        return undefined; // 未设置/空 → 禁用,与生产路径完全一致
    }
    const result = GlobalBudgetOptions.fromConfigString(configString);
    if (result.isError()) {
        telemetryService.sendMSFTTelemetryEvent('incorrectNesGlobalBudgetConfig', { errorMessage: result.err, configValue: configString });
        return undefined; // 坏配置 → 禁用,绝不崩溃
    }
    return result.val;
}

该 JSON 值把三个预算旋钮定义在一起——totalTokensordershares——且 每个字段都是可选的,省略的字段回落到 DEFAULT_TOTAL_TOKENS / DEFAULT_ORDER / DEFAULT_SHARES(解析逻辑见 fromConfigString),因此:

  • undefined / 未设置 / "" → global budget 禁用(生产默认,逐字节一致的 legacy 路径);
  • {}启用,使用体量中性的默认值;
  • {"totalTokens":6000} → 启用,仅覆盖池大小;
  • {"totalTokens":12000,"order":[…],"shares":{…}} → 完全自定义。

fromConfigString 先做结构化校验(通过 GlobalBudgetOptions.VALIDATORxtabPromptOptions.ts#L259-L269,其中 shares 一旦出现就必须列出 全部 片段——渲染片段加 currentFile,部分 shares 对象会被拒绝,保证池被完全分配),再合并到默认值上,最后运行语义 GlobalBudgetOptions.validate;任何解析、结构或语义失败都返回 Result.error 并禁用预算(不抛异常,改由遥测上报)。

启用后,xtabProvider 先汇集级联输入、运行级联,再以上限 currentFileBudget(globalBudget) + cascade.finalSurplus 裁剪当前文件(替代独立的 currentFile.maxTokens)。已运行的级联以 precomputedCascade 传入 getUserPrompt,保证它只渲染 一次getUserPrompt 只在 globalBudget 已设置时才认 precomputedCascade,因此级联恰好运行一次,渲染出的片段与定量完全一致(promptCrafting.ts#L82-L92)。

实验控制的设置:

设置 默认值 用途
chat.advanced.inlineEdits.xtabProvider.globalBudget undefined 定义 totalTokensordershares 的 JSON 字符串;未设置/空字符串即禁用预算

迁移说明:旧的 globalBudget.enabled(布尔)与 globalBudget.totalTokens(数值)设置已被这 单个 JSON 字符串 取代。任何钉住旧键的在线实验分组必须迁移:enabled:true + totalTokens:N 变成 JSON {"totalTokens":N};裸的 enabled:true 变成 {}。由于 totalTokens 同时给 currentFile 供资,钉住旧总量(如 60008000)的分组应迁移到 {}(或 7500)以保持体量中性。

演算示例

以下示例均使用 DEFAULT_ORDERDEFAULT_SHAREStotalTokens = 7500

基础分配:floor(7500 * share) 逐片段 →

片段 基础分配
languageContext 2000
recentlyViewedDocuments 2000
neighborFiles 1000
diffHistory 1000
currentFile(最后裁剪) 1500

总和 = 7500。级联只迭代四个被渲染片段,种子为 0。其循环结束时的剩余(finalSurplus)加到当前文件基础分配上,下表最后一行的 budget 即展示该结果。

示例 A —— 级联片段用量温和;当前文件吸收剩余

片段 流入 surplus budget consumed 流出 surplus
languageContext 0 2000 0 2000
recentlyViewedDocuments 2000 4000 1500 2500
neighborFiles 2500 3500 500 3000
diffHistory 3000 4000 500 3500
currentFile(最后裁剪) 3500(finalSurplus 1500 + 3500 = 5000 5000 0

实际放置 token:0 + 1500 + 500 + 500 + 5000 = 7500。级联只消耗了 2500,其 3500 的剩余流入当前文件,使其从 1500 基础长到 5000,被裁得更少。

示例 B —— 级联为空;当前文件复用整个池

片段 流入 surplus budget consumed 流出 surplus
languageContext 0 2000 0 2000
recentlyViewedDocuments 2000 4000 0 4000
neighborFiles 4000 5000 0 5000
diffHistory 5000 6000 0 6000
currentFile(最后裁剪) 6000(finalSurplus 1500 + 6000 = 7500 ≤ 7500

无语言上下文、历史为空、邻居禁用时,每个级联片段都消耗 0,整个非-currentFile 池(2000 + 2000 + 1000 + 1000 = 6000)全部进入 finalSurplus。当前文件的裁剪上限变成 1500 + 6000 = 7500 = T——它实际上复用了整个池。这是"孤立编辑单个文件"的常见场景。该场景有直接测试佐证:promptCrafting.spec.ts#L1113-L1125 断言 cascade.finalSurplus 恰为 6000,"currentFileBudget 1500 + finalSurplus 6000 = 7500 = T"。

示例 C —— 级联填满池;当前文件只拿到基础份额

片段 流入 surplus budget consumed 流出 surplus
languageContext 0 2000 2000 0
recentlyViewedDocuments 0 2000 2000 0
neighborFiles 0 1000 1000 0
diffHistory 0 1000 1000 0
currentFile(最后裁剪) 0(finalSurplus 1500 + 0 = 1500 1500 0

实际放置 token:2000 + 2000 + 1000 + 1000 + 1500 = 7500。每个级联片段恰好填满自己的份额,finalSurplus = 0,当前文件回落到 1500 基础——即逐片段下限。当前文件永远不会缩到该基础值以下。

被禁用的片段

片段可以因配置(languageContext.enabled = falseneighborFiles.enabled = false)或因没有输入数据(无语言上下文响应、邻居片段为空、无编辑历史)而被禁用。这些片段仍留在 order 中——接线的代码总是使用 DEFAULT_ORDER/DEFAULT_SHARES——所以它们的槽位仍然运行,只是 consumed = 0,其全部份额向前捐赠。到达级联末端的一切都会成为 finalSurplus,由当前文件的裁剪复用而不是被浪费。

从源码结构看,各片段"无输入即零消耗"由级联循环内的条件自然保证:languageContext 分支仅在 if (langCtx) 时调用 appender,neighborFiles 分支仅在 opts.neighborFiles.enabled && neighborSnippets && neighborSnippets.length > 0 时运行(promptCrafting.ts#L297-L309),此时 tokensConsumed 保持为 0,其 budget 全额计入 surplus 流出。

languageContextneighborFiles 同时关闭时的有效上限

DEFAULT_ORDER 与池 T 下,每次捐赠步骤都逐步取 floor(级联种子为 0,设 C_rv = floor(floor(T·langCtxShare) + T·rvShare) 为 recently-viewed 的有效上限):

片段 有效上限
languageContext 0(被消耗掉)
recentlyViewedDocuments C_rv(langCtx 份额 + 自身份额,逐步 floor
neighborFiles 0(被消耗掉)
diffHistory floor(floor((C_rv − consumed_rv) + T·neighborShare) + T·diffShare)(自身份额 + neighbors 份额 + recently-viewed 剩余,逐步 floor

T = 7500 时,C_rv = floor(2000 + 2000) = 4000,级联池总上限为 floor(floor(4000 + 1000) + 1000) = 6000diffHistory 剩下的部分成为当前文件的 finalSurplus

recentlyViewedDocuments 消耗量 diffHistory 上限 finalSurplus → 当前文件
4000(填满上限) 2000 0(当前文件为基础 1500)
1500 4500 最多 4500
0 6000(整个级联池) 最多 6000(当前文件最多 7500)

两个启用片段在 T = 7500 下都"饥饿"的演算:

片段 流入 surplus budget consumed 流出 surplus
languageContext 0 2000 0 2000
recentlyViewedDocuments 2000 4000 4000 0
neighborFiles 0 1000 0 1000
diffHistory 1000 2000 2000 0
currentFile(最后裁剪) 0(finalSurplus 1500 1500 0

级联放置了 6000(recently-viewed 吸收了 langCtx 的捐赠;diff 历史吸收了 neighbors 的捐赠),finalSurplus = 0,当前文件保持在 1500 基础,总计 7500。

顺序注意事项

recentlyViewedDocuments 在级联捐赠池上有 优先索取权languageContext 的份额),因为它在 order 中更早。若 recently-viewed 很"饿",diff 历史在 T = 7500 时只能看到"neighbors + 自身份额 = 2000"——它无法直接触达 languageContext 的份额。diff 历史(最后一个级联片段)剩下的部分仍会流向当前文件。

要让 diff 历史分享捐赠,要么重排使 diffHistory 先于 recentlyViewedDocuments,要么重新平衡 shares。顺序仍须满足校验规则:recentlyViewedDocuments 必须先于 neighborFiles(因为 neighborFiles 会查询由 recently-viewed 填充的 docsInPrompt)。

另一种部署方式是把被禁用的片段 移除order 并重新平衡 shares 使其和为 1——捐赠便被静态地"烘焙"进去,而不是从级联中涌现。接线的代码不会这么做;它总是传入 DEFAULT_ORDERDEFAULT_SHARES

最后裁剪当前文件

当前文件是级联剩余的天然汇聚点,因为它是唯一围绕兴趣点(光标)被裁剪的片段,总能吸收更多上下文。与其"先构建 prompt、测量未用量、再把当前文件放大重构建"(两遍做法有重复计算剩余的风险),实现上直接 最后裁剪当前文件:先运行级联,然后用级联未用掉的全部预算来定当前文件的尺寸。

机制

xtabProvider 中,启用 global budget 时的流程(对应 gatherContextAndClipCurrentFile):

  1. 汇集级联输入(语言上下文、邻居片段)——它们 不依赖 当前文件裁剪,所以可以先行产出;
  2. 运行 runGlobalBudgetCascade(...)(种子为 0;当前文件不捐赠,所以级联只 给出);
  3. 最后裁剪当前文件,上限为 currentFileBudget + cascade.finalSurplusxtabProvider.ts#L303-L306);
  4. 组装 prompt,把已算好的级联作为 precomputedCascade 传给 getUserPromptgetUserPrompt 只在 globalBudget 设置时才认 precomputedCascade,因此级联恰好运行 一次,渲染出的片段与定量一致。

globalBudgetundefined(生产默认)时以上均不适用:当前文件按自己的 currentFile.maxTokens 裁剪,输入在之后汇集,级联根本不运行——与 legacy 路径逐字节一致。裁剪超预算时返回 NoNextEditReason.PromptTooLarge('currentFile')xtabProvider.ts#L307-L309)。

守恒性(证明)

因为当前文件不捐赠,预算单向流动(级联 → 当前文件),所以不存在重复计算。级联种子为 0 时,循环结束剩余伸缩(telescope)为:

finalSurplus ≤ Σ(totalTokens · shareᵢ)        for i in order
             − Σ(consumedᵢ)                    = (T − T·share_cf) − C_cascade

因此当前文件的裁剪上限为:

cfBudget = floor(T · share_cf) + finalSurplus ≤ T · (Σ all shares) − C_cascade
⇒ C_cf + C_cascade ≤ T · (Σ all shares)        (总量受池约束)        ✅

且由于 finalSurplus ≥ 0cfBudget ≥ floor(T · share_cf)——当前文件 永远不会缩到 基础份额以下。非负、经校验的份额(见 校验)是第一个不等式成立的前提。

注意事项 1 —— 份额和的容差validate 接受 |Σ shares − 1| ≤ 1e-3,所以上界是 T · (Σ shares) 而非恰好 T。份额和略大于 1 的配置最多超配 ~1e-3 · T(默认 T = 7500 时约 7.5 个 token)。份额恰好和为 1 的配置(默认值如此)给出干净的 ≤ T 上界。欠配(和 < 1)只是浪费一点预算。

注意事项 2 —— 内部计量,而非完整渲染 prompt:"≤ T" 是针对受预算片段的 内部 token 计量(分页裁剪行成本、原始片段成本、diff 条目成本)而言的。完整渲染的 prompt 还带有标签包裹、related-info 脚手架、lint 与 postscript,它们位于池之外。所以保证是"受预算片段合计消耗 ≤ T",而不是"整个 prompt ≤ T 字符"。测试断言的是当前文件区域 增长 与内部计量,而非绝对的全 prompt 上界。

演算示例

T = 6000languageContext 禁用,级联消耗 rv 1500 + neighbor 500 + diff 500 = 2500C_cascade = 2500),share_cf = 1500/7500 = 1/5 ⇒ 基础 currentFileBudget = floor(6000 · 1/5) = 1200

  • 级联种子为 0;其 finalSurplus = (1600 + 1600 + 800 + 800) − 2500 = 2300
  • 当前文件最后被裁剪到 1200 + 2300 = 3500(= 6000 − 2500)。

当前文件从 1200 → 3500,恰好吸收了级联未用的 2300——符合"预算 6k,第一次构建消耗 4k ⇒ 把剩下的 2k 给当前文件"的直觉。

取舍与注意

  • 当前文件不捐赠。预算只从级联流向当前文件。当前文件较小时,其基础份额 不会 交给级联片段(它们只拿到自己的份额)。若部署目标是丰富 邻居/历史 上下文(而非当前文件上下文),需要重新平衡 shares 给那些片段更多。
  • 等待(await)顺序变化。因为级联先于当前文件裁剪运行,语言上下文与邻居片段的汇集发生在当前文件裁剪之前。因此任何 PromptTooLarge('currentFile') 的提前返回/取消原因在 global budget 下都发生在那些 await 之后。这是该 opt-in 特性可接受的后果;生产路径保持 legacy 顺序。
  • Next-cursor 预测器不受影响xtabNextCursorPredictor 保留自己专用的当前文件上限,从不把 global budget 带入其 prompt,因此无论此特性是否启用都逐字节一致。

测试佐证

该机制的关键性质在 promptCrafting.spec.tsgetUserPrompt — globalBudget cascade 测试组中有系统覆盖:

  • 预算充足时级联路径与 legacy 路径产生 相同 prompt#L1031-L1042);
  • 顺序/重复/份额校验错误会正确抛错(#L1044-L1105);
  • 级联无消耗时 finalSurplus 携带全部未用池(6000)(#L1113-L1125);
  • 级联有消耗时 finalSurplus 相应收缩(#L1127-L1141);
  • precomputedCascade 与内部计算产生 逐字节相同 的 prompt(#L1143-L1157)。

provider 侧另有实验配置用例('{}' 启用默认、totalTokens: 2000/8000 覆盖池大小),见 xtabProvider.spec.ts#L1272-L1290

小结

Global Budget Cascade 是 NES/xtab prompt 构建中一个边界清晰、opt-in 的预算打通方案:以 totalTokens / order / shares 单池三旋钮替代碎片化的逐片段上限,用单向 surplus 级联让被禁用或空置片段(如 languageContext)的份额自动流向活跃片段,并让当前文件在级联之后按"基础份额 + finalSurplus"最后裁剪、复用全部剩余;配合严格的 validate(含非负份额与份额和 ≈ 1 的强制)和 softAssert 的消耗量不变量,保证受预算片段合计消耗不超过池总量。默认配置(池 7500、DEFAULT_ORDERDEFAULT_SHARES)与现有逐片段上限体量中性,未启用时生产路径保持逐字节不变,这使其成为一个低风险、可实验回滚的改进方向。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
982
503
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384