메인 콘텐츠로 건너뛰기
이 노트북은 대화형입니다. 로컬에서 실행하거나 아래 링크를 통해 열 수 있습니다:

Service API를 사용해 트레이스를 로그하고 쿼리하기

이 가이드에서는 Weave Service API를 사용해 트레이스를 로그하는 방법을 설명합니다. 특히 Service API를 사용해 다음 작업을 수행합니다.
  1. 간단한 LLM 호출과 응답을 모킹(mock)하고, 이를 Weave에 로그합니다.
  2. 더 복잡한 LLM 호출과 응답을 모킹하고, 이를 Weave에 로그합니다.
  3. 로그된 트레이스에 대해 샘플 조회 쿼리를 실행합니다.
로그된 트레이스 보기 이 가이드의 코드를 실행할 때 생성되는 모든 Weave 트레이스는 team_id\project_id로 지정된 Weave 프로젝트의 Traces 탭으로 이동한 다음 트레이스 이름을 선택해 확인할 수 있습니다.
시작하기 전에 사전 준비를 완료하세요.

전제 조건: 변수와 엔드포인트 설정

다음 코드는 Service API에 접근하는 데 사용할 URL 엔드포인트를 설정합니다: 또한 다음 변수를 설정해야 합니다:
  • project_id: 트레이스를 기록하려는 W&B 프로젝트 이름입니다.
  • team_id: 사용자의 W&B 팀 이름입니다.
  • wandb_token: 사용자의 W&B API 키입니다.
import datetime
import json

import requests

# 헤더 및 URL
headers = {"Content-Type": "application/json"}
url_start = "https://trace.wandb.ai/call/start"
url_end = "https://trace.wandb.ai/call/end"
url_stream_query = "https://trace.wandb.ai/calls/stream_query"

# W&B 변수
team_id = ""
project_id = ""
wandb_token = ""

간단한 트레이스

다음 섹션에서는 간단한 트레이스를 생성하는 방법을 단계별로 설명합니다.
  1. 간단한 트레이스 시작하기
  2. 간단한 트레이스 종료하기

간단한 트레이스 시작하기

다음 코드는 샘플 LLM 호출인 payload_start를 생성하고 url_start 엔드포인트를 사용해 Weave에 로깅합니다. payload_start 객체는 OpenAI의 gpt-4oWhy is the sky blue?라는 질문을 보내는 호출을 모사합니다. 성공하면 이 코드는 트레이스가 시작되었음을 알리는 메시지를 출력합니다:
Call started. ID: 01939cdc-38d2-7d61-940d-dcca0a56c575, Trace ID: 01939cdc-38d2-7d61-940d-dcd0e76c5f34
python
## ------------
## 트레이스 시작
## ------------
payload_start = {
    "start": {
        "project_id": f"{team_id}/{project_id}",
        "op_name": "simple_trace",
        "started_at": datetime.datetime.now().isoformat(),
        "inputs": {
            # 확장된 트레이스에서 채팅 UI를 생성하려면 이 "messages" 스타일을 사용하세요.
            "messages": [{"role": "user", "content": "Why is the sky blue?"}],
            "model": "gpt-4o",
        },
        "attributes": {},
    }
}
response = requests.post(
    url_start, headers=headers, json=payload_start, auth=("api", wandb_token)
)
if response.status_code == 200:
    data = response.json()
    call_id = data.get("id")
    trace_id = data.get("trace_id")
    print(f"Call started. ID: {call_id}, Trace ID: {trace_id}")
else:
    print("Start request failed with status:", response.status_code)
    print(response.text)
    exit()

간단한 트레이스 종료

간단한 트레이스를 마무리하기 위해, 다음 코드는 예시 LLM 호출인 payload_end를 생성하고 url_end 엔드포인트를 통해 Weave에 로깅합니다. payload_end 객체는 Why is the sky blue?라는 쿼리에 대해 OpenAI의 gpt-4o가 반환하는 응답을 모사합니다. 이 객체는 요금 요약 정보와 채팅 완료 결과가 Weave Dashboard의 트레이스 뷰에서 생성되도록 포맷되어 있습니다. 성공하면, 이 코드는 트레이스가 완료되었음을 나타내는 메시지를 출력합니다:
Call ended.
python
## ------------
## 트레이스 종료
## ------------
payload_end = {
    "end": {
        "project_id": f"{team_id}/{project_id}",
        "id": call_id,
        "ended_at": datetime.datetime.now().isoformat(),
        "output": {
            # 확장된 트레이스의 채팅 UI에 완성 내용을 추가하려면 이 "choices" 스타일을 사용하세요.
            "choices": [
                {
                    "message": {
                        "content": "It’s due to Rayleigh scattering, where shorter blue wavelengths of sunlight scatter in all directions."
                    }
                },
            ]
        },
        # 트레이스 테이블에서 가격 요약 정보를 생성하려면 다음과 같이 summary를 포맷하세요.
        "summary": {
            "usage": {
                "gpt-4o": {
                    "prompt_tokens": 10,
                    "completion_tokens": 20,
                    "total_tokens": 30,
                    "requests": 1,
                }
            }
        },
    }
}
response = requests.post(
    url_end, headers=headers, json=payload_end, auth=("api", wandb_token)
)
if response.status_code == 200:
    print("Call ended.")
