메인 콘텐츠로 건너뛰기
이 가이드에서는 애플리케이션을 계측해 실행 내용이 W&B Weave에 상세한 트레이스로 표시되도록 하는 방법을 설명합니다. 트레이스는 입력, 출력, 각 오퍼레이션의 구조를 Call로 캡처하여 LLM 기반 코드를 디버그하고, 평가하고, 모니터링하는 데 도움이 됩니다. 실행 중인 코드를 Weave에서 상세한 트레이스로 보려면 Call을 생성해야 합니다. 다음 섹션에서는 이를 수행하는 세 가지 주요 방법을 자동화 수준이 높은 방식부터 수동 방식까지 차례로 설명합니다:
  • LLM 라이브러리 Call 자동 추적
  • weave.op을 사용한 맞춤형 함수 추적
  • API를 직접 사용한 수동 Call 추적
무엇을 트레이스할지에 대해 필요한 제어 수준에 맞는 방식을 선택하세요.

LLM 라이브러리 Call 자동 추적

Weave는 openai, anthropic, cohere, mistral, LangChain과 같은 널리 사용되는 라이브러리 및 프레임워크와 자동으로 통합됩니다. LLM 또는 프레임워크 라이브러리를 임포트하고 Weave 프로젝트를 초기화하세요. 그러면 Weave가 추가 코드 변경 없이 LLM 또는 플랫폼에 대한 모든 Call을 프로젝트에 자동으로 트레이스로 기록합니다. 지원되는 라이브러리 인테그레이션의 전체 목록은 Integrations overview를 참조하세요.
import weave

from openai import OpenAI
client = OpenAI()

# Weave Tracing 초기화
weave.init('intro-example')

response = client.chat.completions.create(
    model="gpt-4",
    messages=[
        {
            "role": "user",
            "content": "How are you?"
        }
    ],
    temperature=0.8,
    max_tokens=64,
    top_p=1,
)
자동 동작을 더 세밀하게 제어하려면 Configure automatic LLM call tracking을 참조하세요.

맞춤형 함수 추적

LLM 애플리케이션에는 추적하고 싶은 추가 로직(예: 전처리/후처리, 프롬프트 등)이 있는 경우가 많습니다. Weave가 자동으로 트레이스하는 LLM Call와 함께 자체 함수도 캡처하도록 하려면 이 방법을 사용하세요.
Weave를 사용하면 @weave.op 데코레이터를 사용해 이러한 Call를 수동으로 추적할 수 있습니다. 예를 들면 다음과 같습니다.
import weave

# Weave Tracing 초기화
weave.init('intro-example')

# 함수에 데코레이터 적용
@weave.op
def my_function(name: str):
    return f"Hello, {name}!"

# 함수 호출 -- Weave가 입력과 출력을 자동으로 추적합니다
print(my_function("World"))
클래스의 메서드도 추적할 수 있습니다.

클래스 및 객체 메서드 추적

독립형 함수 외에도 클래스 및 객체 메서드도 추적할 수 있습니다. 메서드에 weave.op 데코레이터를 적용하면 클래스의 모든 메서드를 추적할 수 있습니다.
import weave

# Weave Tracing 초기화
weave.init("intro-example")

class MyClass:
    # 메서드에 데코레이터 적용
    @weave.op
    def my_method(self, name: str):
        return f"Hello, {name}!"

instance = MyClass()

# 메서드 호출 -- Weave가 입력과 출력을 자동으로 추적합니다
print(instance.my_method("World"))

병렬(멀티스레드) 함수 call 트레이스

기본적으로 병렬로 실행된 Call은 Weave에서 각각 별도의 루트 Call로 표시되어 트레이스 계층을 따라가기가 어렵습니다. 동일한 부모 Op 아래에 올바르게 중첩되게 하려면 ThreadPoolExecutor를 사용하세요.
다음 코드 샘플은 ThreadPoolExecutor의 사용 예를 보여줍니다. 첫 번째 함수인 funcx를 받아 x+1을 반환하는 단순한 Op입니다. 두 번째 함수인 outer는 입력 목록을 받는 또 다른 Op입니다. outer 내부에서 ThreadPoolExecutorexc.map(func, inputs)를 사용하면 func에 대한 각 Call이 동일한 부모 트레이스 컨텍스트를 계속 유지합니다.
import weave

@weave.op
def func(x):
    return x+1

@weave.op
def outer(inputs):
    with weave.ThreadPoolExecutor() as exc:
        exc.map(func, inputs)

# Weave 프로젝트 이름을 업데이트하세요
client = weave.init('my-weave-project')
outer([1,2,3,4,5])
Weave UI에서는 이렇게 하면 중첩된 하위 Call 다섯 개를 포함하는 단일 부모 Call이 생성됩니다. 증가 연산이 병렬로 실행되더라도 완전한 계층형 트레이스를 얻을 수 있습니다. outer에 대한 단일 부모 Call과 그 아래 중첩된 하위 Call 다섯 개를 보여주는 Trace UI.

수동 Call 추적

자동 인테그레이션이나 weave.op 데코레이터가 워크플로에 맞지 않는 경우, API를 직접 사용해 Call을 수동으로 생성할 수 있습니다. 이 방식은 더 많은 보일러플레이트 코드가 필요한 대신, Call의 시작과 종료 시점을 완전히 제어할 수 있습니다.
import weave

# Weave Tracing 초기화
client = weave.init('intro-example')

def my_function(name: str):
    # Call 시작
    call = client.create_call(op="my_function", inputs={"name": name})

    # ... 함수 코드 ...

    # Call 종료
    client.finish_call(call, output="Hello, World!")

    # 함수 호출
    print(my_function("World"))