用 Java 接入大模型(九):token 计数与上下文预算

简介: 本文详解大模型中 token 的精准计算与预算管理:为何必须自主统计、估算与精确计数的分工策略、中文/代码的特殊偏差、如何实测专属系数、上下文窗口的倒算公式、超长截断的5条实操规则,以及流式调用中 usage 的正确获取方式。掌握它,才能让成本可控、行为可预测。

前八篇里,token 这个词反复出现:max_tokens 会预扣额度、重试会放大成本、上下文太长会导致回答退化。这一篇专门把这个数讲清楚——怎么算准它,以及预算不够的时候该砍哪一段。


一、为什么必须自己数

大多数人是等到账单超出预期,才开始关心 token。但有几件事,不数就一定会踩:

输入成本会随对话轮次递增。 第五篇讲过,多轮对话的本质是每次把历史重发一遍。也就是说第一轮的输入是一句话,第十轮的输入是九轮对话加一句话。输出长度基本稳定,输入却在不断膨胀。 长对话里,输入成本反超输出成本是常态,而不是例外。

max_tokens 的预扣会造成误解。 第三篇提过:很多厂商按你声明的 max_tokens 做额度预扣,而不是按实际用量。你写 max_tokens = 8000,即使这次只生成 50 个 token,也可能先占 8000 的额度。高并发下就会出现"明明没发几个请求,却报额度不足"——原因是你自己把额度占住了。

上下文超限的表现不一致。 有的实现直接报错,有的悄悄从中间截掉一段。后者最麻烦:请求成功了,但模型看到的历史缺了一块,回答莫名其妙。这类问题只能靠主动控制长度来避免,不能等着报错。

所以数 token 不是为了省钱,它是"让行为可预测"的前提。


二、估算还是精确:两条路各有代价

估算:按字符数乘一个系数。中文大致按字符数估,英文按 4 字符约 1 token,代码介于两者之间。

优点是快——纯本地计算,零开销,可以放在请求入口做粗筛。

缺点是对中文、代码、混合文本的偏差很大,而且是系统性偏移,不是随机误差。这意味着它不会"平均下来差不多",而会稳定地偏某个方向,导致你按估算留的余量在某些场景下根本不够。

精确:用厂商提供的计数接口或 SDK 来算。

优点是准。缺点是多一次调用开销(有网络往返),高频场景下这个开销不能忽略。

务实做法是分场景用:

  • 入口处粗筛用估算——判断"这个请求大概是不是太大了",快速拒绝明显超限的输入;
  • 真正决定截断位置时用精确值——这里估错一点,就可能导致请求被上游拒绝,或者截多了丢信息;
  • 记录成本时用接口返回的 usage(真实值),不要用估算。

一句话:估算用来快速判断,精确值用来做决定,usage 用来记账。


三、中文和代码为什么特别难估

这是本文最需要动手验证的一段,因为"偏差系数"这件事没有通用答案。

几类差异的来源:

  • 中文:一个汉字常常不是 1 个 token。常用字可能 1 个 token,生僻字、专业术语、专有名词可能被切成 2~3 个。所以"字数乘 1"会低估。
  • 代码:缩进、括号、运算符、下划线命名,切分方式极其不规律。一个长变量名可能被切成好几段,一段 JSON 可能比同样字符数的自然语言多出不少 token。
  • 标点与空白:换行、缩进、连续空格都可能单独占 token。
  • Emoji 与特殊符号:往往占多个 token,用字符数估会严重低估。

这些差异加起来,会让同一个"字符数"在不同内容上对应完全不同的 token 数。 所以下面这个方法值得真跑一次。


四、测出你自己的偏差系数

拿三段内容各测一次,对比估算值与精确值:

  • 一段纯中文(比如 500 字的产品说明)
  • 一段代码(比如 200 行的 Java 文件)
  • 一段混合文本(中文里夹杂英文术语和代码片段)

精确值用厂商的计数接口拿。以通义千问的 OpenAI 兼容接口为例:

curl -s https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen3.8-max",
    "max_tokens": 1,
    "messages": [{"role": "user", "content": "<把你的一段测试文本放这里>"}]
  }'

把返回体里的 usage.prompt_tokens 记下来,就是这段文本的精确输入 token 数。

(说明:官方也提供专门的 token 计数接口或 SDK 方法,比上面这种"发一次请求只为看 usage"的做法更省。具体接口名以你所用平台文档为准。)

