이번 주부터 에이전트 운영 이야기예요. 저희 Agent Ops 제품의 바탕이 되는 기술인데, 첫 글은 제일 기본인 트레이싱이에요. 에이전트가 이상한 답을 냈을 때 "왜?"를 알 수 있어야 고칠 수 있잖아요.
로그로는 부족했어요
처음엔 그냥 로그를 찍었어요. 프롬프트, 응답, 걸린 시간. 그런데 에이전트는 한 요청에 LLM을 여러 번 부르고, 중간에 도구를 호출하고, 그 결과로 또 LLM을 불러요. 로그 수십 줄 중에 어떤 게 어떤 요청에 속하는지, 어떤 순서로 일어났는지 다시 맞추는 게 일이었어요.
이건 분산 추적(distributed tracing)이 푸는 문제랑 똑같아요. 그래서 바퀴를 새로 만들지 않고 OpenTelemetry를 썼어요.
스팬 구조
OpenTelemetry에서 트레이스는 스팬(span)의 트리예요. 저희는 에이전트 실행을 이렇게 매핑했어요.
agent.run (사용자 요청 하나)
├── llm.chat (계획 세우기)
├── tool.call (문서 검색)
│ └── http.request
├── llm.chat (검색 결과로 답 만들기)
└── tool.call (결과 전송)
루트 스팬 하나에 사용자 요청이 대응되고, 그 아래에 LLM 호출과 도구 호출이 자식으로 붙어요. 도구 안에서 또 외부 호출이 있으면 손자가 되고요.
LLM 스팬에 뭘 넣냐면요
OpenTelemetry에는 생성형 AI용 시맨틱 컨벤션(gen_ai.*)이 정리되고 있어요. 저희는 그 속성 이름을 따르되 몇 개 더 붙여요.
| 속성 | 예 |
|---|---|
gen_ai.system |
anthropic, openai, vllm |
gen_ai.request.model |
모델 이름 |
gen_ai.usage.input_tokens / output_tokens |
토큰 수 |
gen_ai.request.temperature 등 |
생성 파라미터 |
makeai.prompt_version |
프롬프트 템플릿 버전 (이게 중요해요) |
makeai.cost_usd |
토큰 × 단가 |
makeai.cache_hit |
프롬프트 캐시 적중 여부 |
프롬프트 버전을 속성으로 넣는 게 핵심이에요. 다음 주에 쓸 회귀 평가가 이 속성으로 "버전 A와 B의 품질 차이"를 뽑거든요.
입력과 출력은 어디에 두나
프롬프트 전문과 응답 전문은 스팬 속성에 넣기엔 너무 커요. 그리고 문서 내용이 들어 있어서 폐쇄망 밖으로 보내면 안 돼요. 저희는 이렇게 해요.
- 스팬 속성에는 해시와 길이, 앞 200자만 넣어요.
- 전문은 별도 저장소(고객 환경 안)에 스팬 ID를 키로 저장해요.
- 콘솔에서 스팬을 클릭하면 그 저장소에서 전문을 가져와 보여 줘요.
이렇게 하면 트레이스 자체는 가벼워서 오래 보관할 수 있고, 전문은 보존 기간을 따로 짧게 둘 수 있어요.
도구 호출 스팬
도구 호출은 tool.call 스팬이에요. 도구 이름, 인자(마찬가지로 요약), 결과 크기, 성공 여부를 넣어요. 중요한 건 LLM이 도구를 부르기로 결정한 스팬과 실제 실행 스팬을 연결하는 거예요. 모델이 도구를 두 번 부르려 했는데 한 번만 실행된 경우 같은 걸 찾을 수 있어요.
샘플링은 하지 마세요 (처음엔)
일반 웹 서비스 트레이싱은 1%만 샘플링하고 버려요. 에이전트는 달라요. 호출 수가 많지 않고, 문제는 항상 안 본 요청에서 나와요. 저희는 전부 기록하고 보존 기간으로 조절해요. 트래픽이 정말 커지면 "오류 난 것, 느린 것, 비용 큰 것"은 무조건 남기고 나머지만 샘플링하는 꼬리 샘플링(tail sampling)으로 가요.
실제로 잡은 것들
트레이싱을 넣고 나서 발견한 것들이에요.
- 어떤 요청이 LLM을 같은 프롬프트로 세 번 부르고 있었어요. 재시도 로직 버그였어요. 비용 1/3 절감.
- 도구 호출 중 하나가 평균 4초였는데 전체 지연의 70%였어요. 모델이 느린 줄 알았는데 아니었어요.
- 특정 프롬프트 버전에서 출력 토큰이 두 배로 늘어 있었어요. "자세히 설명하라"는 한 줄이 원인이었어요.
이게 다 로그로는 못 찾던 것들이에요.
시작하는 분께
OpenTelemetry SDK를 쓰시면 수집기(collector)와 백엔드는 뭐든 붙일 수 있어요. 저희 Agent Ops도 표준 OTLP를 받아서, 이미 다른 도구를 쓰고 계셔도 같은 데이터를 보낼 수 있어요. 벤더 종속 없이 시작하실 수 있다는 게 OpenTelemetry를 고른 이유예요.
다음 주는 이 트레이스에서 프롬프트 변경이 품질을 깨뜨렸는지 아는 방법이에요.