大模型应用日志应该记录哪些字段?
简化版
大模型日志应能回答“谁在什么版本上,用了哪些上下文和工具,经历了什么路由,花了多少时间与成本,最终是否合格”,但不能默认记录原始 Prompt、密钥和个人敏感信息。
核心字段包括 trace/request/tenant ID,模型、Prompt、知识库和策略版本,输入/输出 Token,TTFT/TPOT/总延迟,缓存与路由,工具调用状态,重试/错误,质量安全结果、成本和反馈。内容只保存脱敏摘要、哈希或受控采样,并设置访问权限与保留期。
详细版
建议将一次请求拆为 Trace,检索、模型调用、工具和校验作为 Span,使用稳定枚举而非自由文本记录状态。
| 字段组 | 示例 |
|---|---|
| 关联 | trace_id、request_id、session_id、tenant_id |
| 版本 | model、prompt、retrieval_index、policy、app_version |
| 性能 | queue_ms、ttft_ms、tpot_ms、total_ms |
| 用量 | input/output_tokens、cache_hit、attempt、cost |
| 结果 | finish_reason、error_code、schema_valid、safety_result |
| 工具 | tool_name、duration、status、idempotency_key_hash |
日志、指标和 Trace 分工:日志保留离散事件,指标用于聚合告警,Trace 串联跨服务路径。高基数字段不应直接作为指标标签;敏感内容用最小化、脱敏、分级采样和审计访问。
完整版教学
一、日志首先要回答哪些问题
排障要能重建请求经过的版本与步骤;运营要能算成功率、延迟和成本;安全要能审计权限与工具动作;评测要能把失败样本回流。
这些目标决定字段,不应为了“以后可能有用”无界记录用户内容。每个字段都要有用途、责任人和保留期。
二、关联标识如何设计
request_id 标识入口请求,trace_id 串联跨服务调用,span_id 标识检索或模型调用。session_id 用于多轮关联,tenant_id 必须来自可信认证层。
trace
├─ input_guard span
├─ retrieval span
├─ model_call span
├─ tool_call span
└─ output_validation span
标识应随机且不可包含邮箱、手机号等 PII。
三、为什么版本字段是 LLMOps 核心
同一代码版本下,模型、Prompt、Adapter、向量索引、重排器和安全策略都可能独立变化。没有版本字段,线上退化无法归因。
至少记录不可变版本 ID 或内容哈希,不能只写“latest”。回滚后仍要能查询历史请求对应的真实组合。
四、输入上下文记录到什么程度
记录 Token 数、语言、任务类型、内容哈希、检索文档 ID/版本和截断策略通常足以做大量分析。原文只在合法、必要且授权的受控样本中保存。
System Prompt 记录 registry 版本,不重复写完整文本。对敏感字段先脱敏再离开业务进程,避免日志平台成为数据泄露源。
五、模型调用字段有哪些
记录 provider、model/version、region、temperature、top_p、max_tokens、seed(若适用)、输入/输出 Token、finish_reason 和响应 ID。
流式请求还要记录首 Token 时间、最后 Token 时间、取消原因和已发送 Token;否则客户端断连后的浪费无法发现。
字段值必须来自实际执行配置,而不是调用方提交的期望值。例如路由器把请求切到备用模型或服务端收紧了输出上限时,应同时保留 requested 与 effective 配置,才能解释成本、质量和性能差异。
六、延迟要如何拆分
total_ms = gateway + queue + retrieval + prefill/decode
+ tools + validation + network
至少记录 queue_ms、ttft_ms、tpot_ms、total_ms,并统一时钟和单位。仅有总延迟无法判断慢在模型、检索还是工具。
七、检索与 RAG 记录什么
记录查询改写版本、索引/Embedding 版本、过滤条件、TopK、候选文档 ID、分数、重排结果和最终引用。文档正文仍按权限最小化。
这些字段能区分“没召回证据”“召回后未采用”和“模型无视证据”,对应的修复层不同。
八、工具调用如何审计
记录工具名称与版本、参数 Schema 版本、权限主体、耗时、状态、重试、幂等键哈希和结果摘要。密钥、完整支付信息等绝不能进入日志。
对写操作记录审批、执行结果和补偿状态。模型生成参数与真正执行参数应分别留痕,以发现校验层修改或拒绝。
九、质量与安全字段
记录 Schema 校验、引用校验、事实评估、风险分类、拒答原因、人工接管和用户反馈。自动评估器也要记录自身版本与置信度。
安全结果使用稳定分类码,敏感命中详情放受限审计存储。不能把原始违规内容复制到普通日志。
十、错误如何结构化
错误字段包含 stage、internal_error_code、provider_code、retryable、attempt、fallback_path 和 final_status。堆栈用于内部排障,但响应给用户的错误需脱敏。
| 错误层 | 示例 | 典型动作 |
|---|---|---|
| 输入 | invalid_schema | 不重试 |
| 容量 | rate_limited | 退避/路由 |
| 模型 | provider_timeout | 有界重试 |
| 工具 | unknown_commit | 查询状态/人工 |
| 输出 | schema_invalid | 有限修复 |
稳定分类使告警和失败统计不依赖易变文案。
十一、成本字段如何记录
记录单次各组件 Token/调用量、单价版本、估算费用、缓存节省和归属成本中心。多次尝试都归到同一根请求,才能算真实成本。
金额可能是估算值,应标注币种、价格表版本和计价类型。不能在日志里硬编码当前单价后用于永久历史。
十二、日志、指标和 Trace 的边界
日志适合错误事件和审计,指标适合低基数聚合与告警,Trace 适合跨组件时序。request_id 不应作为时序指标 Label,否则造成高基数爆炸。
可从 Trace 生成 RED 指标:Rate、Errors、Duration;再按受控维度如模型、区域和状态分组。
十三、隐私与保留策略
字段按公开、内部、敏感、受限分级,分别设置加密、访问角色、采样和保留期。用户删除请求要传播到日志、样本库和离线评测副本。
生产调试开关应有审批和自动过期,不能临时开启原文日志后永久遗忘。访问敏感日志本身也要审计。
十四、如何验证可观测性
用一次成功、模型超时、检索空结果、工具未知状态和客户端断连请求,验证能否仅凭 Trace 找到版本、耗时、成本和最终处置。
定期检查必填字段缺失率、时钟偏差、Trace 采样完整性与脱敏规则。发布新组件时同时更新 Schema 和数据字典。
好日志既能复盘一次请求,又不会把生产数据复制成新的安全风险。
十五、常见误区与追问
- 误区:记录完整 Prompt 最方便排障。 会扩大隐私、合规和访问风险。
- 误区:只有模型版本需要记录。 Prompt、索引、Adapter 和策略版本同样影响结果。
- 误区:总延迟一个字段足够。 无法定位排队、检索、模型或工具瓶颈。
- 误区:request_id 适合作为指标标签。 高基数会拖垮监控系统。
- 误区:HTTP 200 就是成功。 解析、质量、安全和工具结果也可能失败。
- 追问:不存原文怎么排障? 用版本、哈希、结构化特征和授权采样重建。
- 追问:如何关联多次 fallback? 共用根 trace_id,每次尝试使用独立 span 与 attempt。
十六、加强记忆
先关联:request、trace、span、session、tenant;再版本:模型、Prompt、索引、Adapter、策略、应用;再性能:排队、TTFT、TPOT、总延迟;再用量:Token、缓存、尝试、成本。再结果:错误、质量、安全、工具和反馈;再分载体:日志事件、指标告警、Trace 串链路;最后守红线:最小化、脱敏、权限、保留与审计。
项目实战落地
项目里怎么做的
《AI Agent旅游行程智能规划平台》的所有模型调用都经过统一出口 AiChatService,每调用一次,saveUsageLog 往 ai_usage_log 写一行:
| 字段 | 记什么 |
|---|---|
user_id | 调用人,取当前登录用户,取不到时为空 |
model_config_id、provider、model_name | 用的是哪条模型配置;服务商和模型名从配置里抄一份,配置删了日志仍看得出当时用的是谁 |
prompt_type | 调用场景,例如「模型连通测试」「行程编排」,看板按它分组 |
prompt_content、response_content | 请求内容与返回内容;对话时系统提示词和用户提示词合成一段存,向量化时存被向量化的文本,返回只记「向量维度:n」 |
prompt_tokens、completion_tokens、total_tokens | Token 用量:行程编排由挂在模型上的监听器累加后写入,单轮对话与向量化不记;输入输出都有值时才算总数 |
success、error_message、duration_ms、create_time | 成败、翻译成中文的失败原因、耗时、调用时间 |
几条写法上的规矩:成功和失败都写;失败时 response_content 留空,不把提示词写进去冒充回答;error_message 写库前截到 1000 字;写日志本身出错时整体吞掉异常,不影响模型结果返回给业务。
《AI Agent 智慧医院智能导诊就诊系统》的同名表字段相同,观测页做了两件事:接口开头校验管理员身份,医生和患者账号手输地址也会被拦下;关键字输入框同时在请求原文、返回原文、失败原因三列里模糊匹配,粘一段报错文字就能捞出对应的失败调用。
《AI面试官与刷题训练平台》的 ai_usage_log 字段和上面基本一致,Token 的取法值得看:统一出口 SpringAiInterviewService.chat 调模型时取的是 .call().chatResponse() 而不是 .call().content(),除了正文,还要从响应元数据的 Usage 里读输入、输出和总 Token;失败的调用没有响应,三个 Token 字段留空。学习计划的工具调用请求直接通过 ChatClient 发出、不经过这个出口,所以不在这张表里,工具执行情况要到工具调用日志里看。
《AI企业流程编排系统》的 ai_usage_log 只记大模型节点的调用,比通用字段多了流程 ID 和节点 ID 两列,prompt_type 直接存节点名称,一条日志能对回是哪条流程的哪个节点发起的;知识检索节点的向量化调用不写这张表。Token 用量从 chatResponse() 的 Usage 里取,响应带了用量时逐项读,某一项为空记 0。
《AI Agent智能教务排课与教学质量分析系统》的所有模型调用都走统一出口 EducationAiService,模型连通性测试、Agent 排课执行、AI 教学质量分析三种场景写同一张 ai_usage_log。排课 Agent 的整个工具循环是一次 call(),模型调用这边只记一条,场景是「Agent排课执行」;每次工具调用另由工具外壳各记一条。Token 从 chatResponse() 的 Usage 里取;写日志本身出错时吞掉,不影响调用结果返回。
《AI个人学习计划与资料助手》的 ai_usage_log 按业务记:除了用户、模型配置、服务商、模型名,还记 prompt_code(用的是哪条 Prompt 模板)、business_type(资料总结、知识点提取、学习计划生成、阶段复盘)和 business_id(前两种是资料 ID,后两种是学习目标 ID),一条日志能对回是哪份资料或哪个目标的哪次操作。表里没有 Token 列;失败时 error_message 写的是已经翻译成中文的原因。
为什么这样取舍
- 存原文是为了排查「模型当时看到了什么」。 两个项目的调用日志都保存请求与返回原文,这和正文说的生产做法不同:原文里有全部用户的调用内容,项目靠权限收窄暴露面:医院项目的观测接口只允许管理员调用,旅游项目的运营看板放在只有管理员能看到的菜单分组里。
- 失败日志的返回内容留空。 这次没有模型返回,写成提示词会让调用详情把它当成回答展示。
- 失败原因要截断。
error_message是varchar(1000),第三方库的异常动辄上千字,不截断会直接抛 Data too long,连这条日志都写不进去。
面试官还会追问
- 旅游项目运营看板的调用明细列表,为什么不返回 Prompt 原文?每页条数为什么设了上限?
- 医院项目观测页「调用用途」下拉框的选项是从哪里来的?为什么不写死?
学完《AI Agent旅游行程智能规划平台》,上面这些追问你都会迎刃而解。