Log conversations

You can log chat conversations to the Opik platform and track the full conversations your users are having with your chatbot. Threads allow you to group related traces together, creating a conversational flow that makes it easy to review multi-turn interactions and track user sessions.

Understanding Threads

Threads in Opik are collections of traces that are grouped together using a unique thread_id. This is particularly useful for:

  • Multi-turn conversations: Track complete chat sessions between users and AI assistants
  • User sessions: Group all interactions from a single user session
  • Conversational agents: Follow the flow of agent interactions and tool usage
  • Workflow tracking: Monitor complex workflows that span multiple function calls

The thread_id is a user-defined identifier that must be unique per project. All traces with the same thread_id will be grouped together and displayed as a single conversation thread in the Opik UI.

Logging conversations

You can log chat conversations by specifying the thread_id parameter when using either the low level SDK, Python decorators, or integration libraries:

1import opik
2from opik import opik_context
3
4@opik.track
5def chat_message(input, thread_id):
6 opik_context.update_current_trace(
7 thread_id=thread_id
8 )
9 return "Opik is an Open Source GenAI platform"
10
11thread_id = "f174a"
12chat_message("What is Opik ?", thread_id)
13chat_message("Repeat the previous message", thread_id)

The input to each trace will be displayed as the user message while the output will be displayed as the AI assistant response.

Thread ID Best Practices

Generating Thread IDs

Choose a thread ID strategy that fits your application:

1import uuid
2import opik
3
4# Generate unique thread ID per user session
5user_id = "user_12345"
6session_start_time = "2024-01-15T10:30:00Z"
7thread_id = f"{user_id}-{session_start_time}"
8
9@opik.track
10def process_user_message(message, user_id):
11 from opik import opik_context
12 opik_context.update_current_trace(thread_id=thread_id)
13 # Process message
14 return "Response to: " + message

Integration-Specific Threading

Different integrations handle thread IDs in various ways:

LangChain Integration

1from opik.integrations.langchain import OpikTracer
2
3# Set thread_id at tracer level - applies to all traces
4opik_tracer = OpikTracer(
5 project_name="my-chatbot",
6 thread_id="conversation-123"
7)
8
9# Or pass dynamically via metadata
10chain.invoke(
11 {"input": "Hello"},
12 config={
13 "callbacks": [opik_tracer],
14 "metadata": {"thread_id": "dynamic-conversation-456"}
15 }
16)

LangGraph Integration

1from opik.integrations.langchain import OpikTracer
2
3# LangGraph automatically uses its thread_id as Opik thread_id
4thread_id = "langgraph-conversation-789"
5config = {
6 "callbacks": [OpikTracer()],
7 "configurable": {"thread_id": thread_id}
8}
9
10result = compiled_graph.invoke(input_data, config=config)

OpenAI Agents Integration

1import uuid
2from opik import trace
3
4# Use trace context manager with group_id for threading
5thread_id = str(uuid.uuid4())
6
7with trace(workflow_name="Agent Conversation", group_id=thread_id):
8 # All agent interactions within this context share the thread_id
9 result1 = await Runner.run(agent, "First question")
10 result2 = await Runner.run(agent, "Follow-up question")

Thread Management in Production

For production applications, consider these patterns:

1import opik
2from datetime import datetime, timedelta
3
4class ConversationManager:
5 def __init__(self):
6 self.active_threads = {}
7 self.opik_client = opik.Opik()
8
9 def get_or_create_thread_id(self, user_id: str, session_id: str = None) -> str:
10 """Generate or retrieve thread ID for user session"""
11 if session_id:
12 thread_id = f"{user_id}-{session_id}"
13 else:
14 thread_id = f"{user_id}-{datetime.now().isoformat()}"
15
16 self.active_threads[thread_id] = datetime.now()
17 return thread_id
18
19 @opik.track
20 def process_message(self, user_id: str, message: str, thread_id: str = None):
21 if not thread_id:
22 thread_id = self.get_or_create_thread_id(user_id)
23
24 # Update current trace with thread_id
25 opik_context.update_current_trace(
26 thread_id=thread_id,
27 metadata={
28 "user_id": user_id,
29 "timestamp": datetime.now().isoformat()
30 }
31 )
32
33 # Process the message
34 response = f"AI response to: {message}"
35 return response
36
37 def generate_response(self, message: str) -> str:
38 """Generate AI response to user message"""
39 return f"AI response to: {message}"
40
41 def cleanup_old_threads(self, hours: int = 24):
42 """Remove thread IDs older than specified hours"""
43 cutoff = datetime.now() - timedelta(hours=hours)
44 self.active_threads = {
45 tid: timestamp for tid, timestamp in self.active_threads.items()
46 if timestamp > cutoff
47 }

