1. 一次对话到底花了多少钱为什么你算不明白很多人第一次用 DeepSeek Harness 跑通对话后盯着控制台那行回复就以为完事了。等到月底看账单发现费用比预期高出一截却完全说不清钱花在哪。问题不在模型而在于你从没认真看过返回结果里的usage字段。usage是每次 API 调用后服务端回传的用量凭证它告诉你这次请求消耗了多少输入 Token、多少输出 Token、有多少命中了缓存。DeepSeek Harness 作为第三方协议适配层会把这部分信息整理成结构化对象交给你。但小白常见的做法是只打印message.content把usage直接丢掉等于每次调用都在“盲付”。这篇面向刚上手 Harness 的读者用一个真实对话演示从返回结果里提取prompt_tokens、completion_tokens和缓存命中信息算清单次成本最后落成一张可聚合的 JSONL 账单。全程用 TaoToken 统一 Key 接入方便你在一个控制台里核对用量、验证缓存是否真的生效。适合谁已经能跑通 Harness 最小对话、但还没建立成本观测习惯的开发者。2. 用 TaoToken 统一 Key 接入先把用量口径对齐在算钱之前得先保证你看到的usage是可信的。如果你同时用多个 Key、多个端点账单口径就会打架。TaoToken 的做法是给你一个统一 Key模型对话、Coding Plan、API 调用都走同一个入口用量在控制台里集中呈现核对起来不用来回切换。接入动作很简单打开官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册后进入控制台在 API Keys 页面生成一个 Key。这个 Key 就是你后面所有请求的凭证。注意Key 只在生成时完整显示一次复制后立刻存进环境变量别写进代码。拿到 Key 后把 Harness 的 Base URL 指向 TaoToken 的 API 地址 https://taotoken.net/api。这样你的请求会经过统一网关返回的usage字段和 TaoToken 控制台里的用量统计是同一套口径。后面算出来的成本才能和控制台对得上。注意TaoToken 是统一接入与用量管理入口不是让你绕过任何合规流程。Key 的权限范围、余额、调用记录都在控制台可查出问题先看那里。如果你还没生成 Key直接去 API Keys 页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里面有各语言的 Base URL 配置示例。3. 可复制的 usage 解析脚本与账单模板3.1 环境准备先建一个干净的测试目录装好依赖。Python 用 3.10 以上Harness 用你当前核验过的版本。Key 通过环境变量注入别硬编码。mkdir -p ~/harness-billing cd ~/harness-billing python3 -m venv .venv source .venv/bin/activate pip install deepseek-harness export TAOTOKEN_API_KEY你的Key3.2 最小调用与 usage 提取下面这段脚本做三件事发一次短对话、把usage完整打印出来、把不含正文和密钥的账单追加进 JSONL 文件。你可以直接复制运行。import os import json import time from deepseek_harness import DeepSeekHarness client DeepSeekHarness( api_keyos.environ[TAOTOKEN_API_KEY], base_urlhttps://taotoken.net/api, disable_thinking_by_defaultTrue, ) out client.chat( modeldeepseek-v4-flash, messages[{role: user, content: 用不超过 80 字解释什么是缓存命中}], max_tokens256, extra_body{thinking: {type: disabled}}, ) usage out.get(usage) or {} message out.get(message) or {} record { ts: int(time.time()), model: out.get(model), finish_reason: out.get(finish_reason), prompt_tokens: usage.get(prompt_tokens), completion_tokens: usage.get(completion_tokens), total_tokens: usage.get(total_tokens), prompt_cache_hit_tokens: usage.get(prompt_cache_hit_tokens), prompt_cache_miss_tokens: usage.get(prompt_cache_miss_tokens), estimated_cost_usd: usage.get(estimated_cost_usd), } print(回复:, message.get(content)) print(用量:, json.dumps(record, ensure_asciiFalse)) with open(billing.jsonl, a, encodingutf-8) as f: f.write(json.dumps(record, ensure_asciiFalse) \n)跑完后你会看到类似这样的输出回复: 缓存命中指请求的内容与之前已缓存的前缀一致服务端直接复用无需重新计算从而降低延迟和费用。 用量: {ts: 1755..., model: deepseek-v4-flash, finish_reason: stop, prompt_tokens: 42, completion_tokens: 38, total_tokens: 80, prompt_cache_hit_tokens: 0, prompt_cache_miss_tokens: 42, estimated_cost_usd: 0.000021}3.3 字段含义对照表字段含义小白怎么理解prompt_tokens输入 Token 总数你发出去的内容占多少completion_tokens输出 Token 总数模型回给你的内容占多少total_tokens总 Token上面两项相加prompt_cache_hit_tokens命中缓存的输入 Token这部分通常更便宜prompt_cache_miss_tokens未命中缓存的输入 Token这部分按正常价算estimated_cost_usd估算费用单次成本用于聚合3.4 账单表格模板JSONL 适合程序聚合但人看还是表格直观。你可以用下面这个模板把多次调用汇总成一张账单。把billing.jsonl里的记录读出来按模型和日期分组即可。import json from collections import defaultdict agg defaultdict(lambda: {calls: 0, prompt: 0, completion: 0, hit: 0, cost: 0.0}) with open(billing.jsonl, encodingutf-8) as f: for line in f: r json.loads(line) key r[model] agg[key][calls] 1 agg[key][prompt] r[prompt_tokens] or 0 agg[key][completion] r[completion_tokens] or 0 agg[key][hit] r[prompt_cache_hit_tokens] or 0 agg[key][cost] r[estimated_cost_usd] or 0.0 print(f{模型:20}{调用:6}{输入:8}{输出:8}{缓存命中:10}{费用USD:10}) for model, v in agg.items(): print(f{model:20}{v[calls]:6}{v[prompt]:8}{v[completion]:8}{v[hit]:10}{v[cost]:10.6f})这张表就是你自己的成本账单。每次实验后跑一遍费用变化一目了然。4. 验证请求缓存命中到底有没有生效4.1 设计一个能触发缓存的实验缓存命中的前提是请求前缀稳定。DeepSeek 的缓存机制对相同前缀的输入会复用计算结果。所以你要做的是第一次发一个带长系统提示的请求第二次发同样的系统提示、只改用户问题观察prompt_cache_hit_tokens是否从 0 变成正数。system_prompt 你是一个严谨的技术助手回答必须简洁不超过 100 字。 * 20 def ask(question): out client.chat( modeldeepseek-v4-flash, messages[ {role: system, content: system_prompt}, {role: user, content: question}, ], max_tokens128, extra_body{thinking: {type: disabled}}, ) u out.get(usage) or {} return { q: question, hit: u.get(prompt_cache_hit_tokens), miss: u.get(prompt_cache_miss_tokens), cost: u.get(estimated_cost_usd), } print(ask(什么是 Token)) print(ask(什么是缓存))4.2 成功结果长什么样第一次调用hit应该是 0miss等于prompt_tokens。第二次调用因为系统提示前缀完全一致hit应该变成正数miss明显下降estimated_cost_usd也随之降低。如果你看到第二次的hit大于 0说明缓存生效了。实测下来稳定前缀越长第二次的命中比例越高单次成本下降越明显。这也是为什么工程上建议把系统提示、工具 Schema 这些不变内容放在前面把用户问题放在后面。4.3 用 TaoToken 控制台交叉核对脚本跑完后去 TaoToken 控制台看用量记录。找到对应时间段的调用核对prompt_tokens和completion_tokens是否和你的 JSONL 账单一致。如果一致说明你的解析逻辑没问题如果不一致先检查是不是有别的请求混进来了。控制台入口在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。5. 本篇常见错排查5.1 usage 是空的最常见的原因是中间层把usage字段丢了。有些封装只返回message不返回完整响应对象。解决方法是确认你用的 Harness 版本会透传usage并且你没有在代码里手动裁剪返回结果。另外流式模式下usage可能只在最后一个 chunk 出现需要单独收集。5.2 缓存命中一直是 0先检查前缀是否真的稳定。系统提示里如果混入了时间戳、随机 ID、动态拼接的用户名前缀每次都变缓存自然不命中。把动态内容移到用户消息里系统提示保持纯静态。其次确认模型和端点一致换模型或换 Base URL 都会导致缓存失效。5.3 费用对不上estimated_cost_usd是估算值实际计费以控制台为准。如果你发现脚本算的和控制台差很多先确认是不是有并发请求没写进 JSONL或者 Key 被别的地方用了。另外缓存命中的 Token 单价和未命中不同如果你的估算公式没区分这两部分结果会偏高。5.4 401 或 403Key 没注入成功或者环境变量名写错了。检查TAOTOKEN_API_KEY是否在当前终端可见别把 Key 写进代码后提交到 Git。如果确认 Key 没问题去控制台看余额和权限范围。5.5 finish_reason 是 length输出被max_tokens截断了。这时候completion_tokens等于你设的上限但内容不完整。算成本时要注意截断的请求照样计费。要么提高上限要么把任务拆小。6. 把成本观测变成习惯到这里你已经有了三样东西一个能提取usage的脚本、一张能聚合的账单表、一套验证缓存命中的方法。接下来要做的不是继续加功能而是把这三样固定成每次实验的收尾动作。我的建议是每次跑完 Harness 实验先看finish_reason是不是stop再看usage有没有写进 JSONL最后跑一遍聚合脚本看当天总费用。如果缓存命中率低于预期回头检查前缀是否稳定。这套动作花不了两分钟但能让你在费用失控之前就发现问题。如果你还没生成统一 Key现在去 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 建一个接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。想先验证模型返回的usage结构可以直接在模型对话页试一次https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。如果你打算长期跑编码类 AgentCoding Plan 页面有更集中的用量视图https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。下一篇会接着讲怎么用 pytest 锁住消息、缓存和流式这三条底线让成本观测从手动变成自动。
企业数字化 ERP 产品动态
相关推荐
骁龙8 Elite Gen 6双旗舰深度解析:第六代AI引擎与端侧算力再进化 1. 一发布就是双旗舰:Gen 6 和 Extreme Gen 6 在打什么算盘1.1 同代双芯的分工逻辑高通这次没有憋单颗旗舰,而是直接端出 Snpadragon 8 Elite Gen 6 和 Snapdragon 8 Extreme Gen 6 两颗芯片,放在过去几代产品里不算常见。之前骁龙系旗舰芯片… · 2026/9/25 10:55:03
Themida/WinLicense 1.8-2.x 脱壳与调试辅助实战指南 简介:这是一套面向逆向分析人员的Themida WinLicense脱壳与调试辅助工具集,覆盖1.8.X至2.X版本保护程序,适合具备一定Windows逆向基础、需要开展加壳识别、调试跟踪与脱壳流程验证的从业者。包内共292个文件,约2.23MB,… · 2026/9/25 10:54:56
PaddleSpeech PP-TTS:流式语音合成系统原理、训练优化与服务部署全指南 人工智能语音音频NLP媒体生成 【免费下载链接】PaddleSpeech Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation … · 2026/9/25 11:34:55
从零搭建Soft-RoCE环境:用软件模拟RDMA网卡,攻克RoCE学习门槛 做网络的人,尤其是搞存储和HPC的,应该都听过RoCE的大名。RDMA技术能把数据从网卡直接拷进应用内存,省掉内核协议栈的开销,延迟能压到微秒级。但话又说回来,RoCE的“学习门槛”其实不在协议本身,而在硬件——… · 2026/9/25 11:34:42
Linux装Chrome全指南:rpm与deb选择、源配置与依赖问题排查 在Linux上装Chrome,对老鸟来说不算事,但对刚从Windows切过来的新手,第一关就常卡在安装包上:官网页面给你两个选项,一个后缀是rpm,一个后缀是deb,到底该下哪个?下回来之后怎么装&… · 2026/9/25 11:34:42
网安实战能力怎么练?新手到资深三阶段:基础工具到应急响应全覆盖 网安实战能力怎么练?新手→资深 3 阶段:从基础工具到应急响应全覆盖我在带新人、也偶尔被朋友拉着做技术交流的时候,最常听到的一句话就是:“我教程看了几十个,工具也装了一大堆,怎么一遇到真实的环境还是不… · 2026/9/25 11:34:30
MicYou常见问题终极FAQ:连不上、有延迟、没声音?一次讲清所有排查技巧 MicYou常见问题终极FAQ:连不上、有延迟、没声音?一次讲清所有排查技巧 【免费下载链接】MicYou MicYou is a powerful tool that turns your Android device into a high-quality microphone for your PC. 项目地址: https://gitcode.com/gh_mirrors/mi/MicYou
MicYou … · 2026/9/25 11:33:58
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37