AI 网关怎样记录 token 用量而不泄露提示词
AI 网关既要回答“这次请求用了多少 token”,又不能把客户提示词和模型回答原样写进日志。比较稳妥的做法,是把模型调用数据和观测数据拆成两条路径:原文只在请求转发链路中短暂存在,观测链路只接收 request_id、模型、延迟、状态和 usage 等结构化字段。
记录 token 用量不等于记录提示词。网关应在模型响应边界提取 input_tokens、output_tokens、total_tokens,生成脱敏事件,再写入指标或用量账本;默认不保存 prompt、response、完整请求体和调试异常原文。
下面按一个可迁移到不同模型供应商的架构来拆解。示例使用兼容 OpenAI 风格响应的字段名,字段实际名称应以当前供应商返回值为准。
先把“能计量”和“不能落盘”分开
网关面对的不是一个简单的日志开关,而是两类不同资产:
| 数据 | 用途 | 默认处理 |
|---|---|---|
| input_tokens、output_tokens、total_tokens | 成本、配额、模型对比 | 可以进入结构化用量事件 |
| model、provider、tenant_id、request_id | 路由、追查、聚合 | 按最小必要原则保留 |
| prompt、response、instructions | 业务内容 | 不进入普通日志和 spend 账本 |
| API key、Cookie、用户标识 | 身份与鉴权 | 哈希、截断或完全丢弃 |
这里最容易犯的错,是先把完整请求对象交给通用 logger,再尝试在日志平台里“查询时隐藏”。这已经把泄露窗口扩大到了 SDK debug 日志、异常堆栈、重试记录、缓存和第三方 callback。脱敏要发生在网关把事件交给观测后端之前。

在响应边界提取 usage,而不是重新猜 token 数
最可靠的计量点通常是模型响应返回 usage 的位置。网关可以读取供应商返回的输入、输出和总 token 字段,并把它们复制到内部事件;不要把提示词再送进本地 tokenizer 后当成最终账单,因为不同模型的 tokenizer、系统消息、工具调用和缓存 token 规则可能不同。
type ProviderUsage struct {
// 这些字段来自模型供应商的响应,不保存原始文本。
InputTokens int64 `json:"input_tokens"`
OutputTokens int64 `json:"output_tokens"`
TotalTokens int64 `json:"total_tokens"`
}
type UsageEvent struct {
// request_id 用于关联一次逻辑请求,不能拿提示词摘要代替。
RequestID string `json:"request_id"`
TenantID string `json:"tenant_id"`
Model string `json:"model"`
Status string `json:"status"`
LatencyMS int64 `json:"latency_ms"`
Usage ProviderUsage `json:"usage"`
}
func buildUsageEvent(requestID, tenantID, model, status string, latencyMS int64, u ProviderUsage) UsageEvent {
// 只组装可聚合的标量字段;prompt 和 response 不作为参数传入。
return UsageEvent{
RequestID: requestID,
TenantID: tenantID,
Model: model,
Status: status,
LatencyMS: latencyMS,
Usage: u,
}
}
如果供应商只在流结束时返回 usage,就在流关闭或收到最终事件时写入一次;不要把每个增量片段都当成一次完整调用。非流式请求也应以网关最终收到的 usage 为准,并把缺失字段标记为 unknown,而不是用一个看似精确的估算值覆盖。
脱敏事件和用量账本应该是两层
日志适合回答“哪个请求在什么时候失败”,账本适合回答“某租户在这个时间桶用了多少 token”。把二者混成一张包含请求体的明细表,会让权限和保留期变得很难管理。
// 发送到观测后端的事件只包含结构化字段,不带 messages、input 或 output。
type RedactedEvent struct {
RequestID string `json:"request_id"`
TenantHash string `json:"tenant_hash"`
Model string `json:"model"`
InputTokens int64 `json:"input_tokens"`
OutputTokens int64 `json:"output_tokens"`
TotalTokens int64 `json:"total_tokens"`
Status string `json:"status"`
LatencyMS int64 `json:"latency_ms"`
CostEstimate int64 `json:"cost_micros"`
}
func redactForLog(e RedactedEvent) map[string]any {
// 显式列出允许外发的字段,避免未来新增字段时把原文带入日志。
return map[string]any{
"request_id": e.RequestID,
"tenant_hash": e.TenantHash,
"model": e.Model,
"input_tokens": e.InputTokens,
"output_tokens": e.OutputTokens,
"total_tokens": e.TotalTokens,
"status": e.Status,
"latency_ms": e.LatencyMS,
"cost_micros": e.CostEstimate,
}
}
这里的白名单比“把字段名里含有 prompt 的键删掉”更可靠:嵌套消息、工具参数、异常上下文和自定义 metadata 都可能藏着业务文本。租户维度也不一定要原样保存,可以使用不可逆哈希或内部租户编号,并限制可查询的人员范围。
流式、重试和缓存会改变计量语义
真实网关中,token 统计最容易在边界条件下重复或缺失。建议先定义一条逻辑请求的生命周期:
- 重试:用 request_id 表示用户发起的一次逻辑请求,用 attempt_id 表示每次供应商尝试。成本账本按实际成功或失败尝试汇总,但报表不要把一次重试误报成两次用户请求。
- 流式响应:打开流时只记录 started,收到最终 usage 或连接失败时再写终态;连接中断不能把最后一个增量片段当作完整 output_tokens。
- 缓存命中:命中缓存时要明确是“模型实际 token”还是“按策略估算的节省量”。两者不能共用 total_tokens 字段。
- 错误响应:供应商拒绝、超时和网关限流都可以写 status 和 error_type,但不要把上游原始错误全文直接写入公共日志。
一个小的字段设计能避免后续争议,例如同时保留 request_count、provider_attempt_count、usage_source 和 is_cache_hit。这样财务、SRE 和产品看到的是同一套事实,但可以按不同维度聚合。

