A34第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

延迟、用量与费用

延迟、用量与费用 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

读完本课你能:为一次模型调用记下四个真实数字——首字延迟、总耗时、输入 token、输出 token;看懂缓存命中与长上下文怎么影响账单;用数据决定「下一步优化哪里」,而不是凭感觉猜。

问题场景

月底整理支出,发现这个月 AI 调用花了平时的三倍。翻记录:不是回答变长了,而是聊得越多、每一轮的输入越大——聊天应用每一轮都把历史消息整个重发一遍。另一个发现:同一句话有时快有时慢,慢的那次输入 token 却一样。没有测量,这两个现象都解释不了,更谈不上优化。

核心解释

一次流式调用要看两组数。

延迟分两段:首字延迟(TTFT,Time To First Token,从发出请求到第一个字出现)和总耗时。流式界面的体感由首字延迟决定,「20 秒出全文」和「1 秒出首字」是两个产品。本站 ai_call 表记录的 latency_ms 是整次调用的耗时。

用量是计费依据:prompt_tokens(输入)加 completion_tokens(输出)。关键认知:输入同样要钱,而且多轮对话里输入往往是大头——每轮都携带全部历史。本站的真实做法:请求时带 stream_options: { include_usage: true },流结束时在回调里拿到用量:

// lib/ai/adapter.ts(节选):请求声明要用量,结束时带回
body: JSON.stringify({
  model: cfg.model,
  messages: opts.messages,
  stream: true,
  stream_options: { include_usage: true },
  // ...
}),
// StreamCallbacks 的 onDone: (usage: { prompt_tokens?; completion_tokens? } | null) => void

两个直接影响账单的因素:

  1. 长上下文线性涨价。历史每多一轮,prompt_tokens 就多一截。本站把历史裁剪到最近 20 条(app/api/ai/chat/route.ts),就是在体感与费用之间取的平衡点。
  2. 提示缓存。上游对「提示词开头完全一致」的部分按折扣价计费,前提是开头稳定:系统提示放最前面,别在里面拼时间戳、随机数这类每次都变的内容——变一个字,缓存前缀就从那里断掉。

如何验证测量生效:跟 AI 聊一轮,然后查表:

-- 查 data/database/reactlab.db(sqlite3 命令行或任意 SQLite 工具)
SELECT model, prompt_tokens, completion_tokens, latency_ms, created_at
FROM ai_call ORDER BY created_at DESC LIMIT 5;

有记录、数字随对话轮次增长、latency_ms 与体感一致,测量链路就算通了。

示例拆解

用量落库发生在 app/api/ai/chat/route.ts 的 onDone 回调里:

onDone: (usage) => {
  if (usage) {
    db.prepare(
      "INSERT INTO ai_call (id, conversation_id, message_id, model, kind, prompt_tokens, completion_tokens, latency_ms, status, error, created_at) VALUES (?, ?, ?, ?, 'chat', ?, ?, ?, 'ok', NULL, ?)"
    ).run(
      newId("call_"), convId, assistantMsgId, process.env.AI_MODEL ?? "unknown",
      usage.prompt_tokens ?? null, usage.completion_tokens ?? null,
      Date.now() - started, nowIso()
    );
  }
},

三个关键点:

  • 没有 usage 就不记账:if (usage) 不是可有可无——部分提供方流式不回用量,硬记会写出一堆 null 污染统计(A38 讲迁移时这是核对项)。
  • latency 从请求开始计时:Date.now() - started 覆盖整条链路,包括工具调用轮次,不只是模型生成。
  • 记在服务端:浏览器拿不到也不该拿计费口径,账本要在唯一可信的位置。

动手任务

  1. 实验区是一个「用量台账」模拟器:跑一遍,观察多轮对话里每轮输入 token 的增长和缓存命中对费用的影响;把历史裁剪条数改小再跑,对比总费用。
  2. 本机跟 AI 助手聊 3 轮,用上面的 SQL 查 ai_call,确认三轮的 prompt_tokens 递增。
  3. 拿到自己的数据后写一句话结论:「当前最值得优化的点是 X,依据是 Y」。候选:裁剪历史、稳定提示前缀、调低输出上限。

常见错误

  • 只盯总耗时:流式产品里首字延迟才是体感大头;总时长长但首字快,用户照样觉得「在动」。
  • 以为只有输出计费:多轮对话里输入常占账单大头,优化输入(裁剪、缓存)往往比「让模型少说」有效。
  • 系统提示里拼动态内容:把「今天日期」「随机推荐」写进系统提示开头,缓存前缀永远不命中,每轮都按全价算。
  • 记账后从不回看:ai_call 写了没人查等于没记,测量只有变成结论才有价值。

检查与复盘

  • 能说出延迟要拆成首字延迟和总耗时两段,以及各自影响什么
  • 能解释为什么多轮对话的 prompt_tokens 线性增长,并给出两个对应手段(裁剪、稳定前缀)
  • 查过自己站里 ai_call 的真实数字,能报出最近一次调用的输入输出 token

参考资料

动手实验
动手实验
预览
点击「运行」在隔离沙箱中预览
检查题共 1 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1单选中等

复盘发现:这个月 AI 调用费用涨到平时的三倍,回答长度没变。查 ai_call 表发现长会话的 avg_prompt_tokens 线性上涨。原因与两个对应手段是?

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...