用 Opik 记录多模态追踪:图像、视频、音频附件的完整指南

如果你正在开发 LLM 应用,可能已经习惯了记录文本输入输出。但现实世界中的 AI 应用远不止文字。用户可能会上传一张图片让模型分析,或者你的 agent 会生成一份图表报告,又或者语音助手的对话需要保存音频。这些多模态数据如果不能被追踪,调试和评估就会留下巨大的盲区。Opik 支持多模态追踪,让你不仅能记录文本,还能记录图像、视频、音频以及任何其他媒体文件。这篇文章会详细介绍如何在 Opik 中记录和管理这些附件,包括大小限制、代码示例、最佳实践和程序化管理。

为什么需要记录媒体附件?

想象一下,你做了一个图像问答应用。用户上传一张照片,问"这是什么植物?"模型回答后,用户反馈说答案不对。如果你只记录了文本的输入输出,你根本不知道用户上传的是哪张照片,也无法复现问题。同样,如果你的 agent 生成了一个 PDF 报告,或者调用了外部 API 返回了一张图表,这些内容如果不被保存,后续的分析和评估就无从谈起。

Opik 的附件功能就是为了解决这个问题。它允许你把图片、视频、音频、PDF、JSON 等文件关联到 trace 或 span 上。这些附件会被上传到 Opik 平台,你可以在 UI 中预览和下载它们。更重要的是,Opik 会自动处理一些常见格式,比如 base64 编码的图片和图片 URL,让它们直接在追踪视图中显示出来,而不需要你手动解码。

大小限制:为什么要关心?

在深入代码之前,有必要先了解 Opik 对内容大小的限制。这些限制的存在是为了保证数据摄入的速度和可靠性。如果你不小心把一个大文件直接塞进 inputoutput 字段,可能会导致请求被拒绝,或者追踪数据不完整。

Opik 对内联内容 (也就是直接放在 span 或 trace 的 inputoutputmetadata 里的内容)有两层限制:

  • 每个字段约 20 MB :每个内联的非媒体 input/output 字段应保持在 20 MB 以下。这个上限同样适用于 inputoutput 合并后的总大小。较新版本的 Opik SDK 会在客户端侧截断过大的 input/output,替换为截断标记并记录警告,以确保单个 span 和 trace 不超限。metadata 不会被截断,但也要保持小巧,因为它仍然计入每请求的限制。
  • 每请求限制:单次摄入批次被限制在约 50 MB 压缩后和约 256 MB 未压缩。更大的请求会被拒绝,返回 413 状态码。

不过,Base64 编码的内容是单独处理的,不受上述两个限制约束 。当你把大型 base64 数据块嵌入某个字段时------比如图片、音频、视频、PDF 或其他可识别格式(如 JSON)------SDK 会自动提取大于约 250 KB 的内容,并将其作为附件上传。这是一个独立的上传过程,不属于 JSON 摄入请求的一部分。因此,单个嵌入值可以大到 100 MB。关于这一点,后面"嵌入附件"部分会详细说明。

附件是保留大型内容而不触及这些限制的推荐方式。 与其把大载荷内联在 input/output 里,不如把它记录为附件。完整载荷通过对象存储保存,而你的 trace 保持轻量。一个经验法则是:摘要、top-K 结果或 ID 可以内联记录,任何大型内容都附加为附件。

如何记录附件

在 Python SDK 中,你可以使用 Attachment 类型来给 trace 添加文件。附件可以是图片、视频、音频文件或任何其他你想记录到 Opik 的文件。

每个附件由以下字段组成:

  • data:文件路径、原始字节或文件的 base64 编码字符串。
  • file_name:附件的可选名称(当使用原始字节且没有文件路径时必需)。
  • content_type:文件的 MIME 类型。

这些附件可以通过 opik_context.update_current_spanopik_context.update_current_trace 方法记录到 trace 和 span 上。

使用文件路径

最常见的方式是提供文件路径:

python 复制代码
from opik import opik_context, track, Attachment

@track
def my_llm_agent(input):
    # LLM chain code
    # ...

    # Update the trace with a file path
    opik_context.update_current_trace(
        attachments=[
            Attachment(
                data="<path to the image>",
                content_type="image/png",
            )
        ]
    )

    return "World!"

print(my_llm_agent("Hello!"))

这段代码会在当前 trace 上附加一个 PNG 图片。你只需要指定文件路径和内容类型,Opik 会负责上传。

使用原始字节

你也可以直接传递原始字节。当你手头有文件内容在内存中(比如来自 API 响应、生成的内容或流式数据),不想先写到磁盘时,这很有用:

