跳到主要内容

跟踪数据结构

本文详细介绍了跟踪(trace)及其组成部分的模式。MLflow 跟踪**兼容 OpenTelemetry 规范**,但我们也在 OpenTelemetry Span 之上定义了一些额外的结构层,以提供关于跟踪的额外元数据。

跟踪的结构

**概要**:Trace = TraceInfo + TraceData 其中 TraceData = List[Span]

跟踪结构

MLflow 中的一个Trace 由两个组件组成:Trace InfoTrace Data

有助于解释跟踪的起源、跟踪的状态以及总执行时间的信息存储在 Trace Info 中。Trace Data 完全由构成跟踪核心的已插桩 Span 对象组成。

Trace Architecture

Trace

跟踪是一个由两个组件组成的根对象

提示

请查阅 API 文档中关于这些 dataclass 对象的辅助方法,以了解如何转换或从中提取数据。

Trace Info

Trace Info 是一个包含跟踪元数据的 dataclass 对象。此元数据包括关于跟踪来源、状态和各种其他数据的信息,这些数据有助于在使用 mlflow.client.MlflowClient.search_traces() 时检索和过滤跟踪,以及在 MLflow UI 中导航跟踪。

要了解更多关于如何使用 TraceInfo 元数据进行搜索的信息,您可以在此处查看示例。

TraceInfo 对象中包含的数据用于填充 MLflow tracking UI 中的跟踪视图页面,如下所示。

TraceInfo as it is used in the MLflow UI

MLflow TraceInfo 对象的主要组成部分如下所列。

属性描述注意
request_id

跟踪的唯一标识符。此标识符在 MLflow 和集成系统中用于解析被捕获的事件,并为外部系统提供关联,以便将记录的跟踪映射到原始调用者。

此值由跟踪后端生成且不可变。在跟踪客户端 API 中,您需要特意将此值传递给 span 创建 API,以确保给定的 span 与跟踪相关联。
experiment_id

记录跟踪的实验 ID。所有记录的跟踪都在跟踪生成时(插桩对象调用期间)与当前活动实验相关联。

此值不可变,由跟踪后端设置。这是一个系统控制的值,在使用 Search Traces API 时非常有用。
timestamp_ms

标记跟踪的根 span 创建时刻的时间。这是一个以毫秒为单位的 Unix 时间戳。

此属性中反映的时间是跟踪创建的时间,而不是您的应用程序收到请求的时间。因此,它不计入将请求处理到应用程序所在的运行环境所需的时间,根据网络配置,这可能会增加总的往返时间延迟。

execution_time_ms

标记结束跟踪调用时刻的时间。这是一个以毫秒为单位的 Unix 时间戳。

此时间不包括将响应从生成跟踪的环境发送到使用应用程序调用结果的环境所相关的网络时间。

status表示跟踪状态的枚举值。

TraceStatus 值之一是

  • OK - 跟踪和已插桩调用成功。
  • ERROR - 应用程序插桩时发生错误。错误可以在跟踪的 span 数据中看到。
  • IN_PROGRESS - 跟踪已开始且正在运行。这是一个临时状态,当 span 被记录到跟踪时会更新。
  • TRACE_STATUS_UNSPECIFIED - 不应在记录的跟踪中看到的内部默认状态。
request_metadata

请求元数据是与跟踪关联的额外键值对信息,由跟踪后端设置和修改。

这些信息用户不能添加或修改,但可以提供关于跟踪的额外上下文,例如与跟踪关联的 MLflow run_id

tags

用户定义的键值对,可以应用于跟踪以提供额外上下文,有助于搜索功能,或在创建或成功记录跟踪后提供额外信息。

这些标签完全可变,可以随时更改,即使跟踪已被记录到实验中很久以后也可以。

Trace Data

MLflow TraceData 对象是一个 dataclass 对象,包含跟踪数据的核心。此对象包含以下元素

属性描述注意
requestrequest 属性是整个跟踪的输入数据。输入 str 是一个 JSON 序列化字符串,包含跟踪的输入数据,通常是作为对应用程序调用而提交的最终用户请求。由于可能进入由 MLflow Tracing 插桩的给定应用程序的输入结构各异,为了兼容性,所有输入都采用 JSON 序列化。这使得输入数据可以以一致的格式存储,而无论输入数据的结构如何。
spans此属性是一个 Span 对象列表,表示跟踪的各个步骤。有关 Span 对象结构的更多信息,请参见下面章节。
responseresponse 属性是将返回给应用程序调用者的最终输出数据。与 request 属性类似,此值为 JSON 序列化字符串,以最大化不同格式的兼容性。

Span 模式

Span 是跟踪数据的核心。它们记录了生成式 AI 应用程序中每个步骤的关键、重要数据。

当您在 MLflow UI 中查看跟踪时,您看到的是 span 的集合,如下所示。

Spans within the MLflow UI

下面章节提供了 span 结构的详细视图。

