# LiteLLM SDK 本文介绍如何将 [LiteLLM SDK](https://docs.litellm.ai/docs/#litellm-python-sdk) 运行过程的 Trace 数据自动上报到扣子罗盘。 本文适用于 LiteLLM SDK,如果你需要上报 LiteLLM Proxy 的 Trace 数据,请参考 [LiteLLM Proxy](https://docs.coze.cn/api/open/docs/cozeloop/litellm_proxy_native_trace_report)。 ## 功能介绍 [LiteLLM](https://docs.litellm.ai/docs/) 是一款开源的 Proxy 和 SDK。它提供了统一的 API 接口,能够兼容 OpenAI 的 Endpoint,实现对数百家大语言模型提供商及旗下模型的统一调用与管理。 LiteLLM SDK 能够与 OpenTelemetry 集成,自动捕获 LiteLLM SDK 运行过程中的关键操作和性能指标,并将这些数据作为 Trace 数据上报到扣子罗盘,完成可视化分析。本集成方案适用于监控 LiteLLM SDK 调用 AI 模型过程、排查调用问题的场景。 本文以 Python SDK 为例,介绍如何通过 OpenTelemetry SDK 将 Trace 数据上报至扣子罗盘平台,从而实现对应用程序性能和行为的高效监控与分析。 ## 准备工作 使用 OpenTelemetry Python SDK 前,需要先安装 Python SDK 以及相关的依赖库。 1. 安装 Python SDK。 Opentelemetry Python SDK 要求 Python 3.9 或以上版本。你可以通过如下方法查看 Python 版本。 ```Python # 查看 Python 版本 python --version # 回显信息显示已安装 3.9.2 版本 Python 3.9.2 ``` 2. 安装依赖库。 安装 Python 依赖库,分别用于调用 AI 模型、对调用过程进行可观测性管理以及将 Trace 数据导出到后端系统。 ```Python pip install opentelemetry-sdk pip install opentelemetry-exporter-otlp pip install openinference-instrumentation-litellm pip install litellm ``` ## 配置环境变量 在上报 Trace 数据前,你需要正确配置 OpenTelemetry 的环境变量,以确保 LiteLLM SDK 能够正常调用 OpenAI 模型,并能正确发送 Trace 数据到指定的扣子罗盘空间中。环境变量配置格式及说明如下: ```Python OTEL_EXPORTER_OTLP_HEADERS=cozeloop-workspace-id={CozeLoop空间ID},Authorization=Bearer {个人访问令牌或服务访问令牌} OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://api.coze.cn/v1/loop/opentelemetry/v1/traces OPENAI_BASE_URL=*** OPENAI_API_KEY=*** OPENAI_MODEL_NAME=*** ``` | **环境变量** | **说明** | | --- | --- | | OTEL_EXPORTER_OTLP_HEADERS | 设置上报数据时所需的扣子罗盘认证信息和工作空间标识。包括如下参数:

