AI 코딩 에이전트를 위한 플라이트 레코더를 구축했습니다: 에이전트의 사고 과정을 관찰하며 SigNoz를 통해 배운 점
요약
AI 코딩 에이전트의 내부 동작을 관찰하기 위해 SigNoz와 OpenTelemetry를 활용한 관측성 도구 AXRAY를 구축한 사례를 소개합니다. LLM의 추론 시간과 환경 실행 시간을 분리하여 에이전트의 지연 원인을 정확히 파악하는 방법을 다룹니다.
핵심 포인트
- 에이전트의 지연 시간을 LLM 추론과 환경 실행 시간으로 분리하여 분석해야 함
- OpenTelemetry와 GenAI Semantic Conventions를 활용한 계측 표준 적용
- Time-in-Brain과 Time-in-Environment 지표를 통한 효율성 측정
- 전통적인 로그 방식의 한계를 극복하는 관측성(Observability)의 중요성
AI 에이전트를 구축했습니다... 그러고 나서 그것이 무엇을 하고 있는지 전혀 모르고 있다는 사실을 깨달았습니다
우리의 AI 에이전트는 코드를 작성하고, 테스트를 실행하며, 풀 리퀘스트 (pull requests)를 생성하고 있었지만, 저는 왜 어떤 실행은 8초가 걸리고 다른 실행은 45초가 걸리는지 전혀 알 수 없었습니다.
LLM (Large Language Model)이 너무 오래 생각하고 있었던 걸까요?
실패하는 셸 명령어를 재시도하느라 갇혀 있었던 걸까요?
Docker가 느렸던 걸까요?
알 수 없었습니다.
제가 가진 것이라고는 로딩 스피너와 최종 결과뿐이었습니다.
에이전트가 _무언가를 하고 있다_는 것을 아는 것과 실제로 무엇을 하고 있는지 아는 것 사이의 그 간극이 바로 우리가 SigNoz를 관측성 (observability) 백본으로 사용하여 AXRAY를 구축한 이유입니다.
이것은 이를 구축한 이야기, 그 뒤에 숨겨진 아키텍처, 그리고 모든 것을 거의 망가뜨릴 뻔했던 배포 시의 함정에 관한 이야기입니다.
🛑 왜 이 문제를 해결해야 했는가
자율 코딩 에이전트는 전통적인 마이크로서비스 (microservices)처럼 동작하지 않습니다.
단일 에이전트 턴 (agent turn)에는 다음과 같은 과정이 포함될 수 있습니다:
- LLM이 다단계 계획 생성
- 코드베이스 검색
- 파일 읽기
- 코드 작성
- Docker 내부에서 명령 실행
- 테스트 실행
- Git 커밋 생성
- 풀 리퀘스트 (pull requests) 생성
이러한 모든 동작은 각각 고유한 지연 시간 (latency)을 가집니다.
각각의 동작은 독립적으로 실패할 수 있습니다.
전통적인 애플리케이션 로그는 이 모든 것을 읽을 수 없는 텍스트 벽으로 평탄화(flatten)해 버립니다.
에이전트가 무엇을 하고 있었는지 구분할 수 없습니다:
- 생각 중 (Thinking)
- 대기 중 (Waiting)
- 재시도 중 (Retrying)
- 실제로 코드 실행 중 (Actually executing code)
이러한 구분은 매우 중요합니다.
만약 지연 시간이 LLM이 수천 개의 추론 토큰 (reasoning tokens)을 생성하는 데서 발생한다면, 해결책은 프롬프트 최적화 (prompt optimization)입니다.
만약 지연 시간이 멈춰버린 셸 명령어에서 발생한다면, 해결책은 타임아웃 (timeout) 설정이나 샌드박스 (sandbox) 수정입니다.
이 두 가지를 분리하지 못한다면, 여러분은 그저 추측만 하고 있는 것입니다.
우리가 구축한 것
AXRAY는 자체적인 텔레메트리 스키마 (telemetry schema)를 발명하는 대신, 공식적인 GenAI Semantic Conventions를 따라 OpenTelemetry를 사용하여 모든 에이전트 턴을 계측 (instruments)합니다.
예시는 다음과 같습니다:
gen_ai.request.modelgen_ai.usage.input_tokensgen_ai.usage.output_tokens
모든 스팬 (span)에는 다음과 같은 간단한 단계가 태그로 지정됩니다:
llmtool
이를 통해 모든 에이전트 턴 (agent turn)에 대해 두 가지 지표를 계산합니다.
Time-in-Brain (뇌 체류 시간)
LLM이 생각하고, 추론하고, 토큰을 생성하는 데 소비한 시간입니다.
Time-in-Environment (환경 체류 시간)
Docker가 실제로 명령을 실행하는 데 소비한 시간입니다.
이 두 숫자를 통해 거의 모든 것을 파악할 수 있습니다.
또한, 전체 실행 효율성 점수 (overall execution efficiency score)를 계산합니다:
const efficiencyScore = Math.max(
45,
Math.min(98, 100 - (0.35 * brainPercent + 0.10 * envPercent))
...
실제 실행 세션을 처음 관찰했을 때, 저는 놀라운 사실을 발견했습니다.
**지연 시간 (latency)의 거의 78%**가 LLM 내부에서 발생하고 있었습니다.
우리의 Docker 샌드박스 (sandbox)는 느리지 않았습니다.
우리의 셸 (shell) 명령어도 느리지 않았습니다.
컨텍스트 윈도우 (context window)가 단순히 너무 컸던 것입니다.
이는 추측이 아니었습니다.
다음 쿼리에 대한 SigNoz 트레이스 (trace) 쿼리에서 직접 도출된 결과였습니다:
signoz_traces.signoz_index_v3
SigNoz가 핵심적인 역할을 수행한 부분
결과적으로 SigNoz는 AXRAY의 완전히 다른 세 가지 계층을 지원하게 되었습니다.
1️⃣ 로우 인스트루멘테이션 (Raw Instrumentation)을 위한 OTLP 내보내기
모든 LLM 요청과 모든 도구 호출 (tool invocation)은 OTLP (:4318)를 통해 스팬 (span)을 내보냅니다.
우리의 속성 (attributes)을 공식 GenAI 시맨틱 컨벤션 (semantic conventions)에 매핑하자, 모든 것이 기존의 관찰 가능성 (observability) 생태계에 자연스럽게 맞아떨어지기 시작했습니다.
별도의 커스텀 텔레메트리 (custom telemetry) 형식이 필요하지 않았습니다.
2️⃣ 직접적인 ClickHouse 쿼리
우리는 표준 대시보드에서 직접적으로 노출되지 않는 턴 단위 미만의 지연 시간 세부 분석 (sub-turn latency breakdowns)을 원했습니다.
SigNoz는 ClickHouse 내부에 트레이스를 저장하기 때문에, 다음과 같은 커스텀 SQL을 실행할 수 있었습니다:
SELECT
attributes_string['tool.name'] AS toolName,
avg(durationNano) AS avgDurationNano,
...
해당 쿼리를 통해 전체 세션 동안 어떤 도구가 실행 시간을 가장 많이 소비했는지 즉시 확인할 수 있었습니다.
3️⃣ SigNoz MCP 서버
우리가 정말 원했던 기능 중 하나는 AXRAY 자체 내부에서 실시간 알림 가시성 (live alert visibility)을 확보하는 것이었습니다.
알림 시스템을 새로 구축하는 대신, SigNoz의 MCP 서버에 직접 연결했습니다.
StreamableHTTPClientTransport를 통해 다음을 호출했습니다:
signoz_list_alerts
이를 통해 활성 알림 규칙 (active alert rules)이 포함된 구조화된 JSON을 얻을 수 있었습니다.
이는 다음과 같은 의미를 가집니다:
- 토큰 급증 (token spikes)
- 비용 이상 (cost anomalies)
- 지연 시간 알림 (latency alerts)
SigNoz의 알림 로직을 중복 구현할 필요 없이 AXRAY의 UI 내에 직접 나타날 수 있습니다.
문제를 일으킨 부분
누군가 저에게 미리 경고해 주었으면 했던 부분입니다.
오래된 배포 튜토리얼
대부분의 튜토리얼은 여전히 다음과 같은 고전적인 방식을 설명합니다:
- docker-compose
- install.sh
설치 흐름 말입니다.
하지만 이는 더 이상 권장되는 배포 방식이 아닙니다.
SigNoz는 매우 작은 YAML 매니페스트 (manifest)로 구동되는 Foundry로 전환되었습니다.
apiVersion: v1alpha1
kind: Installation
...
배포는 단 하나의 명령어로 이루어집니다:
foundryctl cast -f casting.yaml
Foundry는 다음과 같은 역할을 수행합니다:
- 선결 조건 검증 (validates prerequisites)
- Docker Compose 생성
- 플랫폼 시작
실제로 이전 방식보다 더 간단합니다.
저는 이것을 발견하기 전까지 오래된 튜토리얼을 따라 하느라 오후 시간을 통째로 날렸습니다.
하드코딩된 UUID
두 번째 버그는 훨씬 더 교묘했습니다.
저는 대시보드와 알림 규칙 (alert rules)을 SigNoz의 Postgres 메타데이터 데이터베이스로 자동 임포트하는 스크립트를 작성했습니다.
모든 것이 완벽하게 작동했습니다.
제 컴퓨터에서는 말이죠.
왜 그랬을까요?
실수로 저의 것을 하드코딩했기 때문입니다:
org_iduser_id
모든 새로운 SigNoz 설치는 완전히 다른 UUID를 생성합니다.
즉, 제가 만든 설정 스크립트가 깨끗한 환경의 설치본에서는 소리 없이 실패한다는 의미였습니다.
해결책은 이를 동적으로 쿼리하는 것이었습니다.
function getOrgAndUser(container) {
const orgId = runSql(
container,
...
작은 변화였습니다.
하지만 엄청난 차이를 만들었습니다.
이것은 정확히 다른 사람이 당신의 프로젝트를 실행할 때만 나타나는 종류의 버그입니다.
과거의 나에게 해주고 싶은 말
- 모든 설정 스크립트를 완전히 새로운 환경의 머신에서 테스트하세요.
- 동적으로 생성될 수 있는 ID를 절대 하드코딩하지 마세요.
- "수행 (doing)"과 별개로 "사고 (thinking)" 과정을 계측 (instrument)하세요.
- 가능한 한 표준 OpenTelemetry 컨벤션을 사용하세요.
- 프로덕션급 AI 시스템에는 프로덕션급 관측성 (observability)이 필요합니다.
마치며
AXRAY는 하나의 단순한 아이디어로 시작되었습니다:
"우리 AI 에이전트에 로깅 (logging)을 좀 추가해 보자."
결국 그것은 훨씬 더 큰 무언가가 되었습니다.
AI 에이전트도 일반적인 프로덕션 백엔드 (production backend)와 똑같이 취급해야 한다는 강력한 근거:
- 모든 것을 추적(Trace)하라.
- 모든 것을 측정(Measure)하라.
- 모든 것에 알림(Alert)을 설정하라.
- 블랙박스 (black box)를 절대 신뢰하지 마라.
SigNoz의 OpenTelemetry 네이티브 아키텍처 (architecture)를 사용함으로써 우리는 자체적인 텔레메트리 (telemetry) 형식을 발명할 필요가 없었고, 그 결정은 우리가 예상했던 것보다 훨씬 더 큰 보상으로 돌아왔습니다.
만약 여러분이 LLM 추론 (reasoning)과 실제 시스템 실행 (execution)을 결합하는 AI 에이전트를 구축하고 있다면, 제가 가장 먼저 계측 (instrument)할 지표는 다음과 같습니다:
두뇌 체류 시간 (Time-in-Brain) vs 환경 체류 시간 (Time-in-Environment)
이를 추가하는 데는 비용이 거의 들지 않습니다.
그리고 에이전트가 느리게 느껴질 때 가장 중요한 단 하나의 질문에 즉각적인 답을 제공합니다:
생각 중이었나... 아니면 멈춰 있었나?
Built for
WeMakeDevs × SigNoz — Agents of SigNoz Hackathon
이 내용이 흥미로우셨다면, 여러분의 피드백을 기다리겠습니다!
🔗 Links
- GitHub: https://github.com/hussainjamal760/axray-signoz
- Live Demo: https://axray-signoz-web.vercel.app/
*읽어주셔서 감사합니다! *
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기