把权限、保留期和调试样本拆开
“日志没有 prompt”并不代表系统天然安全。仍要检查三种常被忽略的旁路:
- 网关进程的 debug 级别是否会打印完整请求或响应;
- 失败回调、重试中间件和 trace exporter 是否自动采集消息内容;
- 缓存键、死信队列、样本录制和人工排障工具是否复制了原始文本。
生产环境可以把数据划成三套权限:普通指标只看聚合 token;脱敏事件可由值班和成本管理员按 request_id 查询;必要的临时调试样本单独存放,设置短保留期、审批和自动删除。不要因为排障方便,就让所有开发账号都能读到第三套数据。
如果使用现成网关或观测集成,先确认它的默认日志行为、每请求覆盖开关和异常日志策略。例如 LiteLLM 的官方日志说明提供了关闭消息记录、保留 usage/cost 元数据和按请求控制脱敏的配置线索,但具体默认值和版本行为要以部署版本文档为准。
官方地址:https://docs.litellm.ai/docs/proxy/logging
上线前用一张清单判断是否真的“没泄露”
可以用下面的最小清单做设计评审:
- 日志事件是否由允许字段白名单构造,而不是从完整请求对象复制?
- input_tokens 和 output_tokens 是否来自供应商响应的 usage,而不是二次猜测?
- 流式结束、重试、缓存命中和上游错误是否有独立状态与计量语义?
- 普通日志、聚合账本、调试样本是否有不同的权限和保留期?
- SDK debug、异常、trace、callback、缓存和队列是否都经过同一条脱敏政策?
- 即使调用方传入“请记录原文”的 metadata,网关是否仍能强制保持默认保护?
最终的判断标准很简单:观测系统能回答“谁、何时、调用哪个模型、用了多少 token、结果怎样”,但拿不到“用户具体说了什么、模型具体回答了什么”。这就是 AI 网关记录用量与保护提示词之间比较稳定的边界。
相关问题
只保存 prompt 的哈希可以吗?可以用于去重或关联,但哈希不是脱敏万能药。短文本、低熵模板和可猜测的系统提示词仍可能被字典攻击,生产上还应限制访问和保留期。
没有 usage 字段时要不要自己计算?可以作为估算字段单独命名,例如 estimated_input_tokens,并保留估算算法与版本;不要把估算值冒充供应商最终计量。
排查单次模型质量问题怎么办?使用经过审批的短期调试样本,单独授权、加密、脱敏并自动过期;不要为了少写一套流程而打开全局 prompt logging。
CNCF 生态怎样推进 Kubernetes 上的机器学习生命周期
- 上一篇
- CNCF 生态怎样推进 Kubernetes 上的机器学习生命周期
- 下一篇
- CNCF 公告怎样看待云原生走向 AI 原生
-
- 科技周边 · 人工智能 | 16分钟前 |
- 模型卡怎样记录训练数据和限制条件
- 400浏览 收藏
-
- 科技周边 · 人工智能 | 21分钟前 |
- Tokenizer 怎样处理超长输入的截断策略
- 500浏览 收藏
-
- 科技周边 · 人工智能 | 21分钟前 | LoRa Transformers PEFT merge_and_unload load_adapter adapter_config.json
- LoRA 适配器怎样与基础模型分离加载
- 102浏览 收藏
-
- 科技周边 · 人工智能 | 22分钟前 | python · Hugging Face Transformers generate tokenizer 新生成 token
- Hugging Face Transformers 怎样只输出生成结果而不带输入提示词
- 145浏览 收藏
-
- 科技周边 · 人工智能 | 28分钟前 | ai agent · 工具调用 · 最小权限 工具权限 Computer Use Holo4 计算机使用代理
- Holo4 这类计算机使用代理怎样划分工具权限
- 212浏览 收藏
-
- 科技周边 · 人工智能 | 28分钟前 | 人工智能 · vLLM KV Cache gpu-memory-utilization max-num-batched-tokens max-num-seqs 批处理显存
- vLLM 怎样限制批处理显存上限
- 265浏览 收藏
-
- 科技周边 · 人工智能 | 29分钟前 |
- RAG 检索结果怎样保留来源片段和页码
- 307浏览 收藏
-
- 科技周边 · 人工智能 | 29分钟前 | 分布式 · 人工智能 · 多进程 多GPU Accelerate CUDA_VISIBLE_DEVICES
- Accelerate 怎样让多个进程分别绑定到不同 GPU
- 444浏览 收藏
-
- 科技周边 · 人工智能 | 34分钟前 | 人工智能 · MLFlow Model Registry 模型别名 champion 模型回滚
- MLflow 模型别名怎样把新版本切换到 champion 并支持回滚
- 296浏览 收藏
-
- 科技周边 · 人工智能 | 34分钟前 |
- PyTorch 怎样用 autocast 控制混合精度范围
- 248浏览 收藏
-
- 科技周边 · 人工智能 | 39分钟前 | AI工程 · 运行日志 Hugging Face Spaces Space日志 构建日志 fetch_space_logs
- Hugging Face Spaces 怎样查看应用运行日志
- 322浏览 收藏
-
- 科技周边 · 人工智能 | 57分钟前 | Hugging Face Transformers 半精度推理 float16 bfloat16
- Hugging Face Transformers 怎样为推理启用半精度
- 212浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 932次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 937次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 1046次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 1016次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 845次使用
-
- 家政服务企业怎样整理服务人员身份核验记录
- 2026-10-27 454浏览
-
- Go log/slog替换Handler实现敏感字段脱敏的方案
- 2026-09-20 415浏览
-
- Go slog.Group 怎么组织嵌套业务字段
- 2026-09-28 447浏览
-
- 结构化日志字段应该在调用处还是 Handler 中补齐
- 2026-10-07 406浏览
-
- AI 网关怎样避免流式请求串线:request_id、连接复用与日志关联
- 2026-08-24 407浏览

