메인 콘텐츠로 건너뛰기
W&B Weave _Threads_를 사용하면 LLM 애플리케이션의 멀티턴 대화를 추적하고 분석할 수 있습니다. Threads는 관련된 Calls를 공유된 thread_id로 묶으므로, 전체 세션을 시각화하고 각 턴에 걸친 대화 수준 메트릭을 추적할 수 있습니다. 스레드는 프로그래밍 방식으로 생성하고 Weave UI에서 시각화할 수 있습니다. Threads를 시작하려면 다음을 수행하세요:
  1. Threads의 기본 사항을 먼저 익힙니다.
  2. 일반적인 사용 패턴과 실제 사용 사례를 보여주는 코드 샘플을 사용해 보세요.

사용 사례

다음과 같은 항목을 정리하고 분석하려는 경우 스레드가 유용합니다.
  • 멀티턴 대화
  • 세션 기반 워크플로
  • 서로 관련된 오퍼레이션의 연속
스레드를 사용하면 컨텍스트별로 Calls를 그룹화할 수 있어, 여러 step에 걸쳐 시스템이 어떻게 응답하는지 더 쉽게 이해할 수 있습니다. 예를 들어, 단일 사용자 세션, 에이전트의 의사결정 체인, 또는 인프라와 비즈니스 로직 계층에 걸친 복잡한 요청을 추적할 수 있습니다. 애플리케이션을 스레드와 턴으로 구조화하면 더 깔끔한 메트릭을 확보하고 Weave UI에서 더 나은 가시성을 얻을 수 있습니다. 모든 저수준 Op를 보는 대신, 중요한 상위 수준 step에 집중할 수 있습니다.

정의

스레드

_스레드(Thread)_는 공통된 대화 맥락을 공유하는 관련된 Call을 논리적으로 묶은 단위입니다. Thread는 다음과 같은 특징이 있습니다.
  • 고유한 thread_id를 가집니다
  • 하나 이상의 _turn_을 포함합니다
  • 여러 Call에 걸쳐 맥락을 유지합니다
  • 전체 사용자 세션 또는 상호작용 흐름을 나타냅니다

Turn

_Turn_은 Thread 내의 상위 수준 오퍼레이션으로, UI에서는 스레드 뷰에서 개별 행으로 표시됩니다. 각 Turn은 다음과 같습니다.
  • 대화 또는 워크플로에서 하나의 논리적 step을 나타냅니다
  • 스레드 컨텍스트의 직접적인 하위 요소이며, 더 낮은 수준의 중첩된 call을 포함할 수 있습니다(스레드 수준 통계에는 표시되지 않음)

Call

_Call_은 애플리케이션에서 @weave.op로 데코레이션된 함수의 실행을 의미합니다.
  • _Turn Calls_는 새로운 턴을 시작하는 최상위 오퍼레이션입니다.
  • _Nested Calls_는 턴 내의 하위 오퍼레이션입니다.

트레이스

_트레이스_는 단일 오퍼레이션의 전체 call 스택을 캡처합니다. 스레드는 동일한 논리적 대화나 세션에 속하는 트레이스를 함께 묶습니다. 다시 말해, 스레드는 여러 턴으로 구성되며 각 턴은 대화의 한 부분을 나타냅니다. Traces에 대한 자세한 내용은 Tracing overview를 참조하세요.

UI Overview

Weave 프로젝트 사이드바에서 Threads를 선택해 Threads list view로 이동합니다.
Weave 사이드바의 Threads 아이콘

Threads 목록 뷰

  • 프로젝트의 최근 Threads를 표시합니다.
  • 열에는 턴 수, 시작 시간, 마지막 업데이트 시간이 표시됩니다.
  • 행을 클릭하면 세부 정보 드로어가 열립니다.
Threads 목록 뷰

