云计算百科
云计算领域专业知识百科平台

用 Opik 记录和分析多轮对话:Threads 完全指南

如果你正在开发聊天机器人或对话式 AI 应用,一定会遇到一个棘手的问题:如何追踪用户和 AI 之间的完整对话?单次问答的追踪已经够复杂了,多轮对话更是让上下文管理、质量评估和问题排查变得难上加难。Opik 提供了一套优雅的解决方案——Threads。它能把相关的追踪记录分组到一起,形成一条完整的对话线程,让你轻松回顾多轮交互、追踪用户会话,并对整个对话进行评分。

这篇文章会从零开始,带你了解 Opik 中 Threads 的概念、如何记录对话、Thread ID 的最佳实践,以及如何审查和评分。无论你用的是 TypeScript 还是 Python,无论你依赖 LangGraph、ADK 还是 OpenAI Agents,都能找到对应的集成方式。

为什么需要 Threads?

在传统的单次请求追踪中,每个 trace 代表一次独立的交互。但聊天场景不一样。用户问“什么是机器学习?”,AI 回答后,用户接着问“能举个例子吗?”,这两轮对话在逻辑上是连续的,上下文是累积的。如果只把它们看作两个孤立的 trace,你就无法还原对话的全貌,也无法分析多轮交互中的连贯性和用户满意度。

Opik 的 Threads 就是为解决这个问题而生的。Thread 是一组相关 trace 的集合,通过一个唯一的 thread_id 分组。所有具有相同 thread_id 的 trace 会聚合在一起,在 Opik UI 中显示为一个对话线程。这样,你就能像翻看聊天记录一样,查看用户和 AI 之间的完整对话。

Threads 的典型用途包括:

  • 多轮对话:追踪用户和 AI 助手之间的完整聊天会话。
  • 用户会话:把单个用户会话中的所有交互归为一组。
  • 对话代理:跟踪代理交互和工具使用的流程。
  • 工作流追踪:监控跨越多个函数调用的复杂工作流。

thread_id 是用户定义的标识符,必须在项目内唯一。你可以自己生成,也可以用系统提供的 ID。只要保证同一段对话的所有 trace 都使用同一个 thread_id,Opik 就能自动把它们串起来。

如何记录对话?

记录对话的核心就是在创建 trace 时指定 thread_id。Opik 支持多种方式,包括低级 SDK、Python 装饰器和各种集成库。下面我们逐一看看。

TypeScript SDK

如果你用 TypeScript,可以在创建 trace 时直接传入 threadId:

import { Opik } from "opik";

const client = new Opik({
apiUrl: "https://www.comet.com/opik/api",
apiKey: "your-api-key",
projectName: "your-project-name",
workspaceName: "your-workspace-name",
});

const threadId = "your-thread-id";

const trace = client.trace({
name: "chat turn",
input: { user: "Hi there" },
output: { assistant: "Hello!" },
threadId
});

这样,这次 trace 就被归到了指定的线程下。

Python 装饰器

Python 用户可以用 @opik.track 装饰器,并通过 opik_args 参数设置 thread_id:

import opik
from opik import opik_context

@opik.track
def chat_message(input):
return "Opik is an Open Source GenAI platform"

chat_message("What is Opik ?", opik_args={"trace": {"thread_id": "f174a"}})

也可以使用 opik_context 模块动态更新:

@opik.track
def chat_message(input):
thread_id = "f174a"
opik_context.update_current_trace(thread_id=thread_id)
return "Opik is an Open Source GenAI platform"

chat_message("What is Opik ?", thread_id)

Python SDK

如果直接用 Python SDK,在 client.trace() 里传入 thread_id 即可:

import opik

opik_client = opik.Opik()
thread_id = "55d84"

trace = opik_client.trace(
name="chat_conversation",
input="What is Opik?",
output="Opik is an Open Source GenAI platform",
thread_id=thread_id
)

LangGraph 集成

LangGraph 会自动把它自己的 thread_id 映射为 Opik 的 thread_id,所以配置起来非常自然:

from langgraph.graph import StateGraph
from opik.integrations.langchain import OpikTracer

graph = StateGraph(...)
compiled_graph = graph.compile()

thread_id = "user-conversation-789"
config = {
"callbacks": [OpikTracer(project_name="langgraph-conversations")],
"configurable": {"thread_id": thread_id}
}

result1 = compiled_graph.invoke(
{"messages": [{"role": "user", "content": "What is machine learning?"}]},
config=config
)

result2 = compiled_graph.invoke(
{"messages": [{"role": "user", "content": "Can you give me an example?"}]},
config=config
)

两轮对话共享同一个 thread_id,因此会被归入同一个线程。

ADK 集成

ADK 会自动把 session_id 映射为 thread_id:

from opik.integrations.adk import OpikTracer
from google.adk import sessions as adk_sessions, runners as adk_runners

session_service = adk_sessions.InMemorySessionService()
session = session_service.create_session_sync(
app_name="my_chatbot",
user_id="user_123",
session_id="conversation_456"
)

opik_tracer = OpikTracer(project_name="adk-conversations")
runner = adk_runners.Runner(
agent=your_agent,
app_name="my_chatbot",
session_service=session_service
)

result1 = runner.run(
user_id="user_123",
session_id="conversation_456",
new_message="What is machine learning?"
)

result2 = runner.run(
user_id="user_123",
session_id="conversation_456",
new_message="Can you give me an example?"
)

OpenAI Agents

对于 OpenAI Agents,可以使用 trace 上下文管理器的 group_id 来实现线程:

import uuid
from opik import trace

thread_id = str(uuid.uuid4())