然后估算用一段本地代码:

public final class TokenEstimator {
   

    /**
     * 粗糙的 token 估算,仅用于入口粗筛。
     * 中文按 0.7 个字符/token 估,英文与符号按 4 字符/token 估。
     * 真实系数请用第四节的测量方法自己标定。
     */
    public static int estimate(String text) {
   
        if (text == null || text.isEmpty()) {
   
            return 0;
        }
        int cjk = 0;
        int other = 0;
        for (int i = 0; i < text.length(); i++) {
   
            char c = text.charAt(i);
            // CJK 统一表意文字基本区与扩展区
            if ((c >= 0x4E00 && c <= 0x9FFF)
                    || (c >= 0x3400 && c <= 0x4DBF)
                    || (c >= 0xF900 && c <= 0xFAFF)) {
   
                cjk++;
            } else {
   
                other++;
            }
        }
        return (int) Math.ceil(cjk / 0.7) + (int) Math.ceil(other / 4.0);
    }
}

跑完你得到的是一张对照关系:同一段文本,估算是多少、精确是多少、偏差百分比是多少。

这张表就是你后续所有预算计算的依据。 别用别人给的系数——它取决于你的内容构成,别人的系数放在你的语料上可能就是错的。

一个必须接受的结论:任何估算都会有偏差,所以预算里必须留余量。 这也是下一节的内容。


五、预算怎么分配

关键认知:上下文窗口是输入和输出共享的。 窗口不是"输入上限",而是"输入加输出的总额度"。你多带一段历史,就少一截输出空间。

所以预算要从窗口里倒着算:

可用输入额度 = 窗口大小 − max_tokens − 安全余量

安全余量必须留,建议 10%~15%。 三个理由:

  • token 估算本身有偏差(上一节刚测过);
  • 部分实现在内部会加结构性的 token(角色标记、分隔符),你的估算里没有;
  • 你偶尔会漏算某个字段。留余量比事后排查"为什么偶尔超长"要省事。

举例说明这个计算为什么重要。假设窗口是 32K,你设 max_tokens = 4000,那么:

可用输入 ≈ 32000 − 4000 − 4000(余量) = 24000

这 24000 才是你带历史的上限,而不是 32K。如果按"窗口 32K"去塞输入,一旦输入接近 32K,输出就没有空间了,回答会被立刻截断——表现是"它突然只说了半句话就停了"。


六、超长了砍哪一段

历史必然有一天会超出预算。砍在哪,直接决定回答质量。第五篇讲过策略(滑窗、摘要压缩、按重要性挑),这里补上按 token 计的落地要点。

第一,用 token 数判断,不要用消息条数。 三条短消息和三条长消息,长度可能差十倍。按条数截必然出现时好时坏。

第二,别把成对结构截断。 这是最容易踩的坑:

  • user 和 assistant 要成对删除,否则会出现连续两条 user,或者历史以 assistant 开头;
  • 用了工具调用时,assistant 消息里的 tool_calls 和紧随其后的 tool 结果消息必须一起保留或一起删除。

从中间硬切会让结构不合法,部分接口直接拒绝请求。

第三,从最老的一轮开始删,system 永远保留。 system 是全部规则的来源,删掉之后模型行为会突然不受约束。

第四,超长输入先摘要,不要硬截。 如果用户粘贴了一份很长的文档,硬截会切掉后半部分的信息,模型基于残缺内容给出自信的错误答案。更稳的做法是先让模型对文档分段摘要,再用摘要去做真正的任务。 多花一次调用,但避免了"看起来在答,其实在编"。

第五,截断位置尽量落在语义边界。 按 token 数算出的位置可能正好切在一句话中间。往前退到最近的句子或段落结束处,读起来更完整,模型理解也更好。


七、流式下的 usage 要显式开启

第二篇提过这一点,这里给完整处理方式。

流式响应默认不返回 usage。如果你在做成本统计,这个数丢了就等于记账断了。开启方式是在请求里加上:

String body = """
    {
   
      "model": "qwen3.8-max",
      "stream": true,
      "stream_options": {
    "include_usage": true },
      "messages": [
        {
   "role": "user", "content": "用三句话介绍杭州西湖"}
      ]
    }
    """;

开启后,usage 会在最后一个数据块里给出。所以读取循环里要单独处理一下:

Usage usage = null;