else:
    print("End request failed with status:", response.status_code)
    print(response.text)

복잡한 트레이스

다음 섹션에서는 여러 단계로 이루어진 RAG 조회와 비슷하게, 자식 스팬을 포함한 더 복잡한 트레이스를 생성하는 과정을 단계별로 안내합니다.
  1. 복잡한 트레이스 시작하기
  2. RAG 문서 조회를 위한 자식 스팬 추가
  3. LLM completion 호출을 위한 자식 스팬 추가
  4. 복잡한 트레이스 종료하기

복잡한 트레이스 시작하기

다음 코드는 여러 개의 스팬을 사용해 더 복잡한 트레이스를 생성하는 방법을 보여준다. 예로는 Retrieval-Augmented Generation(RAG) 문서 검색 후 이어지는 LLM 호출이 있을 수 있다. 첫 번째 부분에서는 전체 작업을 나타내는 부모 트레이스(payload_parent_start)를 초기화한다. 이 경우 작업은 사용자 질의 Can you summarize the key points of this document? 를 처리하는 것이다. payload_parent_start 객체는 여러 단계로 구성된 워크플로의 첫 단계를 나타내며, url_start 엔드포인트를 사용해 Weave에 해당 작업을 로그로 남긴다. 성공하면 이 코드는 부모 호출이 로그에 기록되었음을 나타내는 메시지를 출력한다:
Parent call started. ID: 01939d26-0844-7c43-94bb-cdc471b6d65f, Trace ID: 01939d26-0844-7c43-94bb-cdd97dc296c8
python
## ------------
## 트레이스 시작 (부모)
## ------------

# 부모 호출: 시작
payload_parent_start = {
    "start": {
        "project_id": f"{team_id}/{project_id}",
        "op_name": "complex_trace",
        "started_at": datetime.datetime.now().isoformat(),
        "inputs": {"question": "Can you summarize the key points of this document?"},
        "attributes": {},
    }
}
response = requests.post(
    url_start, headers=headers, json=payload_parent_start, auth=("api", wandb_token)
)
if response.status_code == 200:
    data = response.json()
    parent_call_id = data.get("id")
    trace_id = data.get("trace_id")
    print(f"부모 호출이 시작되었습니다. ID: {parent_call_id}, Trace ID: {trace_id}")
else:
    print("부모 시작 요청이 실패했습니다. 상태 코드:", response.status_code)
    print(response.text)
    exit()

RAG 문서 조회를 위한 자식 스팬 추가

다음 코드는 이전 단계에서 시작한 복잡한 상위 트레이스에 자식 스팬을 추가하는 방법을 보여줍니다. 이 단계에서는 전체 워크플로 내에서 RAG 문서 조회 하위 작업을 모델링합니다. 자식 트레이스는 payload_child_start 객체로 시작되며, 여기에는 다음이 포함됩니다:
  • trace_id: 이 자식 스팬을 상위 트레이스와 연결합니다.
  • parent_id: 자식 스팬을 상위 작업과 연결합니다.
  • inputs: 검색 쿼리를 로깅합니다. 예: "This is a search query of the documents I'm looking for."
url_start 엔드포인트 호출이 성공하면, 코드에서는 자식 호출이 시작되고 완료되었음을 나타내는 메시지를 출력합니다:
Child call started. ID: 01939d32-23d6-75f2-9128-36a4a806f179
Child call ended.
python
## ------------
## 자식 스팬:
## 예시. RAG 문서 조회
## ------------

# 자식 호출: 시작
payload_child_start = {
    "start": {
        "project_id": f"{team_id}/{project_id}",
        "op_name": "rag_document_lookup",
        "trace_id": trace_id,
        "parent_id": parent_call_id,
        "started_at": datetime.datetime.now().isoformat(),
        "inputs": {
            "document_search": "This is a search query of the documents I'm looking for."
        },
        "attributes": {},
    }
}
response = requests.post(
    url_start, headers=headers, json=payload_child_start, auth=("api", wandb_token)
)
if response.status_code == 200:
    data = response.json()
    child_call_id = data.get("id")
    print(f"Child call started. ID: {child_call_id}")
