Skip to content

OpenTelemetry 지원

이 페이지는 AI 에이전트의 추적 및 모니터링을 위한 Koog 에이전트 프레임워크의 OpenTelemetry 지원에 대한 세부 정보를 제공합니다.

개요

OpenTelemetry는 애플리케이션에서 텔레메트리 데이터(추적)를 생성, 수집 및 내보내기 위한 도구를 제공하는 관측 가능성 프레임워크입니다. Koog OpenTelemetry 기능을 사용하면 AI 에이전트에 계측(instrument)을 적용하여 텔레메트리 데이터를 수집할 수 있으며, 이는 다음을 돕습니다:

  • 에이전트 성능 및 동작 모니터링
  • 복잡한 에이전트 워크플로우에서 문제 디버깅
  • 에이전트 실행 흐름 시각화
  • LLM 호출 및 도구 사용 추적
  • 에이전트 동작 패턴 분석

주요 OpenTelemetry 개념

  • 스팬(Span): 스팬은 분산 추적 내의 개별 작업 단위 또는 연산을 나타냅니다. 이는 에이전트 실행, 함수 호출, LLM 호출 또는 도구 호출과 같은 애플리케이션 내의 특정 활동의 시작과 끝을 나타냅니다.
  • 속성(Attribute): 속성은 스팬과 같은 텔레메트리 관련 항목에 대한 메타데이터를 제공합니다. 속성은 키-값 쌍으로 표현됩니다.
  • 이벤트(Event): 이벤트는 스팬의 수명 동안 특정 시점(스팬 관련 이벤트)에 발생한 잠재적으로 주목할 만한 일을 나타냅니다.
  • 익스포터(Exporter): 익스포터는 수집된 텔레메트리 데이터를 다양한 백엔드 또는 대상으로 전송하는 역할을 하는 구성 요소입니다.
  • 컬렉터(Collector): 컬렉터는 텔레메트리 데이터를 수신, 처리 및 내보냅니다. 이들은 애플리케이션과 관측 가능성 백엔드 사이의 중간자 역할을 합니다.
  • 샘플러(Sampler): 샘플러는 샘플링 전략에 따라 추적을 기록할지 여부를 결정합니다. 이들은 텔레메트리 데이터의 양을 관리하는 데 사용됩니다.
  • 리소스(Resource): 리소스는 텔레메트리 데이터를 생성하는 엔티티를 나타냅니다. 이들은 리소스에 대한 정보를 제공하는 키-값 쌍인 리소스 속성으로 식별됩니다.

Koog의 OpenTelemetry 기능은 다음을 포함하여 다양한 에이전트 이벤트에 대한 스팬을 자동으로 생성합니다:

  • 에이전트 실행 시작 및 종료
  • 노드 실행
  • LLM 호출
  • 도구 호출

설치

Koog와 함께 OpenTelemetry를 사용하려면 OpenTelemetry 기능을 에이전트에 추가하세요:

kotlin
val agent = AIAgent(
    executor = simpleOpenAIExecutor(apiKey),
    llmModel = OpenAIModels.Chat.GPT4o,
    systemPrompt = "You are a helpful assistant.",
    installFeatures = {
        install(OpenTelemetry) {
            // Configuration options go here
        }
    }
)

구성

기본 구성

다음은 에이전트에서 OpenTelemetry 기능을 구성할 때 설정할 수 있는 사용 가능한 속성 전체 목록입니다:

이름데이터 유형기본값설명
serviceNameStringai.koog계측되는 서비스의 이름입니다.
serviceVersionString현재 Koog 라이브러리 버전계측되는 서비스의 버전입니다.
isVerboseBooleanfalseOpenTelemetry 구성 디버깅을 위한 상세 로깅을 활성화할지 여부입니다.
sdkOpenTelemetrySdk텔레메트리 수집에 사용할 OpenTelemetry SDK 인스턴스입니다.
tracerTracer스팬 생성을 위해 사용되는 OpenTelemetry 트레이서 인스턴스입니다.

NOTE

sdktracer 속성은 접근 가능한 공개 속성이지만, 아래에 나열된 공개 메서드를 통해서만 설정할 수 있습니다.

OpenTelemetryConfig 클래스에는 다양한 구성 항목과 관련된 작업을 나타내는 메서드도 포함되어 있습니다. 다음은 기본 구성 항목 집합으로 OpenTelemetry 기능을 설치하는 예시입니다:

kotlin
install(OpenTelemetry) {
    // Set your service configuration
    setServiceInfo("my-agent-service", "1.0.0")
    
    // Add the Logging exporter
    addSpanExporter(LoggingSpanExporter.create())
}

사용 가능한 메서드에 대한 참조는 아래 섹션을 참조하세요.

setServiceInfo

