Agent를 관측하는 3가지 도구 살펴보기 ft. LLM Token

– Tool 권한 설계 시리즈, 마지막 편

지난 글에서 Agent에게 Tool을 어떻게 제한적으로 열어줬는지를 정리했다.

그 글 마지막에 이렇게 썼다.

“다음 글에서는 이렇게 설계한 멀티 에이전트 시스템을 실제로 운영하면서 겪은 모니터링과 디버깅 이야기를 정리해보려 한다.”

이번 글이 그 이야기다.

처음부터 모니터링을 제대로 짜둔 건 아니었다.

어느 날 token 사용량이 급격히 줄어든 걸 보고 나서야 깨달았다.

이유를 모르겠다. 비용이 줄어든 건 다행인데 왜 줄었는지를 모르니까 다행인지 위험인지도 판단이 안 됐다.

“내 Agent가 지금 뭘 하고 있는지 모르겠다.”

이 한 줄이 모든 시작이었다.


1. 첫 시도 — print() 로그

처음에는 print()로 시작했다.

Tool 호출 직전·직후에 prompt와 응답을 출력했고 잘못된 동작은 터미널 로그에서 잡아냈다.

작은 시스템에서는 충분했다.

문제는 흘러가는 로그에서 패턴이 안 잡힌다는 점이었다.

“어제는 비용이 얼마였지”, “Reader Agent가 평소보다 호출이 많은가” 같은 질문에 답을 못 하니까

운영이 손에 잡히지 않았다.

집계가 필요했다.

print()로는 안 되는 영역이었다.


2. 두 번째 — LangSmith

두 번째로 LangSmith를 붙였다.

트레이스를 잘 잡아주고 한 요청 안의 흐름은 깔끔하게 보였다.

문제는 본인이 정의한 단위로 쪼개기가 어렵다는 점이었다.

Reader / Writer / Orchestrator 같은 내 시스템의 Agent 단위로 자유롭게 집계하려면 한 번 더 후처리가 필요했다.

“어제 vs 오늘 같은 Agent의 평균 비용”이 한 화면에서 안 떴다.

트레이스 도구는 한 요청을 깊게 들여다보는 데는 좋다.

그치만 운영 전체 흐름을 한눈에 보기에는 한계가 있었다.


3. 세 번째 — 모델별 공식 대시보드

그 다음으로는 모델 공급자 대시보드를 매일 봤다.

OpenAI Usage. Anthropic Console.

비용은 정확하게 나왔고 모델별 토큰 사용량도 나왔다.

그런데 두 곳을 매일 둘 다 봐야 한다는 게 피로했다.

게다가 두 대시보드 모두 본인이 정의한 Agent 단위로는 쪼개주지 않는다.

“Reader Agent가 오늘 OpenAI에서 얼마, Anthropic에서 얼마를 썼는지”를 보려면 결국 직접 합쳐야 했다.

LangSmith든 공급자 대시보드든, 공통점은 하나였다.

내가 정의한 단위로 쪼개주지 않는다.


4. 결국 — DB에 직접 적재 하기 시작

결국 DB에 직접 적기 시작했다.

단일 테이블 하나로 시작했고 지금도 그 구조다.

class AgentEvent(Base):
    __tablename__ = "agent_events"

    id = Column(Integer, primary_key=True)
    request_id = Column(String, index=True)         # 한 요청에 속한 이벤트 묶기
    agent_id = Column(String, index=True)           # Reader / Writer / Orchestrator
    event_type = Column(String)                     # tool_call / llm_call / failure
    model = Column(String)
    payload = Column(JSON)                          # 가변 필드
    input_tokens = Column(Integer, nullable=True)
    output_tokens = Column(Integer, nullable=True)
    duration_ms = Column(Integer)
    status = Column(String)
    error = Column(Text, nullable=True)
    created_at = Column(DateTime, default=datetime.utcnow)

모든 LLM 호출, 모든 Tool 호출, 모든 실패가 한 테이블에 들어간다.

OpenAI든 Anthropic이든 같은 스키마.

복잡한 시각화는 처음에는 없어도 됐다. SQL 한 줄이면 보고 싶은 게 보였다.

-- Agent별 일일 비용 (Sonnet 4.6 기준 가격으로 환산)
SELECT
    DATE(created_at) AS day,
    agent_id,
    SUM(input_tokens)  AS in_tokens,
    SUM(output_tokens) AS out_tokens,
    SUM(input_tokens * 0.003 + output_tokens * 0.015) / 1000 AS cost_usd