* cozeloop-workspace-id:配置为扣子罗盘工作空间 ID。获取步骤,请参考[获取扣子罗盘工作空间 ID](https://docs.coze.cn/api/open/docs/cozeloop/get_workspace_id_and_token#01dede13)。
* Authorization:配置为扣子罗盘的个人访问令牌或服务访问令牌。获取步骤,请参考[配置个人访问令牌](https://docs.coze.cn/api/open/docs/cozeloop/authentication-for-sdk#05d27a86)、[配置服务访问令牌](https://docs.coze.cn/api/open/docs/cozeloop/authentication-for-sdk#83f924a1)。 | | OTEL_EXPORTER_OTLP_TRACES_ENDPOINT | 指定 OpenTelemetry 数据的上报地址,固定为 `https://api.coze.cn/v1/loop/opentelemetry/v1/traces`。 | | OPENAI_BASE_URL | 配置 OpenAI 模型的服务地址。

* 推荐使用方舟模型,其 BASE_URL 固定为 `https://ark.cn-beijing.volces.com/api/v3`。
* 使用其他 OpenAI 模型时,配置为其对应的 BASE_URL 即可。 | | OPENAI_API_KEY | 配置 OpenAI 模型的 API Key。

* 推荐使用方舟模型,其 API Key 获取步骤,请参考 [API Key 管理](https://www.volcengine.com/docs/82379/1361424)。
* 使用其他 OpenAI 模型时,配置为其对应的 API Key 即可。 | | OPENAI_MODEL_NAME | 配置 OpenAI 模型名称,需使用模型提供方作为前缀。例如 openai/doubao-1-5-pro-256k-250115。 | ## 上报 Trace 完成上述配置后,你可以调用 OpenAI 模型开展 AI 对话,并通过 OpenTelemetry Python SDK 上报 Trace 数据到扣子罗盘。以下示例将分别展示自动上报 Trace 数据与上报自定义 Span 节点数据的具体实现方式。 ### 自动上报 在本示例中,使用 LiteLLM 框架调用火山方舟 doubao-1-5-pro-256k-250115 模型完成天气查询功能,并通过 OpenTelemetry SDK 自动收集和上报 OpenAI 模型执行过程中的 Trace 数据到扣子罗盘,包括 LLM 调用、工具执行、请求处理流程等数据,从而实现对 AI 模型调用过程的全面监控和分析。 ```Python from time import sleep import os import json from typing import Any, Dict, List, Optional import litellm from opentelemetry import trace from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter from opentelemetry.sdk.trace import TracerProvider # noqa: E402 from opentelemetry.sdk.trace.export import SimpleSpanProcessor, BatchSpanProcessor from openinference.instrumentation.litellm import LiteLLMInstrumentor # OpenAI env os.environ['OPENAI_BASE_URL'] = 'https://ark.cn-beijing.volces.com/api/v3' # use ark model url os.environ['OPENAI_API_KEY'] = '***' # your ark api key, from https://www.volcengine.com/docs/82379/1361424 os.environ['OPENAI_MODEL_NAME'] = 'openai/doubao-1-5-pro-256k-250115' # your model name, use model provider as prefix, like openai/ # OTEL env os.environ["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"] = "https://api.coze.cn/v1/loop/opentelemetry/v1/traces" # cozeloop otel endpoint os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = "cozeloop-workspace-id=***,Authorization=Bearer ***" # set your 'spaceID' and 'pat or sat token' # Set up OpenTelemetry tracing otlp_exporter = OTLPSpanExporter( timeout=10, ) trace.set_tracer_provider(TracerProvider()) trace.get_tracer_provider().add_span_processor( BatchSpanProcessor(otlp_exporter) ) tracer = trace.get_tracer(__name__) LiteLLMInstrumentor().instrument() def get_current_weather(location: str, unit: str = "fahrenheit") -> str: with tracer.start_as_current_span("get_current_weather") as span: span.set_attribute("cozeloop.span_type", "tool") span.set_attribute("cozeloop.input", json.dumps({"location": location, "unit": unit})) res = "" if "tokyo" in location.lower(): res = json.dumps({"location": "Tokyo", "temperature": "10", "unit": "celsius"}) elif "san francisco" in location.lower(): res = json.dumps({"location": "San Francisco", "temperature": "72", "unit": "fahrenheit"}) elif "paris" in location.lower(): res = json.dumps({"location": "Paris", "temperature": "22", "unit": "celsius"}) else: res = json.dumps({"location": location, "temperature": "unknown"}) span.set_attribute("cozeloop.output", res) return res def to_dict_message(msg: Any) -> Dict[str, Any]: return { "role": str(getattr(msg, "role", "")), "content": str(getattr(msg, "content", "")), } def parallel_function_call() -> None: try: messages: List[Dict[str, Any]] = [ { "role": "user", "content": "What's the weather like in San Francisco, Tokyo, and Paris?", } ] tools = [ { "type": "function", "function": { "name": "get_current_weather", "description": "Get the current weather in a given location", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "The city and state, e.g. San Francisco, CA", }, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}, }, "required": ["location"], }, }, } ] response: Any = litellm.completion( base_url=os.environ["OPENAI_BASE_URL"], api_key=os.environ["OPENAI_API_KEY"], model=os.environ["OPENAI_MODEL_NAME"], messages=messages, tools=tools, tool_choice="auto", ) print("\nFirst LLM Response:\n", response) response_message: Any = response.choices[0].message tool_calls: Optional[Any] = getattr(response_message, "tool_calls", None) print("\nLength of tool calls", len(list(tool_calls or []))) if tool_calls: available_functions = { "get_current_weather": get_current_weather, } messages.append(response_message) for tool_call in list(tool_calls): function_name = getattr(getattr(tool_call, "function", None), "name", None) if function_name is None: continue function_to_call = available_functions[function_name] function_args = json.loads( getattr(getattr(tool_call, "function", None), "arguments", "{}") ) function_response = function_to_call( location=function_args.get("location"), unit=function_args.get("unit"), ) messages.append( { "tool_call_id": getattr(tool_call, "id", ""), "role": "tool", "name": function_name, "content": function_response, } ) second_response: Any = litellm.completion( base_url=os.environ["OPENAI_BASE_URL"], api_key=os.environ["OPENAI_API_KEY"], model=os.environ["OPENAI_MODEL_NAME"], messages=messages, ) print("\nSecond LLM response:\n", second_response) except Exception as e: print(f"Error occurred: {e}") if __name__ == "__main__": # set custom span, name is root_span with tracer.start_as_current_span("root_span") as span: span.set_attribute("cozeloop.span_type", "custom") # start call parallel_function_call() ``` ### 上报自定义节点 如果你需要上报自定义 Span 节点,可以使用 OpenTelemetry SDK 手动创建 Span 并上报。OpenTelemetry SDK 手动上报的节点数据支持和自动上报的节点数据串联,形成完整的 Trace 调用树。 自定义的 OpenTelemetry Span,attribute 和 event 规范需遵循 [OpenTelemetry 字段映射](https://loop.coze.cn/open/docs/cozeloop/opentelemetry_field_mapping)。 ```Python # 设置自定义Span节点 root_span with tracer.start_as_current_span("root_span") as span: span.set_attribute("cozeloop.span_type", "custom") # 启动LiteLLM调用 parallel_function_call() ``` ## 查看 Trace 数据 上报 Trace 数据后,你可以在[扣子罗盘](https://loop.coze.cn/)的 **Trace** 页面,找到并单击目标 Span,查看上报的 Trace 数据。 ![Image](https://p9-arcosite.byteimg.com/tos-cn-i-goo7wpa0wc/8f848c48af274935ad9842518cf2ebdf~tplv-goo7wpa0wc-image.image) ## 更多示例 关于上报 LiteLLM SDK Trace 数据的更多示例,请参考 [LiteLLM SDK](https://github.com/coze-dev/cozeloop-examples/tree/main/python/integration/framework/litellm_sdk)。