else:
    print("Child start request failed with status:", response.status_code)
    print(response.text)
    exit()

# 자식 호출: 종료
payload_child_end = {
    "end": {
        "project_id": f"{team_id}/{project_id}",
        "id": child_call_id,
        "ended_at": datetime.datetime.now().isoformat(),
        "output": {
            "document_results": "This will be the RAG'd document text which will be returned from the search query."
        },
        "summary": {},
    }
}
response = requests.post(
    url_end, headers=headers, json=payload_child_end, auth=("api", wandb_token)
)
if response.status_code == 200:
    print("Child call ended.")
else:
    print("Child end request failed with status:", response.status_code)
    print(response.text)

LLM completion 호출을 위한 자식 스팬 추가

다음 코드는 복합 상위 트레이스에 LLM completion 호출을 나타내는 또 다른 자식 스팬을 추가하는 방법을 보여줍니다. 이 단계는 이전 RAG 연산에서 가져온 문서 컨텍스트를 기반으로 AI의 응답 생성 과정을 모델링합니다. LLM completion 트레이스는 다음을 포함하는 payload_child_start 객체에서 시작됩니다:
  • trace_id: 이 자식 스팬을 상위 트레이스에 연결합니다.
  • parent_id: 자식 스팬을 전체 워크플로에 연결합니다.
  • inputs: 사용자 질의와 추가된 문서 컨텍스트를 포함하여 LLM에 대한 입력 메시지를 로깅합니다.
  • model: 연산에 사용된 모델을 지정합니다 (gpt-4o).
성공 시, 코드는 LLM 자식 스팬 트레이스가 시작되고 종료되었음을 나타내는 메시지를 출력합니다:
Child call started. ID: 0245acdf-83a9-4c90-90df-dcb2b89f234a
작업이 완료되면 payload_child_end 객체가 output 필드에 LLM이 생성한 응답을 로깅하면서 트레이스를 종료합니다. 사용량 요약 정보도 함께 로깅됩니다. 성공하면 코드에서 LLM 자식 span 트레이스가 시작되고 종료되었음을 알리는 메시지를 출력합니다:
Child call started. ID: 0245acdf-83a9-4c90-90df-dcb2b89f234a
Child call ended.
python
## ------------
## 자식 스팬:
## LLM completion 호출 생성
## ------------

# 자식 호출: 시작
payload_child_start = {
    "start": {
        "project_id": f"{team_id}/{project_id}",
        "op_name": "llm_completion",
        "trace_id": trace_id,
        "parent_id": parent_call_id,
        "started_at": datetime.datetime.now().isoformat(),
        "inputs": {
            "messages": [
                {
                    "role": "user",
                    "content": "With the following document context, could you help me answer:\n Can you summarize the key points of this document?\n [+ appended document context]",
                }
            ],
            "model": "gpt-4o",
        },
        "attributes": {},
    }
}
response = requests.post(
    url_start, headers=headers, json=payload_child_start, auth=("api", wandb_token)
)
if response.status_code == 200:
    data = response.json()
    child_call_id = data.get("id")
    print(f"Child call started. ID: {child_call_id}")
else:
    print("Child start request failed with status:", response.status_code)
    print(response.text)
    exit()

# 자식 호출: 종료
payload_child_end = {
    "end": {
        "project_id": f"{team_id}/{project_id}",
        "id": child_call_id,
        "ended_at": datetime.datetime.now().isoformat(),
        "output": {
            "choices": [
                {"message": {"content": "This is the response generated by the LLM."}},
            ]
        },
        "summary": {
            "usage": {
                "gpt-4o": {
                    "prompt_tokens": 10,
                    "completion_tokens": 20,
                    "total_tokens": 30,
                    "requests": 1,
                }
            }
        },
    }
}
response = requests.post(
    url_end, headers=headers, json=payload_child_end, auth=("api", wandb_token)
)
if response.status_code == 200:
    print("Child call ended.")
else:
    print("Child end request failed with status:", response.status_code)
    print(response.text)

복잡한 트레이스 종료하기

