工程化06:可观测性 从Prompt、上下文、模型、耗时、成本到失败原因,全面记录和分析AI应用的运行状态。
字数 1334
阅读时长 ≈ 4 分钟
2026-7-19 2026-7-27 AI 应用的可观测性比传统应用更复杂,需要记录 Prompt、上下文、模型响应、Token 消耗等特殊信息。有效的可观测性能帮助快速定位问题、优化性能、控制成本。
可观测性三要素
日志、指标、链路追踪
要素 说明 用途 日志 记录详细事件 问题排查、审计 指标 聚合统计数据 性能监控、告警 链路追踪 追踪请求路径 性能分析、依赖分析
AI 特有可观测数据
数据类型 说明 用途 Prompt 用户输入 问题复现、Prompt优化 Context 上下文信息 问题定位、上下文分析 Model Response 模型输出 质量分析、效果评估 Token Usage Token消耗 成本监控、优化 Model Parameters 模型参数 版本追踪、效果对比
日志记录
日志内容设计
字段 说明 示例 request_id 请求唯一标识 UUID timestamp 请求时间 ISO 8601 user_id 用户标识 用户ID prompt 用户输入 原始输入文本 context 上下文信息 历史对话、参考文档 model 使用的模型 gpt-4o model_params 模型参数 temperature: 0.7 response 模型响应 生成的文本 input_tokens 输入Token数 1000 output_tokens 输出Token数 500 latency 响应延迟 2500ms error 错误信息 null或错误描述 status 请求状态 success/failed/timeout
日志实现示例
@ Aspect
@ Component
public class ModelLoggingAspect {
private final Logger log = LoggerFactory. getLogger (ModelLoggingAspect.class);
@ Around ( "execution(* com.example.service.ModelService.generate(..))" )
public Object logModelRequest (ProceedingJoinPoint joinPoint ) throws Throwable {
long startTime = System. currentTimeMillis ();
String requestId = UUID. randomUUID (). toString ();
Object [] args = joinPoint. getArgs ();
String prompt = args.length > 0 ? args[ 0 ]. toString () : "" ;
ModelConfig config = args.length > 1 ? (ModelConfig) args[ 1 ] : null ;
try {
Object result = joinPoint. proceed ();
long latency = System. currentTimeMillis () - startTime;
log. info ( "Model request completed" , Map. of (
"request_id" , requestId,
"prompt" , truncate (prompt, 500 ),
"model" , config != null ? config. model () : "unknown" ,
"response" , truncate (result. toString (), 500 ),
"latency_ms" , latency,
"status" , "success"
));
return result;
} catch (Exception e ) {
long latency = System. currentTimeMillis () - startTime;
log. error ( "Model request failed" , Map. of (
"request_id" , requestId,
"prompt" , truncate (prompt, 500 ),
"model" , config != null ? config. model () : "unknown" ,
"latency_ms" , latency,
"error" , e. getMessage (),
"status" , "failed"
));
throw e;
}
}
private String truncate (String text , int maxLength ) {
return text != null && text. length () > maxLength
? text. substring ( 0 , maxLength) + "..."
: text;
}
}
日志分级策略
级别 说明 使用场景 DEBUG 详细调试信息 开发调试 INFO 正常运行信息 请求完成、状态变更 WARN 警告信息 缓存失效、降级触发 ERROR 错误信息 请求失败、异常抛出 FATAL 致命错误 系统崩溃、严重故障
指标监控
核心监控指标
指标 说明 计算方式 请求量 每秒处理请求数 QPS 成功率 请求成功比例 success / total 延迟 请求响应时间 P50/P90/P99 错误率 请求错误比例 error / total 超时率 请求超时比例 timeout / total 输入Token 每秒输入Token数 sum(input_tokens) 输出Token 每秒输出Token数 sum(output_tokens) 成本 每秒成本 sum(cost)
指标实现示例
@ Component
public class ModelMetricsService {
private final Counter requestCounter = Counter. builder ( "model.request.count" )
. description ( "Total number of model requests" )
. register ();
private final Timer latencyTimer = Timer. builder ( "model.request.latency" )
. description ( "Request latency in milliseconds" )
. register ();
private final Counter tokenCounter = Counter. builder ( "model.token.consumption" )
. description ( "Token consumption by type" )
. tag ( "type" , "input" )
. register ();
public void recordRequest ( long latencyMs , boolean success ,
long inputTokens , long outputTokens ) {
requestCounter. increment ();
latencyTimer. record (latencyMs, TimeUnit.MILLISECONDS);
if (success) {
tokenCounter. tag ( "type" , "input" ). increment (inputTokens);
tokenCounter. tag ( "type" , "output" ). increment (outputTokens);
}
}
public void recordError () {
Counter. builder ( "model.request.errors" )
. description ( "Number of failed requests" )
. register ()
. increment ();
}
}
告警规则
规则 条件 级别 通知方式 错误率过高 错误率 > 5% 持续 5 分钟 严重 即时通知 延迟过高 P99 > 5s 持续 10 分钟 警告 延迟通知 成本超支 日成本 > 预算的 80% 警告 延迟通知 Token 耗尽 Token 余额 < 1000 严重 即时通知 请求量突增 QPS > 基线 2 倍 警告 延迟通知
链路追踪
链路追踪设计
步骤 说明 示例 Trace ID 请求唯一标识 贯穿整个请求 Span 单个操作 模型调用、缓存查询 Parent Span 父操作 业务服务调用 Tags 操作标签 model=GPT-4, prompt_length=100 Logs 操作日志 开始时间、结束时间
链路追踪实现
@ Component
public class ModelTracingService {
private final Tracer tracer;
public String generateWithTrace (String prompt , ModelConfig config ) {
Span span = tracer. spanBuilder ( "Model.generate" )
. setAttribute ( "prompt.length" , prompt. length ())
. setAttribute ( "model" , config. model ())
. setAttribute ( "temperature" , config. temperature ())
. startSpan ();
try (Scope scope = span. makeCurrent ()) {
String response = modelService. generate (prompt, config);
span. setAttribute ( "response.length" , response. length ());
span. setAttribute ( "status" , "success" );
return response;
} catch (Exception e ) {
span. setAttribute ( "status" , "failed" );
span. setAttribute ( "error" , e. getMessage ());
throw e;
} finally {
span. end ();
}
}
}
链路分析
分析维度 说明 用途 延迟分析 各步骤延迟分布 定位性能瓶颈 依赖分析 调用关系分析 优化依赖结构 错误分析 错误分布分析 定位错误原因 并发分析 并发调用分析 优化资源使用
成本监控
成本追踪指标
指标 说明 计算方式 总Token消耗 输入+输出Token sum(input + output) 日均Token消耗 每日平均消耗 total / days 单次请求成本 每次请求平均成本 total_cost / requests 模型成本分布 各模型成本比例 model_cost / total_cost 用户成本分布 各用户成本比例 user_cost / total_cost
成本监控实现
@ Service
public class CostMonitoringService {
private final AtomicLong dailyInputTokens = new AtomicLong ( 0 );
private final AtomicLong dailyOutputTokens = new AtomicLong ( 0 );
private final Map< String , Long > modelTokenUsage = new ConcurrentHashMap<>();
public void recordUsage (String model , long inputTokens , long outputTokens ) {
dailyInputTokens. addAndGet (inputTokens);
dailyOutputTokens. addAndGet (outputTokens);
modelTokenUsage. merge (model, inputTokens + outputTokens, Long :: sum);
}
public CostReport generateReport () {
long totalInput = dailyInputTokens. get ();
long totalOutput = dailyOutputTokens. get ();
double cost = calculateCost (totalInput, totalOutput);
return new CostReport (totalInput, totalOutput, cost,
new HashMap<>(modelTokenUsage));
}
private double calculateCost ( long inputTokens , long outputTokens ) {
double inputCost = inputTokens * 0.0005 / 1000 ;
double outputCost = outputTokens * 0.0015 / 1000 ;
return inputCost + outputCost;
}
}
record CostReport ( long inputTokens, long outputTokens,
double totalCost, Map < String, Long > modelUsage) {}
可观测性最佳实践
数据存储策略
数据类型 存储方式 保留时间 日志 Elasticsearch 30天 指标 Prometheus 15天 链路追踪 Jaeger 7天 成本数据 数据库 永久
监控仪表盘设计
面板 内容 说明 概览面板 请求量、成功率、延迟、成本 整体状态 性能面板 P50/P90/P99延迟、吞吐量 性能指标 错误面板 错误率、错误类型分布 错误分析 成本面板 Token消耗、成本趋势、模型分布 成本监控 链路面板 请求链路、各步骤耗时 链路分析
故障排查流程
1. 发现告警 → 2. 查询日志 → 3. 分析链路 → 4. 定位问题 → 5. 修复验证
常见问题与解决方案
问题1:日志过多
表现 :日志量过大,存储成本高
解决方案 :
分级记录日志
压缩敏感信息
设置日志保留时间
采样记录
问题2:指标不全
表现 :缺少关键监控指标
解决方案 :
识别关键业务指标
添加AI特有指标
设置合理的告警规则
问题3:链路追踪缺失
表现 :无法追踪请求完整路径
解决方案 :
添加Trace ID
记录关键Span
整合分布式追踪
问题4:成本监控缺失
表现 :无法准确了解成本分布
解决方案 :
项目判断清单
需要问题排查 → 完善日志记录
需要性能监控 → 设置核心指标
需要追踪请求路径 → 实现链路追踪
需要控制成本 → 建立成本监控
需要快速定位问题 → 设置告警规则
需要全面分析 → 设计监控仪表盘
需要历史分析 → 合理存储数据
需要持续改进 → 建立可观测性体系