이름과 버전을 포함한 서비스 정보를 설정합니다. 다음 인수를 사용합니다:

이름데이터 유형필수기본값설명
serviceNameString계측되는 서비스의 이름입니다.
serviceVersionString계측되는 서비스의 버전입니다.

addSpanExporter

외부 시스템으로 텔레메트리 데이터를 전송할 스팬 익스포터를 추가합니다. 다음 인수를 사용합니다:

이름데이터 유형필수기본값설명
exporterSpanExporter사용자 정의 스팬 익스포터 목록에 추가될 SpanExporter 인스턴스입니다.

addSpanProcessor

스팬이 내보내지기 전에 처리할 스팬 프로세서를 추가합니다. 다음 인수를 사용합니다:

이름데이터 유형필수기본값설명
processorSpanProcessor내보내기 전에 텔레메트리 데이터를 처리하는 사용자 정의 로직을 포함하는 스팬 프로세서입니다.

addResourceAttributes

서비스에 대한 추가 컨텍스트를 제공하기 위해 리소스 속성을 추가합니다. 다음 인수를 사용합니다:

이름데이터 유형필수기본값설명
attributesMap<AttributeKey<T>, T>서비스에 대한 추가 세부 정보를 제공하는 키-값 쌍입니다.

setSampler

어떤 스팬을 수집할지 제어하기 위한 샘플링 전략을 설정합니다. 다음 인수를 사용합니다:

이름데이터 유형필수기본값설명
samplerSamplerOpenTelemetry 구성을 위해 설정할 샘플러 인스턴스입니다.

setVerbose

OpenTelemetry 구성 디버깅을 위한 상세 로깅을 활성화 또는 비활성화합니다. 다음 인수를 사용합니다:

이름데이터 유형필수기본값설명
verboseBooleanfalsetrue인 경우 애플리케이션은 더 상세한 텔레메트리 데이터를 수집합니다.

고급 구성

더 고급 구성을 위해서는 다음 구성 옵션을 사용자 정의할 수도 있습니다:

  • 샘플러(Sampler): 수집된 데이터의 빈도와 양을 조정하기 위한 샘플링 전략을 구성합니다.
  • 리소스 속성(Resource attributes): 텔레메트리 데이터를 생성하는 프로세스에 대한 추가 정보를 추가합니다.
kotlin
install(OpenTelemetry) {
    // Set your service configuration
    setServiceInfo("my-agent-service", "1.0.0")
    
    // Add the Logging exporter
    addSpanExporter(LoggingSpanExporter.create())
    
    // Set the sampler 
    setSampler(Sampler.traceIdRatioBased(0.5)) 

    // Add resource attributes
    addResourceAttributes(mapOf(
        AttributeKey.stringKey("custom.attribute") to "custom-value")
    )
}

샘플러

샘플러를 정의하려면 opentelemetry-java SDK의 Sampler 클래스(io.opentelemetry.sdk.trace.samplers.Sampler)에 해당하는 메서드를 사용하여 원하는 샘플링 전략을 나타내세요.

기본 샘플링 전략은 다음과 같습니다:

  • Sampler.alwaysOn(): 모든 스팬(추적)이 샘플링되는 기본 샘플링 전략입니다.

사용 가능한 샘플러 및 샘플링 전략에 대한 자세한 내용은 OpenTelemetry Sampler 문서를 참조하세요.

리소스 속성

리소스 속성은 텔레메트리 데이터를 생성하는 프로세스에 대한 추가 정보를 나타냅니다. Koog는 기본적으로 설정되는 리소스 속성 집합을 포함합니다:

  • service.name
  • service.version
  • service.instance.time
  • os.type
  • os.version
  • os.arch

service.name 속성의 기본값은 ai.koog이며, service.version의 기본값은 현재 사용 중인 Koog 라이브러리 버전입니다.

기본 리소스 속성 외에도 사용자 정의 속성을 추가할 수 있습니다. Koog의 OpenTelemetry 구성에 사용자 정의 속성을 추가하려면 키와 값을 인수로 받는 OpenTelemetry 구성의 addResourceAttributes() 메서드를 사용하세요.

kotlin
addResourceAttributes(mapOf(
    AttributeKey.stringKey("custom.attribute") to "custom-value")
)

스팬 유형 및 속성

OpenTelemetry 기능은 에이전트의 다양한 작업을 추적하기 위해 자동으로 여러 유형의 스팬을 생성합니다:

  • CreateAgentSpan: 에이전트를 실행할 때 생성되며, 에이전트가 종료되거나 프로세스가 종료될 때 닫힙니다.
  • InvokeAgentSpan: 에이전트 호출.
  • NodeExecuteSpan: 에이전트 전략에서 노드의 실행. 이는 Koog 고유의 사용자 정의 스팬입니다.
  • InferenceSpan: LLM 호출.
  • ExecuteToolSpan: 도구 호출.