다음 코드는 상위 트레이스를 종료하여 전체 워크플로가 완료되었음을 표시하는 방법을 보여준다. 이 단계에서는 모든 하위 span(예: RAG 조회 및 LLM 결과)을 집계하고 최종 출력과 메타데이터를 로깅한다. 트레이스는 다음을 포함하는 payload_parent_end 객체를 사용해 종료한다:
  • id: 최초 상위 트레이스 시작 시 사용된 parent_call_id.
  • output: 전체 워크플로의 최종 결과를 나타낸다.
  • summary: 전체 워크플로에 대한 사용량 데이터를 통합한다.
  • prompt_tokens: 모든 프롬프트에 사용된 토큰의 총합.
  • completion_tokens: 모든 응답에서 생성된 토큰의 총합.
  • total_tokens: 워크플로 전체의 토큰 합계.
  • requests: 발생한 요청의 총 개수(이 경우에는 1).
성공하면 코드는 다음과 같은 결과를 출력한다:
Parent call ended.
python
## ------------
## 트레이스 종료
## ------------

# 상위 호출: 종료
payload_parent_end = {
    "end": {
        "project_id": f"{team_id}/{project_id}",
        "id": parent_call_id,
        "ended_at": datetime.datetime.now().isoformat(),
        "output": {
            "choices": [
                {"message": {"content": "This is the response generated by the LLM."}},
            ]
        },
        "summary": {
            "usage": {
                "gpt-4o": {
                    "prompt_tokens": 10,
                    "completion_tokens": 20,
                    "total_tokens": 30,
                    "requests": 1,
                }
            }
        },
    }
}
response = requests.post(
    url_end, headers=headers, json=payload_parent_end, auth=("api", wandb_token)
)
if response.status_code == 200:
    print("Parent call ended.")
else:
    print("Parent end request failed with status:", response.status_code)
    print(response.text)

조회 쿼리 실행하기

다음 코드는 이전 예제에서 생성한 trace를 조회하는 방법을 보여 주며, inputs.model 필드가 gpt-4o와 동일한 trace만 필터링합니다. query_payload 객체에는 다음 항목이 포함됩니다.
  • project_id: 조회할 팀과 프로젝트를 지정합니다.
  • filter: 쿼리가 trace 루트(최상위 trace)만 반환하도록 합니다.
  • query: $expr 연산자를 사용해 필터 로직을 정의합니다.
    • $getField: inputs.model 필드를 가져옵니다.
    • $literal: inputs.model"gpt-4o"와 동일한 trace와 일치시킵니다.
  • limit: 쿼리 결과를 최대 10,000개로 제한합니다.
  • offset: 쿼리를 첫 번째 결과부터 시작합니다.
  • sort_by: started_at 타임스탬프를 기준으로 내림차순 정렬합니다.
  • include_feedback: 결과에서 피드백 데이터를 제외합니다.
쿼리가 성공하면 응답에는 쿼리 파라미터와 일치하는 trace 데이터가 포함됩니다.
{'id': '01939cf3-541f-76d3-ade3-50cfae068b39', 'project_id': 'cool-new-team/uncategorized', 'op_name': 'simple_trace', 'display_name': None, 'trace_id': '01939cf3-541f-76d3-ade3-50d5cfabe2db', 'parent_id': None, 'started_at': '2024-12-06T17:10:12.590000Z', 'attributes': {}, 'inputs': {'messages': [{'role': 'user', 'content': 'Why is the sky blue?'}], 'model': 'gpt-4o'}, 'ended_at': '2024-12-06T17:47:08.553000Z', 'exception': None, 'output': {'choices': [{'message': {'content': 'It’s due to Rayleigh scattering, where shorter blue wavelengths of sunlight scatter in all directions.'}}]}, 'summary': {'usage': {'gpt-4o': {'prompt_tokens': 10, 'completion_tokens': 20, 'requests': 1, 'total_tokens': 30}}, 'weave': {'status': 'success', 'trace_name': 'simple_trace', 'latency_ms': 2215963}}, 'wb_user_id': 'VXNlcjoyMDk5Njc0', 'wb_run_id': None, 'deleted_at': None}
python
query_payload = {
    "project_id": f"{team_id}/{project_id}",
    "filter": {"trace_roots_only": True},
    "query": {
        "$expr": {"$eq": [{"$getField": "inputs.model"}, {"$literal": "gpt-4o"}]}
    },
    "limit": 10000,
    "offset": 0,
    "sort_by": [{"field": "started_at", "direction": "desc"}],
    "include_feedback": False,
}
response = requests.post(
    url_stream_query, headers=headers, json=query_payload, auth=("api", wandb_token)
)
if response.status_code == 200:
    print("Query successful!")
    try:
        data = response.json()
        print(data)
    except json.JSONDecodeError as e:
        # 대체 디코딩
        json_objects = response.text.strip().split("\n")
        parsed_data = [json.loads(obj) for obj in json_objects]
        print(parsed_data)
else:
    print(f"Query failed with status code: {response.status_code}")
    print(response.text)