ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

大模型后端上线后,怎样把慢调用拆开看

大模型后端上线后,怎样把慢调用拆开看 大模型后端上线后怎样把慢调用拆开看下面以一次模拟排障为例网关的尾延迟持续升高需要区分检索、模型调用和工具执行分别占用了多少时间。常规链路追踪往往只把一次请求记成一个很长的 HTTP Span。此时无法区分时间耗在检索、模型调用还是工具执行也无法看出是否发生了不必要的重试。下面讨论的是用于设计观测点的排查模型具体阈值应由服务的容量和用户等待预期决定。这就是大模型服务集成的典型痛点。当系统从传统的确定性 RPC 调用演变为多轮交互的 Agent 链条基于传统 HTTP/RPC 协议栈的链路追踪体系瞬间失灵。要彻底排查这类问题必须在工程层将 LLM 语义拆解为粒度极细的 Trace 节点。1. 传统 APM 探针失灵的根因拆解在传统的微服务架构中一个 RPC 请求的生命周期是确定且线性的。调用方发出 Request提供方返回 Response中间的数据库与缓存操作都有明确的客户端 Client 拦截器进行 Span 记录。大模型 Agent 的运行逻辑截然不同。一个用户的 Prompt 提交给 Agent 后内部可能会经历以下过程向量数据库Vector DB进行 3 次相似度检索拼接 Context首次调用 LLM API模型返回tool_calls指令网关执行本地工具如查询 MySQL 订单表将工具返回结果二次打包给 LLM APILLM API 以 Server-Sent Events (SSE) 流式返回最终结果。如果只记录网关入口和出口这 5 个内部步骤的时间花在哪儿完全不可见。更危险的是若 LLM 陷入 Tool Calling 死循环网关内存和连接数会在极短时间内被撑爆。我们需要重新定义大模型语义下的 OpenTelemetry 属性字段Semantic Conventions将 Prompt Token 数量、Completion Token 数量、模型名称以及 Tool 调用的上下文强制注入到 Trace Span 中。2. Agent 链条全链路 Trace 模型设计为了厘清大模型服务集成的物理调用关系我们将 Trace 模型划分为三层结构Gateway Main Span记录整个大模型请求的生命周期与总耗时。Child Spans分别记录rag.retrieval知识库检索、llm.chat_completion大模型交互与agent.tool_execution本地工具执行。Stream Metrics记录 SSE 首字延迟TTFT, Time To First Token与吐字速率Tokens/sec。请求进入网关后应在服务调用、异步任务和响应返回的每个节点持续透传 Trace 上下文。3. 基于 OpenTelemetry Java SDK 的生产级埋点实现在 Spring Boot 3.x 架构下我们通过实现自定义的 OpenTelemetryTracer与ClientHttpRequestInterceptor拦截所有的 HTTP 请求与 SSE 流准确采集 LLM 元数据。以下为生产环境落地的核心拦截器代码package com.example.llm.gateway.observability; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Scope; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.stereotype.Component; import java.io.IOException; import java.nio.charset.StandardCharsets; Component public class LlmTracingInterceptor implements ClientHttpRequestInterceptor { private final Tracer tracer; public LlmTracingInterceptor(Tracer tracer) { this.tracer tracer; } Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { // 只有针对 LLM API 的请求才注入专门的 LLM 语义 Span if (!request.getURI().getPath().contains(/chat/completions)) { return execution.execute(request, body); } Span span tracer.spanBuilder(llm.chat_completion) .setAttribute(gen_ai.system, DeepSeek) .setAttribute(gen_ai.request.model, deepseek-chat) .setAttribute(gen_ai.request.temperature, 0.7) .startSpan(); try (Scope scope span.makeCurrent()) { // 解析请求体中的 Prompt 字节数避免全量解析 JSON 损耗性能 span.setAttribute(gen_ai.request.prompt_bytes, body.length); long startTime System.currentTimeMillis(); ClientHttpResponse response execution.execute(request, body); long latency System.currentTimeMillis() - startTime; span.setAttribute(gen_ai.response.latency_ms, latency); span.setAttribute(http.status_code, response.getStatusCode().value()); // 提取响应 Header 中的真实 Token 统计如 Provider 支持 String promptTokens response.getHeaders().getFirst(x-llm-prompt-tokens); String completionTokens response.getHeaders().getFirst(x-llm-completion-tokens); if (promptTokens ! null) { span.setAttribute(gen_ai.usage.prompt_tokens, Long.parseLong(promptTokens)); } if (completionTokens ! null) { span.setAttribute(gen_ai.usage.completion_tokens, Long.parseLong(completionTokens)); } span.setStatus(StatusCode.OK); return response; } catch (Exception ex) { span.recordException(ex); span.setStatus(StatusCode.ERROR, LLM API Call Failed: ex.getMessage()); throw ex; } finally { span.end(); } } }配合 Micrometer 自定义 Metrics我们将 Token 消耗和首字延迟推送到 Prometheuspackage com.example.llm.gateway.observability; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Timer; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; Component public class LlmMetricsCollector { private final Counter promptTokenCounter; private final Counter completionTokenCounter; private final Timer ttftTimer; public LlmMetricsCollector(MeterRegistry registry) { this.promptTokenCounter Counter.builder(llm.tokens.prompt.total) .description(Total prompt tokens consumed) .register(registry); this.completionTokenCounter Counter.builder(llm.tokens.completion.total) .description(Total completion tokens generated) .register(registry); this.ttftTimer Timer.builder(llm.response.ttft.latency) .description(Time To First Token latency) .publishPercentiles(0.5, 0.95, 0.99) .register(registry); } public void recordTokens(long prompt, long completion) { promptTokenCounter.increment(prompt); completionTokenCounter.increment(completion); } public void recordTTFT(long durationMs) { ttftTimer.record(durationMs, TimeUnit.MILLISECONDS); } }4. 现场排障过程与生产终端调试命令探针上线后当报警再次响应时工程师可以通过跳板机直接使用 Shell 命令和 OpenTelemetry 导出日志进行准确定位。首先检查 Prometheus 输出的 LLM 网关可观测指标口径# 查询当前网关节点实时暴露的 LLM 指标过滤 Token 计数与延迟 curl -s $SERVICE_BASE_URL/actuator/prometheus | grep llm_ # 输出结果示例 # llm_tokens_prompt_total 452810.0 # llm_tokens_completion_total 128490.0 # llm_response_ttft_latency_seconds_max 6.42如果发现ttft_latency最大值达到 6.4 秒说明大模型服务提供商本身响应缓慢。但如果ttft正常而总耗时极长则需要进一步抓取 Jaeger/Tempo 中的 TraceId。在 Linux 终端通过 Grep 在网关日志中追查特定的异常 Trace# 过滤包含超时异常且带有 TraceId 的请求日志 grep llm.chat_completion /var/log/llm-gateway/app.log | grep ERROR -B 2 -A 5 # 使用 bpftrace 检查 JVM 网关进程与远端 LLM API (443 端口) 的 TCP 建立连接与 RTT 耗时 sudo bpftrace -e kprobe:tcp_v4_connect { $sk (struct sock *)arg0; start[tid] nsecs; } kretprobe:tcp_v4_connect /start[tid]/ { $lat nsecs - start[tid]; printf(Connect latency: %d ms\n, $lat / 1000000); delete(start[tid]); }如果追踪显示模型首字延迟正常而工具 Span 反复出现就应检查格式校验失败后的重试策略。不要先假定问题在模型侧先用同一请求的 Span 顺序、错误类别和重试记录复现再调整重试上限或回退路径。5. 生产环境告警收口与观测防线有了细粒度的可观测数据后告警规则不能再粗暴地只对网关 HTTP P99 延迟进行报警。必须建立分层防线第一道防线LLM Provider 状态告警当llm.response.ttft.latency的 P99 超过 4000ms 时自动触发 API 提供商降级熔断切换到备用模型节点。第二道防线Agent 死循环闸门在 Trace 拦截器中计数单次 Request 内agent.tool_execution的 Span 数量超过 5 个时强制终止 Trace 并抛出ToolLoopException不再浪费 Token 预算。第三道防线Token 异常飙高拦截监控单次请求gen_ai.usage.prompt_tokens 16384 的异常 Request直接熔断并记录快照日志防范恶意 Prompt 攻击拖垮网关内存。通过这三层观测防线的建立大模型服务不再是一个黑盒。排障时间从过去的上百分钟缩短到了 5 分钟以内。
返回列表