measurement.md 4.0 KB

实测方法(token / 速度 / 比例)

一切数字来自当轮真实调用,不拍脑袋。凡估算必须标注;有服务端真值以真值为准。 本项目所有实测数据可在页面「📊 框架实测」面板复现。


一、Token 归因

1.1 分项口径

每次 LLM 调用,把「真正发出去的东西」逐项记账:

分项 内容 用途
system 框架/业务注入的固定前置 框架预置 prompt 归此项 → 为 0 说明框架不注入
tools 工具 schema 描述(预筛前/后各测一次) 直观展示预筛砍掉多少
history 历史消息 —
retrieved 检索节点按需召回 验证「无关历史不进 prompt」
context 其余 —

1.2 估算口径(无真值时)

中文 ≈ 1 token/字;英文/数字/符号 ≈ 1 token/4 字符。 对中文场景误差通常 ±10%,够做横向对比,但不是计费值。

1.3 真值优先

服务端返回 usage 时覆盖估算,并在 UI 单列一行标明「服务端真值」。 ⚠️ 绝不把估算当真值展示。


二、输入 / 输出 token 比例(关键指标)

2.1 为什么单列

设计类 / 工具类 Agent 的成本几乎全在「喂进去」——prompt 远长于回复。 把这个比例摆出来,才知道优化该往哪使劲(砍 tools schema?砍 history?)。

2.2 怎么算

输入 : 输出 = Σ input_tokens : Σ output_tokens
  • 输入:有 usage.prompt_tokens 用真值,否则用估算(并标出有多少来自估算)。
  • 输出:只有真值(本地不估算补全)。没有输出真值 → 比例渲染为 —,不硬凑。
  • 输出占比 out/(in+out):输出占比越低,说明成本越集中在输入。

2.3 数据来源(本项目三处真值)

来源 字段
LLM(chat/completions) usage.prompt_tokens / usage.completion_tokens
出图(images/generations、images/edits) usage.input_tokens / usage.output_tokens(含 image_tokens 明细)

LLM 与出图分开列,再给一行「综合」 —— 两者成本结构完全不同,混在一起看不出问题。


三、速度:框架调度 vs IO(最容易算错)

3.1 口径

框架纯调度 = 总墙钟 − Σ 节点内 IO(LLM + 网络)

框架自身动作单独计时:graph.compile()、checkpoint 序列化、路由/检索、边判断、节点调度。

3.2 ⚠️ 实测踩坑(必看)

用 t0() 包住整个 await LLM,会把 LLM 的 1.4s 算进「框架纯调度」, 导致框架开销占比虚高到 96%(实测)。修正:await 期间暂停框架计时, 只把「发起调用前后」算框架的。

正确姿势:

t0('plan:dispatch');                       // 记「发起前」
const res = await pauseIO(profile, 'plan:llm', () => chatComplete(...));
// pauseIO 把 LLM 墙钟记进 IO,并从框架计时里扣除
add(profile.framework, 'plan:dispatch', Math.max(0, t1('plan:dispatch') - lastIO(profile, 'plan:llm')));

3.3 期望结论

框架调度占比应 ≲ 1%(本项目实测 0.1%~0.7%)—— 用户感知的延迟 99% 来自出图(20~60s)与 LLM(2~3s)。


四、上下文膨胀比

膨胀比 = 末次调用 prompt / 首次调用 prompt
  • < 1 是设计使然(本项目:第二轮 reflect 只发「用户原话 + 出图摘要」,不背全量历史)。
  • > 1 且持续上升 = 线性追加历史,需裁剪 / 检索。
  • 附带看膨胀量(末次 − 首次;负值 = 被裁掉了)。

五、报告纪律

  • 每个数字标注来源(真值 / 估算)与口径(含不含某部分)。
  • 环境说明写清楚(headless Chromium / 本地 or 线上);线上通常更快(网络更近 + 尺寸档不同)。
  • 不在文档里写来路不明的数字 —— 面板能复现的才写。