> ## 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.

# OpenTelemetry span을 Agent Lens로 전송하기

> 모든 OpenTelemetry 파이프라인에서 OTLP 트레이스 데이터를 Agent Lens 엔드포인트로 전송하세요. SDK 인테그레이션은 필요하지 않습니다.

CoreWeave Agent Lens는 [OpenTelemetry (OTel)](https://opentelemetry.io/docs/concepts/)를 기반으로 구축되었으며, span을 Agent Lens로 수집하는 전용 OTLP 엔드포인트를 제공합니다. 따라서 OTel 익스포터의 현재 엔드포인트 설정을 Agent Lens의 엔드포인트 설정으로 바꾸기만 하면 OTel로 계측된 애플리케이션의 span을 Agent Lens로 빠르게 보낼 수 있습니다. CoreWeave Forge SDK를 설치할 필요도 없습니다.

이 페이지에서는 엔드포인트에 인증하고 span을 전송하는 방법을 설명합니다.

<Note>
  Agent Lens는 Weights & Biases와 동일한 백엔드에서 실행되므로 일부 설정 이름에는 `wandb` 접두사가 그대로 남아 있습니다. `WANDB_API_KEY` 환경 변수, `wandb-api-key` 헤더, `wandb.entity` 및 `wandb.project` 리소스 속성이 이에 해당합니다. 이 이름들은 표시된 그대로 사용하세요.
</Note>

<h2 id="authentication">
  인증
</h2>

다음 엔드포인트로 span을 전송할 수 있습니다.

* **경로**: `/agents/otel/v1/traces`
* **메서드**: `POST`
* **Content-Type**: `application/x-protobuf`
* **기본 URL**: Multi-tenant Cloud의 경우 `https://trace.wandb.ai`

이 엔드포인트는 OTLP protobuf 페이로드만 허용합니다. `opentelemetry-exporter-otlp-proto-http`(Python) 또는 `@opentelemetry/exporter-trace-otlp-proto`(TypeScript) 같은 protobuf HTTP 익스포터를 사용하세요. `gzip` 및 `deflate` 콘텐츠 인코딩도 지원합니다.

엔드포인트에 인증하려면 `OTLPSpanExporter` 설정에서 다음 값 중 하나를 사용해 [API 키](https://forge.coreweave.com/settings#apikeys)를 지정하세요.

* `wandb-api-key`: Forge API 키를 값으로 지정합니다.
* `Authorization`: 사용자 이름은 `api`, 비밀번호는 Forge API 키로 지정하는 HTTP Basic 인증입니다. 익스포터나 수집기가 표준 인증 헤더만 지원하는 경우에 유용합니다.

<h2 id="project-routing">
  프로젝트 라우팅
</h2>

Agent Lens는 OTel 리소스 속성 또는 요청 헤더를 기준으로 span을 프로젝트에 라우팅합니다.

* **리소스 속성**(권장): `TracerProvider` 리소스에서 `wandb.entity`를 Forge 팀 이름 또는 사용자 이름으로, `wandb.project`를 프로젝트 이름으로 설정하세요.
* **`project_id` 헤더**: 값을 `[YOUR-TEAM]/[YOUR-PROJECT]`로 설정하세요.

둘 다 지정된 경우 리소스 속성이 우선합니다. entity와 프로젝트가 지정되지 않은 span은 삭제됩니다.

<h2 id="shape-spans-for-the-conversations-tab">
  Conversations 탭에 맞게 span 구성하기
</h2>

Agent Lens는 어떤 OTel span이든 수용하고 해당 span의 속성을 모두 저장하지만, Conversations 탭에는 [OTel GenAI 시맨틱 규칙](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-agent-spans/)을 따르는 span만 렌더링됩니다. 각 span에 `gen_ai.operation.name`을 설정하여 해당 span이 무엇을 나타내는지 Agent Lens에 알려 주세요.

| `gen_ai.operation.name` | 의미 | 렌더링 형태 |
| - | - | - |
| `invoke_agent` | 사용자와 에이전트 간에 이루어지는 한 번의 완전한 교환. | 턴. |
| `chat` | 언어 모델 API 호출 한 번. | 턴 내의 LLM Call. |
| `execute_tool` | 도구 실행 한 번. | 도구 Call. |

그룹화를 제어하는 속성이 두 가지 더 있습니다.

* `gen_ai.conversation.id`: 턴을 하나의 대화로 묶습니다. 대화가 지속되는 동안 변하지 않는 고정 ID를 사용하세요.
* `gen_ai.agent.name`: Conversations 탭에서 대화를 이름이 지정된 에이전트 아래로 묶습니다.

`gen_ai.request.model`, `gen_ai.usage.input_tokens`, `gen_ai.usage.output_tokens` 등 그 밖의 GenAI 시맨틱 규칙 속성은 선택 사항이지만, 설정하면 모델 이름과 토큰 수가 함께 표시되어 더 풍부한 정보를 확인할 수 있습니다. 이러한 span이 매핑되는 에이전트 데이터 모델에 대해서는 [에이전트 트레이싱하기](/ko/products/agent-lens/tracing/instrument#the-agent-data-model)를 참조하세요.

<h2 id="configure-with-environment-variables-only">
  환경 변수만으로 설정하기
</h2>

애플리케이션 또는 수집기가 표준 OTel 익스포터 환경 변수를 읽는다면, 다음 변수를 설정하는 것만으로 코드를 변경하지 않고도 span을 Agent Lens로 라우팅할 수 있습니다.

```bash lines theme={"system"}
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://trace.wandb.ai/agents/otel/v1/traces"
export OTEL_EXPORTER_OTLP_TRACES_HEADERS="wandb-api-key=$WANDB_API_KEY"
export OTEL_RESOURCE_ATTRIBUTES="wandb.entity=[YOUR-TEAM],wandb.project=[YOUR-PROJECT]"
export OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf
```

* `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`: 익스포터가 span을 전송할 Agent Lens 엔드포인트입니다.
* `OTEL_EXPORTER_OTLP_TRACES_HEADERS`: `wandb-api-key` 헤더에 담아 보내는 Forge API 키로 각 요청을 인증합니다.
* `OTEL_RESOURCE_ATTRIBUTES`: span을 사용자의 프로젝트로 전송합니다. `wandb.entity`에는 Forge 팀 이름 또는 사용자 이름을, `wandb.project`에는 프로젝트 이름을 설정하세요.
* `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL`: 익스포터가 HTTP를 통해 OTLP protobuf를 전송하도록 설정합니다. 엔드포인트는 protobuf 페이로드만 허용하므로 값은 반드시 `http/protobuf`여야 합니다.

<h2 id="example-emit-agent-spans-without-the-agent-lens-sdk">
  예시: Agent Lens SDK 없이 에이전트 span 내보내기
</h2>

다음 예시는 OTel 패키지만 사용하여 최소한의 에이전트 턴을 계측합니다. 턴에 대한 `invoke_agent` span, LLM Call에 대한 `chat` span, 도구 Call에 대한 `execute_tool` span을 생성한 다음, 이를 Agent Lens 에이전트 엔드포인트로 내보냅니다.

먼저 필요한 의존성을 설치하세요.

<CodeGroup>
  ```bash Python theme={"system"}
  pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
  ```

  ```bash TypeScript theme={"system"}
  npm install @opentelemetry/api @opentelemetry/sdk-trace-node @opentelemetry/sdk-trace-base @opentelemetry/resources @opentelemetry/exporter-trace-otlp-proto
  ```
</CodeGroup>

그런 다음 아래 코드에서 `YOUR-TEAM`, `YOUR-PROJECT`, `YOUR_API_KEY`를 실제 값으로 바꾼 후 실행하세요.

<CodeGroup>
  ```python Python lines highlight="8,9,14" theme={"system"}
  import os
  from opentelemetry import trace
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
  from opentelemetry.sdk import trace as trace_sdk
  from opentelemetry.sdk.resources import Resource
  from opentelemetry.sdk.trace.export import BatchSpanProcessor

  ENTITY = "[YOUR-TEAM]"
  PROJECT = "[YOUR-PROJECT]"

  AGENTS_LENS_OTLP_ENDPOINT = "https://trace.wandb.ai/agents/otel/v1/traces"

  # https://forge.coreweave.com/settings#apikeys 에서 API 키를 생성하세요.
  WANDB_API_KEY = [YOUR-API-KEY]

  exporter = OTLPSpanExporter(
      endpoint=WEAVE_AGENTS_OTLP_ENDPOINT,
      headers={"wandb-api-key": WANDB_API_KEY},
  )

  tracer_provider = trace_sdk.TracerProvider(resource=Resource({
      "wandb.entity": ENTITY,
      "wandb.project": PROJECT,
  }))
  tracer_provider.add_span_processor(BatchSpanProcessor(exporter))
  trace.set_tracer_provider(tracer_provider)

  tracer = trace.get_tracer("my-agent")

  CONVERSATION_ID = "conversation-001"

  # 한 턴: 사용자가 질문하면 에이전트가 도구를 호출한 다음 답변합니다.
  with tracer.start_as_current_span("invoke_agent my-agent") as turn:
      turn.set_attribute("gen_ai.operation.name", "invoke_agent")
      turn.set_attribute("gen_ai.agent.name", "my-agent")
      turn.set_attribute("gen_ai.conversation.id", CONVERSATION_ID)

      with tracer.start_as_current_span("chat gpt-4o") as llm:
          llm.set_attribute("gen_ai.operation.name", "chat")
          llm.set_attribute("gen_ai.conversation.id", CONVERSATION_ID)
          llm.set_attribute("gen_ai.request.model", "gpt-4o")
          # 실제 LLM Call로 바꾸고 해당 토큰 사용량을 기록하세요.
          llm.set_attribute("gen_ai.usage.input_tokens", 100)
          llm.set_attribute("gen_ai.usage.output_tokens", 20)

          with tracer.start_as_current_span("execute_tool get_weather") as tool:
              tool.set_attribute("gen_ai.operation.name", "execute_tool")
              tool.set_attribute("gen_ai.conversation.id", CONVERSATION_ID)
              tool.set_attribute("gen_ai.tool.name", "get_weather")
              # 실제 도구 실행으로 바꾸세요.

  tracer_provider.shutdown()  # 종료하기 전에 대기 중인 span을 모두 플러시합니다.
  ```

  ```typescript TypeScript lines highlight="7,8,13" theme={"system"}
  import { trace } from "@opentelemetry/api";
  import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
  import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
  import { Resource } from "@opentelemetry/resources";

  const ENTITY = "[YOUR-TEAM]";
  const PROJECT = "[YOUR-PROJECT]";

  const WEAVE_AGENTS_OTLP_ENDPOINT = "https://trace.wandb.ai/agents/otel/v1/traces";

  // https://forge.coreweave.com/settings#apikeys 에서 API 키를 생성하세요.
  const WANDB_API_KEY = [YOUR-API-KEY]!;

  const exporter = new OTLPTraceExporter({
    url: WEAVE_AGENTS_OTLP_ENDPOINT,
    headers: { "wandb-api-key": WANDB_API_KEY },
  });

  const provider = new NodeTracerProvider({
    resource: new Resource({
      "wandb.entity": ENTITY,
      "wandb.project": PROJECT,
    }),
    spanProcessors: [new BatchSpanProcessor(exporter)],
  });

  provider.register();

  const tracer = trace.getTracer("my-agent");

  const CONVERSATION_ID = "conversation-001";

  // 턴 1회: 사용자가 질문하면 에이전트가 도구를 호출한 뒤 답변합니다.
  tracer.startActiveSpan("invoke_agent my-agent", (turn) => {
    turn.setAttribute("gen_ai.operation.name", "invoke_agent");
    turn.setAttribute("gen_ai.agent.name", "my-agent");
    turn.setAttribute("gen_ai.conversation.id", CONVERSATION_ID);

    tracer.startActiveSpan("chat gpt-4o", (llm) => {
      llm.setAttribute("gen_ai.operation.name", "chat");
      llm.setAttribute("gen_ai.conversation.id", CONVERSATION_ID);
      llm.setAttribute("gen_ai.request.model", "gpt-4o");
      // 실제 LLM Call로 대체하고 해당 토큰 사용량을 기록하세요.
      llm.setAttribute("gen_ai.usage.input_tokens", 100);
      llm.setAttribute("gen_ai.usage.output_tokens", 20);

      tracer.startActiveSpan("execute_tool get_weather", (tool) => {
        tool.setAttribute("gen_ai.operation.name", "execute_tool");
        tool.setAttribute("gen_ai.conversation.id", CONVERSATION_ID);
        tool.setAttribute("gen_ai.tool.name", "get_weather");
        // 실제 도구 실행으로 대체하세요.
        tool.end();
      });

      llm.end();
    });

    turn.end();
  });

  await provider.shutdown(); // 종료 전에 대기 중인 모든 span을 플러시합니다.
  ```
</CodeGroup>

같은 대화에 턴을 더 추가하려면 동일한 `gen_ai.conversation.id`로 `invoke_agent` span을 추가로 내보내세요. 각 턴은 자체 트레이스의 루트 span이므로 턴끼리 부모 span을 공유할 필요가 없습니다.

애플리케이션에서 span을 내보낸 후 Agent Lens 프로젝트의 **Conversations** 뷰를 여세요.
