• 欢迎关注我的微信公众号“ Falost ” 右边扫描关注 --->>

上下文工程收官:怎么管住一次请求的 token 花销?我做了一张“上下文预算表”

AI / 大模型 神棍 220℃ 0评论

上下文工程这个系列写到今天,该收尾了。前几篇我一直在算账:KV Cache 那篇算显存账,提示缓存+压缩那篇算钱账,Lost in the Middle 那篇算准确率账。三本账算完,我却发现账是分开记的——缓存开关在配置里,压缩阈值在代码深处,检索条数在另一个函数里。真被问到“一次请求到底花了多少钱、哪些 token 本可以不花”,我答不上来。

所以这篇我干的事很直接:把三本账合成一张“上下文预算表”,给每次请求做预算。先把要装进窗口的内容拆成几块,标清每块多少 token、能不能缓存、该不该压缩、要不要检索;请求前估算,请求后拿官方返回的 usage 对账。下面把表的结构、算账代码、对账方法一次给全,读完能直接照抄一份。

一、预算第一步:把一次请求拆成四块

先上结论:别一上来就想着“省 token”,先搞清楚这次的 token 都花在了哪些内容上。我每次请求的 messages 固定拆成四块:

内容块 我这边典型量级 变不变 默认动作 放哪
固定前缀(系统提示+工具定义) 2000 token 左右 基本不变 原样保留,吃前缀缓存 消息最前
知识材料(检索文档) 3~5 条共 3000 上下 每次不同 重排精减,smart_order 摆位 中间
对话历史 1000 起步,随轮次涨 一直涨 截断+总结压缩 中间靠后
本次问题 200 左右 每次不同 不处理,交给路由 消息最后

量级是我自己的典型值,关键是分类:分清哪些内容几乎不变、哪些每次都在变,后面的决策都围绕它展开。

二、先量后省:token 怎么数、钱怎么算

结论:不要凭感觉估 token,把“测量”做成流水线的一部分。每次响应的 usage 里就带着这些数:DeepSeek 官方文档定义 prompt_tokens = prompt_cache_hit_tokens + prompt_cache_miss_tokens,另有 completion_tokens、total_tokens。我测单块 token 的办法:先发只带固定前缀的请求,再把内容块一块块加回去,两次的 prompt_tokens 相减,就是新增那块的大小。

算钱公式就一行:费用 = 命中token×命中单价 + 未命中token×未命中单价 + 输出token×输出单价,单价以官方定价页为准。按 2026-09 定价页 deepseek-v4-flash 的 off-peak 价:每百万输入 token,缓存命中 0.007 美元、未命中 0.22 美元,输出 0.66 美元;工作日部分时段是 peak,价格翻倍,其余时段半价。

拿上面的量级算:输入共 6200 token,输出按 500。第一次请求缓存还没建起来,6200 全算未命中:6200×0.22/100万=0.001364 美元,加输出 500×0.66/100万=0.00033 美元,合计约 0.001694 美元。第二次起固定前缀 2000 token 命中:2000×0.007/100万=0.000014 美元,剩下 4200 未命中约 0.000924 美元,加输出合计约 0.001268 美元——单次省约 25%。这套逻辑我写成了个小类,本地跑过,输出与上面一致:

from dataclasses import dataclass

@dataclass
class Block:
    name: str
    tokens: int
    fixed: bool      # True=属于固定前缀,可命中缓存

class ContextBudget:
    def __init__(self, hit=0.007, miss=0.22, out=0.66):
        self.hit, self.miss, self.out = hit, miss, out  # 美元/百万token

    def estimate(self, blocks, output_tokens=500):
        fixed = sum(b.tokens for b in blocks if b.fixed)
        dynamic = sum(b.tokens for b in blocks if not b.fixed)
        def usd(hit_tok, miss_tok):
            return (hit_tok * self.hit + miss_tok * self.miss
                    + output_tokens * self.out) / 1_000_000
        first = usd(0, fixed + dynamic)      # 首次:缓存未建立,全 miss
        steady = usd(fixed, dynamic)         # 之后:固定前缀命中
        return first, steady

blocks = [Block("system+tools", 2000, True),
          Block("history", 1000, False),
          Block("retrieved_docs", 3000, False),
          Block("question", 200, False)]