属性描述注意
inputs

inputs 存储为 JSON 序列化字符串,表示传递到应用程序特定阶段(步骤)的输入数据。由于生成式 AI 应用程序特定阶段之间可以传递的输入数据种类繁多,这些数据可能非常大(例如在使用向量存储检索步骤的输出时)。

检查单个阶段的 Inputs 和 Outputs 可以显著提高诊断和调试应用程序响应中存在问题的能力。

outputs

outputs 存储为 JSON 序列化字符串,表示从应用程序特定阶段(步骤)传递出的输出数据。

与 Inputs 一样,Outputs 可能相当大,取决于在阶段之间传递的数据的复杂性。

attributes

Attributes 是与应用程序中给定步骤关联的元数据。这些 attributes 是键值对,可用于深入了解函数和方法调用的行为修改,从而了解修改它们如何影响应用程序的性能。

可能与给定 span 关联的常见 attributes 示例包括

  • model
  • temperature
  • document_count

这些 attributes 为 span 的 outputs 属性中呈现的结果提供了额外的上下文和见解。

events

Events 是一个系统级属性,仅当 span 执行期间出现问题时才选择性地应用于 span。这些 events 包含关于已插桩调用中抛出的异常以及堆栈跟踪的信息。

此数据结构化在一个 SpanEvent 对象中,包含以下属性
  • name
  • timestamp
  • attributes

attributes 属性包含 span 执行期间抛出的异常的堆栈跟踪(如果在执行期间发生此类错误)。

parent_id

parent_id 属性是一个标识符,用于建立给定 span 与其父 span 的分层关联。这用于建立 span 的事件链,有助于确定应用程序执行中哪个步骤紧随另一个步骤。

一个 span 必须设置 parent_id

span_id

span_id 是为跟踪中的每个 span 生成的唯一标识符。此标识符用于区分不同的 span,并允许在跟踪内其他 span 的顺序执行中正确关联 span。

一个 span_id 在创建 span 时设置,且不可变。
request_id

request_id 属性是为每个跟踪生成的唯一标识符,并传播到属于该跟踪的每个 span。

request_id 是一个系统生成的属性,且不可变。
name

跟踪的名称可以是用户定义的(在使用 fluent 和 client API 时可选),也可以通过 CallBack 集成或在调用 fluent 或 client API 时省略 name 参数时自动生成。如果名称未被覆盖,则名称将基于被插桩的函数或方法的名称生成。

建议在使用 client 或 fluent API 进行手动插桩时,为您的 span 提供一个唯一且与正在执行的功能相关的名称。span 的通用名称或令人困惑的名称会使得在查看跟踪时难以诊断问题。

status

span 的状态反映在枚举对象 SpanStatusCode 中的一个值。如果 status_code 反映了发生的错误,则 span status 对象包含一个可选的描述。status 可能具有的值是

  • OK - span 和底层的已插桩调用成功。
  • UNSET - span 的状态尚未设置(这是默认值,不应在记录的跟踪事件中看到)
  • ERROR - 被插桩的调用中发生问题。description 属性将包含关于发生的错误的额外信息。

评估 span 的状态可以大大减少诊断应用程序问题所需的时间和精力。

start_time_nsspan 开始时的 Unix 时间戳(以纳秒为单位)。

此属性的精度高于跟踪开始时间的精度,允许对非常短命的 span 的执行时间进行更精细的分析。

end_time_nsspan 结束时的 Unix 时间戳(以纳秒为单位)。

此精度高于跟踪时间戳,类似于上面的 start_time_ns 时间戳。

Span 类型

Span 类型是一种在跟踪中对 span 进行分类的方式。默认情况下,使用 trace decorator 时,span 类型设置为 "UNKNOWN"。MLflow 提供了一组预定义的 span 类型用于常见用例,同时也允许您设置自定义 span 类型。

以下 span 类型可用

Span 类型描述
"LLM"表示对 LLM 端点或本地模型的调用。
"CHAT_MODEL"表示对聊天模型的查询。这是 LLM 交互的一个特例。
"CHAIN"表示一系列操作(chain)。
"AGENT"表示自主智能体(agent)操作。
"TOOL"表示工具执行(通常由智能体 agent 执行),例如查询搜索引擎。
"EMBEDDING"表示文本嵌入操作。
"RETRIEVER"表示上下文检索操作,例如查询向量数据库。
"PARSER"表示解析操作,将文本转换为结构化格式。
"RERANKER"表示重排操作,根据相关性对检索到的上下文进行排序。
"UNKNOWN"未指定其他 span 类型时使用的默认 span 类型。

要设置 span 类型,您可以将 span_type 参数传递给 @mlflow.trace decorator 或 mlflow.start_span() 上下文管理器。当您使用自动跟踪时,MLflow 会自动设置 span 类型。

import mlflow
from mlflow.entities import SpanType