python 复制代码
from opik import opik_context, track, Attachment

@track
def process_image(image_bytes: bytes):
    # Process the image
    # ...

    # Log the raw bytes as an attachment
    opik_context.update_current_trace(
        attachments=[
            Attachment(
                data=image_bytes,  # Raw bytes
                file_name="processed_image.png",  # Required for bytes
                content_type="image/png",
            )
        ]
    )

    return "Image processed!"

# Example: Reading a file into memory and logging it
with open("image.png", "rb") as f:
    image_data = f.read()

print(process_image(image_data))

注意:当使用原始字节时,Opik 会自动创建一个临时文件用于上传,并在附件上传完成后清理它。如果你没有指定 content_type,Opik 会尝试从 file_name 推断,或者默认使用 application/octet-stream

记录来自 HTTP 响应的图片

一个常见的用例是记录从外部 API 或 URL 获取的图片:

python 复制代码
import httpx
from opik import opik_context, track, Attachment

@track
def analyze_remote_image(image_url: str):
    # Fetch image from URL
    response = httpx.get(image_url)
    image_bytes = response.content
    content_type = response.headers.get("content-type", "image/jpeg")

    # Log the fetched image as an attachment
    opik_context.update_current_trace(
        attachments=[
            Attachment(
                data=image_bytes,
                file_name="remote_image.jpg",
                content_type=content_type,
            )
        ]
    )

    # Process the image...
    return "Image analyzed!"

# Analyze an image from a URL
result = analyze_remote_image("https://example.com/image.jpg")

这样,即使图片是动态获取的,也能被完整记录下来。

记录生成的内容

你也可以记录动态生成的内容,比如图表或报告:

python 复制代码
from opik import opik_context, track, Attachment
import json

@track
def generate_report(data: dict):
    # Generate a JSON report
    report_bytes = json.dumps(data, indent=2).encode("utf-8")

    opik_context.update_current_trace(
        attachments=[
            Attachment(
                data=report_bytes,
                file_name="report.json",
                content_type="application/json",
            )
        ]
    )

    return "Report generated!"

直接使用 Opik 客户端

你还可以直接使用 Opik 客户端记录附件,支持文件路径和原始字节:

python 复制代码
import opik
from opik import Attachment

client = opik.Opik()

# Create a trace
trace = client.trace(
    name="my-trace",
    input={"query": "Process this data"},
    project_name="my-project",
)

# Log attachment with file path
span_with_file = client.span(
    trace_id=trace.id,
    name="file-attachment-span",
    attachments=[
        Attachment(
            data="/path/to/document.pdf",
            content_type="application/pdf",
        )
    ],
)

# Log attachment with raw bytes
binary_data = b"Hello, this is binary content!"
span_with_bytes = client.span(
    trace_id=trace.id,
    name="bytes-attachment-span",
    attachments=[
        Attachment(
            data=binary_data,
            file_name="data.bin",
            content_type="application/octet-stream",
        )
    ],
)

client.flush()

附件会被上传到 Opik 平台,并且可以在 UI 中预览和下载。

支持的内容类型

为了在 UI 中预览附件,你需要提供受支持的内容类型。Opik 支持以下类型:

  • 图片image/jpegimage/pngimage/gifimage/svg+xml
  • 视频video/mp4video/webm
  • 音频audio/wavaudio/vorbisaudio/x-wav
  • 文本text/plaintext/markdown
  • PDFapplication/pdf
  • 其他application/jsonapplication/octet-stream

如果你希望支持更多图片格式,可以在 GitHub 上反馈。

程序化管理附件

你还可以使用 AttachmentClient 以编程方式管理附件:

python 复制代码
import opik

opik_client = opik.Opik()
attachment_client = opik_client.get_attachment_client()

# Get list of attachments
attachments_details = attachment_client.get_attachment_list(
    project_name="my-project",
    entity_id="some-trace-uuid-7",
    entity_type="trace"
)

# Download an attachment
attachment_data = attachment_client.download_attachment(
    project_name="my-project",
    entity_type="trace",
    entity_id="some-trace-uuid-7",
    file_name="report.pdf",
    mime_type="application/pdf"
)

# Upload a new attachment
attachment_client.upload_attachment(
    project_name="my-project",
    entity_type="trace", 
    entity_id="some-trace-uuid-7",
    file_path="/path/to/document.pdf"
)

这让你可以在自动化流程中获取、下载或上传附件,比如在批量分析或数据迁移时。

自动预览 base64 图片和图片 URL

