docs: document the response observability fields for v1.1.0

2026-07-31 08:22:11 -04:00
parent 4c8dc0954c
commit d40f89378c
4 changed files with 36 additions and 5 deletions
+1 -1
@@ -2,7 +2,7 @@
实验室统一的大语言模型调度与中转库:LLM / VLM / OCR / Embedding 共用同一套生产级治理栈(多源多账号、限流、错误分类重试、熔断、响应缓存、流式看门狗、遥测与成本)。治理单位是**一次模型调用**;任务编排与业务解析留在业务侧。
当前版本 **v1.0.3**,已通过 GovDoc-SaaS 与 CHSAnalyzer 两个真实项目的全量迁移验收。
当前版本 **v1.1.0**,已通过 GovDoc-SaaS 与 CHSAnalyzer 两个真实项目的全量迁移验收。
## 文档地图(按你此刻要干什么选入口)
+3 -1
@@ -20,11 +20,13 @@
| prompt_tokens / completion_tokens | int | 用量 |
| latency_ms | int | 总耗时 |
| ttft_ms / max_inter_token_ms | float\|None | 流式时延指标 |
| cache_hit | bool | 是否缓存命中 |
| cache_hit | bool | **PolyGateway 自身响应缓存**是否命中(未产生网关调用);与供应商 prompt cache 无关,后者见 cached_prompt_tokens |
| call_id | str | 遥测主键 |
| cost | float\|None | 按价格表折算(缺表为 None) |
| usage_source | str | measured / estimated / unavailable(v1.0.3 起三态,口径见 [[指南-遥测与成本]]) |
| structured_data | Any\|None | `structured=` 时的校验结果 |
| cached_prompt_tokens | int\|None | 供应商 prompt cache 命中的输入 token 数(v1.1.0 新增)。`None` = 该源未上报;`0` = 上报了真实零命中——两者不可混同,口径见 [[指南-遥测与成本]] |
| model_reported | str\|None | API 响应体实际返回的 model(v1.1.0 新增);`None` = 未上报。与 `model`(配置别名)可能分叉,实验复现应认这个串 |
## EmbeddingClient
+1 -1
@@ -37,7 +37,7 @@
| `PGW_CACHE_BACKEND` | none / memory / redis | 必填;redis 需 NAMESPACE + TTL_S + REDIS_URL |
| `PGW_CACHE_NAMESPACE` / `PGW_CACHE_TTL_S` | | 缓存启用时必填;TTL 必须 > 0 |
| `PGW_TELEMETRY_BACKEND` | none / sqlite / postgres | 必填;sqlite 需 `PGW_TELEMETRY_SQLITE_PATH`,postgres 需 `PGW_TELEMETRY_PG_DSN` |
| `PGW_PRICING_PATH` | 路径 | 可选,价格表 JSON;缺省 cost 恒 None |
| `PGW_PRICING_PATH` | 路径 | 可选,价格表 JSON;缺省 cost 恒 None。条目支持可选第三档 `cached_input_per_1m`(v1.1.0,供应商 prompt cache 命中部分的单价;不填则命中部分也按 input 全额计) |
| `PGW_STRUCTURED_MAX_RETRIES` | int | 结构化重问上限,缺省 2 |
| `PGW_LEASE_TTL_S` | float | permit 租约,缺省 1500;须 ≥ 最大源 timeout |
| `REDIS_URL` | dsn | redis 后端共用 |
+31 -2
@@ -12,7 +12,7 @@ PGW_TELEMETRY_SQLITE_PATH=logs/telemetry.db # sqlite 时必填
实验室纪律:PG DSN 只许指向专用库 `polygateway`,严禁在用业务库。
## 表结构(`llm_calls`,18 字段冻结)
## 表结构(`llm_calls`,20 字段冻结)
| 字段组 | 字段 |
|---|---|
@@ -22,6 +22,9 @@ PGW_TELEMETRY_SQLITE_PATH=logs/telemetry.db # sqlite 时必填
| 用量 | prompt_tokens / completion_tokens / usage_source(三态,见下) |
| 时延 | latency_ms / ttft_ms / max_inter_token_ms |
| 结果 | cache_hit / error(异常类名前缀,如 `TransientError: ...`)/ cost |
| 可观测(v1.1.0) | cached_prompt_tokens / model_reported(见下) |
v1.1.0 新增的两列会**自动补到已存在的旧表上**(SQLite 与 Postgres 都在初始化期幂等 ALTER),无需手工迁移;历史行的新列为 NULL。
`session_id`/`parent_call_id` 由调用方传入(`client.chat(..., session_id=...)`),用于把一次业务任务下的多次调用串成链。
@@ -42,11 +45,13 @@ PGW_PRICING_PATH=config/prices.json
```
```json
{"MiniMax-M3": {"input_per_1m": 2.1, "output_per_1m": 8.4}}
{"MiniMax-M3": {"input_per_1m": 2.1, "output_per_1m": 8.4, "cached_input_per_1m": 0.42}}
```
配了价格表后每行遥测带 `cost`(元);缓存命中 token=0 天然零成本。缺价格表时 cost 恒 None,不报错。
`cached_input_per_1m` 是**可选**的第三档(v1.1.0):供应商 prompt cache 命中的那部分输入按更低单价计费。配了它,cost 就按 `(prompt - cached) × input + cached × cached_input` 分段算;**不配就退化为全额输入价**——库不会替你猜一个折扣率,所以不配时 cost 会比实际账单偏高。命中数若超过输入总数(网关口径异常),按总数夹取并记一条 warning,不会算出负数。
**cost 的口径**:产生了真实调用、但用量不可得的行 `cost` 为 NULL——库不会编一个数字,免得"免费"与"未知"在数据上混为一谈。缓存命中行**不在此列**:它没产生新调用,`0.0` 是事实,所以即便 `usage_source='unavailable'`,cost 仍是 `0.0`
因此 `SUM(cost)` 天然跳过不可得的行,而账目缺口要这样量化:
@@ -61,3 +66,27 @@ WHERE usage_source = 'unavailable' AND cache_hit = false;
## 降级方向
遥测后端不可用 → warning 后静默丢弃该行,**绝不影响业务调用**。共享后端注意:不要在真实批跑期间并发跑库的集成测试(时序隔离,详见主仓库 CLAUDE.md)。
## 供应商 prompt cache(v1.1.0)
`cached_prompt_tokens` 是**供应商服务器**复用了你的提示词前缀、按更低单价计费的那部分输入 token 数。它和 `cache_hit` 是两件事:
| | `cache_hit` | `cached_prompt_tokens` |
|---|---|---|
| 指的是 | **PolyGateway 自己的** Redis 响应缓存 | **供应商侧**的 prompt cache |
| 有没有联网 | 没有,直接返回旧答案 | 联了,只是对方省了算力 |
| 花不花钱 | 不花(cost 恒 0.0) | 花,但命中那部分打折 |
`None``0` 必须分开看:`None` = 这个源不上报这个数(你无法对它做缓存成本校正,论文里该声明),`0` = 它上报了,这次真的一次都没命中。
**统计命中率时 `WHERE cache_hit = false` 不可省**:
```sql
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;
```
原因和上面 cost 缺口的口径一样:缓存命中行里这两个字段是**原样回放**的历史值(与 `model``prompt_tokens` 同一规则——命中时只有时延类字段被清零),计进去就是重复计数。
`model_reported` 是 API 响应体里实际返回的 model,和 `.env` 里配的别名可能不是一个东西——供应商把别名指向新权重时,只有它认得出当时真正跑的版本。要做可复现的实验快照,记这一列。