Reviewing conversations

Conversations can be viewed at a project level in the threads tab. All conversations are tracked and by clicking on the thread ID you will be able to view the full conversation.

The thread view supports markdown making it easier for you to review the content that was returned to the user. If you would like to dig in deeper, you can click on the View trace button to deepdive into how the AI assistant response was generated.

By clicking on the thumbs up or thumbs down icons, you can quickly rate the AI assistant response. This feedback score will be logged and associated to the relevant trace. By switching to the trace view, you can review the full trace as well as add additional feedback scores through the annotation functionality.

Scoring conversations

It is possible to assign conversation level feedback scores. For that, you need to understand how threads work in Opik. Threads are aggregated traces that are created when tracking agents or simply traces interconnected by a thread_id. In order to score a conversation, we need to ensure that the thread is inactive, meaning that no new traces are being created.

By default, threads are marked as inactive after 15 minutes of inactivity. You can change this value by setting the OPIK_TRACE_THREAD_TIMEOUT_TO_MARK_AS_INACTIVE environment variable (If you are using the Opik self-hosted version). On cloud, you can change this setting at workspace level.

Threads are automatically marked as inactive after the timeout period and you can also manually mark a thread as inactive via UI using the Status button on top right corner of the thread view.

Once a thread is inactive, you can assign a feedback score to the thread. This score will be associated to the thread and will be displayed in the thread view.

And in the conversation list, you can see the feedback score associated to the thread.

Important: The human feedback scores are removed in case the thread is re-activated. This is to avoid any confusion and to ensure that the feedback scores are always based on the full context of the conversation.

You can also tag a thread and add comments to it. This is useful to add additional context during the review process or investigate a specific conversation.

Advanced Thread Features

Filtering and Searching Threads

You can filter threads using the thread_id field in various Opik features:

In Data Export

When exporting data, you can filter by thread_id using these operators:

  • = (equals), != (not equals)
  • contains, not_contains
  • starts_with, ends_with
  • >, < (lexicographic comparison)

In Thread Evaluation

You can evaluate entire conversation threads using the thread evaluation features. This is particularly useful for:

  • Conversation quality assessment
  • Multi-turn coherence evaluation
  • User satisfaction scoring across complete interactions

Thread Lifecycle Management

Threads have a lifecycle that affects how you can interact with them:

  1. Active: New traces can be added to the thread
  2. Inactive: No new traces can be added, thread can be scored

Threads automatically become inactive after 15 minutes of no new trace activity. This timeout can be configured: - Self-hosted: Set OPIK_TRACE_THREAD_TIMEOUT_TO_MARK_AS_INACTIVE environment variable - Cloud: Configure at workspace level in settings

Programmatic Thread Management

You can also manage threads programmatically using the Opik SDK:

1import opik
2
3# Initialize client
4client = opik.Opik()
5
6# Search for threads by various criteria
7threads = client.search_traces(
8 project_name="my-chatbot",
9 filter_string='thread_id contains "user-session"'
10)
11
12# Get specific thread content
13for trace in threads:
14 if trace.thread_id:
15 thread_content = client.get_trace_content(trace.id)
16 print(f"Thread: {trace.thread_id}")
17 print(f"Input: {thread_content.input}")
18 print(f"Output: {thread_content.output}")
19
20# Add feedback scores to thread traces
21for trace in threads:
22 trace.log_feedback_score(
23 name="conversation_quality",
24 value=0.8,
25 reason="Good multi-turn conversation flow"
26 )

Thread Metadata and Context

Enhance your threads with rich metadata for better organization:

1import opik
2from opik import opik_context
3
4@opik.track
5def enhanced_conversation_turn(user_message, user_id, conversation_type):
6 thread_id = f"{user_id}-{conversation_type}-session"
7
8 opik_context.update_current_trace(
9 thread_id=thread_id,
10 metadata={
11 "user_id": user_id,
12 "conversation_type": conversation_type,
13 "channel": "web_chat",
14 "language": "en",
15 "session_start": "2024-01-15T10:30:00Z"
16 },
17 tags=["production", "customer-support", conversation_type]
18 )
19
20 # Process message and return response
21 return f"AI response to: {user_message}"

This enhanced metadata helps with:

  • Filtering: Find conversations by type, channel, or language
  • Analytics: Analyze conversation patterns and user behavior
  • Debugging: Quickly identify problematic conversation flows
  • Reporting: Generate insights on conversation quality and outcomes