FROM agent_events
WHERE event_type = 'llm_call'
GROUP BY day, agent_id
ORDER BY day DESC, cost_usd DESC;

이런 쿼리 몇 개로 LangSmith·공급자 대시보드에서 보지 못한 게 보이기 시작했다.


5. 그제야 보인 것: Claude가 생각보다 비싸다

가장 먼저 보인 건 모델별 비용 비중이었다.

Claude가 API 비용을 많이 잡아먹었다.

성능이 좋은 만큼 비싸다는 얘기는 알고 있었는데 막상 일자별 그래프로 보니까 체감 강도가 달랐다.

특히 output token 비용이 input의 5배라는 점이 크게 작용했다.

내 시스템에서는 Writer 역할(긴 응답 생성)을 Claude가 맡고 있었는데, 그게 비용 지배 요인이었다.

여기서 한 가지 선택이 생겼다.

  • Writer 역할을 더 저렴한 모델(Haiku 또는 GPT 계열)로 옮기거나
  • Claude는 Reader 역할(input 많고 output 짧은) 쪽으로 재배치하거나

이런 의사결정은 관측이 없으면 못 한다.

그저 “월말에 청구서 보고 놀라기”가 전부였을 거다.


6. 놓쳤던 것: 토큰값의 의미

지금 와서 가장 후회되는 건 토큰값(가격 구조)의 의미를 처음부터 확실히 알고 시작하지 않았다는 점이다.

LLM의 token은 단순히 “input × 가격 + output × 가격”이 아니다.

처음에는 그렇게만 생각했다.

실제로는 다섯 가지 종류가 있다.

  • Input tokens – 보낸 프롬프트 전체. Tool 정의도 여기에 포함된다 (자주 까먹는 함정)
  • Output tokens – 모델이 생성한 응답
  • Cached tokens – Anthropic 기준 cache_creation / cache_read (가격이 다르다)
  • Thinking tokens – Claude extended thinking에서 보이지 않게 발생
  • System prompt – 매 요청마다 반복되니까 누적 비용이 크다

이 다섯 가지를 처음부터 분리해서 추적했더라면

“왜 토큰이 갑자기 줄었지” 같은 질문에 더 빨리 답할 수 있었을 거다.

이 후회를 다음 글에서 적어보려 한다.

다음 글은 토큰값을 진짜로 이해하고 나서 시작한 프롬프트 캐싱 실험이다.


7. 지금 내가 관측하는 체크리스트

새 Tool이나 Agent를 시스템에 추가할 때 이 질문들을 던진다.

  1. 이 호출이 DB에 기록되는가? (기록되지 않는 호출은 결국 운영 사각지대가 된다.)
  2. Agent 단위로 쪼개져 있는가? (“전체 비용”으로만 보이면 어떤 Agent를 줄이고 어떤 Agent를 키울지 판단할 수 없다.)

  3. input·output·cached·thinking을 따로 기록하는가? (합쳐서 기록하면 어디서 비용이 새는지 못 본다.)

  4. 실패가 분류되는가?(단순히 “error”가 아니라 timeout / wrong_tool / wrong_args / api_error 같은 카테고리로.)
  5. 트레이스가 가능한가?(한 요청에 속한 모든 이벤트가 같은 request_id로 묶이는가.)

5개 질문 모두 “예”가 되어야 운영에 자신이 생긴다.


마무리하며

모니터링은 Agent 운영에서 가장 늦게 챙기게 되는 영역이다.

기능을 만드는 건 재미있고 모니터링은 지루하니까.

그런데 관측이 없으면 디버깅도 없다.

“내 Agent가 지금 뭘 하고 있는지”를 답하지 못하는 시스템은 운영이 아니라 실험이다.

Tool 권한을 세 가지 원칙으로 정리하고 나서 마지막에 추가된 네 번째 원칙이 있다.

“모든 호출은 기록된다.”

기록이 없으면 권한도 의미가 없다. 권한이 어떻게 쓰이고 있는지 모르니까.

이 시리즈는 여기서 일단 마무리한다.

세 가지 원칙 + 모니터링까지, 운영 시작점은 충분히 만들어졌다고 본다.

다음 글에서는 운영 비용을 줄이는 가장 강력한 도구 중 하나인 “프롬프트 캐싱” 을 실측해본 이야기를 정리한다.

댓글 남기기