// 在读取每个数据块时
JsonNode node = mapper.readTree(data);
if (node.has("usage") && !node.get("usage").isNull()) {
   
    usage = mapper.treeToValue(node.get("usage"), Usage.class);
}

// 流结束后落库
if (finished && usage != null) {
   
    recordCost(usage.getPromptTokens(), usage.getCompletionTokens());
}

有两点要注意:

  • 不是所有兼容实现都支持这个参数。 接之前先用一个真实请求试一次,确认 usage 真的回来了,别默认它有。
  • 流中断的情况下拿不到 usage。 因为它在最后一块里。这时候要么按估算记账,要么就不计——但要明确选一个,别让成本统计里出现大量静默缺失的记录。

八、成本预估该怎么做

有了准确的 token 数,成本预估就变成了简单的乘法。但它有几个容易被忽略的系数:

  • 输入和输出分开计价,而且输出通常更贵。所以"减少输出"和"减少输入"的收益不一样,要分别看。
  • 重试会放大成本。 第四篇算过:重试最多 2 次的情况下,最坏消耗是请求数的 3 倍。而且重试的是失败请求,用户什么都没拿到。做预估时要把这个系数算进去。
  • 多轮对话的输入是累积的。 不能按单轮输入乘轮数,那样会低估。
  • 是否开启了思考/推理模式会影响输出 token,同一句话可能生成数倍的 token。如果你的模型有这个开关,预估时要单独看待。

一个够用的估算模板:

单次成本 ≈ (输入 token × 输入单价) + (输出 token × 输出单价)
月度成本 ≈ 单次成本 × 日请求量 × 30 × 重试系数 × (1 + 余量)

余量建议留 20%~30%——因为用户的实际用法一定比你预估的更费。


九、小结

把这一篇收成几句可操作的:

  • 必须自己数:输入随轮次递增、max_tokens 会预扣、超限可能被静默截断
  • 估算与精确分工:粗筛用估算、决定截断用精确值、记账用 usage
  • 偏差系数自己测:中文、代码、混合文本各测一次,别用别人的系数
  • 预算倒着算:可用输入 = 窗口 − max_tokens − 10%~15% 余量
  • 截断守规则:按 token 不按条数、成对删除、保留 system、超长先摘要
  • 流式记 usage:显式开启,中断时要有明确处理

最后一句判断标准:如果你说不出自己当前请求的输入大概占了多少 token,那这个系统的成本和行为就都是不可控的。


下一篇

token 能算准之后,下一个话题是并发。单请求跑通了,把它放进一个每秒几十次调用的服务里,会冒出三类新问题:连接池、线程池、上游配额。还有一个很隐蔽的坑——线程池复用时 ThreadLocal 没清理,导致数据串到别的请求上。下一篇讲并发与限流。


本文是「用 Java 接入大模型」系列第九篇。上一篇讲了提示词的模板化与版本管理,建议按顺序看。文中 token 估算系数与预算比例为实践起点,真实数值请按第四节的方法自行标定;具体接口与参数以你使用的平台文档为准。

