LLM调用日志采集方案怎么选,怎么排查问题

文章导读
选 LLM 调用日志采集方案,先不要比较工具功能表,而要先定三件事:日志最终存放在哪里,也就是存放在本地、自建存储还是第三方平台;采集边界在哪里,也就是只记录元数据,还是连提示词和返回正文一起记录;排查时需要多快定位到某一次具体调用。很多“采了但查不出问题”的情况,通常是因为 request_id 没有贯通,或者字段标准不统一,而不是采集工具本身不行。下面按判断、处理、验证、边界四个部分展开;每一
📋 目录
  1. A 先确认当前场景和目标
  2. B 按这个顺序处理
  3. C 改完后怎么验证
  4. D 容易忽略的边界
A A

选 LLM 调用日志采集方案,先不要比较工具功能表,而要先定三件事:日志最终存放在哪里,也就是存放在本地、自建存储还是第三方平台;采集边界在哪里,也就是只记录元数据,还是连提示词和返回正文一起记录;排查时需要多快定位到某一次具体调用。很多“采了但查不出问题”的情况,通常是因为 request_id 没有贯通,或者字段标准不统一,而不是采集工具本身不行。下面按判断、处理、验证、边界四个部分展开;每一步都可以拿到自己的环境里核对。

适用场景:已有 LLM 调用链路,需要记录日志用于成本核算、错误定位或审计留存。操作动作:先统一日志字段标准,包括 request_id、起止时间、模型、状态、耗时、token 数,再选采集点,在 SDK 内埋点、在网关层记录或由代理层旁路采集。验证方式:用一条已知输入的调用,只凭 request_id 就能完整还原这次调用,包括失败和流式中断的调用。风险边界:如果合规要求不允许记录正文,就不要把提示词写进日志;流式响应和异步回调容易漏记,需要单独处理;只采元数据时无法做内容质量排查。

先确认当前场景和目标

先确认当前属于哪类场景,因为不同场景需要采集的最低字段集差别很大。只做成本统计,记录模型、输入输出 token 数、时间戳就够;要做错误定位,必须带上状态码、错误分类、首 token 耗时和总耗时;要做审计或内容回溯,才需要考虑把提示词与返回正文写入存储。跨多个服务时,还要判断是否需要 trace_id,把上游业务请求和下游模型调用串起来;如果只在单个服务里调用模型,request_id 通常就够用。

建议先确认这些信息:调用方有几个、是否经过自建网关、是否使用流式输出、是否走异步任务或回调、日志保留多久、正文允许谁查看、单条日志的体量上限。下面这张表可以用来判断最小字段集是否够用:

  • 只算成本:模型、token 数、时间戳、调用方标识,不需要正文。
  • 定位超时或限流:在上面基础上加 status、error_type、latency_ms、first_token_ms。
  • 内容回溯与审计:加 prompt_hash 和 payload_ref,也就是正文另存的位置引用,不必直接写进日志行。
  • 跨服务链路还原:加 trace_id 和上游业务单号,缺一个都会在排查时断链。

按这个顺序处理

第一步是确定字段标准,先写日志结构,选定存储或工具以后不要改动。字段名一旦分散在各处,后面做聚合和检索都要重新补数据。可以先定一份最小骨架,实际字段按环境替换:

{
  "request_id": "调用方生成,全链路透传",
  "trace_id": "有网关或链路追踪时填写,否则与 request_id 同值",
  "ts_start": "ISO8601 带时区",
  "ts_end": "ISO8601 带时区",
  "model": "模型标识",
  "stream": true,
  "status": "ok | error | timeout | rate_limited | blocked",
  "error_type": "归类后的错误类型,不直接塞原始堆栈",
  "latency_ms": 0,
  "first_token_ms": 0,
  "input_tokens": 0,
  "output_tokens": 0,
  "prompt_hash": "正文不入库时用于去重与比对",
  "payload_ref": "正文另存位置的引用,可为空"
}

第二步是选采集点。在业务代码的 SDK 封装层记录,字段最全、可拿到业务上下文,但要改代码,并且调用方越多越容易漏;在网关上记录,能统一收口,但拿不到调用方内部上下文;旁路采集不改业务代码,但流式响应和错误路径的还原难度更高。通常建议在封装层记录主记录、在网关层补链路标识,两边都改时先确认字段标准一致。

第三步是确定写入方式。同步写入日志存储会拉长调用耗时,尤其在模型响应本来就慢的路径上更容易放大;建议用异步队列或本地文件再异步采集。无论哪种,都要处理写失败,不能让采集失败反过来中断业务调用。

第四步是采样与脱敏。采样率只作用于成功调用,错误调用建议全量保留,否则排查时样本会偏;正文中的手机号、证件号、密钥等字段,应在写入前脱敏,而不是指望下游再过滤。

LLM调用日志采集方案怎么选,怎么排查问题

写入前建议做一次字段校验,缺字段的日志在排查时基本没用:

REQUIRED = ("request_id", "ts_start", "model", "status", "latency_ms")

def check(record):
    missing = [k for k in REQUIRED if not record.get(k)]
    if missing:
        # 记到采集链路自身的错误日志,不要静默丢弃
        report("llm_log_missing_fields", missing, record.get("request_id"))
    return record

改完后怎么验证

验证不要只看“有没有日志”,而要检查能不能还原一次具体调用。可以按以下顺序做:

  • 发起一条内容已知的成功调用,用返回或透传出来的 request_id 检索,检查是否能得到时间、模型、耗时、token 数齐全的一条记录。
  • 发起一条流式调用,确认 first_token_ms 和 latency_ms 都有值,中途主动断开一次,确认这次调用也留下记录且状态不是 ok。
  • 构造一次失败调用,比如超时或参数错误,确认 status 和 error_type 有值,而不是只有空字段。
  • 检查脱敏是否生效:在输入里放入一个特征明显的测试串,检索日志存储,确认原文没有出现。
  • 检查时钟:对比调用方和采集端的时间戳,确认时区一致,否则聚合时会出现负数耗时。

预期信号是:一次调用对应一条主记录,流式调用可由片段补齐;按时间、模型、状态三个维度能筛出结果;错误调用不会被采样漏掉。如果检索时经常出现“记录存在但字段为空”,通常说明采集点选在了拿不到上下文的层,需要回到第二步调整采集点。

容易忽略的边界

流式响应最容易漏记:只在结束时写日志,中途断连就没有记录,建议在开始时就写一条待更新记录,结束时补齐。异步任务和回调也一样,请求线程结束不代表模型调用结束,需要按任务标识回填。跨服务场景下,调用方和服务端时钟偏差会让耗时统计失真,建议统一使用 UTC 或带时区的时间格式。

合规和成本也是边界。长上下文场景下,单条日志如果包含完整正文,体量会很大,检索和存储都会变重;这类场景建议只存 prompt_hash 和引用位置。如果合规不允许记录提示词,就不要设置“先采下来再脱敏”的流程,这类方案在审计时通常过不去。

回退建议:采集链路出问题、影响业务时,先降级为只采元数据,正文暂不写入;如果异步队列积压,可以先落到本地文件,再由采集器补偿读取,而不是直接丢弃。以上判断需要结合具体存储和权限配置确认,不同环境的写入限额、字段长度限制和保留策略都不一样,上线前建议在预发环境按上面的验证清单走一遍。