
SigNoz를 사용하여 폭주하는 AI 에이전트 루프를 포착하고 제어한 방법
요약
AI 에이전트 간의 무한 루프 발생 문제를 SigNoz와 Taraol을 활용해 관측하고 제어하는 방법을 다룹니다. OpenTelemetry를 기반으로 에이전트의 워크플로 상태를 시각화하여 비용 폭증과 같은 실패 사례를 해결하는 과정을 설명합니다.
핵심 포인트
- 에이전트 간의 잘못된 워크플로 상태로 인한 무한 루프 및 비용 증가 문제 식별
- Taraol을 통한 에이전트, LLM 호출, 도구에 대한 OpenTelemetry 인스트루멘테이션 적용
- 토큰 사용량, 모델 비용, 콘텐츠 캡처 등 에이전트 전용 메트릭 관측 가능
- 실험 ID와 변형(variant)을 활용한 다양한 시나리오별 워크플로우 비교 분석
최종 HTTP 응답만 확인했을 때 우리의 여행 계획 데모는 정상적으로 보였습니다. 요청은 완료되었고, 모델은 유효한 텍스트를 반환했으며, 어떤 서비스도 충돌하지 않았습니다.
하지만 일정 작성자(itinerary writer)와 예산 비평가(budget critic)는 1,630달러짜리 여행을 800달러 예산에 맞추려고 시도하며 갇혀 있었습니다. 작성자는 계획을 수정했고, 비평가는 이를 거부했으며, 이 사이클이 반복되었습니다. 한 번의 실행에서 해당 경로는 1,012개의 출력 토큰(output tokens)을 생성하여 0.0028달러의 비용이 발생했는데, 이는 성공적인 버전의 633개 토큰과 0.0017달러 비용과 비교됩니다.
실패 원인은 잘못된 모델 응답이 아니었습니다. 그것은 잘못된 워크플로 상태(workflow state)였습니다.
우리는 이러한 실패를 가시화하고 조치 가능하게 만들기 위해 Agents of SigNoz 해커톤 기간 동안 Taraol을 구축했습니다. Taraol은 OpenTelemetry 인스트루멘테이션(instrumentation)과 에이전트 전용 런타임 제어 기능을 추가하며, SigNoz는 우리가 트레이스(traces)를 검사하고, 로그를 쿼리하며, 실험을 비교하고, 대시보드를 구축하며, 알림을 트리거하는 관측성(observability) 백엔드로 남습니다.
이 포스트는 이 프로젝트를 형성하게 된 정확한 실패 사례를 살펴봅니다.
워크플로 (The workflow)
데모에는 네 가지 역할이 포함되어 있습니다:
- 명소와 리뷰를 수집하는 스카우트(scout);
- 계획을 생성하는 일정 작성자(itinerary writer);
- 여행 비용을 계산하는 예산 비평가(budget critic);
- 승인된 일정을 확정하는 예약자(booker).
우리는 인스트루멘테이션(instrumentation)이 애플리케이션 코드와 가깝게 유지되기를 원했으므로, Taraol은 우리가 중요하게 생각하는 작업인 에이전트(agents), LLM 호출(LLM calls), 도구(tools)에 대해 세 가지 데코레이터(decorators)를 노출합니다.
from taraol import agent, chat, instrument, tool
instrument("trip-planner", capture_content=True)
...
각 데코레이터는 OpenTelemetry 스팬(span)을 생성합니다. 채팅 스팬(chat span)은 또한 지원되는 SDK 응답 형식에서 모델 이름, 입력 토큰(input tokens), 출력 토큰(output tokens), 종료 사유(finish reason), 그리고 추정되는 직접적인 모델 비용을 추출하려고 시도합니다.
이 데모에서는 정확한 수정 프롬프트(revision prompt)를 검사할 수 있도록 콘텐츠 캡처(content capture)를 활성화했습니다. 콘텐츠 캡처는 기본적으로 꺼져 있으며, 그것이 더 안전한 설정입니다. 현재 구현에서는 캡처된 값을 잘라내지만(truncate), 완전한 비밀 정보나 개인 데이터의 비식별화(redaction)를 수행하지는 않습니다.
세 가지 조건 하에서 동일한 워크플로우 비교
단 한 번의 정상적인 실행만으로는 시스템이 견고한지 판단하기에 충분하지 않았습니다. 그래서 우리는 동일한 워크플로우의 세 가지 변형(variant)을 만들었습니다:
comfort: 깨끗한 리뷰 데이터와 $2,500의 예산;shoestring: 깨끗한 리뷰 데이터와 $800의 예산;poisoned: 리뷰 텍스트 내부에 주입된 지시문(instruction)이 포함된 $2,500의 예산.
AgentLab은 모든 자식 스팬(child span)에 실험 ID(experiment ID), 변형(variant), 실행 ID(run ID)를 부착합니다:
from taraol import Experiment
result = (
...
변형들은 순차적으로 실행됩니다. 만약 하나가 실패하더라도, AgentLab은 전체 실험을 중단하는 대신 실패를 기록하고 남은 변형들을 계속 진행합니다.
comfort 케이스는 완료됩니다. poisoned 케이스는 정찰병(scout)이 검색된 리뷰 텍스트를 스캔할 때 거부됩니다. shoestring 케이스는 개별 모델 응답이 모두 유효하기 때문에 훨씬 더 미묘합니다.
데모에서는 결정론적(deterministic) 가격을 사용합니다:
Flight: $650
Hotel: $120 x 4 nights
Food: $55 x 5 days
...
워크플로우가 가정을 변경하지 않는 한, 아무리 다시 작성하더라도 $800의 예산을 맞출 수 없습니다. 작성자(writer)는 이 사실을 알지 못합니다. 작성자는 비판을 받고 다시 시도합니다.
SigNoz 내부에서 우리는 쿼리 빌더(Query Builder)를 사용하여 experiment.variant별로 그룹화된 P95 지연 시간(latency), 직접적인 LLM 비용, 그리고 출력 토큰(output tokens)을 비교했습니다.
그림 1. SigNoz에서의 AgentLab 결과. 이 실행에서 Shoestring 변형은 Comfort 변형보다 더 많은 시간, 직접적인 모델 비용 및 출력 토큰을 소비했습니다.
이 대시보드만으로도 비정상적인 변형을 식별하기에는 충분했습니다. 하지만 원인을 설명하기에는 부족했습니다.
대시보드가 설명하지 못한 것을 트레이스(trace)가 설명하다
우리는 트레이스 익스플로러(Trace Explorer)를 열고 다음과 같이 필터링했습니다:
experiment.variant = shoestring
가장 긴 트레이스는 동일한 작업 쌍이 반복되는 것을 보여주었습니다:
itinerary-writer -> budget-critic
itinerary-writer -> budget-critic
itinerary-writer -> budget-critic
해당 패턴은 대시보드에서 나타난 세 가지 증상을 연결했습니다:
- 워크플로우(workflow)가 계속해서 수정(revising)을 반복했기 때문에 지연 시간(latency)이 증가했습니다.
- 모든 수정 단계마다 추가적인 생성(generation)이 필요했기 때문에 출력 토큰(output tokens)이 증가했습니다.
- 모든 수정 단계가 또 다른 모델 호출(model call)이었기 때문에 직접 비용(direct cost)이 증가했습니다.
이것은 우리가 Taraol을 구축하면서 배운 가장 유용한 차이점이었습니다. 대시보드는 어떤 실행(run)이 비정상적인지를 알려주는 반면, 트레이스(trace)는 왜 비정상적이 되었는지를 알려줍니다.
데모가 무한히 실행되는 것을 방지하기 위해, 우리는 엣지(edge)별 회로 차단기(circuit breaker)를 추가했습니다:
from taraol.breaker import BreakerConfig, EdgeBreakerRegistry, edge_key
EDGE = edge_key("itinerary-writer", "budget-critic")
...
세 번의 수정이 거부된 후, 다음 탐색(traversal)은 차단됩니다. 워크플로우는 토큰을 계속 소비하며 지속되는 대신, 눈에 보이는 제한된 실패(bounded failure)로 종료됩니다.
이 차단기는 애플리케이션 전체가 아닌 의도적으로 엣지(edge)에 연결되어 있습니다. 작성자(writer)와 비평가(critic) 사이의 문제가 관련 없는 에이전트들을 자동으로 비활성화해서는 안 되기 때문입니다.
동일한 아이디어를 분산 메시(distributed mesh)로 확장하기
단일 프로세스 데모는 차단기 상태를 로컬에 유지할 수 있습니다. 하지만 실제 멀티 에이전트 시스템(multi-agent system)은 개별 서비스가 전체 경로를 볼 수 없기 때문에 더 어렵습니다.
우리의 두 번째 데모는 다섯 개의 서비스를 별도의 컨테이너에서 실행합니다:
planner -> researcher -> writer -> critic -> router
Taraol은 각 에이전트 간 요청(agent-to-agent request)에 W3C 트레이스 컨텍스트(trace context)를 주입하고, 이를 수신 서비스에서 추출합니다. 그러면 SigNoz는 하나의 분산 트레이스(distributed trace)를 재구성하고 관찰된 호출로부터 서비스 맵(Service Map)을 도출할 수 있습니다. 우리는 별도의 토폴로지 데이터베이스(topology database)나 시각화 레이어(visualization layer)를 구축하지 않았습니다.
우리는 Foundry를 사용하여 관측성 스택(observability stack)을 설치했으며, 이를 재현하는 데 필요한 파일들을 커밋했습니다:
casting.yaml
casting.yaml.lock
렌더링된 배포(deployment)는 다음 명령어로 다시 생성할 수 있습니다:
cd src/taraol/data/signoz
foundryctl cast -f casting.yaml
로컬 개발을 위해, Taraol은 커밋된 Compose 출력을 시작하는 바로가기도 제공합니다:
taraol signoz up
이 스택에는 SigNoz MCP 서버가 포함되어 있습니다. Taraol은 별도의 텔레메트리 저장소 (telemetry store)를 도입하는 대신, 트레이스 설명 (trace explanation) 및 재생 (replay)을 위한 읽기 전용 경로로 이를 사용합니다.
탐지에서 강제 집행까지 (From detection to enforcement)
서킷 브레이커 (circuit breaker)는 프로세스가 어떤 엣지 (edge)가 실패하고 있는지 이미 알고 있을 때 작동합니다. 분산 루프 (distributed loop)는 여러 서비스에 걸쳐 있을 수 있으므로, 우리는 최근의 SigNoz 텔레메트리 (telemetry)를 쿼리하는 Watcher를 추가했습니다.
Watcher는 에이전트 간 스팬 (agent-to-agent spans)을 트레이스 (trace) 및 엣지 (edge)별로 그룹화하고, 반복적인 트래버설 (traversal)을 탐색하며, 설정된 임계값 (threshold)에 도달하면 구조화된 loop_detected 로그를 생성합니다.
이것이 에이전트를 직접 일시 중단시키지는 않습니다. 우리는 운영 정책을 SigNoz에 유지했습니다:
loop_detected 로그
-> SigNoz 로그 알림 (log alert)
-> 컨트롤러 웹훅 (Controller webhook)
...
이러한 분리가 중요합니다. Watcher는 텔레메트리가 의심스럽다고 결정합니다. SigNoz는 해당 조건이 운영 응답을 트리거해야 하는지 결정합니다. 컨트롤러 (Controller)는 설정된 동작을 수행합니다. 결과적으로 발생하는 agent_paused 또는 edge_broken 이벤트는 SigNoz로 다시 내보내져 개입 과정이 관찰 가능 (observable)하게 유지됩니다.
우리는 탐지기 내부에 강제 집행 (enforcement) 로직을 숨기는 대신 이 설계를 선호했습니다. 이 방식은 운영자에게 가시적인 쿼리 (query), 알림 규칙 (alert rule), 알림 경로 (notification path), 그리고 감사 추적 (audit trail)을 제공합니다.
잘 작동한 점과 변경하고 싶은 점
해커톤 동안 세 가지 결정이 효과적이었습니다:
- 실험 속성 (experiment attributes)이 혼합된 결과를 방지했습니다.
experiment.id,experiment.variant,experiment.run_id가 없었다면 반복되는 워크플로 (workflow) 실행을 신뢰성 있게 비교하기 어려웠을 것입니다. - 표준 트레이스 전파 (trace propagation)만으로 충분했습니다. W3C 컨텍스트 (context) 덕분에 커스텀 트레이싱 백엔드 (tracing backend) 없이도 SigNoz가 서비스 간 워크플로를 재구성할 수 있었습니다.
- 알림 (Alerts)이 유용한 정책 경계를 형성했습니다. 탐지와 강제 집행이 분리되어 가시적이고 설정 가능하게 유지되었습니다.
현재 구현에는 다음과 같은 한계도 있습니다:
- 일시 중지(pause) 및 차단기(breaker) 상태가 메모리에 저장되므로, 프로세스를 재시작하면 초기화됩니다.
- 프롬프트 인젝션(prompt-injection) 탐지기가 정규 표현식(regex) 기반이므로, 이를 완전한 보안 계층으로 간주해서는 안 됩니다.
- SigNoz 알림 채널(notification-channel) 설정이 여전히 부분적으로 수동입니다.
- 콘텐츠 캡처(content capture)는 실제 운영 환경(production)에서 사용하기 전에 더 엄격한 비식별화(redaction) 작업이 필요합니다.
- Watcher에서 알림(alert)을 거쳐 컨트롤러(Controller)로 이어지는 전체 경로에 대해 더 강력한 엔드 투 엔드(end-to-end) 테스트가 필요합니다.
이것들이 다음 엔지니어링 과제입니다. 또한 우리가 Taraol을 완성된 운영용 안전 플랫폼이 아닌, 작동 가능한 해커톤 시스템으로 설명하는 이유이기도 합니다.
결론
유용한 결과물은 단순히 또 하나의 AI 대시보드가 아니었습니다. 그것은 다음과 같은 폐쇄적이고 관찰 가능한 경로였습니다:
계측 (instrumentation) -> 실험 (experiment) -> 트레이스 (trace) -> 탐지 (detection) -> 알림 (alert) -> 강제 집행 (enforcement) -> 감사 (audit)
대시보드는 Shoestring 실행이 건강하지 않음을 보여주었습니다. 트레이스는 writer-critic 루프를 보여주었습니다. 차단기(breaker)는 국소적인 실패를 제한했으며, SigNoz 알림 경로는 분산된 에이전트 전반에 걸쳐 동일한 제어 모델을 적용할 수 있는 방법을 제공했습니다.
소스 코드와 재현 가능한 Foundry 설정은 Taraol 리포지토리에서 확인할 수 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기