first, steady = ContextBudget().estimate(blocks)
print(f"首次请求 {first:.6f} 美元,之后每次 {steady:.6f} 美元,省 {(first-steady)/first*100:.1f}%")

三、预算表四块怎么填:缓存、精减、压缩、路由

结论:预算表不只是记账工具,每块内容都要有一个默认动作。我的顺序:固定前缀→缓存;知识材料→检索精减;对话历史→压缩截断;本次问题→路由分流。

1. 固定前缀:让它稳定,缓存才认你

DeepSeek 的上下文缓存默认对所有用户开启,不用改代码——但它是前缀缓存,命中规则很严格:后续请求必须完整匹配某个已持久化的缓存前缀单元,中间多一条消息、改一个字,整条前缀就断了。官方文档的例子:两个请求结尾不同就都 miss,公共前缀被识别持久化后,第三个请求才命中。所以我的铁律:固定内容永远放最前面且顺序不变,检索结果、本轮问题这类动态内容一律往后放,绝不插进固定前缀中间。

缓存也不是立刻生效:前缀持久化要几秒,我基本按“首次请求 miss、之后才命中”来估;缓存几小时到几天不用会被自动清掉,长尾低频场景别指望它。值不值:off-peak 下每百万 token 命中 0.007 美元、未命中 0.22 美元,差 30 倍出头。上面例子只靠 2000 token 固定前缀就省 25%,固定资料越多的应用省得越多。

2. 知识材料:检索精减,别全文硬塞

这块在 Lost in the Middle 那篇讲过:检索只留 3~5 条、重排、smart_order 把关键文档钉头尾。它是准确率账,更是钱账——少塞一万 token 就少花一万 token 的钱,答案还更准。细节见上篇,链接在文末。

3. 对话历史:压缩优先于截断

长会话直接砍历史会丢信息。我的做法沿提示缓存那篇:早几轮先总结成固定格式摘要放历史开头(内容不变,能跟着固定前缀吃缓存),最近 N 轮原样保留;还不够就上 LLMLingua 这类 prompt 压缩。

4. 本次问题:省不了,但能决定带不带前面三块

问题本身没法压缩,但能决定其他三块要不要出场:简单问答直接走 short 路径,不挂文档也不带历史——整个输入都省了,这笔省得最狠。路由规则上篇给过,直接复用。

四、对账:钱花完要看得见

结论:预算表要闭环,最后一步是把每次请求的真实花费记下来。usage 字段是官方返回的,我写了个小函数直接落日志:

def log_usage(resp):
    u = resp["usage"]
    return {
        "prompt_tokens": u["prompt_tokens"],
        "cache_hit": u.get("prompt_cache_hit_tokens", 0),
        "cache_miss": u.get("prompt_cache_miss_tokens", 0),
        "completion_tokens": u["completion_tokens"],
    }

每周汇总只看三个数:命中率(hit/(hit+miss))、单请求平均输入 token、费用趋势。命中率长期上不去,八成是固定内容没放稳或被动态内容打断;输入 token 涨得快,基本是历史块失控。我给自己定的线:命中率 50% 以上、周费用环比不涨,超了就当周排查。

总结

一句话:上下文预算不是“少给模型看”,而是让每块内容都知道自己为什么在这里、值多少钱——先分类(固定/材料/历史/问题),再测量(usage 对账),后决策(缓存/精减/压缩/路由),最后复盘(命中率、费用趋势)。上下文工程的三本账——显存、钱、准确率——到这篇算是合成一本了。

下期预告:上下文工程这条线到这篇就收官了。账算完,我发现还有一类 token 没算进去——图片。DeepSeek 已上线视觉模型 deepseek-v4-flash-vision-exp,图片按像素折算 token 计费,和文本不是一套账,一张截图可能比几千字还贵。下个系列写多模态:视觉模型怎么选、一张图折算多少 token、图片能不能进 RAG。想一起折腾的,下篇见。

参考文献

转载请注明:Falost的小窝 » 上下文工程收官:怎么管住一次请求的 token 花销?我做了一张“上下文预算表”

如果你觉得这篇文章不错或者对你有帮助,想请我喝一杯咖啡,可以打赏
喜欢 (0)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址