스팬은 중첩된 계층 구조로 구성됩니다. 다음은 스팬 구조의 예시입니다:

text
CreateAgentSpan
    InvokeAgentSpan
        NodeExecuteSpan
            InferenceSpan
        NodeExecuteSpan
            ExecuteToolSpan
        NodeExecuteSpan
            InferenceSpan

스팬 속성

스팬 속성은 스팬과 관련된 메타데이터를 제공합니다. 각 스팬은 고유한 속성 집합을 가지며, 일부 스팬은 속성을 반복할 수도 있습니다.

Koog는 OpenTelemetry의 생성형 AI 이벤트에 대한 시맨틱 컨벤션을 따르는 사전 정의된 속성 목록을 지원합니다. 예를 들어, 이 컨벤션은 일반적으로 스팬의 필수 속성인 gen_ai.conversation.id라는 속성을 정의합니다. Koog에서 이 속성의 값은 에이전트 실행의 고유 식별자이며, agent.run() 메서드를 호출할 때 자동으로 설정됩니다.

또한 Koog는 Koog 고유의 사용자 정의 속성을 포함합니다. 대부분의 속성은 koog. 접두사로 식별할 수 있습니다. 사용 가능한 사용자 정의 속성은 다음과 같습니다:

  • koog.agent.strategy.name: 에이전트 전략의 이름. 전략은 에이전트의 목적을 설명하는 Koog 관련 엔티티입니다. InvokeAgentSpan 스팬에서 사용됩니다.
  • koog.node.name: 실행 중인 노드의 이름. NodeExecuteSpan 스팬에서 사용됩니다.

이벤트

스팬에는 _이벤트_가 첨부될 수도 있습니다. 이벤트는 관련 있는 일이 발생한 특정 시점을 설명합니다. 예를 들어, LLM 호출이 시작되거나 종료될 때. 이벤트는 속성을 가지며, 추가적으로 이벤트 _본문 필드_를 포함합니다.

OpenTelemetry의 생성형 AI 이벤트에 대한 시맨틱 컨벤션에 따라 다음 이벤트 유형이 지원됩니다:

  • SystemMessageEvent: 모델에 전달된 시스템 지시사항.
  • UserMessageEvent: 모델에 전달된 사용자 메시지.
  • AssistantMessageEvent: 모델에 전달된 어시스턴트 메시지.
  • ToolMessageEvent: 모델에 전달된 도구 또는 함수 호출의 응답.
  • ChoiceEvent: 모델의 응답 메시지.

NOTE

optentelemetry-java SDK는 이벤트를 추가할 때 이벤트 본문 필드 매개변수를 지원하지 않습니다. 따라서 Koog의 OpenTelemetry 지원에서는 이벤트 본문 필드가 키가 body이고 값 유형이 문자열인 별도의 속성으로 처리됩니다. 이 문자열에는 일반적으로 JSON과 같은 객체인 이벤트 본문 필드의 내용 또는 페이로드가 포함됩니다. 이벤트 본문 필드의 예시는 OpenTelemetry 문서를 참조하세요. opentelemetry-java의 이벤트 본문 필드 지원 상태는 관련 GitHub 이슈를 참조하세요.

익스포터

익스포터는 수집된 텔레메트리 데이터를 OpenTelemetry 컬렉터 또는 다른 유형의 대상이나 백엔드 구현으로 전송합니다. 익스포터를 추가하려면 OpenTelemetry 기능을 설치할 때 addSpanExporter() 메서드를 사용하세요. 이 메서드는 다음 인수를 사용합니다:

이름데이터 유형필수기본값설명
exporterSpanExporter사용자 정의 스팬 익스포터 목록에 추가될 SpanExporter 인스턴스입니다.

아래 섹션에서는 opentelemetry-java SDK에서 가장 일반적으로 사용되는 익스포터 중 일부에 대한 정보를 제공합니다.

로깅 익스포터

콘솔에 추적 정보를 출력하는 로깅 익스포터입니다. LoggingSpanExporter (io.opentelemetry.exporter.logging.LoggingSpanExporter)는 opentelemetry-java SDK의 일부입니다.

이러한 유형의 내보내기는 개발 및 디버깅 목적으로 유용합니다.

kotlin
install(OpenTelemetry) {
    // Add the logging exporter
    addSpanExporter(LoggingSpanExporter.create())
    // Add more exporters as needed
}

OpenTelemetry HTTP 익스포터

OpenTelemetry HTTP 익스포터(OtlpHttpSpanExporter)는 opentelemetry-java SDK (io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter)의 일부이며, HTTP를 통해 스팬 데이터를 백엔드로 전송합니다.

