bowenliang123/dsh-context
bowenliang123/dsh-contextDSH 上下文洞察面板:Context 仪表盘 + /context命令 + Context 浏览器,查看 Context的分类组成、内容详情、演进趋势、压缩/注入事件、统计等一站式 Context 全生命周期管理。
DeepSeek Harness(dsh)的记账插件:从会话日志里读出每一次计费请求的 token 用量,存成一本本地账本,按官方价目现算金额。
session/event,不监听任何 waterfall 事件,也不调用 agent.inject()。ctx.tallyPricing(报价)、ctx.tallyUsage(账本查询)、ctx.tallyBalance(余额)和 ctx.tallyBudget(预算)。接口定义在 dsh-tally-api。tally 访问同样的数据,见下文 Remote API。dsh plugin --profile web add dsh-tally@beta
插件需要 Node.js >=22.19.0(使用内置的 node:sqlite)。
回填在后台进行,不阻塞 dsh 启动,回填期间实时记账照常进行:
ctx.tallyUsage.status() 查看。rebuild() 之后才会补记。rebuild() 之后才会补记。| 配置项 | 默认值 | 说明 |
|---|---|---|
providerRoutes | { "deepseek-official": "deepseek", "deepseek-account": "deepseek" } | 会话日志里的路由 id 到价格数据厂商的映射。官方 DeepSeek 在日志里有两个路由 id:deepseek-official(API key 登录)和 deepseek-account(账号登录)。没有映射的路由按同名厂商查价格。 |
overrides | [] | 厂商价格覆盖,见下文。可以在设置页修改,改动立即生效,插件不重新加载。 |
dbPath | $DSH_HOME/tally/tally.v1.sqlite | 账本数据库文件。未设置 DSH_HOME 时为 ~/.dsh/tally/tally.v1.sqlite。 |
backfillBatch | 20 | 回填时每批同时读取的会话数。 |
balanceDebounceMs | 3000 | 记账后刷新余额前的去抖时间(毫秒)。 |
balanceMinIntervalMs | 60000 | 同一路由两次自动刷新余额之间的最小间隔(毫秒)。 |
balanceTimeoutMs | 10000 | 余额请求的超时时间(毫秒),超时后请求被中止。 |
budgetPath | $DSH_HOME/tally/budgets.json | 预算文件,同时保存已提醒的阈值。未设置 DSH_HOME 时为 ~/.dsh/tally/budgets.json。 |
timeZone | 宿主时区 | 预算按日、按月统计时使用的 IANA 时区,例如 Asia/Shanghai。 |
自建网关或第三方转发的路由不在价格数据里,可以用 overrides 指定价格。同一个路由只有第一条覆盖生效。
按某个已知厂商(及可选的模型)的价格计:
overrides:
- provider: my-gateway
kind: alias
target:
provider: deepseek
model: deepseek-flash # 可选,省略时沿用日志里的模型名
手动填写单价(每百万 token,十进制字符串):
overrides:
- provider: my-gateway
kind: manual
currency: CNY
models:
my-model:
input: "2"
cacheRead: "0.5" # 可选
cacheWrite: "2" # 可选
output: "8"
手动价格没有峰谷之分(band 为 flat),推理 token 计入输出。某个维度没有填单价却用到了 token 时,这条记录视为“无价格”(cost: null),不会按 0 计。
单价写错(例如不是合法的十进制数)、或者别名指向的厂商或模型不在价格数据里的覆盖会被跳过:插件照常运行,日志里输出一条警告,ctx.tallyUsage.status().errors 里也会列出这条覆盖对应的厂商。
overrides 是可在设置页直接编辑的配置项(volatile):保存后价格规则在原地重建,插件不重新加载。因为金额是查询时现算的,修改后所有查询(包括历史账目)立即按新规则计价,不需要重建账本;Remote 的 status 和 catalog 也随之更新,打开着的 watch 流会收到一条新的 hello,提示界面重新读取数据。
计量单位是一次计费尝试。幂等键是 (会话 id, 结算事件的 seq),同一条记录不会被记两次。
用量规则与官方 token-meter 一致:同一次尝试里,最终的 assistant 消息用量替换流式阶段的用量;llm/retry-started 之后算作新的一次计费,所以失败重试的请求也会计费。
厂商与模型:优先取 assistant/message 里记录的 provider 和 model;以 assistant/attempt 或 llm/retry 结束的尝试,取最近一次 request/header 里的配置。
计费时间取尝试开始时的事件时间(step/start 或 llm/retry-started),据此判断峰谷时段。
用量未知:厂商没有上报用量、或上报的用量不满足官方校验规则时,记为 usageKnown: false,不计金额,在汇总里计入 unknownUsageCount。
分叉会话:从父会话继承来的前缀事件不记账,只记分叉之后自己的部分。
子代理会话照常计费,归到最近的非子代理祖先名下(rootSessionId)。分叉会话的 rootSessionId 是它自己。
金额不入库。账本只存 token 数、路由、模型和计费时间,查询时按当前价格数据现算,所以价格数据被修正后,历史金额会自动更正。
金额一律是十进制字符串,内部用 BigInt 定点运算,不使用浮点数。不同币种分开统计,不做换算。查不到价格时返回 null,永远不会当作 "0"。
重建:ctx.tallyUsage.rebuild() 清空账本(包括“回填已完成”标记)后从全部会话日志重新回填。数据库 schema 版本不一致时也会自动删库重建。
未结束的对话:读取一个停在对话中间的会话时,会话查询服务会在日志末尾补上表示“中断”的结束事件,方便展示。账本不记录这些补出来的事件,等写入方之后写下真实的结束事件时再继续记账,不会漏记。
tally/recorded 事件只在实时记账时发出,即一次请求结算的时候;回填和重建写入的历史账目不发事件,需要历史数据时请用 query 或 session 查询。
已删除的会话:dsh 没有“会话已删除”事件,所以每次回填扫描(包括启动时的增量扫描)在列出会话之后,把账本里有账目、但已不在会话列表中的会话标记为已删除;之后重新出现在列表里的会话会清除标记。只有当列表里至少有一个已持久化的会话时才做这项比对,避免持久化服务不可用时把所有会话都误标为删除;正在运行的会话不会被标记。删除标记不影响任何金额:账目保留,汇总照常计入。按会话分组查询(groupBy: 'session')时,每组的 deletedCount 给出其中属于已删除会话的账目数。
ctx.tallyBalance 是各路由余额查询函数的注册表,本身不读取凭据、也不发起网络请求;查询函数由厂商插件(例如 dsh-tally-balance-deepseek)通过 register(route, fetcher) 注册。route 是会话日志里的路由 id,例如 deepseek-official、deepseek-account。
get(route) 返回缓存的快照,没有缓存时先查询一次;没有注册查询函数的路由返回 null。refresh(route) 立即查询,不受自动刷新间隔限制;同一路由同时只有一个请求在途。tally/recorded)后,该路由会在去抖时间(balanceDebounceMs)后自动刷新一次;同一路由两次自动刷新的起点之间至少间隔 balanceMinIntervalMs。stale: true,附带 error 和 errorCode;从未成功过时返回 wallets: [] 并附带 error、errorCode。errorCode 取值为 'not-configured'(未配置密钥)、'signed-out'(账号已退出)、'timeout'(超时)、'failed'(其他)。fetcher 抛出的异常带有字符串属性 code,且取值是这四个之一时照搬;超时一律为 'timeout',其余为 'failed'。get() 遇到从未成功过的快照(wallets 为空且带 error),并且距离上次尝试已超过 balanceMinIntervalMs 时,会重新查询,而不是一直返回旧的失败结果。tally/balance-changed 事件。ctx.tallyBudget 管理按日或按月的花费上限,并在花费达到上限的 80% 和 100% 时各提醒一次。
ctx.tallyBudget.set({
id: 'daily-cny',
period: 'day', // 'day' 或 'month'
scope: { workspaceId: 'my-project' }, // 可省略 workspaceId,表示全部工作区
limit: { amount: '10', currency: 'CNY' },
})
ctx.tallyBudget.list() // 返回副本
ctx.tallyBudget.remove('daily-cny')
ctx.tallyBudget.status() // { errors: string[] },加载预算文件时的问题;一切正常时 errors 为空数组
ctx.on('tally/budget-crossed', (alert) => {
// { budgetId, threshold: 0.8 | 1, spent, limit, periodStart }
})
timeZone 配置的时区划分自然日或自然月(夏令时当天可能是 23 或 25 小时)。scope.workspaceId 的预算只统计该工作区的花费。tally/recorded,因此不会触发提醒。set 会先校验,不合法(例如金额不是大于 0 的十进制字符串)时抛出 tally: invalid budget: …,文件保持不变。status().errors;文件会在下一次写入时(持有文件锁的情况下)被改名为 budgets.json.corrupt-<时间戳>,读取不会动它。set、remove、记录提醒)都在文件锁(budgets.json.lock)内先重新读盘、合并本次修改,再原子替换文件(权限 0600)。多个 dsh 进程共用同一个 $DSH_HOME 时不会互相覆盖。提醒记录合并时,同一预算取周期更新的一条;周期相同则阈值取并集,已提醒的状态不会被回退。list 等)前会比较文件的修改时间和大小,别的进程改过就自动重新加载,不需要重启。set 和 remove 仍是同步方法:修改先进入队列,list() 立即可见,文件随后按顺序写入。写入失败(例如等锁超时)时会记一条警告日志,修改从 list() 中消失,原因出现在 status().errors 里。需要确认结果时用 setAndWait / removeAndWait(不属于 dsh-tally-api),写入失败时它们会拒绝;Remote 的 setBudget / removeBudget 用它们,失败时返回 tally/write-failed。只依赖类型包 dsh-tally-api,不需要依赖 dsh-tally:
pnpm add -D dsh-tally-api
import type { Context } from '@deepseek-ai/cordis'
import type {} from 'dsh-tally-api'
export const name = 'my-cost-widget'
export function apply(ctx: Context) {
ctx.inject(['tallyUsage'], async (ctx) => {
// 今天(按宿主时区)的花费,按模型分组
const start = new Date()
start.setHours(0, 0, 0, 0)
const groups = await ctx.tallyUsage.query({
from: start.getTime(),
to: Date.now() + 1, // 左闭右开
groupBy: 'model',
})
for (const g of groups) {
// g.cost 按币种分开,例如 [{ amount: '1.234', currency: 'CNY' }]
console.log(g.key, g.requestCount, g.cost.map(m => `${m.amount} ${m.currency}`).join(' + '))
}
// 实时记账时每结算一次请求触发一次;回填和重建的历史账目不触发
ctx.on('tally/recorded', (entry) => {
if (entry.cost) console.log(entry.sessionId, entry.model, entry.band, entry.cost.amount, entry.cost.currency)
else console.log(entry.sessionId, entry.model, entry.usageKnown ? '无价格' : '用量未知')
})
// 一个会话(含子代理明细)的用量
// const usage = await ctx.tallyUsage.session(sessionId)
// 回填进度和错误
const { backfill, errors } = ctx.tallyUsage.status()
if (backfill) console.log(`回填 ${backfill.done}/${backfill.total}`)
for (const e of errors) console.warn(e.sessionId ?? '-', e.message)
})
}
单独报价可以用 ctx.tallyPricing.quote({ provider, model, usage, at }),provider 直接传日志里的路由 id 即可,映射和覆盖会自动应用。
浏览器端(dsh Web 界面)通过 dsh API Gateway 调用本插件的 Remote 命名空间 tally,Host 端服务名为 tallyRemote。dsh-tally-ui 就是基于这套接口实现的;第三方界面也可以直接使用。
request(一个对象)。Host 端用 dsh-tally/remote 中同一份 zod schema 校验参数,不合法时返回 gateway/bad-request,消息里带出错的字段路径。from/to 的请求是左闭右开区间,要求 from < to,跨度不超过 400 天。tz 必须是合法的 IANA 时区名。null 或计入 unpricedCount,不会出现 "0"。| 方法 | 参数 request | 返回 | 说明 |
|---|---|---|---|
status | 无 | TallyStatusView | 插件状态:回填进度和错误、价格目录版本、被忽略的价格覆盖、预算文件问题、已注册余额的路由。空闲时 usage.backfill 为 null,只有回填或重建正在进行时才有进度(与 ctx.tallyUsage.status() 不同,后者可能保留最后一次进度) |
query | UsageQuery | UsageGroup[] | 与 ctx.tallyUsage.query 相同;groupBy: 'session' 时已删除会话的分组带 deletedCount |
session | { sessionId } | SessionView | 会话用量(含子代理明细),以及该会话最近一次请求的路由和模型 |
turn | { sessionId, fromSeq, toSeq } | TurnUsage | 一轮对话的用量:[fromSeq, toSeq] 是会话事件序号的闭区间,返回合计和每次请求(含重试) |
band | { route, model, at } | BandView | null | at 时刻该路由和模型所处的价格时段及下次变化时间;没有价格时为 null |
balance | { route } | BalanceSnapshot | null | 缓存中的余额;还没有快照,或者从未成功且距上次尝试已超过 balanceMinIntervalMs 时,先查询一次。该路由没有余额提供者时为 null |
refreshBalance | { route } | BalanceSnapshot | null | 立即查询余额。失败时返回上一次成功的值并标记 stale: true,errorCode 说明原因 |
budgets | 无 | Budget[] | 全部预算 |
setBudget | Budget | 无 | 新建或替换预算,等待写盘完成后才返回 |
removeBudget | { id } | 无 | 删除预算,等待写盘完成后才返回 |
catalog | 无 | CatalogView | 价格目录:每个模型当前的价格版本、生效日期和来源,以及生效中的价格覆盖(overrides) |
peakSavings | { from, to } | PeakSavings | 高峰时段费用与同样用量按空闲时段价格计算的费用(按币种) |
exportCsv | { from, to, tz } | string | 区间内的全部账目,CSV 格式(RFC 4180),时间按 tz 带时区偏移输出;没有价格的行 cost 等列留空 |
rebuild | 无 | 无 | 从会话日志重建整本账本;进度通过 watch 的 status 帧和 status() 报告 |
watch | 无(可传 AbortSignal 取消) | 流:TallyWatchFrame | 实时推送,见下文 |
| 代码 | 出现在 | 含义 |
|---|---|---|
gateway/bad-request | 所有带参数的方法 | 参数不符合 schema(缺字段、类型不对、区间不合法、时区未知、fromSeq > toSeq 等) |
tally/invalid-budget | setBudget | 预算没有通过校验,例如金额不是大于 0 的十进制字符串;文件保持不变 |
tally/not-found | removeBudget | 没有这个 id 的预算 |
tally/write-failed | setBudget、removeBudget | 预算文件没能写入(例如等文件锁超时),修改没有生效 |
watch 帧watch 是一个流式方法,每一帧是下列之一:
| 帧 | 内容 | 何时出现 |
|---|---|---|
{ type: 'hello', status } | TallyStatusView | 流的第一帧;队列溢出(积压超过 1000 帧,最旧的被丢弃)或价格覆盖被修改后,会再发一次 |
{ type: 'recorded', entry } | TallyEntry | 实时记账写入一条记录(回填和重建不发) |
{ type: 'balance-changed', snapshot } | BalanceSnapshot | 某个路由的余额快照变化(包括变为过期) |
{ type: 'budget-crossed', alert } | BudgetAlert | 预算达到 80% 或 100%;只由记录这次提醒的进程发出,同一阈值在一个周期内只提醒一次 |
{ type: 'status', status } | TallyStatusView | 回填或重建进行中,最多每秒一帧;结束时再发一帧(此时 backfill 为 null) |
每收到一个 hello 都要重新拉取全部数据(status、当前会话、今日用量、余额等),不能在旧数据上继续累加之后的增量帧:hello 意味着中间可能丢了帧,或者全部金额已经按新的价格覆盖重新计算。断线重连(包括页面重新加载)后的第一帧也是 hello,按同样的规则处理即可保证重连前后显示的数值一致。
浏览器端需要一份接口描述(TypertRemoteContribution)才能调用 tally 命名空间。dsh-tally 在子路径 dsh-tally/remote 中提供(手写,形状与 dsh 官方生成的 typert.remote-client.js 相同;只依赖 zod,可以放进浏览器 bundle)。客户端插件在 apply 里挂载它:
import type { Context } from '@deepseek-ai/cordis'
import type {} from '@deepseek-ai/dsh-api-remotes/client'
import type { TallyWatchFrame } from 'dsh-tally-api'
import { mountTallyRemote } from 'dsh-tally/remote'
export const inject = ['remote']
export async function apply(ctx: Context) {
// 挂载后 ctx.remote.tally 才可用;插件卸载时命名空间随之撤回。
// 其他插件(例如 dsh-tally-ui)可能已经挂载过:mountTallyRemote 发现 remote.tally 已存在时跳过挂载,
// 并发挂载时宿主报出的“已挂载”错误也当作成功。
await mountTallyRemote(ctx)
ctx.inject(['remote.tally'], (ctx) => {
const tally = ctx.remote.tally
async function reloadAll() {
const status = await tally.status()
if (status.ok) console.log('价格目录版本', status.value.catalogVersion)
const today = new Date()
today.setHours(0, 0, 0, 0)
const groups = await tally.query({ from: today.getTime(), to: Date.now() + 1, groupBy: 'model' })
if (!groups.ok) console.warn(groups.error.code, groups.error.message)
}
// 断线自动重连的实时流。在独立的循环里消费,不要在 inject 回调里 for await:
// 回调要立即返回,流的生命周期交给 AbortController 和 ctx.effect。
const stream = ctx.remote.$stream<TallyWatchFrame>({
name: 'tally',
open: (signal: AbortSignal) => tally.watch(signal),
ended: () => new Error('tally stream ended'),
})
const abort = new AbortController()
ctx.effect(() => () => {
abort.abort()
return stream.dispose()
}, 'tally watch')
void (async () => {
try {
for await (const item of stream) {
if (abort.signal.aborted) return
const frame = item.value
if (frame.type === 'hello') await reloadAll() // 每个 hello 之后重新拉取全部数据
else if (frame.type === 'recorded') { /* 防抖后重新拉取 */ }
// 其他帧类型(包括以后新增的)按需处理,不认识的直接忽略
item.accept()
}
} catch (error) {
if (!abort.signal.aborted) console.warn('tally stream failed', error)
}
})()
})
}
注意:
package.json 中,dsh.client.inject 要包含 remote。dsh-tally/remote 和 zod 必须打包进自己的 bundle:dsh 宿主只共享固定的几个模块(react、@deepseek-ai/cordis、@deepseek-ai/dsh-client-ui-* 等),别的模块在浏览器里无法 require()。RemoteResult:成功为 { ok: true, value },失败为 { ok: false, error: { code, message } },code 见上文的错误代码表。ctx.remote.$mount(TALLY_REMOTE):同一页面里第二次挂载会失败(宿主报 client api: direct method tally/… is already mounted)。mountTallyRemote(ctx) 先检查 remote.tally 服务是否已存在,存在就不再挂载;并发挂载输掉时出现的这类错误(以及 Typert 注册表的 Remote package "dsh-tally" is already registered)都视为成功。它返回 true 表示由本插件挂载(随本插件卸载而撤回),返回 false 表示已由其他插件挂载(命名空间随那个插件卸载而撤回,此时 ctx.inject(['remote.tally'], …) 的回调会随之停止)。除“已挂载”以外的错误照常抛出。from、to、at)必须是 Date 能表示的整数毫秒(绝对值不超过 8.64e15),否则返回 gateway/bad-request。watch 流增加新的帧类型。客户端遇到不认识的帧类型必须忽略(照常 accept()),不能报错或断开。注意:把旧版 dsh-tally/remote 打包进自己 bundle 的客户端用的是旧的严格编解码器,遇到新的帧类型或枚举值会解码失败;需要随 dsh-tally 升级重新打包。服务接口以 dsh-tally-api 为准,严格遵守 semver、只做加法;破坏性修改必须升主版本,旧主版本继续维护 6 个月。
MIT
dsh-tally 自带一个命令行工具,用来把账本算出的费用和厂商账单逐行对比:
# DeepSeek 官方导出:直接传 ZIP,或传其中的费用表
dsh-tally reconcile --bill ./deepseek-export.zip
dsh-tally reconcile --bill ./cost-2026-10-01_2026-10-31.csv
# 其他厂商或手工整理的账单:通用 CSV
dsh-tally reconcile --bill ./bill.csv --format csv
它按“天 × 模型 × 币种”对比账本和账单,日期默认按北京时间(--tz Asia/Shanghai)划分。账本数据库以只读方式打开,不会被修改;工具不读凭据,也不联网。账本所在目录不可写时(例如备份目录),只要没有未合并的 -wal 文件,也能直接读取。--from、--to 之外的账单行不参与对账,报告会说明有几行。
| 参数 | 说明 |
|---|---|
--bill <文件> | 账单文件(必填):CSV,或 DeepSeek 官方导出的 ZIP |
--format <格式> | 账单格式:csv(通用 CSV)、deepseek(DeepSeek 费用表或官方 ZIP)。不指定时,DeepSeek 费用表和 ZIP 会按内容自动识别 |
--from / --to | 对账日期范围(含首尾),默认取账单覆盖的范围 |
--tz <时区> | 划分日期的时区,默认 Asia/Shanghai。账单里带时区偏移的时间(如 DeepSeek 的 2026-10-01T00:00:00+08:00)也按这个时区换算成日期 |
--db <路径> | 账本数据库,默认 $DSH_HOME/tally/tally.v1.sqlite |
--currency <币种> | 账单没有币种列时使用的币种 |
--column <列=列名> | 指定列名,可重复使用。默认识别 日期/date、模型/model、金额/费用/amount/cost、币种/currency |
--model-map <a=b> | 把账单里的模型名 a 当作 b,例如 deepseek-chat=deepseek-flash |
--route <路由=厂商> | 追加路由映射,可重复使用。默认已包含 deepseek-official 和 deepseek-account → deepseek;如果你在插件配置里改过 providerRoutes,这里要传同样的映射 |
--overrides <文件> | 价格覆盖配置文件(JSON 数组,格式与插件配置中的 overrides 相同),让对账和界面上的金额一致 |
--tolerance <比例> | 容差,默认 0.01(1%),依据设计文档的准确性标准 |
--json | 输出 JSON(字段名为英文) |
通用 CSV 解析器能处理 UTF-8 BOM、中文表头、带引号的字段、千分位金额(分组不规范的如 1,2 会被拒绝,不会被当成 12)、货币符号,以及退款写法(-1.50 或 (1.50))。币种单元格为空时使用 --currency。引号没有闭合时直接报错并给出行号。无法解析的行不会被静默丢弃,报告里会列出它们的行号和原因。
账本中的模型名会按价格数据里的别名归并,例如 deepseek-v4-flash 计入 deepseek-flash。账本里完全无法计价的“天 × 模型”(全部未定价或用量未知)也会单独列出,不会被漏掉;汇总里会给出未计入金额的请求数。
DeepSeek 开放平台导出的账单是一个 ZIP,里面有两张表,作用不同:
| 表 | 文件名示例 | 内容 | 对账中的用途 |
|---|---|---|---|
| 费用表 | cost-2026-10-01_2026-10-31.csv | 每天、每个模型、每个钱包扣了多少钱(cost、currency、wallet_type) | 唯一的金额来源 |
| 用量表 | amount-2026-10-01_2026-10-31.csv | 每个 API key 每天的 token 数和请求次数(type、price、amount) | 只用来统计请求次数;amount 是数量不是金额,不会算进账单 |
--format csv 强行读取)会被拒绝,提示改用费用表,避免把 token 数量当成金额。--model-map 显式指定。目前只用一份合成样例(人民币、Paid 和 Granted 两种钱包、一个模型)验证过格式;其他币种、其他钱包类型、退款、合计行等情况还没有实际样例。遇到无法识别的行,报告会列出行号和原因,不会静默丢弃。
价格数据开始之前的日子:账本只有价格数据生效之后的价格(例如 DeepSeek 的价格从 2026-10-10 开始)。账单里早于这一天、账本里又没有金额的行,会单独列在“不参与比较”里,不计入汇总和超出容差的行数,也不影响退出码;汇总会写明这部分账单金额。
退出码:0 表示全部在容差内;1 表示有超出容差的行,或者账单中有无法解析的行;2 表示参数或文件错误。
dsh plugin add 不会把命令加入 PATH,请用 npx dsh-tally@beta reconcile … 运行(npx 会一并装好它依赖的 dsh 包)。不要直接用 node 运行插件目录里的 lib/cli.js:那些 dsh 包由 dsh 自己提供,不在插件目录下,会报找不到模块。从源码安装时,在仓库根目录用 node packages/tally/lib/cli.js reconcile …。
超出容差时的提示:
| 提示 | 含义 |
|---|---|
| 未定价请求 N 次 | 这些请求没有价格(例如价格历史未录入,或路由没有价格映射),没有计入账本金额 |
| 用量未知请求 N 次 | 厂商没有上报用量,没有计入金额 |
| 节假日日历缺失的高峰请求 N 次 | 当年节假日数据未录入,这些请求按高峰价计算,可能偏高 |
| 账单中没有这一行 / 账本中没有这一行 | 只有一边有数据,差异按 100% 计 |
对账结果是估算,以厂商账单为准。