目录
相关文章
|
2月前
|
机器学习/深度学习 JSON 前端开发
用 Java 接入大模型(一):把流式输出跑通
本文是「用 Java 接入大模型」系列首篇,聚焦最易卡壳的流式输出:详解 SSE 协议原理、手写 60 行无依赖流式客户端(兼容通义千问/OpenAI),并揭示新手必踩的三大坑——不按行解析、漏传 `stream:true`、忽略空 content。代码即学即用,拒绝玄学,只讲真实可跑的 Java 实现。
285 1
|
26天前
|
人工智能 移动开发 自然语言处理
千问办公QwenWork指南:介绍、能力、官网、使用、版本及价格说明
千问办公(QwenWork)是阿里巴巴推出的一站式AI Agent生产力平台,支持“对话即交付”:用户用自然语言描述需求,即可自动生成PPT、Excel、网页、报告等可落地成果;深度集成钉钉,覆盖多端,赋能个人与企业高效办公。阿里千问办公QwenWork官网:https://t.aliyun.com/U/0VCTGt
|
25天前
|
监控 Java 关系型数据库
SpringCloud Alibaba完整使用
SpringCloud Alibaba 微服务全栈实战教程,从零带你搭建 Nacos 注册配置中心、Gateway 网关、Sentinel 限流熔断、Seata 分布式事务、OpenFeign 远程调用等完整微服务组件。配套全套视频教程 + 保姆级搭建文档,手把手教你跑通微服务项目,适合在校生、转行及 1-3 年后端开发者学习练手、简历加分,文末可领学习资料与技术交流群。
|
27天前
|
人工智能 安全 测试技术
Agent 也要被测试:如何评测一个 Agent 的 Skills?
本文探讨AI Agent中Skill评测的核心挑战与实践方法:指出Skill失效常表现为“行为漂移”而非报错;提出三层评测体系——静态校验(结构/安全/去重)、轨迹评估(步骤与序列合理性)、端到端配对测试(A/B对照测Skill Lift);强调需兼顾结果有效性、路径合理性、副作用与退化风险,并倡导将评测嵌入CI门禁,用最小可行方案启动防退化机制。
|
26天前
|
存储 缓存 安全
阿里云2核2G99元和2核4G199元云服务器活动,最长4年续费周期,个人建站首选
本文梳理阿里云99元经济型e实例(2核2G/3M/40G)与199元u1实例(2核4G/5M/80G)的适用场景、参与资格、续费规则与成本结构。二者面向个人开发者、学生、小微团队等入门上云用户,在活动延期至2029年3月31日的规则下,符合条件者经多次续费可接近4年低价持有。文章提醒用户关注配置一致性、续费窗口、保有数量与截止时间,并按项目生命周期选型,做好域名、备案、存储与备份等完整成本规划。
|
26天前
|
缓存 前端开发 程序员
DeepSeek-V4.1-Flash-1017 上手实测:3D 直出,效果到底如何
DeepSeek-V4.1-Flash-1017 上手实测:3D 飞行特斯拉、千里江山、折叠掌机一次直出,跑分 77%、最高档单任务 1.3 元
DeepSeek-V4.1-Flash-1017 上手实测:3D 直出,效果到底如何
|
25天前
|
人工智能 IDE 安全
开发者手册|阿里云Qoder CN指南:产品能力、用途、订阅收费全解析,免费300 Credits
Qoder CN是阿里云推出的国产AI智能体产品系列(原通义灵码),专注软件开发与办公场景。含IDE插件、CLI工具、Cloud Agents平台及数字员工等,支持自主编程、深度IDE集成与本地安全运行。注册即赠800次3.8-Max调用,个人版59元/月起。阿里云Qoder CN官网:https://t.aliyun.com/U/fEiOLV
262 0
|
25天前
|
弹性计算 人工智能 安全
真香啊!DeepSeek Harness保姆级教程:基于阿里云计算巢已部署安装Deepseek-Harness社区版
本方案介绍如何通过阿里云计算巢,一键部署开源智能体框架 DeepSeek Harness(基于 Cordis 引擎、“一切皆插件”架构),支持快速接入 DeepSeek/兼容大模型,构建专属 AI Agent 平台。阿里云计算巢官网:https://t.aliyun.com/U/PEl9rE
|
25天前
|
人工智能 API 调度
阿里云百炼 Token Plan 深度解析:统一额度计费,底层架构与 API 接入指南
Token Plan通过统一Credits抵扣、多协议网关适配、多模型资源池调度的架构,解决了传统多模型开发中计费复杂、工具集成繁琐、账单管理困难的痛点。一份订阅,即可调用文本、图像、视频、语音等多模态模型,搭配联网检索、代码解释器等Agent工具,并且兼容市面上主流AI编程客户端,大幅降低AI应用开发成本。
206 0
|
25天前
|
弹性计算 人工智能 安全
从 0 到 1 玩转 DeepSeek Harness 保姆级教程:阿里云计算巢 + ECS 一键部署,含百炼 API Key 配置与代码命令+避坑教程
综合来看,基于阿里云计算巢 + ECS 搭建 DeepSeek Harness 智能体平台,门槛低、上手快。先获取百炼 API Key(注意地域隔离与密钥保存),再通过计算巢一键部署,最后通过 Nginx 反向代理与计算巢安全代理安全访问 Web UI,即可投入使用。开发者也可以不依赖计算巢,直接使用 npx 命令或源码安装方式在本地或 ECS 上运行。部署时重点关注 API Key 只显示一次、地域不能混用、13080 与 3080 端口分工等要点,即可快速搭建一套功能完整、安全可控、可灵活扩展的 AI Agent 平台,支撑各类智能体应用的开发与落地。
199 0

热门文章

最新文章