kotlin
install(OpenTelemetry) {
   // Add OpenTelemetry HTTP exporter 
   addSpanExporter(
      OtlpHttpSpanExporter.builder()
         // Set the maximum time to wait for the collector to process an exported batch of spans 
         .setTimeout(30, TimeUnit.SECONDS)
         // Set the OpenTelemetry endpoint to connect to
         .setEndpoint("http://localhost:3000/api/public/otel/v1/traces")
         // Add the authorization header
         .addHeader("Authorization", "Basic $AUTH_STRING")
         .build()
   )
}

OpenTelemetry gRPC 익스포터

OpenTelemetry gRPC 익스포터(OtlpGrpcSpanExporter)는 opentelemetry-java SDK (io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter)의 일부입니다. gRPC를 통해 텔레메트리 데이터를 백엔드로 내보내며, 데이터를 수신하는 백엔드, 컬렉터 또는 엔드포인트의 호스트와 포트를 정의할 수 있습니다. 기본 포트는 4317입니다.

kotlin
install(OpenTelemetry) {
   // Add OpenTelemetry gRPC exporter 
   addSpanExporter(
      OtlpGrpcSpanExporter.builder()
          // Set the host and the port
         .setEndpoint("http://localhost:4317")
         .build()
   )
}

Jaeger와 통합

Jaeger는 OpenTelemetry와 연동되는 인기 있는 분산 추적 시스템입니다. Koog 리포지토리의 examplesopentelemetry 디렉터리에는 Jaeger 및 Koog 에이전트와 함께 OpenTelemetry를 사용하는 예시가 포함되어 있습니다.

전제 조건

Koog 및 Jaeger로 OpenTelemetry를 테스트하려면 제공된 docker-compose.yaml 파일을 사용하여 다음 명령을 실행하여 Jaeger OpenTelemetry 올인원 프로세스를 시작하세요:

bash
docker compose up -d

제공된 Docker Compose YAML 파일에는 다음 내용이 포함되어 있습니다:

yaml
# docker-compose.yaml
services:
  jaeger-all-in-one:
    image: jaegertracing/all-in-one:1.39
    container_name: jaeger-all-in-one
    environment:
      - COLLECTOR_OTLP_ENABLED=true
    ports:
      - "4317:4317"
      - "16686:16686"

Jaeger UI에 접근하여 추적을 보려면 http://localhost:16686을 여세요.

예시

Jaeger에서 사용할 텔레메트리 데이터를 내보내기 위해 이 예시에서는 opentelemetry-java SDK의 LoggingSpanExporter (io.opentelemetry.exporter.logging.LoggingSpanExporter) 및 OtlpGrpcSpanExporter (io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter)를 사용합니다.

다음은 전체 코드 샘플입니다:

kotlin
fun main() {
    runBlocking {
        val agent = AIAgent(
            executor = simpleOpenAIExecutor(openAIApiKey),
            llmModel = OpenAIModels.Reasoning.O4Mini,
            systemPrompt = "You are a code assistant. Provide concise code examples."
        ) {
            install(OpenTelemetry) {
                // Add a console logger for local debugging
                addSpanExporter(LoggingSpanExporter.create())

                // Send traces to OpenTelemetry collector
                addSpanExporter(
                    OtlpGrpcSpanExporter.builder()
                        .setEndpoint("http://localhost:4317")
                        .build()
                )
            }
        }

        agent.use { agent ->
            println("OpenTelemetry 추적을 사용하여 에이전트를 실행 중입니다...")

            val result = agent.run("Tell me a joke about programming")

            println("에이전트 실행이 결과: '$result'와 함께 완료되었습니다." +
                    "
추적을 보려면 http://localhost:16686에서 Jaeger UI를 확인하세요")
        }
    }
}

문제 해결

일반적인 문제

  1. Jaeger 또는 Langfuse에 추적이 나타나지 않음

    • 서비스가 실행 중이고 OpenTelemetry 포트(4317)에 접근 가능한지 확인하십시오.
    • OpenTelemetry 익스포터가 올바른 엔드포인트로 구성되었는지 확인하십시오.
    • 추적을 내보내기 위해 에이전트 실행 후 몇 초 정도 기다려야 합니다.
  2. 스팬 누락 또는 불완전한 추적

    • 에이전트 실행이 성공적으로 완료되는지 확인하십시오.
    • 에이전트 실행 후 너무 빨리 애플리케이션을 닫지 않도록 하십시오.
    • 스팬이 내보내질 시간을 허용하도록 에이전트 실행 후 지연을 추가하십시오.
  3. 과도한 수의 스팬

    • sampler 속성을 구성하여 다른 샘플링 전략을 사용하는 것을 고려하십시오.
    • 예를 들어, 추적의 10%만 샘플링하려면 Sampler.traceIdRatioBased(0.1)을 사용하십시오.