입찰이 어려운 부분이 아니었습니다: SigNoz를 사용한 자율 RFP 입찰 에이전트의 계측 (Instrumentation)
요약
자율 RFP 입찰 에이전트인 Cognivern의 복잡한 결정 과정을 OpenTelemetry와 SigNoz를 통해 계측하고 관찰 가능성을 확보하는 방법을 다룹니다. 로그 검색의 어려움을 넘어 트레이싱을 통해 에이전트의 동작, 비용, 정책 준수 여부를 시각화하는 과정을 설명합니다.
핵심 포인트
- 단순 로깅을 넘어 OpenTelemetry 기반의 트레이싱 도입 필요성 강조
- SigNoz를 활용한 에이전트 결정 루프의 스팬 트리(Span Tree) 시각화
- 토큰 사용량, 액션 허용/거부, 지연 시간 등 핵심 메트릭 대시보드 구축
- 분산된 서비스 간의 결정 경로를 단일 Trace ID로 추적 가능
입찰이 어려운 부분이 아니었습니다: SigNoz를 사용한 자율 RFP 입찰 에이전트의 계측 (Instrumentation)
Cognivern는 기업의 RFP(제안 요청서)를 위해 Canton DevNet에서 밀봉 입찰(sealed-bid) 경매를 실행하는 자율 에이전트입니다. 매 주기마다 이 에이전트는 공개된 제안 요청서를 읽고, LLM(대규모 언어 모델)을 호출하며, 입찰가를 선택하고, 서명한 뒤, Daml 계약에 제출합니다. 이 과정에는 인간의 개입이 없습니다. 솔직히 말해서 입찰 로직을 구축하는 것은 쉬운 부분이었습니다. 진짜 어려운 부분은 그 이후에 훨씬 더 단순해 보이는 질문에 답하는 것이었습니다: 에이전트가 실제로 방금 무엇을 했는가, 해당 주기에 비용이 얼마나 들었는가, 그리고 정책 엔진(policy engine)이 올바른 이유로 이를 허용했는가?
SigNoz를 연결하기 전에는 에이전트를 "디버깅(debugging)"한다는 것이 네 개의 별도 서비스에 걸쳐 있는 콘솔 출력을 grep으로 검색하며 타임스탬프가 일치하기를 바라는 것을 의미했습니다. 그것은 관찰 가능성(observability)이 아니라 고고학입니다. 이 포스트는 에이전트가 내리는 모든 결정 앞에 OpenTelemetry와 SigNoz를 배치한 후 무엇이 변했는지에 관한 것입니다.
RFP 입찰 에이전트에 로그 이상의 것이 필요한 이유
Cognivern의 결정 루프는 단일 함수 호출이 아니라 하나의 체인입니다: 에이전트가 공개된 RFP를 읽고 입찰가를 예측하면, LLM 라우터가 모델을 선택하고 폴백(fallback) 기능을 포함하여 실행하며, 거버넌스 서비스(governance service)가 해당 동작이 허용되는지 평가하고, 감사 서비스(audit service)가 기록을 작성합니다. 이 각각의 요소는 스택의 서로 다른 부분에 존재합니다. 입찰 거부, 느린 LLM 호출, 예상치 못한 비용 급증 등 문제가 발생했을 때, 저는 살펴볼 수 있는 단일 지점이 없었습니다. 저에게는 네 개의 로그와 두통뿐이었습니다.
제가 실제로 필요했던 것은 단 하나의 질문에 즉각적으로 답하는 것이었습니다: 이 하나의 결정에 대해, 타이밍과 비용이 부착된 상태로 발생한 모든 일을 순서대로 보여달라. 이것은 로깅(logging)의 문제가 아니라 트레이싱(tracing)의 문제이며, 바로 OpenTelemetry와 SigNoz가 만들어진 목적입니다.
OpenTelemetry를 한 번에 모든 곳에 연결하기
이제 단일 결정 주기에 대한 스팬 트리(span tree)는 다음과 같이 보입니다:
agent.sapience.forecast_cycle 842ms
└─ llm.execute_with_fallback 388ms
└─ governance.evaluate_decision 112ms
...
하나의 Trace ID. 네 개의 Span. "에이전트가 입찰을 예측하기로 결정함"에서부터 "감사 로그에 영구적인 기록이 남음"까지의 전체 경로를 이제는 기억을 되살리거나 grep으로 뒤져서 재구성할 필요 없이, 클릭 한 번으로 확인할 수 있게 되었습니다.
트레이스(Traces)를 사람이 실제로 확인하는 대시보드로 전환하기
트레이스(Traces)는 어떤 결정을 조사해야 할지 이미 알고 있을 때는 매우 유용합니다. 하지만 대부분의 경우 여러분은 무엇을 조사해야 할지 모릅니다. 대신 화면을 슥 훑어보는 것만으로 무언가 잘못되었는지 알고 싶어 하죠. 그래서 트레이싱(tracing)과 병행하여 토큰 사용량(token spend), 허용/거부된 액션 수(allowed/denied action counts), 그리고 제공자 지연 시간(provider latency)에 대한 OTel 메트릭(metrics)을 생성하고, 이를 기반으로 세 가지 네이티브 SigNoz 대시보드를 구축했습니다.
- 거버넌스 개요 (Governance Overview) — 얼마나 많은 액션이 허용되었고 거부되었는지, 그리고 그 이유는 무엇인지
- 토큰 비용 (Token Cost) — 사이클당 비용을 표시하여, LLM 루프가 폭주할 경우 다음 달 청구서를 받기 전에 즉시 확인할 수 있도록 함
- 제공자 상태 (Provider Health) — LLM 제공자 체인의 지연 시간(latency) 및 폴백(fallback) 동작
이 세 가지 모두 Cognivern 웹 앱의 /observability 페이지에 직접 임베딩되어 있어, 운영자가 에이전트의 상태를 확인하기 위해 별도의 SigNoz 로그인이 필요하지 않습니다. 즉, 제품의 일부로 통합되어 있습니다. 또한 SigNoz는 딥링크(deep-linking)를 지원하기 때문에, 대시보드에서 거버넌스 이벤트를 클릭하면 해당 이벤트를 생성한 정확한 트레이스로 바로 연결되며, 전체 LLM 컨텍스트(context), 정책 추론(policy reasoning), 그리고 서명된 페이로드(signed payload)가 그 자리에 모두 놓여 있게 됩니다.
마지막 부분은 제가 예상했던 것보다 더 중요했습니다. 감사(audit) 서비스는 모든 로그 기록과 함께 traceId를 저장합니다. 이는 감사 항목이 단순히 "액션 X가 시간 Y에 허용됨"이라는 정보에 그치지 않고, 왜 그런 일이 일어났는지를 설명하는 정확한 트레이스로 돌아가는 영구적인 포인터(pointer)가 된다는 것을 의미합니다. 자율적이고 서명된 온체인(on-chain) 결정을 내리는 시스템에서, 사후에 "왜 그렇게 했는가"에 대해 답할 수 있는 능력은 있으면 좋은 기능(nice-to-have)이 아니라, 시스템의 존재 이유 그 자체입니다.
현재 레저(ledger)에 실제로 올라와 있는 것들
이 중 어느 것도 가짜 데이터를 대상으로 실행되는 시뮬레이션이 아닙니다. 이 글을 쓰는 동안 라이브 VPS를 확인해 보니, Canton에 서로 다른 상태를 가진 세 개의 RFP 라운드가 놓여 있습니다:
- 보안 감사 RFP (security audit RFP) — 아직 진행 중, 현재까지 두 개의 입찰 완료
- 2026년 법률 고문 계약 (legal-counsel retainer for 2026) — 입찰 결과 공개,
bob-cognivern::1220…가 185,000의 낙찰가로 낙찰 - 클라우드 인프라 3년 약정 (cloud-infrastructure 3-year commit) — 진행 중, 한 개의 입찰 완료
세 가지 모두 backend: canton을 사용합니다. 이는 모의 데모 화면이 아니라 Daml 상에서 진행되는 실제 밀봉 입찰 (sealed-bid) 라운드입니다. 이것이 바로 트레이스 (trace)/대시보드 설정이 실제로 관찰하고 있는 대상입니다. 즉, 실제 상태를 가진 실제 RFP와 실제 낙찰자들을 보고 있는 것입니다.
처음에 내가 틀렸던 점
나의 첫 번째 본능은 트레이싱 (tracing)을 무언가 고장 났을 때 덧붙이는 것으로 취급하는 것이었습니다. 에이전트 시스템에서는 그것이 반대로 된 생각입니다. 무언가 고장 났을 때쯤이면, 흥미로운 컨텍스트 (어떤 모델이 응답했는지, 정책 엔진 (policy engine)이 실제로 무엇을 평가했는지, 입찰 금액이 얼마였는지 등)는 대개 어딘가 터미널에서 스크롤되어 사라진 상태이기 때문입니다. audit.log_action과 같은 "지루한" 스팬 (span)들을 포함하여 모든 스팬을 사전에 계측 (instrumenting)한 것이 사후 디버깅 (post-hoc debugging)을 가능하게 만든 핵심이었습니다. 만약 내가 LLM 호출만 트레이싱했다면, 비용에 대한 가시성은 확보했겠지만 거버넌스 (governance)에 대한 이야기는 얻지 못했을 것입니다.
내가 과소평가했던 또 다른 점은, 첫날부터 공유된 traceId를 설정하지 않았을 때 치러야 하는 대가입니다. 네 개의 서비스에 걸쳐 하나의 설정 파일을 소급 적용하는 것은 간단했습니다. 하지만 서비스들이 로그를 남기는 방식이 이미 서로 달라진 후에 이를 소급 적용하는 것은 결코 간단하지 않았을 것입니다.
교훈 (Takeaways)
- 서비스 전반에 걸친 단일 공유 트레이스 ID (trace ID)는 초기에 추가할 수 있는 가장 레버리지가 높은 요소입니다. 이것이 네 개의 분리된 로그를 하나의 이야기로 바꿔줍니다.
- 메트릭 (Metrics) 대시보드는 "지금 무언가 잘못되었는가"에 답하고, 트레이스 (traces)는 "왜 이 특정 일이 발생했는가"에 답합니다. 당신은 이 두 가지가 모두 필요하며, 이들이 서로 연결되어 있기를 원할 것입니다.
- 실제 결정을 내리는 자율 에이전트에게 감사 추적 (audit trail)은 그 뒤에 있는 정확한 트레이스를 가리킬 수 있는 능력만큼만 가치가 있습니다. 단순히 "승인됨"이라고만 적힌 기록은 감사 추적이 아니라, 단순한 승인 도장 (rubber stamp)에 불과합니다.
- 에이전트 스택과 함께 SigNoz를 셀프 호스팅 (Self-hosting)함으로써, LLM 프롬프트 (prompts)와 서명된 페이로드 컨텍스트 (signed payload context)를 포함한 이 모든 텔레메트리 (telemetry) 데이터가 내가 제어하는 인프라를 벗어나지 않도록 할 수 있었습니다.
마무리
입찰 로직이 Cognivern을 경매에 참여시켰습니다. OpenTelemetry와 SigNoz는 일단 참여한 이후에 시스템이 실제로 무엇을 하고 있는지 신뢰할 수 있게 해준 핵심 요소였습니다. 하나의 트레이스 ID (trace ID), 서로 연관된 네 개의 스팬 (spans), 그리고 제가 직접 재구성할 필요 없이 전체 상황을 설명해 주는 대시보드까지 갖추게 되었습니다. 만약 실질적인 결과(consequences)를 초래하는 자율적인 결정을 내리는 무언가를 구축하고 있다면, 문제가 발생한 후가 아니라 발생하기 전에 반드시 계측 (instrumentation)을 해두시기 바랍니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기