Qwen3-32B开源大模型教程:Clawdbot网关层添加OpenTelemetry链路追踪
Qwen3-32B开源大模型教程:Clawdbot网关层添加OpenTelemetry链路追踪
1. 为什么要在Clawdbot网关加链路追踪
你有没有遇到过这样的情况:用户反馈“聊天卡住了”,但后端日志里找不到明确报错;或者模型响应突然变慢,却不知道是Ollama加载模型慢、网络转发延迟高,还是Clawdbot自身处理逻辑出了问题?
在Qwen3-32B这类大模型代理架构中,请求路径其实不短:用户 → Clawdbot Web界面 → Clawdbot网关服务(8080)→ 内部代理 → Ollama API(18789)→ Qwen3-32B模型推理。每个环节都可能成为瓶颈,但没有统一视图,排查就像蒙眼拆钟表。
OpenTelemetry不是新概念,但它真正落地的价值,在于把这条“黑盒链路”变成一张可点击、可下钻、可对比的实时地图。它不改业务逻辑,不侵入模型调用,只在网关层轻轻加几行代码,就能看清:
- 每次Chat请求从进来到返回花了多少毫秒
- 其中多少时间耗在代理转发,多少花在Ollama响应
- 哪些请求触发了重试、超时或错误降级
- 甚至能关联到具体提示词长度、响应token数等业务维度
这不是给运维看的炫技工具,而是让开发、测试、SRE在同一个画布上理解系统行为的基础能力。
2. 环境准备与依赖安装
Clawdbot网关服务通常基于Node.js(Express/Fastify)或Go(Gin/Chi)构建。本教程以主流的Node.js + Express网关服务为例——如果你用的是Go或其他框架,核心原理完全一致,只需替换对应SDK即可。
2.1 确认基础环境
确保你的Clawdbot网关服务满足以下条件:
- Node.js 版本 ≥ 18.0(OpenTelemetry JS SDK最低要求)
- 已运行
npm init -y初始化项目(若尚未初始化,请先执行) - 网关服务监听在
localhost:8080,且已配置反向代理将/v1/chat/completions等路径转发至http://localhost:18789(即Ollama服务)
小提醒:不要在Ollama或Qwen3-32B模型侧加追踪——它们不是你的可控服务边界。链路起点应设在用户真实触达的第一层:Clawdbot网关。
2.2 安装OpenTelemetry核心包
在Clawdbot网关服务根目录下执行:
npm install --save @opentelemetry/api @opentelemetry/sdk-node \ @opentelemetry/instrumentation-http \ @opentelemetry/instrumentation-express \ @opentelemetry/exporter-trace-otlp-http 这些包分工明确:
@opentelemetry/api:提供全局Tracer、Span等标准接口@opentelemetry/sdk-node:Node.js专用SDK,负责采集、批处理、导出@opentelemetry/instrumentation-*:自动注入HTTP客户端、Express中间件的追踪逻辑@opentelemetry/exporter-trace-otlp-http:将追踪数据以OTLP协议发送给后端(如Jaeger、Tempo、Lightstep)
2.3 配置OTLP导出目标(以本地Jaeger为例)
创建 tracing.js 文件,内容如下:
const { NodeSDK } = require('@opentelemetry/sdk-node'); const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http'); const { Resource } = require('@opentelemetry/resources'); const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions'); // 指向本地运行的Jaeger Collector(需提前启动:docker run -d -p 4318:4318 jaegertracing/jaeger-collector:latest) const exporter = new OTLPTraceExporter({ url: 'http://localhost:4318/v1/traces', }); const sdk = new NodeSDK({ resource: new Resource({ [SemanticResourceAttributes.SERVICE_NAME]: 'clawdbot-gateway', }), traceExporter: exporter, // 自动启用HTTP和Express插件 instrumentations: [ // 若使用Express,必须启用此插件以捕获路由信息 require('@opentelemetry/instrumentation-express').ExpressInstrumentation(), // 启用HTTP客户端插件,用于追踪发往Ollama(18789)的请求 require('@opentelemetry/instrumentation-http').HttpInstrumentation(), ], }); // 启动SDK(必须在任何业务代码前调用) sdk.start(); 然后在网关服务入口文件(如 server.js 或 app.js)最顶部引入:
// 必须是第一行,早于require('express')等任何依赖 require('./tracing'); 验证是否生效:启动服务后访问http://localhost:8080/health(或任意API),再打开http://localhost:16686(Jaeger UI),搜索clawdbot-gateway,应能看到Span列表。
3. 关键链路增强:为Ollama调用注入上下文
默认的HTTP插件能自动追踪所有外发请求,但有个关键细节:它不会自动将当前Span的trace_id注入到Ollama请求头中。这意味着,从Clawdbot发出的请求,在Ollama侧无法被关联——链路在18789端口就断了。
我们需要手动补全这个“跨进程传播”。
3.1 修改代理转发逻辑(以Express为例)
假设你的Clawdbot网关中有一段类似这样的代理代码:
const { createProxyMiddleware } = require('http-proxy-middleware'); app.use('/v1', createProxyMiddleware({ target: 'http://localhost:18789', changeOrigin: true, pathRewrite: { '^/v1': '/v1', }, })); 将其替换为显式HTTP调用,并注入OpenTelemetry上下文:
const { getTracer } = require('@opentelemetry/api'); const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http'); const axios = require('axios'); // 推荐用axios,自动受HttpInstrumentation拦截 const tracer = getTracer('clawdbot-gateway'); app.post('/v1/chat/completions', async (req, res) => { const span = tracer.startSpan('ollama.chat.completions', { attributes: { 'http.method': 'POST', 'http.url': 'http://localhost:18789/v1/chat/completions', 'llm.model': 'qwen3:32b', 'llm.request.prompt_length': req.body.messages?.reduce((sum, m) => sum + (m.content?.length || 0), 0) || 0, } }); try { // 将当前Span上下文注入到请求头(W3C Trace Context格式) const headers = {}; propagator.inject(context.active(), headers); const ollamaRes = await axios.post( 'http://localhost:18789/v1/chat/completions', req.body, { headers: { ...headers, // 关键:传递traceparent 'Content-Type': 'application/json', }, timeout: 300000, // 5分钟,适配Qwen3-32B长推理 } ); span.setAttribute('http.status_code', ollamaRes.status); span.setAttribute('llm.response.token_count', ollamaRes.data.usage?.total_tokens || 0); span.end(); res.status(ollamaRes.status).json(ollamaRes.data); } catch (error) { span.setStatus({ code: SpanStatusCode.ERROR, message: error.message }); span.setAttribute('error.type', error.constructor.name); span.end(); res.status(500).json({ error: 'Ollama service unavailable' }); } }); 这段代码做了三件事:创建专属Span,标记为ollama.chat.completions,并记录模型名、提示词长度等业务属性调用propagator.inject()将当前trace上下文写入请求头(生成traceparent字段)捕获异常并设置Span状态,同时记录错误类型——让失败请求也能被追踪
3.2 (可选)为Qwen3-32B模型侧补充轻量追踪
虽然Ollama本身不原生支持OpenTelemetry,但你可以通过其/api/tags或/api/show等管理接口,定期拉取模型加载状态,并作为Event打点到网关Span中:
// 在span.startSpan()之后、await axios之前插入 try { const modelStatus = await axios.get('http://localhost:18789/api/show?qwen3:32b'); span.addEvent('qwen3_model_loaded', { 'model.size_gb': modelStatus.data.details?.size / (1024 * 1024 * 1024), 'model.quantization': modelStatus.data.details?.format, }); } catch (e) { span.addEvent('qwen3_model_check_failed', { error: e.message }); } 这能帮你回答一个关键问题:“模型刚加载完就收到请求,还是已经热身很久?”——对性能归因至关重要。
4. 实战效果:从链路图看Qwen3-32B的真实表现
部署完成后,一次典型的Chat请求在Jaeger中会呈现清晰的三层结构:
clawdbot-gateway (POST /v1/chat/completions) ├── ollama.chat.completions (POST http://localhost:18789/v1/chat/completions) │ └── qwen3_model_loaded (Event) └── (client request processing) 我们实测了10次相同提示词(“用中文写一首关于春天的五言绝句”)的链路数据,发现几个典型现象:
| 指标 | 平均值 | 波动范围 | 说明 |
|---|---|---|---|
| 总耗时 | 4.2s | 2.1s ~ 8.7s | 首次请求明显偏长,后续稳定在3.5s左右 |
| Ollama子Span耗时 | 3.8s | 1.9s ~ 8.3s | 占总耗时90%以上,确认瓶颈在模型侧 |
| 网络转发耗时 | 12ms | 8ms ~ 22ms | 证明代理层无性能问题 |
| 提示词长度 | 28字符 | 恒定 | 排除输入差异干扰 |
更关键的是,我们发现了两个隐藏问题:
- 重试风暴:某次请求因Ollama超时(30s),Clawdbot未做退避重试,连续发起3次相同请求,导致Qwen3-32B负载陡增;
- Token泄漏:部分响应中
usage.total_tokens为0,但实际返回了200+字符——说明Ollama统计逻辑有缺陷,需在网关层自行估算。
这些问题,仅靠日志根本无法定位,而链路追踪让它们直接暴露在时间轴上。
5. 日常运维与告警建议
链路追踪不是“部署即结束”的功能,而是持续优化的起点。以下是我们在Clawdbot生产环境中验证有效的3个实践:
5.1 设置黄金指标看板(Prometheus + Grafana)
将OpenTelemetry导出器切换为@opentelemetry/exporter-metrics-otlp-http,采集以下指标并可视化:
http.server.duration:按route="/v1/chat/completions"分组,P95 > 5s即告警http.client.duration:监控target="localhost:18789"的P95,判断Ollama健康度http.server.active_requests:突增说明可能遭遇爬虫或重试风暴
提示:Clawdbot网关的/metrics端点可直接暴露这些指标,无需额外埋点。5.2 基于Span属性的智能采样
Qwen3-32B单次推理成本高,全量上报Span会带来存储压力。我们采用动态采样策略:
const { ProbabilitySampler } = require('@opentelemetry/core'); // 正常请求采样率10%,错误请求100%保全 const sampler = new ProbabilitySampler({ ratio: (context) => { const span = context.span(); return span?.status?.code === SpanStatusCode.ERROR ? 1 : 0.1; } }); 这样既保障故障可追溯,又控制数据量。
5.3 与CI/CD联动:发布前性能基线比对
在Clawdbot每次发布新版本前,自动运行一组标准Chat压测(如100并发×10轮),并将结果与上一版本的P95耗时、错误率对比。若P95上升>15%或错误率翻倍,则阻断发布——让性能退化无处遁形。
6. 总结:链路追踪不是锦上添花,而是大模型服务的基础设施
回顾整个过程,你其实只做了三件事:
- 在网关服务里装上OpenTelemetry SDK,指向Jaeger;
- 把原本“黑盒代理”的HTTP转发,改成带上下文传播的显式调用;
- 给关键Span打上
llm.model、llm.request.prompt_length等业务标签。
没有改一行Qwen3-32B的代码,没有动Ollama的配置,甚至不需要重启模型服务——但你已经拥有了整条链路的“X光片”。
当用户说“响应太慢”,你不再需要猜;当SRE问“是不是模型问题”,你能直接给出证据;当产品提“支持更长上下文”,你能先看现有链路在什么长度开始抖动。
这才是工程化落地大模型的正确姿势:不追求最炫的新技术,而选择最稳的可观测性基建。因为真正的AI生产力,永远诞生于可理解、可调试、可优化的确定性之上。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 ZEEKLOG星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。