> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreweave.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 에이전트 트레이싱

> Agent Lens SDK로 멀티턴 에이전트형 애플리케이션을 계측하고 Agent Lens에서 해당 활동을 확인하세요.

CoreWeave Forge SDK로 멀티턴 에이전트형 애플리케이션을 계측하여 에이전트의 동작을 확인하고, 디버그하고, 평가하는 방법을 알아보세요. 이 가이드는 에이전트를 구축하거나 통합하는 개발자 중 대화, 턴, LLM Call, 도구 실행을 구조적으로 파악하려는 분을 대상으로 합니다.

Agent Lens SDK는 멀티턴 에이전트 대화의 전체 라이프사이클을 모델링합니다. 여기에는 여러 대화를 소유하는 에이전트, 턴을 묶는 대화, 사용자와 에이전트 간의 각 주고받기(턴), 턴 내의 LLM Call, 그리고 LLM이 트리거하는 도구 실행이 포함됩니다. 트레이스는 CoreWeave Agent Lens 프로젝트의 **Conversations** 탭에 표시됩니다. 각 대화는 중첩된 도구 Call, 토큰 사용량, 피드백을 포함한 멀티턴 타임라인으로 표시됩니다.

Agent Lens는 분산 트레이싱을 위한 개방형 표준인 [OpenTelemetry (OTel)](https://opentelemetry.io/docs/concepts/)를 기반으로 구축되었습니다. 모든 턴, LLM Call, 도구 Call은 OTel *span*(하나의 오퍼레이션에 대한 구조화된 기록)을 생성합니다. 각 span에는 `gen_ai.agent.name`, `gen_ai.conversation.id` 같은 [GenAI semantic-convention](https://opentelemetry.io/docs/specs/semconv/gen-ai/) 속성이 태그로 지정됩니다.

<h2 id="before-you-begin">
  시작하기 전에
</h2>

먼저 Agent Lens SDK를 설치하고 프로젝트를 초기화하세요. 이 단계에서 entity와 프로젝트가 Agent Lens에 등록되므로 SDK가 span을 UI의 올바른 위치로 보낼 수 있습니다. SDK는 `WANDB_API_KEY` 환경 변수에서 API 키를 읽어 옵니다.

<Tabs>
  <Tab title="Python">
    ```bash lines theme={"system"}
    pip install coreweave
    ```

    `[YOUR-TEAM]`은 Forge entity 이름으로, `[YOUR-PROJECT]`는 프로젝트 이름으로 바꾸세요. entity는 필수 항목입니다.

    ```python lines theme={"system"}
    from coreweave.forge.agentlens import tracing

    tracing.init("[YOUR-TEAM]/[YOUR-PROJECT]")
    ```

    `start_conversation()`, `start_turn()`, `start_llm()`, `start_tool()`, `start_subagent()`를 호출하기 전에 먼저 `tracing.init()`을 호출하세요. `init()`이 실행되기 전이나 `shutdown()` 이후에는 트레이싱 함수가 아무 동작도 하지 않고 조용히 넘어갑니다. 따라서 계측 코드를 프로덕션 코드에 그대로 둔 채 설정으로 제어할 수 있습니다. 프로세스가 종료될 때 `tracing.shutdown()`을 호출하여 버퍼에 남아 있는 span을 플러시하세요. 이 함수는 `atexit`에도 등록되어 있습니다.
  </Tab>

  <Tab title="TypeScript">
    ```bash lines theme={"system"}
    npm install @coreweave/forge-sdk
    ```

    `[YOUR-TEAM]`은 Forge entity 이름으로, `[YOUR-PROJECT]`는 프로젝트 이름으로 바꾸세요. entity는 필수 항목입니다.

    ```typescript lines theme={"system"}
    import { tracing } from '@coreweave/forge-sdk/agentlens';

    await tracing.init('[YOUR-TEAM]/[YOUR-PROJECT]');
    ```

    `startConversation()`, `startTurn()`, `startLLM()`, `startTool()`, `startSubagent()`를 호출하기 전에 `tracing.init()`을 한 번 호출하세요. TypeScript에서는 모든 요청이나 에이전트 실행을 `tracing.runIsolated()`로 래핑해야 합니다. 이 함수는 `await`를 거치는 동안에도 해당 실행의 트레이싱 상태를 유지하며, 동시에 들어온 요청들이 하나의 대화를 공유하지 않도록 합니다. `tracing.runIsolated()` 밖에서 span을 시작하면 오류가 발생합니다. 프로세스가 종료될 때 `tracing.shutdown()`을 호출하여 버퍼에 남아 있는 span을 플러시하세요.
  </Tab>
</Tabs>

<h2 id="the-agent-data-model">
  에이전트 데이터 모델
</h2>

Agent Lens는 에이전트 동작을 일대다 관계의 계층 구조로 모델링합니다. 각 에이전트는 여러 대화를, 각 대화는 여러 턴을, 각 턴은 여러 LLM Call을 가질 수 있으며, 각 LLM Call은 여러 도구 Call을 트리거할 수 있습니다.

| 개념 | Agent Lens SDK 클래스 | OTel span 유형 | 설명 | 레퍼런스 페이지 |
| - | - | - | - | - |
| 에이전트 | *(클래스 없음)* | *(span 없음, `agent_name` 속성으로 그룹화됨)* | 하나 이상의 대화를 포함하는 에이전트형 애플리케이션입니다. | |
| 대화 | `Conversation` | *(span 없음, 턴은 `conversation_id` 속성으로 그룹화됨)* | 하나 이상의 턴을 포함하는 대화 또는 run입니다. | [Python](/ko/products/agent-lens/reference/python-sdk/conversation) <br /> [TypeScript](/ko/products/agent-lens/reference/typescript-sdk/classes/conversation) |
| 턴 | `Turn` | `invoke_agent` | 사용자 메시지 하나와 이에 대한 에이전트의 전체 응답입니다. | [Python](/ko/products/agent-lens/reference/python-sdk/turn) <br /> [TypeScript](/ko/products/agent-lens/reference/typescript-sdk/classes/turn) |
| LLM Call | `LLM` | `chat` | 언어 모델 API를 한 번 호출하는 것입니다. | [Python](/ko/products/agent-lens/reference/python-sdk/llm) <br /> [TypeScript](/ko/products/agent-lens/reference/typescript-sdk/classes/llm) |
| 도구 Call | `Tool` | `execute_tool` | LLM 응답으로 트리거되는 한 번의 도구 Call입니다. | [Python](/ko/products/agent-lens/reference/python-sdk/tool) <br /> [TypeScript](/ko/products/agent-lens/reference/typescript-sdk/classes/tool) |
| 하위 에이전트 Call | `SubAgent` | `invoke_agent` | 중첩된 에이전트 호출로, 주로 한 에이전트가 다른 에이전트에 작업을 위임할 때 발생합니다. | [Python](/ko/products/agent-lens/reference/python-sdk/subagent) <br /> [TypeScript](/ko/products/agent-lens/reference/typescript-sdk/classes/subagent) |

다음 다이어그램은 하나의 에이전트가 여러 대화로, 하나의 대화가 여러 턴으로 이어지는 계층 구조를 보여줍니다.

```mermaid theme={"system"}
flowchart TB
    Agent["에이전트<br/>agent_name"]

    Agent --> S1 & S2

    S1["대화 1<br/>conversation_id<br/>(OTel span 없음)"]
    S2["대화 2<br/>conversation_id<br/>(OTel span 없음)"]

    S1 --> T1 & T2
    S2 --> T3

    T1["턴 1<br/>invoke_agent<br/>(루트 span, 독립적인 트레이스)"]
    T2["턴 2<br/>invoke_agent<br/>(루트 span, 독립적인 트레이스)"]
    T3["턴 1<br/>invoke_agent<br/>(루트 span, 독립적인 트레이스)"]

    T1 --> L1 & L2
    L1["LLM Call<br/>chat"]
    L2["LLM Call<br/>chat"]

    L1 --> Tool1["도구 Call<br/>execute_tool"]

    classDef agent fill:#DE72FF33,stroke:#454B52,stroke-width:2px
    classDef conversation fill:#FFD95C33,stroke:#454B52,stroke-width:2px
    classDef turn fill:#00CDDB33,stroke:#454B52,stroke-width:2px
    classDef llm fill:#FFCBAD33,stroke:#454B52,stroke-width:2px
    classDef tool fill:#f4f4f5,stroke:#454B52,stroke-width:2px

    class Agent agent
    class S1,S2 conversation
    class T1,T2,T3 turn
    class L1,L2 llm
    class Tool1 tool
```

대화는 부모 span이 아니라 공유 `conversation_id` 속성을 기준으로 턴을 그룹화하므로, 각 턴은 자체 OTel 트레이스를 시작합니다. 이러한 설계 덕분에 분산 트레이싱과 병렬 실행이 가능합니다. 클라이언트는 서버 측 집계 없이 span을 OTel collector로 직접 전송합니다.

<Tip>
  Claude Agent SDK나 Codex 같은 에이전트 SDK 또는 하니스와 Agent Lens를 통합하려면 [에이전트 인테그레이션 선택](/ko/products/agent-lens/get-started/integrations)을 참조하세요. 이러한 인테그레이션은 SDK 또는 하니스 세션을 기반으로 `conversation_id`를 설정하므로, 턴을 그룹화하기 위해 `start_conversation()`을 호출할 필요가 없습니다. 반면 LLM 공급자 SDK 인테그레이션(OpenAI, Anthropic, Google Gen AI)은 대화를 생성하지 않으며, Agent Lens는 대화 안에서 실행된 Call만 표시합니다. 따라서 이 페이지의 API를 사용해 해당 Call을 감싸는 대화와 턴을 열어야 합니다.
</Tip>

<h2 id="agent-tracing-apis">
  에이전트 트레이싱 API
</h2>

다음 섹션에서는 각 최상위 트레이싱 함수와 각 함수가 받는 인수를 설명합니다. 이 함수들을 사용하여 이전 섹션에서 설명한 데이터 모델의 대화, 턴, LLM Call, 도구 Call 계층을 계측하세요.

Agent Lens는 다음과 같은 최상위 함수를 제공합니다. 각 함수가 반환하는 객체는 컨텍스트 관리자로 사용할 수 있으며(Python에서는 `with`, TypeScript에서는 `try/finally` 사용), `.end()`를 호출하여 수동으로 닫을 수도 있습니다.

<h3 id="start-a-conversation">
  대화 시작하기
</h3>

`start_conversation()`(Python) 또는 `startConversation()`(TypeScript)은 모든 하위 span에 `conversation_id` 속성을 지정하여 턴이 **Conversations** 탭에서 그룹화되도록 합니다. `conversation_id` / `conversationId`를 전달하는 경우, 이 값은 대화가 유지되는 동안 변경되지 않아야 합니다. 기존 대화에 새 턴을 추가하려면 같은 ID를 재사용하세요. 이 값을 생략하면 SDK가 UUID를 자동으로 생성합니다.

활성 대화는 컨텍스트(Python `ContextVar` 또는 Node.js `AsyncLocalStorage`)에 저장됩니다. 따라서 동일한 비동기 컨텍스트에서 실행되는 코드라면 대화 객체를 명시적으로 전달하지 않아도 `tracing.get_current_conversation()` / `tracing.getCurrentConversation()`으로 활성 대화를 가져올 수 있습니다.

<Tabs>
  <Tab title="Python">
    ```python lines theme={"system"}
    conversation = tracing.start_conversation(
        agent_name="my-agent",    # 선택: UI에서 에이전트를 식별합니다. 생략하면 대화가 특정 이름의 에이전트로 그룹화되지 않습니다.
        conversation_id="",       # 선택: 턴을 그룹화하는 고정 ID입니다. 비어 있으면 자동 생성됩니다.
        model="",                 # 선택: 이 대화의 턴에 사용할 기본 모델입니다.
        conversation_name="",     # 선택: UI에 표시되는, 사람이 읽기 쉬운 레이블입니다.
        include_content=True,     # 선택: False로 설정하면 span에서 메시지 본문을 제외합니다.
        continue_parent_trace=False,  # 선택: 새 트레이스를 시작하는 대신 기존 OTel 트레이스에 연결합니다.
    )
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript lines theme={"system"}
    const conversation = tracing.startConversation({
      agentName: 'my-agent',  // 선택: UI에서 에이전트를 식별합니다. 생략하면 대화가 특정 이름의 에이전트로 그룹화되지 않습니다.
      conversationId: '',     // 선택: 턴을 그룹화하는 고정 ID입니다. 비어 있으면 자동 생성됩니다.
      model: '',              // 선택: 이 대화의 턴에 사용할 기본 모델입니다.
    });
    ```
  </Tab>
</Tabs>

<h3 id="start-a-turn">
  턴 시작하기
</h3>

`start_turn()`(Python)과 `startTurn()`(TypeScript)은 새 OTel 트레이스의 루트가 되는 새 `invoke_agent` span을 생성합니다. Agent Lens는 이 span을 사용해 타임라인 뷰에서 사용자와 에이전트 간의 주고받기 한 번을 온전하게 나타냅니다.

<Tabs>
  <Tab title="Python">
    ```python lines theme={"system"}
    turn = tracing.start_turn(
        user_message="What is the weather in Tokyo?",  # 사용자의 입력 텍스트.
        agent_name="my-agent",   # 선택: 대화 수준의 에이전트 이름을 재정의합니다.
        model="gpt-4o",          # 선택: 이 턴에 사용하는 모델.
    )
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript lines theme={"system"}
    const turn = tracing.startTurn({
      userMessage: 'What is the weather in Tokyo?',  // 사용자의 입력 텍스트.
      agentName: 'my-agent',  // 선택: 대화 수준의 에이전트 이름을 재정의합니다.
      model: 'gpt-4o',        // 선택: 이 턴에 사용하는 모델.
    });
    ```
  </Tab>
</Tabs>

두 가지 방식으로 호출할 수 있습니다.

* **최상위 함수로 호출** (`tracing.start_turn(...)` / `tracing.startTurn(...)`): 아래 예시에서 사용하는 방식입니다. 컨텍스트에서 활성 대화를 찾아 해당 대화 ID를 상속합니다. 활성 대화가 없으면 턴이 `conversation_id` 없이 생성되며 다른 턴과 그룹화되지 않습니다.
* **인스턴스 메서드로 호출**: 참조를 가지고 있는 대화 객체에서 호출합니다(`conversation.start_turn(...)` / `conversation.startTurn(...)
  `). 컨텍스트 관리자 블록 내부처럼 범위 내에 명시적인 대화 객체가 있을 때 유용합니다. 이 가이드 뒷부분의 ["컨텍스트 관리자 또는
  try-finally 패턴"](#context-manager-or-try-finally-pattern) 예시가 이 방식을 사용합니다. 두 SDK의 `Conversation`, `Turn`, `LLM`, `Tool`, `SubAgent` 레퍼런스 페이지로 바로 가는 링크는 앞서 소개한 [데이터 모델
  table](#the-agent-data-model)을 참조하세요.

<h3 id="start-an-llm-call">
  LLM Call 시작하기
</h3>

`start_llm()` / `startLLM()`은 현재 턴 아래에 중첩된 `chat` span을 생성합니다. Agent Lens는 이 span을 사용해 UI에 토큰 사용량, 모델 이름, 입력 및 출력 메시지, 추론을 표시합니다.

<Tabs>
  <Tab title="Python">
    ```python lines theme={"system"}
    llm = tracing.start_llm(
        model="gpt-4o",             # 모델 식별자.
        provider_name="openai",     # 선택 사항: 공급자 이름(예: "openai", "anthropic"). 아래 참고 사항을 확인하세요.
        system_instructions=["Be concise."],  # 선택 사항: system 프롬프트 문자열.
    )
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript lines theme={"system"}
    const llm = tracing.startLLM({
      model: 'gpt-4o',          // 모델 식별자.
      providerName: 'openai',   // 선택 사항: 공급자 이름(예: "openai", "anthropic"). 아래 참고 사항을 확인하세요.
    });
    ```
  </Tab>
</Tabs>

LLM Call이 완료되면 `llm` 객체가 닫히기 전에 응답 데이터를 이 객체에 할당하세요.

<Tabs>
  <Tab title="Python">
    ```python lines theme={"system"}
    with tracing.start_llm(model="gpt-4o", provider_name="openai") as llm:
        response = openai_client.chat.completions.create(...)
        llm.input_messages = [Message(role="user", content="...")]
        llm.output_messages = [Message(role="assistant", content=response.choices[0].message.content)]
        llm.usage = Usage(
            input_tokens=response.usage.prompt_tokens,
            output_tokens=response.usage.completion_tokens,
        )
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript lines theme={"system"}
    const llm = tracing.startLLM({ model: 'gpt-4o', providerName: 'openai' });
    try {
      const response = await openaiClient.chat.completions.create({ ... });
      llm.record({
        inputMessages: [{ role: 'user', content: '...' }],
        outputMessages: [{ role: 'assistant', content: response.choices[0].message.content ?? '' }],
        usage: {
          inputTokens: response.usage?.prompt_tokens,
          outputTokens: response.usage?.completion_tokens,
        },
      });
    } finally {
      llm.end();
    }
    ```

    `llm.record()`는 `inputMessages`, `outputMessages`, `usage`, `reasoning`을 한 번의 호출로 할당하는 단축 메서드입니다. 필요하면 각 속성을 개별적으로 설정해도 됩니다. Python SDK에서도 같은 메서드를 `llm.record(...)`로 제공하며, 키워드 인수는 snake\_case로 전달합니다.
  </Tab>
</Tabs>

`provider_name` / `providerName`은 명시적으로 전달하세요. Agent Lens는 모델 문자열에서 공급자를 추론하지 않습니다.

<h3 id="start-a-tool-call">
  도구 Call 시작하기
</h3>

`start_tool()` / `startTool()`은 `execute_tool` span을 생성합니다. 이 span은 컨텍스트에서 현재 활성 상태인 OTel span(일반적으로 해당 도구 Call을 생성한 LLM Call의 `chat` span)의 하위 span이 됩니다.

<Tabs>
  <Tab title="Python">
    ```python lines theme={"system"}
    tool = tracing.start_tool(
        name="get_weather",                  # LLM에 선언한 도구 이름.
        arguments='{"city": "Tokyo"}',       # 도구 인수를 담은 JSON 문자열.
        tool_call_id="call_abc123",          # 선택: LLM 응답에 포함된 도구 Call ID.
    )
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript lines theme={"system"}
    const tool = tracing.startTool({
      name: 'get_weather',            // LLM에 선언한 도구 이름.
      args: '{"city": "Tokyo"}',      // 선택: 도구 인수를 담은 JSON 문자열.
      toolCallId: 'call_abc123',      // 선택: LLM 응답에 포함된 도구 Call ID.
    });
    ```
  </Tab>
</Tabs>

span을 종료하기 전에 도구 결과를 할당하세요.

<Tabs>
  <Tab title="Python">
    ```python lines theme={"system"}
    with tracing.start_tool(name="get_weather", arguments='{"city": "Tokyo"}') as tool:
        result = get_weather_api("Tokyo")
        tool.result = result  # dict, list, string을 사용할 수 있으며 자동으로 JSON 인코딩됩니다.
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript lines theme={"system"}
    const tool = tracing.startTool({ name: 'get_weather', args: '{"city": "Tokyo"}' });
    try {
      tool.result = await getWeatherApi('Tokyo');
    } finally {
      tool.end();
    }
    ```
  </Tab>
</Tabs>

<h2 id="usage-patterns-for-agent-tracing">
  에이전트 트레이싱 사용 패턴
</h2>

다음 섹션에서는 에이전트 코드의 구조에 따라 이러한 함수를 조합하는 방법을 설명합니다.

다음 예시에서는 Agent Lens SDK의 두 가지 유형을 사용합니다.

* `Message` ([Python](/ko/products/agent-lens/reference/python-sdk/message-types#message) · [TypeScript](/ko/products/agent-lens/reference/typescript-sdk/interfaces/message))는 대화의 단일 항목을 나타내며, 사용자 입력, assistant 응답, system 프롬프트, 도구 결과가 이에 해당합니다. 모델이 받은 내용을 기록하려면 메시지 목록을 `llm.input_messages` / `llm.inputMessages`에 부여하고, 모델이 생성한 내용을 기록하려면 `llm.output_messages` / `llm.outputMessages`에 부여하세요.
* `Usage` ([Python](/ko/products/agent-lens/reference/python-sdk/message-types#usage) · [TypeScript](/ko/products/agent-lens/reference/typescript-sdk/interfaces/usage))는 LLM 응답의 토큰 수를 캡처하며, `llm.usage`에 부여됩니다.

Agent Lens는 이 두 유형을 사용하여 각 LLM Call의 입력, 출력, 토큰 사용량을 UI에 표시합니다.

<h3 id="context-manager-or-try-finally-pattern">
  컨텍스트 관리자 또는 try-finally 패턴
</h3>

대부분의 에이전트에서는 Python의 컨텍스트 관리자 패턴이나 TypeScript의 try-finally 패턴을 사용하세요. 예외가 발생하더라도 블록이 끝나면 span이 닫히고 전송됩니다.

Agent Lens는 활성 대화, 턴, LLM Call을 컨텍스트에 저장합니다. 따라서 블록 안에서 호출되는 함수는 부모에 대한 명시적인 참조 없이도 `start_llm()` / `startLLM()` 또는 `start_tool()` / `startTool()`을 호출할 수 있습니다. 코드가 동일한 비동기 컨텍스트에서 실행되기만 하면 모듈 경계를 넘어서도 동작합니다. 호출 스택의 어느 위치에서든 활성 객체를 가져오려면 `tracing.get_current_conversation()` / `tracing.getCurrentConversation()`, `tracing.get_current_turn()` / `tracing.getCurrentTurn()`, `tracing.get_current_llm()` / `tracing.getCurrentLLM()`을 사용하세요.

<Tabs>
  <Tab title="Python">
    ```python lines highlight="13,14,17,25,29" theme={"system"}
    from coreweave.forge.agentlens import tracing
    from coreweave.forge.agentlens.tracing import Message, Usage

    # 자리 표시자 함수: 직접 구현한 함수로 교체하세요.
    def call_openai(*args, **kwargs):
        pass  # LLM 클라이언트 호출로 교체하세요.

    def get_weather_api(city: str) -> str:
        return "24°C, sunny"  # 날씨 API 호출로 교체하세요.

    tracing.init("[YOUR-TEAM]/[YOUR-PROJECT]")

    with tracing.start_conversation(agent_name="weather-bot") as conversation:
        with conversation.start_turn(user_message="What is the weather in Tokyo?") as turn:

            # 첫 번째 LLM Call: 도구 Call을 반환합니다.
            with tracing.start_llm(model="gpt-4o", provider_name="openai") as llm:
                response = call_openai(...)
                llm.input_messages = [Message(role="user", content="What is the weather?")]
                llm.think("User wants weather data, I should call get_weather.")
                llm.output("Let me check the weather for you.")
                llm.usage = Usage(input_tokens=100, output_tokens=20)

                # 도구 Call: 이 도구를 요청한 LLM Call의 하위 항목입니다.
                with tracing.start_tool(name="get_weather", arguments='{"city":"Tokyo"}') as tool:
                    tool.result = get_weather_api("Tokyo")  # "24°C, sunny"를 반환합니다.

            # 두 번째 LLM Call: 최종 답변을 생성합니다.
            with tracing.start_llm(model="gpt-4o", provider_name="openai") as llm:
                llm.input_messages = [Message(role="user", content="What is the weather?")]
                llm.output("It is 24°C and sunny in Tokyo today.")
                llm.usage = Usage(input_tokens=150, output_tokens=30)

    tracing.shutdown()
    ```
  </Tab>

  <Tab title="TypeScript">
    에이전트 run을 `tracing.runIsolated()`로 감싸세요. 그러면 해당 대화, 턴, LLM 컨텍스트가 `await`를 거쳐도 유지되고, 동시에 실행되는 다른 run과도 분리됩니다.

    ```typescript lines highlight="11,12,14,17,25,36" theme={"system"}
    import { tracing } from '@coreweave/forge-sdk/agentlens';
    import type { Message, Usage } from '@coreweave/forge-sdk/agentlens/tracing';

    // 자리 표시자 함수: 직접 구현한 함수로 교체하세요.
    async function getWeatherApi(city: string): Promise<string> {
      return '24°C, sunny';  // 날씨 API 호출로 교체하세요.
    }

    await tracing.init('[YOUR-TEAM]/[YOUR-PROJECT]');

    await tracing.runIsolated(async () => {
      const conversation = tracing.startConversation({ agentName: 'weather-bot' });
      try {
        const turn = conversation.startTurn({ userMessage: 'What is the weather in Tokyo?' });
        try {
          // 첫 번째 LLM Call: 도구 Call을 반환합니다.
          const llm = tracing.startLLM({ model: 'gpt-4o', providerName: 'openai' });
          try {
            llm.inputMessages = [{ role: 'user', content: 'What is the weather?' }];
            llm.think('User wants weather data, I should call get_weather.');
            llm.output('Let me check the weather for you.');
            llm.usage = { inputTokens: 100, outputTokens: 20 };

            // 도구 Call: 이 도구를 요청한 LLM Call의 하위 항목입니다.
            const tool = tracing.startTool({ name: 'get_weather', args: '{"city":"Tokyo"}' });
            try {
              tool.result = await getWeatherApi('Tokyo');  // "24°C, sunny"를 반환합니다.
            } finally {
              tool.end();
            }
          } finally {
            llm.end();
          }

          // 두 번째 LLM Call: 최종 답변을 생성합니다.
          const llm2 = tracing.startLLM({ model: 'gpt-4o', providerName: 'openai' });
          try {
            llm2.inputMessages = [{ role: 'user', content: 'What is the weather?' }];
            llm2.output('It is 24°C and sunny in Tokyo today.');
            llm2.usage = { inputTokens: 150, outputTokens: 30 };
          } finally {
            llm2.end();
          }
        } finally {
          turn.end();
        }
      } finally {
        conversation.end();
      }
    });

    await tracing.shutdown();
    ```
  </Tab>
</Tabs>

<h3 id="manual-start-and-end-pattern">
  수동 시작 및 종료 패턴
</h3>

`with` 블록이나 `try/finally`를 사용할 수 없는 경우에는 `.end()`를 명시적으로 호출하세요. 예를 들어 서로 다른 함수 호출에서 span을 열고 닫거나, 코루틴 외부에서 비동기 라이프사이클을 관리하는 경우가 여기에 해당합니다. span이 제대로 닫히고 collector로 플러시되려면 생성한 모든 객체에 대해 직접 `.end()`를 호출해야 합니다. TypeScript에서는 턴이나 대화를 종료하면 아직 열려 있는 하위 항목도 모두 함께 닫힙니다.

<Tabs>
  <Tab title="Python">
    ```python lines highlight="1,2,4,9,15" theme={"system"}
    conversation = tracing.start_conversation(agent_name="weather-bot")
    turn = conversation.start_turn(user_message="What is the weather?")

    llm = tracing.start_llm(model="gpt-4o", provider_name="openai")
    llm.input_messages = [Message(role="user", content="What is the weather?")]
    llm.output("Let me check.")
    llm.usage = Usage(input_tokens=100, output_tokens=20)

    tool = tracing.start_tool(name="get_weather", arguments='{"city": "Tokyo"}')
    tool.result = "24°C, sunny"
    tool.end()   # end()는 멱등적이므로 여러 번 호출해도 안전합니다.

    llm.end()

    llm2 = tracing.start_llm(model="gpt-4o", provider_name="openai")
    llm2.output("It is 24°C and sunny in Tokyo.")
    llm2.usage = Usage(input_tokens=150, output_tokens=30)
    llm2.end()

    turn.end()
    conversation.end()
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript lines highlight="1,2,3,5,10,16" theme={"system"}
    await tracing.runIsolated(async () => {
      const conversation = tracing.startConversation({ agentName: 'weather-bot' });
      const turn = conversation.startTurn({ userMessage: 'What is the weather?' });

      const llm = tracing.startLLM({ model: 'gpt-4o', providerName: 'openai' });
      llm.inputMessages = [{ role: 'user', content: 'What is the weather?' }];
      llm.output('Let me check.');
      llm.usage = { inputTokens: 100, outputTokens: 20 };

      const tool = tracing.startTool({ name: 'get_weather', args: '{"city": "Tokyo"}' });
      tool.result = '24°C, sunny';
      tool.end();  // end()는 멱등적이므로 여러 번 호출해도 안전합니다.

      llm.end();

      const llm2 = tracing.startLLM({ model: 'gpt-4o', providerName: 'openai' });
      llm2.output('It is 24°C and sunny in Tokyo.');
      llm2.usage = { inputTokens: 150, outputTokens: 30 };
      llm2.end();

      turn.end();
      conversation.end();
    });
    ```
  </Tab>
</Tabs>

<h2 id="semantic-conventions">
  시맨틱 규칙
</h2>

Agent Lens SDK는 [GenAI 시맨틱 규칙](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/) 및 [GenAI 에이전트 span 규칙](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-agent-spans/)을 준수하는 OTel span을 내보냅니다. Agent Lens는 어떤 OTel span이든 수신하여 모든 속성을 저장하고, 이를 쿼리할 수 있게 합니다. 모든 Agent Lens 트레이싱 객체에서 `set_attributes()` / `setAttributes()`를 사용하면 span에 임의의 속성을 추가할 수 있습니다. 자세한 내용은 [에이전트 span에 속성 설정하기](/ko/products/agent-lens/tracing/attributes)를 참조하세요.

SDK는 자체 비공개 OpenTelemetry 트레이서 프로바이더를 사용합니다. 이 프로바이더는 Agent Lens를 통해 생성된 span만 내보내며, 애플리케이션의 전역 프로바이더를 대체하거나 관련 없는 계측에서 생성된 span을 내보내지 않습니다.

<h2 id="how-data-appears-in-the-agent-lens-ui">
  Agent Lens UI에서 데이터가 표시되는 방식
</h2>

앞서 설명한 패턴으로 에이전트를 계측하고 실행하면 `https://`에 있는 Agent Lens 프로젝트의 **Conversations** 탭에 트레이스가 표시됩니다.

* **Conversations 탭**에는 모든 대화가 턴 활동 미니맵과 함께 표시됩니다.
* **Conversation 상세 뷰**는 대화를 클릭하면 열리며, 해당 대화의 모든 턴과 LLM Call, 도구 실행, 토큰 수, 연결된 피드백을 보여 줍니다.

Agent Lens에서 캡처된 데이터를 확인하는 방법은 [에이전트 활동 보기](/ko/products/agent-lens/conversations/view-activity)를 참조하세요.