스레드 세부 정보 드로어

  • 아무 행이나 클릭하면 해당 행의 세부 정보 드로어가 열립니다.
  • 스레드 내의 모든 턴을 표시합니다.
  • 턴은 시작한 순서대로 나열됩니다(지속 시간이나 종료 시간이 아니라 시작 시간을 기준으로 함).
  • Call 수준 메타데이터(지연 시간, inputs, outputs)를 포함합니다.
  • 로깅된 경우 메시지 콘텐츠 또는 구조화된 데이터를 선택적으로 표시합니다.
  • 턴의 전체 실행을 보려면 스레드 세부 정보 드로어에서 해당 턴을 열 수 있습니다. 이렇게 하면 해당 턴 중 발생한 모든 중첩 오퍼레이션을 자세히 살펴볼 수 있습니다.
  • 턴에 LLM calls에서 추출된 메시지가 포함되어 있으면 해당 메시지는 채팅 창에 표시됩니다. 이러한 메시지는 일반적으로 지원되는 인테그레이션(예: openai.ChatCompletion.create)에서 이루어진 calls에서 오며, 표시되려면 특정 기준을 충족해야 합니다. 자세한 내용은 채팅 뷰 동작을 참조하세요.

채팅 뷰 동작 방식

채팅 창에는 각 턴에서 발생한 LLM Calls에서 추출한 구조화된 메시지 데이터가 표시됩니다. 이 뷰는 상호작용을 대화 형식으로 렌더링해 보여줍니다.
구조화된 LLM 메시지를 보여주는 Threads 채팅 창

무엇이 메시지로 간주되나요

Weave는 한 턴 내에서 LLM 제공자와 직접 상호작용한 Calls(예: 프롬프트를 보내고 응답을 받는 경우)에서 메시지를 추출합니다. 다른 Calls 안에 더 깊게 중첩되지 않은 Calls만 메시지로 표시됩니다. 이렇게 하면 중간 step이나 내부 집계 로직이 중복 표시되는 것을 방지할 수 있습니다. 일반적으로 자동 패치된 서드파티 SDK는 다음과 같은 메시지를 생성합니다.
  • openai.ChatCompletion.create
  • anthropic.Anthropic.completion

메시지가 없으면 어떻게 되나요

어떤 턴에서도 메시지가 생성되지 않으면 채팅 창에 해당 턴의 빈 메시지 섹션이 표시됩니다. 채팅 창에는 같은 스레드의 다른 턴에서 온 메시지가 계속 표시될 수 있습니다.

Turn 및 chat 상호작용

  • 턴을 클릭하면 채팅 창이 해당 턴의 메시지 위치로 스크롤됩니다(고정 동작).
  • 채팅 창을 스크롤하면 turn 목록에서 해당 턴이 강조 표시됩니다.
해당 항목을 클릭하면 턴의 전체 트레이스를 열 수 있습니다. 스레드 상세 뷰로 돌아갈 수 있도록 왼쪽 상단에 뒤로 가기 버튼이 표시됩니다. Weave는 화면 전환 시 UI 상태(예: 스크롤 위치)를 유지하지 않습니다.
스레드 뷰로 돌아가기 위한 뒤로 가기 버튼이 있는 Threads 상세 drawer

SDK 사용

다음 섹션에서는 Weave SDK를 사용해 프로그래밍 방식으로 스레드를 생성하고 관리하는 방법을 설명합니다. 이 섹션의 각 예제는 애플리케이션에서 턴과 스레드를 구성하는 서로 다른 전략을 보여줍니다. 대부분의 예제에서는 스텁 함수 내부에 자체 LLM call 또는 시스템 동작을 구현해야 합니다.
  • 세션이나 대화를 추적하려면 weave.thread() 컨텍스트 관리자를 사용하세요.
  • 논리적 오퍼레이션에 @weave.op를 데코레이션해 턴 또는 중첩된 Calls로 추적하세요.
  • thread_id를 전달하면 Weave는 해당 블록의 모든 오퍼레이션을 같은 스레드로 묶는 데 사용합니다. thread_id를 생략하면 Weave가 고유한 값을 자동으로 생성합니다.
weave.thread()의 반환 값은 thread_id 속성을 가진 ThreadContext 객체이며, 이를 기록하거나 재사용하거나 다른 시스템에 전달할 수 있습니다. 중첩된 weave.thread() 컨텍스트는 같은 thread_id를 재사용하지 않는 한 항상 새 스레드를 시작합니다. 하위 컨텍스트가 종료되어도 상위 컨텍스트가 중단되거나 덮어써지지 않습니다. 따라서 앱 로직에 따라 분기된 스레드 구조나 계층형 스레드 오케스트레이션을 구현할 수 있습니다.

