Files
PolyGateway/research-wiki/schemas/llm-calls.md
T
iomgaa 15b9b02e96 fix: make the sampling invariant test actually enforce the constraint
The test passed overlay and sampling as separate objects while production
aliases them, so an in-place mutation slipped through it. Also syncs the
telemetry schema page and adds the missing postgres round-trip assertion.
2026-07-31 22:01:51 -04:00

5.8 KiB

type, node_id, title, date
type node_id title date
schema schema:llm-calls 表结构: llm_calls(遥测 21 字段) 2026-07-20

表结构: llm_calls(遥测 21 字段)

列定义(冻结,M1 设计 §4.4 / ARCH §7.8)

类型 说明
call_id TEXT PRIMARY KEY 每次尝试独立 UUID;INSERT OR IGNORE 幂等
parent_call_id / session_id TEXT 调用链路(agent step → LLM call)
model / provider / source_name TEXT NOT NULL 溯源;model 由旧 Protocol 的 model_name 更名(VT 迁移 §8)
messages / response / thinking TEXT NOT NULL messages 落库前多模态 part 摘要(与缓存 key 共用 digest_messages)
prompt_tokens / completion_tokens INTEGER NOT NULL usage 帧;帧缺失记 0/0(不编造估值,由 usage_source 标注)
usage_source TEXT NOT NULL measured / estimated / unavailable(2026-07-30 起三态,见下)
latency_ms INTEGER NOT NULL 尝试耗时;缓存命中 0
ttft_ms / max_inter_token_ms REAL 流式活性测量
cache_hit INTEGER NOT NULL DEFAULT 0 命中标记
error TEXT 异常信息;取消记 "cancelled"
cost REAL M2 起 pricing 换算;usage_source='unavailable' 的真实调用行为 NULL(缓存命中行例外,仍为 0.0)
created_at TEXT NOT NULL DEFAULT (datetime('now')) 落库时刻
cached_prompt_tokens INTEGER 供应商 prompt cache 命中的输入 token(2026-07-31,issue #3);NULL = 该源未上报,0 = 上报了真实零命中,两者不可混同
model_reported TEXT API 响应体实际返回的 model;NULL = 未上报。与 model(配置别名)可能分叉
sampling TEXT 本次调用的采样参数 canonical JSON(2026-07-31,issue #4);NULL = 未传。见下方口径

usage/成本口径(2026-07-30,est_tokens 解耦)

usage_source 含义 生产者 cost
measured usage 帧完整可信 正常路径;OCR 成功行(0 token 是事实) 按 token 换算
estimated 有实测数字但可信度降级 打捞路径(收到 usage 帧但流被截断) 按 token 换算
unavailable 用量信息不可得 usage 帧缺失、失败尝试、终态失败 NULL

SUM(cost) 天然跳过 NULL,故账单汇总不再被虚构的估值污染;账目缺口的度量口径固定为 WHERE usage_source = 'unavailable' AND cache_hit = falsecache_hit 限定不可省:缓存命中行未产生新调用,cost 是事实上的 0.0 而非未知,本无账目缺口,漏掉该条件会让缺口度量偏高。

供应商 prompt cache 口径(2026-07-31,issue #3)

新增两列排在 created_at 之后——旧表只能经 ALTER TABLE ADD COLUMN 追加到末尾,DDL 里若插在前面,新建库与升级库的物理列序会分叉(列序断言无合规修法)。两个后端在初始化期幂等补列:CREATE TABLE IF NOT EXISTS 不会给旧表加列,不补则每行写入被逐行 warning 丢弃、遥测静默全失。两侧都先探测缺列再 ALTER(ADD COLUMN IF NOT EXISTS 即使列已存在也先取 ACCESS EXCLUSIVE 锁,遥测是内联 await,锁共享审计表会拖垮业务调用),且补列失败只降级为逐行丢弃,绝不让 recorder 整体失能——两侧纪律必须对称。

cache_hitPolyGateway 自身响应缓存,与供应商 prompt cache 是两回事。缓存命中行的这两列是原样回放的历史值(与 model/prompt_tokens 同一口径),故命中率度量口径固定为:

SELECT SUM(cached_prompt_tokens)::float / NULLIF(SUM(prompt_tokens), 0)
FROM llm_calls WHERE cache_hit = false AND cached_prompt_tokens IS NOT NULL;

WHERE cache_hit = false 不可省,理由与上面 cost 缺口口径同源:回放行计入即重复计数。

采样参数口径(2026-07-31,issue #4)

sampling 列 = 「调用方采样意图 ⊎ 生效源 extra_body」的 canonical JSON,空则 NULL。不含结构化输出注入的 response_format——列名是采样参数,schema 不是,且数 KB schema 逐行落库会让审计表无谓膨胀。补列纪律与 issue #3 两列逐字相同(排在末尾、先探测再 ALTER、失败只逐行降级)。

三个 emit 入口的取值必须各自定死,否则同一列在不同行含义不同:

入口 调用者 有生效源? 记什么
emit_attempt RetryMW(最内) merge(source.extra_body, request.sampling)
emit_cache_hit TelemetryMW(最外) request.sampling
emit_terminal_failure TelemetryMW request.sampling

后两行缺 extra_body 是客观事实而非口径瑕疵——它们没有"生效源"可言,与 model/source_name 在终态行置空是同一先例;缓存命中行亦无损:sampling 已进缓存 key,能命中即意味调用级参数与历史那次逐字相同。三者统一读 request.sampling 而非 request.overlay(后者在 RetryMW 处已被结构化注入污染、在 TelemetryMW 处未被污染,直接用必然三行分叉)。

OCR / embedding 路径的该列恒为 NULL:两条路径的 transport 不发 extra_body(embed payload 硬编码 {model, input}、MonkeyOCR 只发 multipart),故其源在构造期就被剥离——不剥离则该列会记录一个从未发出的参数,那是数据造假而非参数失效。

复现某批实验的解码条件:

SELECT DISTINCT sampling FROM llm_calls
WHERE session_id = $1 AND cache_hit = false AND error IS NULL;

埋点位置(单一 helper 铁律)

  • middleware/telemetry.py::TelemetryEmitter 是全库唯一 record_llm_call 调用点;
  • TelemetryMW(最外层)记缓存命中与最终失败;RetryMW._emit 经同一 Emitter 逐次记尝试;
  • 写失败降级 warning 不冒泡;取消路径 finally 尽力记录。

评估基线

首版无历史基线,标"待首次运行后建立";验收断言: 单测覆盖成功/失败/缓存命中/取消四路径各产生恰一行;并发 50 协程写全落库。