大规模语言模型(LLM)的可观测性
随着大规模语言模型(LLM)在各个领域的广泛应用,确保这些模型的稳定性和性能变得至关重要。为了实现这一目标,可观测性(Observability)成为了一个关键因素。OpenTelemetry 作为一个开源的可观测性框架,提供了强大的工具来监控和诊断 LLM 的运行状况。通过使用 OpenTelemetry,观测云可以轻松集成多种语言和框架,实现统一的数据收集、处理和实时监控。
可观测性是指系统能够通过其外部输出(如日志、指标和追踪数据)来推断其内部状态的能力。对于 LLM 来说,可观测性可以帮助我们了解模型的推理过程、性能瓶颈以及潜在的错误。通过收集和分析这些数据,我们可以更好地优化模型并确保其在生产环境中的稳定性。
观测云
观测云( www.guance.com )是一个提供数据可视化和监控服务的平台。它通常用于收集、处理和展示来自各种数据源的信息,帮助用户实时监控业务指标、系统性能和网络状态等。观测云通过图表、仪表板和报警系统,使得用户能够快速识别和响应数据变化,从而优化业务流程和提高效率。它支持多种数据集成方式,包括API、数据库、日志文件等,并且可以自定义仪表板,以满足不同用户的需求。观测云的界面友好,易于配置和使用,是企业数据监控和管理的有力工具。
OpenTelemetry
OpenTelemetry 是一个由 CNCF 托管的开源项目,旨在提供统一的 API 和 SDK 来收集和传输遥测数据(如日志、指标和追踪)。它支持多种编程语言,并且与各种后端系统兼容。对于 LLM,OpenTelemetry 可以帮助我们:
- 收集详细的追踪数据:通过追踪每个推理请求的执行路径,我们可以识别出哪些部分是性能瓶颈。
- 记录关键指标:例如推理时间、内存使用情况等,这些指标可以帮助我们评估模型的性能。
- 捕获日志信息:当出现问题时,日志可以提供宝贵的调试信息。
LLM 关键信号
在应用程序中使用大规模语言模型(LLM)与传统机器学习(ML)模型有所不同。主要区别在于,LLM 通常通过外部 API 调用访问,而不是在本地或内部运行。因此,捕获事件序列(通过追踪)尤为重要,特别是在基于 RAG(检索增强生成)的应用程序中,LLM 使用前后可能会有多个事件。此外,分析聚合数据(通过指标)可以快速提供关于请求、令牌和成本的概览,这对于优化性能和管理成本非常重要。以下是需要监控的关键信号:
追踪(Tracing)
1、请求元数据:在 LLM 的上下文中,请求元数据非常重要,因为各种参数(如温度和 top_p)会显著影响响应质量和成本。具体需要监控的方面包括:
- Temperature:表示希望从模型输出中获得的创造力或随机性水平。调整此参数会显著影响生成内容的性质。
- top_p:决定模型的选择性,即从最有可能的词汇中选择一定比例的词。较高的 top_p 值意味着模型考虑的词汇范围更广,使文本更加多样化。
- 模型名称或版本:对于跟踪随时间的变化至关重要,因为 LLM 的更新可能会影响性能或响应特性。
- 提示词:发送给 LLM 的确切输入。与内部 ML 模型不同,LLM 的输入可能变化很大,这会影响输出复杂性和成本。
2、响应元数据:由于 LLM 是通过 API 进行交互的,因此跟踪响应的具体细节对于成本管理和质量评估至关重要:
- Token:直接影响成本,并且是响应长度和复杂性的度量。
- Cost:对于预算规划至关重要,因为基于 API 的成本会随着请求数量和每个请求的复杂性而增加。
- 响应详情:类似于提示详情,但从响应的角度提供对模型输出特性的洞察,以及潜在的效率低下或意外成本领域。
指标(Metric)
- 请求量:发送到 LLM 服务的总请求数。这有助于了解需求模式,并识别使用中的任何异常情况,例如突然的激增或下降。
- 请求时长:从发出请求到接收到 LLM 响应所需的时间。这包括网络延迟和 LLM 生成响应所需的时间,能够提供关于 LLM 服务性能和可靠性的洞察。
- 成本和令牌计数:跟踪随时间累积的总成本和消耗的令牌数量对于预算规划和成本优化策略至关重要。监控这些指标可以提醒你是否存在意外增加的情况,这可能表明 LLM 的使用效率低下或需要进行优化。
OpenLIT
OpenLIT 是一个基于 OpenTelemetry 的库,旨在通过为各种大规模语言模型(LLM)和向量数据库(VectorDB)提供自动 instrumentation 来简化 LLM 基础应用程序的监控。
它遵循由 OpenTelemetry 社区建立的 GenAI 语义约定,并通过不依赖于供应商特定的跨度或事件属性以及 OTLP 端点配置的环境变量,确保了平滑的集成过程,提供了一个标准的解决方案。
具体来说:
- 自动 instrumentation:OpenLIT 可以自动为多种 LLM 和 VectorDB 添加监控代码,减少了手动配置的工作量。
- 遵循语义约定:确保与 OpenTelemetry 社区的标准保持一致,便于数据的统一管理和分析。
- 标准化集成:无需使用供应商特定的配置项,使得集成过程更加简单和通用,适用于不同的环境和平台。
通过使用 OpenLIT,您可以更轻松地实现对 LLM 应用程序的全面监控,从而更好地管理和优化这些复杂系统。
实施步骤
要为 LLM 实现可观测性,以下是几个关键步骤:
- 集成 OpenTelemetry SDK:首先,在你的 LLM 应用中集成 OpenTelemetry SDK。这可以通过安装相应的库并配置数据收集器来完成。
- 定义追踪范围:确定你希望追踪的具体操作,例如每次推理请求的开始和结束。
- 设置指标采集:选择需要监控的关键性能指标,并配置相应的采集器。
- 配置日志记录:确保所有重要的事件都被记录下来,以便后续分析。
- 可视化和报警:使用 Grafana 或其他工具将收集到的数据进行可视化展示,并设置报警机制以及时发现异常情况。
实践
为了更好地理解如何应用这些概念,让我们来看一个具体的案例。假设我们有一个基于 Python 的 LLM 应用程序。通过集成 OpenTelemetry SDK,我们可以轻松地为每个推理请求添加追踪上下文,并记录相关的性能指标和日志信息。然后,我们可以将这些数据发送到观测云进行存储和查询,通过构建场景模板进行实时监控。
DataKit
DataKit 是一个开源的、跨平台的数据收集和监控工具,由观测云开发并维护。它旨在帮助用户收集、处理和分析各种数据源,如日志、指标和事件,以便进行有效的监控和故障排查。DataKit 支持多种数据输入和输出格式,可以轻松集成到现有的监控系统中。
安装 DataKit
登录观测云控制台,在「集成」 - 「DataKit」选择对应安装方式,当前采用 Linux 主机部署 DataKit。
将安装命令复制到服务器上执行,服务器会自动下载和安装 DataKit 服务。
开启 OpenTelemetry 采集器
bash
cd /usr/local/datakit/conf.d/opentelemetry/
cp opentelemetry.conf.sample opentelemetry.conf
修改 opentelemetry.conf
的 customer_tags,提取关键 tag。
ini
customer_tags = ["gen_ai.application_name","gen_ai.request.model","gen_ai.prompt","gen_ai.completion","gen_ai.request.temperature","gen_ai.usage.input_tokens","gen_ai.usage.output_tokens","gen_ai.usage.total_tokens","gen_ai.endpoint","gen_ai.system"]
重启 DataKit
datakit service -R
LLM 应用
安装 OpenLIT
用以下命令安装 OpenLIT 的 Python 库:
pip install openlit
调整代码
在 LLM 应用上添加以下代码:
ini
import openlit
# 初始化 OpenLit
openlit.init(
otlp_endpoint="http://127.0.0.1:9529/otel",
application_name="kimi_openlit_flask"
)
运行
正常运行 LLM 应用即可。
观测云效果
登录观测云控制台,点击「场景」 -「新建仪表板」,输入 "OpenLIT", 选择 "OpenLIT 监控视图",点击 "确定" 即可添加视图。
通过观测云可以分析 Token 的消耗、调用模型的分布、成本、请求等数据。
也可以直接通过链路分析实际模型调用的历史记录。
通过查看链路详情,可以看到模型 input 和 output。
参考代码
该代码是基于 OpenAI 调用 Kimi 大模型并通过接口方式对外提供服务。
python
import os
import httpx
from flask import Flask, request, Response,jsonify,stream_with_context
import openlit
from openai import OpenAI
from opentelemetry.instrumentation.flask import FlaskInstrumentor
app = Flask(__name__)
# 使用FlaskInstrumentor自动插桩Flask应用
FlaskInstrumentor().instrument_app(app)
# 初始化 OpenLit
openlit.init(
otlp_endpoint="http://127.0.0.1:9529/otel",
application_name="kimi_openlit_flask"
)
# 从环境变量中获取 API Key
api_key = os.getenv("MOONSHOT_API_KEY")
if not api_key:
raise ValueError("请设置 MOONSHOT_API_KEY 环境变量")
client = OpenAI(
api_key=api_key,
base_url="https://api.moonshot.cn/v1",
)
def estimate_token_count(input_messages) -> int:
"""
计算输入消息的 Tokens 数量。
"""
try:
header = {
"Authorization": f"Bearer {api_key}",
}
data = {
"model": "moonshot-v1-128k",
"messages": input_messages,
}
with httpx.Client() as client:
print("请求接口")
r = client.post("https://api.moonshot.cn/v1/tokenizers/estimate-token-count", headers=header, json=data)
r.raise_for_status()
response_data = r.json()
print(response_data["data"]["total_tokens"])
return response_data["data"]["total_tokens"]
except httpx.RequestError as e:
print(f"请求失败: {e}")
raise
except (KeyError, ValueError) as e:
print(f"解析响应失败: {e}")
raise
def select_model(input_messages, max_tokens=1024) -> str:
"""
根据输入的上下文消息和预期的 max_tokens 值选择合适的模型。
"""
if not isinstance(max_tokens, int) or max_tokens <= 0:
raise ValueError("max_tokens 必须是正整数")
prompt_tokens = estimate_token_count(input_messages)
total_tokens = prompt_tokens + max_tokens
if total_tokens <= 8 * 1024:
return "moonshot-v1-8k"
elif total_tokens <= 32 * 1024:
return "moonshot-v1-32k"
elif total_tokens <= 128 * 1024:
return "moonshot-v1-128k"
else:
raise ValueError("tokens 数量超出限制 ?")
@app.route('/ask', methods=['POST'])
def ask():
data = request.json
messages = data.get('messages')
max_tokens = data.get('max_tokens', 2048)
if not messages:
return jsonify({"error": "messages 字段不能为空"}), 400
try:
model = select_model(messages, max_tokens)
completion = client.chat.completions.create(
model=model,
messages=messages,
max_tokens=max_tokens,
temperature=0.3,
stream=True # 启用流式生成
)
def generate():
for chunk in completion:
# yield chunk.choices[0].delta.content or ''
delta = chunk.choices[0].delta
if delta.content:
print(delta.content, end="")
yield delta.content or ''
return Response(stream_with_context(generate()), content_type='text/event-stream')
except Exception as e:
return jsonify({"error": str(e)}), 500
# export MOONSHOT_API_KEY=sk-xxxxxxxx
# python3 kimi_openlit_stream_flask.py
# 请求接口
# curl -X POST http://127.0.0.1:5000/ask -H "Content-Type: application/json" -H "Accept-Charset: UTF-8" -d '{
# "messages": [
# {"role": "system", "content": "你是 Kimi"},
# {"role": "user", "content": "你好,请给我讲一个童话故事。"}
# ],
# "max_tokens": 2048
# }'
if __name__ == '__main__':
app.run(debug=True)
结论
通过引入 OpenTelemetry,我们可以显著提高 LLM 的可观测性,从而更好地管理和优化这些复杂的系统。同时结合观测云的能力,能及时快速的对系统进行监控和分析,无论你是开发者还是运维人员,掌握这些技能都将为你带来巨大的价值。