기본 스레드 생성

다음 코드 예제는 weave.thread()를 사용해 하나 이상의 오퍼레이션을 공유 thread_id로 묶는 방법을 보여줍니다. 이는 애플리케이션에서 스레드 사용을 시작하는 간단한 방법입니다.

수동 에이전트 루프 구현

이 예제에서는 @weave.op 데코레이터와 weave.thread() 컨텍스트 관리를 사용해 대화형 에이전트를 수동으로 정의하는 방법을 보여줍니다. process_user_message를 호출할 때마다 스레드에 새로운 턴이 생성됩니다. 이 패턴은 자체 에이전트 루프를 구축하면서 컨텍스트와 중첩 처리 방식을 완전히 제어하고 싶을 때 사용할 수 있습니다. 수명이 짧은 상호작용에는 자동 생성된 스레드 ID를 사용하고, 세션 간에 스레드 컨텍스트를 유지하려면 맞춤형 세션 ID(user_session_123 등)를 전달하세요.

호출 깊이가 맞지 않는 수동 에이전트

이 예제에서는 스레드 컨텍스트를 적용하는 방식에 따라 Call stack의 서로 다른 깊이에서 턴을 정의할 수 있음을 보여줍니다. 이 샘플은 두 개의 프로바이더(OpenAI 및 Anthropic)를 사용하며, 각 프로바이더는 턴 경계에 도달하기 전까지의 Call 깊이가 서로 다릅니다. 모든 턴은 동일한 thread_id를 공유하지만, 턴 경계는 프로바이더 로직에 따라 스택의 서로 다른 수준에 나타납니다. 이는 서로 다른 백엔드에 대해 호출을 다르게 트레이스해야 하면서도, 여전히 이를 동일한 스레드로 그룹화해야 할 때 유용합니다.

이전 세션 재개

이전에 시작한 세션을 반드시 재개하고 같은 스레드에 call을 계속 추가해야 하는 경우가 있습니다. 반면 기존 세션을 재개할 수 없어 대신 새 스레드를 시작해야 하는 경우도 있습니다. 선택적 스레드 재개를 구현할 때는 thread_id 파라미터를 절대로 None으로 두지 마세요. 이렇게 하면 스레드 그룹화가 비활성화됩니다. 대신 항상 유효한 스레드 ID를 제공하세요. 새 스레드를 만들려면 generate_id()와 같은 함수를 사용해 고유 식별자를 생성하세요. thread_id를 지정하지 않으면 Weave의 내부 구현이 무작위 UUID v7을 자동으로 생성합니다. 이 동작을 직접 구현한 generate_id() 함수로 재현해도 되고, 원하는 고유 문자열 값을 사용해도 됩니다.

중첩된 스레드

이 예제는 서로 연계된 여러 스레드를 사용해 복잡한 애플리케이션을 구성하는 방법을 보여줍니다. 각 계층은 자체 스레드 컨텍스트에서 실행되므로 관심사를 깔끔하게 분리할 수 있습니다. 상위 애플리케이션 스레드는 공유 ThreadContext를 사용해 스레드 IDs를 설정함으로써 각 계층을 조율합니다. 시스템의 서로 다른 부분을 각각 독립적으로 분석하거나 모니터링하면서도, 이를 하나의 공유 세션에 연결해야 할 때 이 패턴을 사용하세요.

API 사양

다음 섹션에서는 Threads 쿼리 엔드포인트와 해당 요청 및 응답 스키마, 그리고 스레드 데이터를 프로그래밍 방식으로 조회할 때 사용할 수 있는 일반적인 쿼리 패턴을 설명합니다.

엔드포인트

엔드포인트: POST /threads/query

요청 스키마

응답 스키마

최근 활성 스레드 쿼리

이 예제는 가장 최근에 업데이트된 스레드 50개를 가져옵니다. my-project를 실제 프로젝트 ID로 바꾸세요.

활동 수준에 따라 스레드 쿼리하기

이 예제는 턴 수를 기준으로 정렬해 가장 활동적인 스레드 20개를 가져옵니다.

최근 스레드만 쿼리하기

이 예제는 지난 24시간 이내에 시작된 스레드를 반환합니다. timedeltadays 값을 조정해 시간 범위를 변경할 수 있습니다.