Opik 会自动检测记录到平台的 base64 编码图片和 URL。一旦检测到图片,它会隐藏字符串以提高可读性,并在 UI 中显示图片。这支持追踪视图、数据集视图和实验视图。

例如,如果你使用 OpenAI SDK,并且以 URL 的形式传递图片给模型,Opik 会自动检测并显示图片:

python 复制代码
from opik.integrations.openai import track_openai
from openai import OpenAI

# Make sure to wrap the OpenAI client to enable Opik tracing
client = track_openai(OpenAI())

response = client.chat.completions.create(
  model="gpt-4o-mini",
  messages=[
    {
      "role": "user",
      "content": [
        {"type": "text", "text": "What's in this image?"},
        {
          "type": "image_url",
          "image_url": {
            "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg",
          },
        },
      ],
    }
  ],
  max_tokens=300,
)

print(response.choices[0])

这样,你在 Opik UI 里就能直接看到图片,而不需要手动复制 URL 去浏览器打开。

嵌入附件

当你把 base64 编码的媒体直接嵌入到 trace/span 的 inputoutputmetadata 字段时,Opik 会自动优化存储和检索性能。

工作原理

对于大于 250 KB 的 base64 编码内容,Opik 会自动提取并单独存储。这个过程是透明的,你不需要修改代码。

当你之后检索 trace 或 span 时,附件默认会自动包含。如果你不需要附件数据,想要更快的查询,可以使用 strip_attachments=true 参数。

嵌入 base64 大小限制

Opik Cloud 支持每个字段最多 100 MB 的嵌入附件。这个限制适用于 inputoutputmetadata 字段中的单个 base64 字符串值,并且与前面的内联字段/请求限制是分开的------因为大于约 250 KB 的 base64 媒体会被提取并作为附件上传,而不是内联发送。

需要注意的是,Base64 编码会使文件大小增加约 33%。例如,一个 75 MB 的视频在 base64 编码后会变成约 100 MB。

如果你需要处理更大的文件:

  1. 使用 Attachment API :通过 AttachmentClient 单独上传文件(推荐用于大于 50 MB 的文件)。
  2. 联系我们:如果你需要更高的限制,可以联系 Opik 团队。
  3. 自托管 Opik:配置你自己的限制。

最佳实践

  • 较小的文件直接嵌入------Opik 会高效处理。
  • 对于大于 50 MB 的文件,使用 Attachment API 以获得更好的性能。
  • 查询时如果不需要附件数据,使用 strip_attachments=true

下载附件

你可以通过两种方式下载附件:

  1. 从 UI:将鼠标悬停在附件上,点击下载图标。
  2. 以编程方式 :使用 AttachmentClient,如前面的示例所示。

总结

多模态追踪是 LLM 应用可观测性的重要组成部分。Opik 通过附件功能,让你可以轻松记录图像、视频、音频、PDF、JSON 等文件。理解大小限制和嵌入附件的工作机制,能帮助你避免常见的陷阱。无论是通过文件路径、原始字节、HTTP 响应还是生成内容,Opik 都提供了灵活的 API。程序化管理附件和自动预览功能,进一步简化了工作流程。如果你正在构建涉及多模态数据的 AI 应用,不妨从今天开始,把重要的媒体文件附加到你的 trace 上。这样,当问题出现时,你拥有的不仅是文本日志,而是完整的上下文。

相关推荐
oscar99920 小时前
给 LLM 应用加上追踪:Opik 日志记录实战指南
opik
星云_byto20 小时前
大模型测评:从最新出的DeepSeekV4.1模型看这19项指标
chatgpt·agent·多模态·codex·deepseek·大模型测评·opus-4.8
东姬AI2 天前
当世界模型学会“开口说话“:从EchoWM开源,看AI生成内容的音画合流
多模态·ai数字人·世界模型·ai视频生成·音画同步
XLYcmy3 天前
DeepMMSearch-R1: Empowering Multimodal LLMs in Multimodal Web Search论文分享
llm·sft·强化学习·多模态·苹果·rag·检索
oscar9997 天前
Ollie:Opik 内置的 AI 助手,让 Agent 调试从“看”变成“修”
人工智能·opik·ollie
william_yangshun9 天前
【AI Agent 实战】agent-vision-toolkit 中文版:给纯文本模型(DeepSeek)装上视觉能力
人工智能·多模态
CV山月10 天前
大模型强化学习对齐:从 RLHF 框架到 PPO 算法原理
人工智能·python·大模型·强化学习·多模态·研究生
deepseek2311 天前
Google Gemini Agentic Video 上线:88% 少 token 的主动取样如何改写长视频理解
python·多模态·ai agent·gemini·视频理解