ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

蓝耘 MaaS 的「思考成本」怎么算?我写了个剖析器,把 6 个模型的 Token 账本翻了个遍

蓝耘 MaaS 的「思考成本」怎么算?我写了个剖析器,把 6 个模型的 Token 账本翻了个遍 1. 蓝耘 MaaS 上「思考成本」到底藏在哪一次 KV Cache 提问引出的 Token 账本问题蓝耘 MaaS 的「思考成本」指的是模型在返回可见回答之前内部推理reasoning所消耗的那部分 Token 费用。它不在 content 里却实实在在计入 completion_tokens 并参与计费。很多人第一次看到账单会疑惑我明明只让它回两句话为什么 completion_tokens 是 900 多答案往往就藏在 reasoning_tokens 这个字段里。这篇内容面向已经在用蓝耘 MaaS、或者准备把多个模型接进生产链路的开发者交付一个可复制的 Token 成本剖析器把 6 个模型的输入、输出、思考链路拆开算清楚。我先把问题场景说透。假设你做一个客服自动回复用户问「订单什么时候到」你期望模型回一句 30 字以内的话。如果这个模型默认开启强推理它可能先在心里推演 800 个 token 的「思考过程」最后才吐出 30 个字。你为 830 个 token 付费其中 800 个你根本看不见。这就是「思考税」——reasoning_tokens 除以 completion_tokens 的比值。比值越高你为看不见的部分付的钱越多。更麻烦的是不同模型对同一道题的思考深度差异极大。同一句 prompt有的模型零思考直接答有的模型想近千 token 还答非所问甚至有的模型烧了 token 却输出 0 个字符。这些差异不会在报错里体现只会悄悄反映在账单上。所以上生产前你必须回答一个比「能不能调通」更关键的问题同样一句话不同模型到底烧了多少 Token其中多少是看不见的钱要回答这个问题光看控制台的总消耗不够你需要一个能逐模型拆解 usage 的剖析器。核心思路是同一道题并行喂给 N 个模型用流式调用同时捕获 content 和 reasoning_content再从 usage.completion_tokens_details.reasoning_tokens 提取隐藏思考量最后算出思考税、有效信息密度和估算成本。下面我把这套剖析器的配置、调用参数和核算脚本完整给出来你可以直接复现。在动手之前先明确三个指标的定义后面所有数据都围绕它们展开。思考税 reasoning_tokens ÷ completion_tokens衡量「看不见的钱」占比有效信息密度 输出字符数 ÷ completion_tokens衡量每个 token 换回多少可见内容估算成本 输入 token × 输入价 输出 token × 输出价 思考 token × 输出价÷ 1M。注意思考 token 通常按输出价计费这是很多账单被低估的原因。2. TaoToken 前置把多模型调用统一到一个 OpenAI 兼容入口在写剖析器之前得先解决一个工程问题6 个模型如果各自用不同的 SDK、不同的鉴权方式剖析器会变得又长又难维护。我的做法是统一走 OpenAI 兼容接口这样一份 client 代码就能打所有模型。TaoToken 提供的就是这样一个统一入口Base URL 是https://taotoken.net/api兼容 OpenAI SDK 的调用方式模型 ID 通过 model 参数区分。为什么剖析器要先讲接入层因为 Token 账本的准确性依赖 usage 字段的完整返回。如果接入层对 usage 做了裁剪或者流式模式下不返回 usage你的剖析器就抓不到 reasoning_tokens。TaoToken 的接口在流式模式下支持stream_options{include_usage: True}能在最后一个 chunk 里带回完整 usage这是剖析器能工作的前提。具体接入分三步。第一步拿到 API Key。登录后在控制台的 API Keys 页面创建建议给剖析器单独建一个 key方便后续按项目统计消耗。第二步确认 Base URL 和模型 ID。Base URL 固定为https://taotoken.net/api模型 ID 用你实际要测的那几个比如 deepseek 系列、qwen 系列、glm 系列、kimi 系列、minimax 系列。第三步用 OpenAI SDK 初始化 client把 api_key 和 base_url 传进去。这里有个容易踩的坑不同模型的 model ID 命名规则不一样。有的平台用短名如deepseek-v4-flash有的用带路径的全名如/maas/deepseek-ai/DeepSeek-V3.2。剖析器里我用了 DEFAULT_MODELS 列表把两种命名都放进去运行时如果某个模型报 404就说明这个 ID 在当前路由下不存在需要去模型列表接口确认。你可以先用client.models.list()拉一遍可用模型再填进剖析器。接入层还有一个细节超时和重试。剖析器要跑 6 个模型如果某个模型响应慢整个脚本会被拖住。我在 client 初始化时设置了 timeout60并对每个模型的调用做了 try/except单个模型失败不影响其他模型。这样即使某个模型路由不稳定你依然能拿到其余 5 个的完整数据。对于长期做多模型对比的场景可以考虑用 Coding Plan 来降低频繁调用的成本把剖析器跑成日常任务。需要强调的是剖析器的价值不在于「连上」而在于「连上之后能拿到结构化数据」。所以接入层要保证三件事usage 完整返回、reasoning_content 可捕获、单模型失败可隔离。这三点做到了后面的核算脚本才有意义。3. 可复制配置剖析器的 JSON 配置、调用参数与核算脚本这一节是全文的核心我把剖析器的配置拆成三块模型清单配置、调用参数配置、成本核算脚本。你可以直接复制运行。先看模型清单和价格表用一个 JSON 结构管理方便后续替换。价格字段仅作估算参考实际以控制台实时单价为准{ base_url: https://taotoken.net/api, models: [ {id: deepseek-v4-flash, input_price: 2, output_price: 8}, {id: /maas/deepseek-ai/DeepSeek-V3.2, input_price: 2, output_price: 8}, {id: qwen3.6-flash, input_price: 1, output_price: 4}, {id: /maas/zhipuai/GLM-5.2, input_price: 4, output_price: 12}, {id: kimi-k2.5, input_price: 4, output_price: 12}, {id: minimax-m3, input_price: 1, output_price: 4} ], prompt: 用不超过80字解释 KV Cache 是什么并给一个生活类比。, max_tokens: 400, temperature: 0.3 }价格单位是「元 / 百万 token」。注意思考 token 按 output_price 计费这是核算脚本里的关键假设。如果你的目标模型对思考 token 有单独定价需要在这里拆成三个价格字段。接着是调用参数配置。剖析器用流式调用因为只有流式才能同时拿到 reasoning_content 和 content 的增量也才能测出 TTFT首字延迟。关键参数有三个streamTrue开启流式stream_options{include_usage: True}让最后一个 chunk 带回 usagemax_tokens400限制输出上限防止某个模型无限思考。temperature 设 0.3让输出相对稳定便于横向对比。下面是核算脚本的核心逻辑用 Python 写成依赖 openai1.40import json, time from dataclasses import dataclass, asdict from openai import OpenAI dataclass class Row: model: str; ok: bool; err: str ttft: float 0.0; total_sec: float 0.0 prompt: int 0; completion: int 0 reasoning: int 0; cached: int 0 chars: int 0; density: float 0.0 tax: float 0.0; cost_yuan: float 0.0 head: str def profile(client, model, prompt, max_tokens, in_price, out_price): t0 time.time(); ttft None content, reasoning [], [] stream client.chat.completions.create( modelmodel, messages[{role: user, content: prompt}], max_tokensmax_tokens, temperature0.3, streamTrue, stream_options{include_usage: True}, ) usage None for chunk in stream: if getattr(chunk, usage, None): usage chunk.usage if not chunk.choices: continue d chunk.choices[0].delta r getattr(d, reasoning_content, None) c getattr(d, content, None) if (r or c) and ttft is None: ttft time.time() - t0 if r: reasoning.append(r) if c: content.append(c) sec time.time() - t0 txt .join(content); reason .join(reasoning) if usage is None: return Row(modelmodel, okFalse, errno usage) p getattr(usage, prompt_tokens, 0) or 0 comp getattr(usage, completion_tokens, 0) or 0 ctd getattr(usage, completion_tokens_details, None) rt getattr(ctd, reasoning_tokens, None) or 0 ptd getattr(usage, prompt_tokens_details, None) cached getattr(ptd, cached_tokens, None) or 0 cost (p * in_price comp * out_price rt * out_price) / 1_000_000 return Row( modelmodel, okTrue, ttftround(ttft or sec, 3), total_secround(sec, 3), promptp, completioncomp, reasoningrt, cachedcached, charslen(txt), densityround(len(txt) / comp, 2) if comp else 0, taxround(rt / comp, 2) if comp else 0, cost_yuanround(cost, 5), headtxt[:60].replace(\n, ), )主函数负责遍历模型清单、隔离异常、输出 JSONdef main(): cfg json.load(open(profiler_config.json, encodingutf-8)) client OpenAI(api_keyos.getenv(TAOTOKEN_API_KEY), base_urlcfg[base_url], timeout60) rows [] for m in cfg[models]: try: r profile(client, m[id], cfg[prompt], cfg[max_tokens], m[input_price], m[output_price]) except Exception as e: r Row(modelm[id], okFalse, errstr(e)[:80]) rows.append(r) print(f[{OK if r.ok else ERR}] {m[id]:35s} f税{r.tax:.2f} 密度{r.density:.2f} ¥{r.cost_yuan}) ok [r for r in rows if r.ok] print(\n--- 思考税排行越低越省---) for r in sorted(ok, keylambda x: x.tax): print(f {r.model:35s} 税{r.tax:.2f} 密{r.density:.2f}) json.dump({rows: [asdict(r) for r in rows]}, open(profiler_results.json, w, encodingutf-8), ensure_asciiFalse, indent2)运行方式很简单export TAOTOKEN_API_KEY你的密钥 python3 maas_profiler.py脚本会自动对每个模型发同一道题捕获 reasoning_content 和 content从 usage 里提取 reasoning_tokens 和 cached_tokens算出思考税、密度和估算成本最后写出 profiler_results.json。这份 JSON 就是你的「Token 账本」后续画图、排序、做选型决策都基于它。配置里还有两个可调项值得说明。一是 max_tokens设太小会截断输出导致密度失真设太大又会让某些模型无限思考400 是个折中值。二是 temperature剖析器追求可复现所以设低如果你要测创意类任务的成本可以调高但同一批对比要保持一致。把这两项固定下来横向对比才有意义。4. 验证请求与成功结果6 个模型的 Token 账本实测配置写好后跑一遍看结果。我用同一道题「用不超过 80 字解释 KV Cache 是什么并给一个生活类比」喂给 6 个模型这是一道需要理解加类比加字数控制的综合题能较好区分哪些模型在认真思考、哪些在空转。终端输出节选如下[OK] deepseek-v4-flash 税0.61 密度0.72 ¥0.00168 [OK] DeepSeek-V3.2 税0.00 密度1.95 ¥0.00039 [OK] qwen3.6-flash 税0.95 密度0.07 ¥0.00724 [OK] GLM-5.2 税0.00 密度0.00 ¥0.00492 [OK] kimi-k2.5 税0.00 密度1.84 ¥0.00076 [OK] minimax-m3 税1.00 密度3.48 ¥0.00339逐行解读。DeepSeek-V3.2 思考税 0密度 1.95成本最低是性价比标杆。kimi-k2.5 同样零思考税密度 1.84成本略高但延迟更短。deepseek-v4-flash 思考税 0.61属于「有思考但可控」适合需要推理能力的场景。minimax-m3 密度最高 3.48但思考税 100%且输出里混入了推理标签需要后处理。qwen3.6-flash 思考税 0.95密度只有 0.07意味着每 14 个 token 才换回 1 个可见字符成本还是最高的。GLM-5.2 密度 0烧了 400 个 completion token 却输出 0 字符属于空转翻车。把数据拆开看更清楚。qwen3.6-flash 的 completion_tokens 是 927其中 reasoning_tokens 是 877占 94.6%实际输出只有 69 字。它花了近千 token 在「想」最后只挤出 69 个字还花了 5 秒多。这不是 bug是 Qwen 系列默认开启强推理模式的特性对简单题也会做大量内部推理。如果你用它做高频低复杂度任务这笔思考税会让账单膨胀好几倍。GLM-5.2 的情况更隐蔽。它烧了 400 个 completion token但既没有返回 reasoning_content也没有返回 content可见字符为 0。最离谱的是它的估算成本还是第二高的。这说明它在这道题上出现了纯空转token 计费了用户什么都没收到。生产环境里这种「白花钱」比 404 更危险因为你连报错都没有只是账单悄悄涨。minimax-m3 有三个问题叠加思考税 100%把 Think 标签漏进了正文还无视 80 字限制输出了 1391 字。唯一亮点是它命中了 128 个 cached tokens说明 MiniMax 这条线路的缓存机制在工作。但缓存命中的内容如果还是带泄漏标签的超长回复那缓存只是在加速错误。验证成功的标志是 profiler_results.json 里每个模型都有完整的 prompt、completion、reasoning、cached、density、tax、cost 字段。你可以用这份 JSON 做二次分析比如按思考税排序、按密度排序、按成本排序找出最适合你场景的模型。我实测下来零思考税的模型在高频简单任务上优势明显而带适度思考的模型在推理任务上更稳。如果你想进一步验证模型对话效果可以拿同一道题在模型对话里手动跑一遍对比剖析器抓到的 usage 和界面显示是否一致。这一步能帮你确认剖析器的数据没有偏差。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth 报错对照剖析器跑起来后最常见的报错集中在接入层和 usage 解析层。下面按真实报错逐条排查。401 Unauthorized。这是鉴权失败九成是 API Key 没传对。检查三处环境变量名是否和脚本里一致我用的是 TAOTOKEN_API_KEYKey 是否有多余空格Base URL 是否写成了https://taotoken.net/api而不是带 /v1 的旧地址。如果 Key 是从控制台复制的注意别把前后引号也复制进去。local proxy failed / connection error。这类报错通常是网络层问题不是 Key 的问题。先确认你的运行环境能正常访问 Base URL可以用 curl 测一下curl -s https://taotoken.net/api/models \ -H Authorization: Bearer $TAOTOKEN_API_KEY如果 curl 也失败说明是网络出口问题检查本机网络配置。如果 curl 成功但 Python 失败多半是 SDK 版本太旧升级到 openai1.40。reading choices / NoneType object has no attribute choices。这是流式解析的经典坑。流式返回的 chunk 里最后一个 chunk 的 choices 可能是空列表只带 usage。如果你直接访问chunk.choices[0]就会报这个错。正确写法是先判断if not chunk.choices: continue再取 delta。剖析器代码里已经处理了这一点如果你自己改代码务必保留这个判断。OAuth / token expired。如果你用的是带 OAuth 的接入方式报这个错说明 token 过期了需要重新走授权流程。剖析器场景建议直接用 API Key避免 OAuth 的刷新逻辑增加复杂度。如果你确实需要 OAuth把刷新逻辑封装在 client 初始化之前。no usage 报错。剖析器里如果 usage 为 None会返回errno usage。这通常是因为没开stream_options{include_usage: True}。有些模型在流式模式下默认不返回 usage必须显式开启。如果开了还是拿不到检查该模型是否支持 usage 返回可以先用非流式调用测一次。404 model not found。模型 ID 写错了。不同路由下模型命名规则不同有的用短名有的用全路径。先用client.models.list()拉一遍可用列表再填进配置。如果列表里没有你要的模型说明当前账号或当前路由没有开通。思考税算出来是 0 但 completion 很大。这不一定是对可能是该模型不返回 reasoning_tokens 字段。有些模型把思考内容直接混进 contentusage 里不单列 reasoning_tokens。这种情况下思考税会显示 0但实际成本已经包含在 completion 里。判断方法是看输出里有没有推理标签残留如果有说明思考内容被算进了 content。排查顺序建议先确认 401 和网络再确认流式解析最后确认 usage 字段。大部分报错集中在前两步。如果你在排查过程中需要确认模型 ID 或 usage 结构可以到接入文档里对照字段说明。6. 语义一致 CTA把剖析器变成日常选型工具剖析器跑通一次只是开始真正的价值在于把它变成日常选型工具。我的做法是每周跑一次把 profiler_results.json 存档观察同一模型在不同时间的思考税和成本是否稳定。如果某个模型的思考税突然飙升说明上游可能调整了推理策略这时候就要重新评估它在生产链路里的位置。对于长期做多模型对比和 Agent 开发的场景频繁调用会产生不小的成本。可以考虑用 Coding Plan 来覆盖日常的编码和调试调用把剖析器跑成定时任务。这样你既能看到真实的 Token 账本又不用为每次实验付全价。如果你更关注单个模型的对话效果想手动验证剖析器抓到的数据可以直接在模型对话里跑同一道题对比界面显示的 token 消耗和剖析器的记录。两者一致说明你的剖析器配置正确。最后给一个实用技巧把剖析器的输出按「思考税 × 成本」排序找出「又贵又想」的模型优先从生产链路里剔除。我实测下来零思考税加高密度的模型在高频任务上几乎总是更优而带思考的模型只在真正需要推理的任务上才值得。选型不是选最强的是选账本最匹配你场景的。
返回列表