with trace(workflow_name="Agent Conversation", group_id=thread_id):
result1 = await Runner.run(agent, "What is machine learning?")
print(result1.final_output)

with trace(workflow_name="Agent Conversation", group_id=thread_id):
new_input = result1.to_input_list() + [
{"role": "user", "content": "Can you give me an example?"}
]
result2 = await Runner.run(agent, new_input)
print(result2.final_output)

注意,每个 trace 的输入会显示为用户消息,输出会显示为 AI 助手响应。这样在 Opik UI 里查看时,就像在看真实的聊天记录。

Thread ID 最佳实践

thread_id 的生成策略取决于你的应用场景。下面几种常见方式供参考。

用户会话基础:把用户 ID 和会话开始时间组合起来,确保每个用户会话有唯一的线程。

import uuid
import opik

user_id = "user_12345"
session_start_time = "2024-01-15T10:30:00Z"
thread_id = f"{user_id}{session_start_time}"

@opik.track
def process_user_message(message, user_id):
return "Response to: " + message

process_user_message("What is Opik ?", opik_args={"trace": {"thread_id": thread_id}})

UUID 基础:为每段对话生成一个随机 UUID。

import uuid
import opik

thread_id = str(uuid.uuid4())

@opik.track
def start_conversation(initial_message):
return f"Processing: {initial_message}"

start_conversation("What is Opik ?", opik_args={"trace": {"thread_id": thread_id}})

时间戳基础:用时间戳做分组,适合按时间窗口归类的场景。

import time
import opik

thread_id = f"conversation-{int(time.time())}"

@opik.track
def handle_conversation_turn(message):
return f"Response to: {message}"

handle_conversation_turn("What is Opik ?", opik_args={"trace": {"thread_id": thread_id}})

不同的集成对线程的处理方式略有不同。例如,LangChain 可以在 tracer 级别设置 thread_id,也可以通过 metadata 动态传递;LangGraph 自动使用自己的 thread_id;OpenAI Agents 用 group_id;GenAI 和 OpenAI 则通过 opik_args 传入。具体用法可以参考官方文档。

审查对话

对话记录可以在项目级别的 threads 标签页中查看。所有对话都会被追踪,点击 thread ID 就能看到完整对话。线程视图支持 Markdown,方便你阅读返回给用户的内容。如果想深入了解 AI 助手响应是如何生成的,可以点击 View trace 按钮,钻取到具体的 trace 和 span。

在对话视图中,你可以直接给 AI 助手的回复点赞或点踩。这个反馈分数会被记录,并关联到对应的 trace。切换到 trace 视图后,你还可以查看完整 trace,并通过注释功能添加更多反馈分数。

对话评分

你可以随时给线程分配对话级别的反馈分数。线程是由追踪代理或通过 thread_id 相互连接的 trace 聚合而成的。在对话列表中,你能看到每个线程关联的反馈分数。

除了评分,你还可以给线程打标签、添加评论。这对于在审查过程中添加上下文或调查特定对话非常有用。

线程在线评分规则冷却期

对于线程级别的在线评估规则(自动评分),Opik 会在线程最后一次活动之后等待一个“冷却期”,然后再运行规则。这给对话留出沉淀的时间,避免在对话还在进行时就匆忙评分。

默认情况下,冷却期是 15 分钟。如果你是自托管版本,可以通过设置 OPIK_TRACE_THREAD_TIMEOUT_TO_MARK_AS_INACTIVE 环境变量来修改。在云版本中,可以在工作区级别修改“Thread online scoring rule cooldown period”。

当新的 trace 被添加到已有线程时,会发生以下事情:

  • 现有反馈分数保留:你手动添加的或在线评估生成的分数都会保留。
  • 冷却计时器重启:计时器从新 trace 添加的那一刻重新开始,确保在线评估等待完整的冷却期后再对更新后的线程评分。
  • 在线评估重新运行:冷却期结束后,线程级别的在线评分规则会自动重新评估完整对话。如果新分数与已有分数同名,旧分数会被更新。

高级线程功能

过滤和搜索线程

你可以在各种 Opik 功能中使用 thread_id 字段过滤线程。在数据导出时,支持的操作符包括 =、!=、contains、not_contains、starts_with、ends_with 以及字典序比较 >、<。在线程评估中,你也可以评估整个对话线程,这对对话质量评估、多轮连贯性评估和用户满意度评分特别有用。

线程管理

线程可以随时添加新的 trace,你也可以在线程上添加反馈分数、评论和标签,无论是否还有新 trace 加入。

程序化线程管理

你还可以用 Opik SDK 以编程方式管理线程。例如,在 Python 中:

import opik

client = opik.Opik()

threads = client.search_traces(
project_name="my-chatbot",
filter_string='thread_id contains "user-session"'
)

for trace in threads:
if trace.thread_id:
thread_content = client.get_trace_content(trace.id)
print(f"Thread: {trace.thread_id}")
print(f"Input: {thread_content.input}")
print(f"Output: {thread_content.output}")

for trace in threads:
trace.log_feedback_score(
name="conversation_quality",
value=0.8,
reason="Good multi-turn conversation flow"
)

这样你就能批量搜索线程、获取内容、添加反馈分数。

下一步

给多轮对话加上可观测性之后,你可以进一步:

  • 运行离线的多轮对话评估。
  • 创建在线评估规则,在生产环境中对多轮对话进行评分。
  • Opik 的 Threads 功能让多轮对话的追踪、审查和评分变得简单自然。无论你用什么框架,都能找到合适的集成方式。花点时间把 thread_id 用起来,你会发现对话质量的管理不再是一件头疼的事。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 用 Opik 记录和分析多轮对话:Threads 完全指南
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!