# Using a built-in span type
@mlflow.trace(span_type=SpanType.RETRIEVER)
def retrieve_documents(query: str):
...


# Setting a custom span type
with mlflow.start_span(name="add", span_type="MATH") as span:
span.set_inputs({"x": z, "y": y})
z = x + y
span.set_outputs({"z": z})

print(span.span_type)
# Output: MATH

特定 span 类型的模式

MLflow 有 10 种预定义的 span 类型(参见 mlflow.entities.SpanType),并且某些 span 类型需要特定的属性,以便在 UI 中启用附加功能以及下游任务(如评估)。

Retriever Spans

RETRIEVER span 类型用于涉及从数据存储中检索数据的操作(例如,从向量存储中查询文档)。RETRIEVER span 类型具有以下模式

属性描述注意
Input对 span 的 inputs 没有限制
Output输出必须是 List[mlflow.entities.Document] 类型,或与 dataclass 结构匹配的 dict*。dataclass 包含以下属性
  • id (Optional[str]) - 文档的可选唯一标识符。
  • page_content (str) - 文档的文本内容。
  • metadata (Optional[Dict[str,any]]) - 与文档关联的元数据。有两个重要的元数据键是为 MLflow UI 和评估指标保留的
    • "doc_uri" (str): 文档的 URI。这用于在 UI 中渲染链接。
    • "chunk_id" (str): 如果您的文档在数据存储中被分割成块(chunk),则此键可用于识别文档所属的块。一些评估指标会使用此键。

如果通过 MLflow autologging 为 LangChain 和 LlamaIndex flavors 生成跟踪,则保证提供此输出结构。通过符合此规范,RETRIEVER span 将在 MLflow UI 中以更用户友好的方式呈现,并且下游任务(如评估)将按预期运行。

Attributes对 span 的 attributes 没有限制

* 例如,[Document(page_content="Hello world", metadata={"doc_uri": "https://example.com"})][{"page_content": "Hello world", "metadata": {"doc_uri": "https://example.com"}}] 都是 RETRIEVER span 的有效输出。

Chat Completion Spans

类型为 CHAT_MODELLLM 的 span 用于表示与聊天补全 API 的交互(例如,OpenAI 的 chat completions,或 Anthropic 的 messages API)。由于提供商的 API 可能具有不同的模式,因此对 span 的 inputs 和 outputs 格式没有限制。

然而,为了启用某些 UI 功能(例如富对话显示)并使评估函数更容易编写,拥有一个通用模式仍然很重要。为了支持这一点,我们为标准化聊天消息和工具定义指定了一些自定义 attributes

Attribute 名称描述注意

mlflow.chat.messages

此 attribute 表示与聊天模型对话中涉及的系统/用户/助手消息。它可以在 UI 中实现富对话渲染,将来也会用于 MLflow 评估。

类型必须是 List[ ChatMessage ]

可以使用 mlflow.tracing.set_span_chat_messages() 函数方便地设置此 attribute。如果数据不符合规范,此函数将抛出验证错误。

mlflow.chat.tools

此 attribute 表示聊天模型可调用的工具。在 OpenAI 上下文中,这等同于 Chat Completions API 中的 tools 参数。

类型必须是 List[ ChatTool ]

可以使用 mlflow.tracing.set_span_chat_tools() 函数方便地设置此 attribute。如果数据不符合规范,此函数将抛出验证错误。

请参考下面的示例,快速了解如何使用上述实用函数,以及如何使用 span.get_attribute() 函数检索它们。

import mlflow
from mlflow.entities.span import SpanType
from mlflow.tracing.constant import SpanAttributeKey
from mlflow.tracing import set_span_chat_messages, set_span_chat_tools

# example messages and tools
messages = [
{
"role": "system",
"content": "please use the provided tool to answer the user's questions",
},
{"role": "user", "content": "what is 1 + 1?"},
]

tools = [
{
"type": "function",
"function": {
"name": "add",
"description": "Add two numbers",
"parameters": {
"type": "object",
"properties": {
"a": {"type": "number"},
"b": {"type": "number"},
},
"required": ["a", "b"],
},
},
}
]


@mlflow.trace(span_type=SpanType.CHAT_MODEL)
def call_chat_model(messages, tools):
# mocking a response
response = {
"role": "assistant",
"tool_calls": [
{
"id": "123",
"function": {"arguments": '{"a": 1,"b": 2}', "name": "add"},
"type": "function",
}
],
}

combined_messages = messages + [response]

span = mlflow.get_current_active_span()
set_span_chat_messages(span, combined_messages)
set_span_chat_tools(span, tools)

return response


call_chat_model(messages, tools)

trace_id = mlflow.get_last_active_trace_id()
trace = mlflow.get_trace(trace_id)
span = trace.data.spans[0]

print("Messages: ", span.get_attribute(SpanAttributeKey.CHAT_MESSAGES))
print("Tools: ", span.get_attribute(SpanAttributeKey.CHAT_TOOLS))