ZeroClaw 멀티 에이전트 환경을 위한 고정밀 비용 모니터링 구축하기
안녥하세요. 기술 블로그를 운영하고 있는 개발자입니다.
최근 ZeroClaw 프로젝트와 같은 고성능 멀티 에이전트 런타임을 구축하면서 가장 큰 골칫거리 중 하나는 바로 ‘예기치 못한 클라우드 비용’입니다. LLM을 호출하는 에이전트들이 동시다발적으로 돌아가다 보면, 청구서가 도착했을 때 그 액수에 놀라는 경우가 많습니다.
특히 최근 Hacker News에서 Infracost가 “에이전트에게 클라우드 비용 인지 기능을 부여하겠다"며 Senior Dev Advocate를 채용한다는 소식을 접하고, 저 또한 내 시스템에 비용 최적화를 위한 피드백 루프를 직접 구현해보기로 결심했습니다.
이번 포스트에서는 Rust 기반의 ZeroClaw 아키텍처 내에서, 에이전트의 활동을 실시간으로 추적하고 예상 비용을 계산하여 사용자에게 알려주는 시스템을 구축한 과정을 공유합니다.
문제 정의: 보이지 않는 리소스의 소모
기존의 [blog-api-server] 모니터링 대시보드에서는 주로 ‘상태’에 집중했습니다. 에이전트가 죽지 않았는지, API가 응답하는지 등이었습니다. 하지만 다음과 같은 질문에는 답할 수 없었습니다.
- “이번 팀 회의록 생성 작업에 든 실제 토큰 비용은 얼마인가?”
- “현재 돌아가는 5개의 하위 에이전트가 시간당 얼마의 리소스를 소모하고 있는가?”
단순히 로그를 남기는 것으로는 부족했습니다. 우리는 비용을 ‘코드’로 관리하고, 에이전트가 스스로 비용을 의식하도록 만들어야 했습니다.
솔루션 아키텍처

ZeroClaw의 메인 루프에 Cost Tracker 모듈을 추가하고, 각 에이전트의 통신 프로토콜 계층에서 토큰 사용량을 가로채기로 했습니다.
- Agent Layer: 각 에이전트는 메시지를 주고받을 때마다 토큰 사용량을 보고합니다.
- Cost Estimator: 토큰 수와 모델 단가(Input/Output)를 기반으로 실시간 비용을 계산합니다.
- Reporter: 일정 임계값(Threshold)을 초과하거나 작업이 완료되었을 때 Slack/Discord 등으로 리포트를 발송합니다.
Rust로 구현하는 토큰 메트릭 수집기
ZeroClaw는 Rust로 작성되었으므로, 비용 계산 로직 또한 타입 안전성과 고성능을 보장해야 합니다. 먼저, 각 모델의 가격 정보를 정의하는 구조체를 만들어 보겠습니다.
// src/cost/model.rs
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelPricing {
pub input_price_per_1k: f64, // USD
pub output_price_per_1k: f64, // USD
}
pub struct PricingRegistry {
prices: HashMap<String, ModelPricing>,
}
impl PricingRegistry {
pub fn new() -> Self {
let mut prices = HashMap::new();
// 예시: GPT-4o 가격 (2024년 기준)
prices.insert("gpt-4o".to_string(), ModelPricing {
input_price_per_1k: 2.50,
output_price_per_1k: 10.00,
});
// 예시: Claude 3.5 Sonnet 가격
prices.insert("claude-3-5-sonnet".to_string(), ModelPricing {
input_price_per_1k: 3.00,
output_price_per_1k: 15.00,
});
Self { prices }
}
pub fn get(&self, model: &str) -> Option<&ModelPricing> {
self.prices.get(model)
}
}
이제 실제로 에이전트가 LLM API를 호출하고 나서, 그 결과를 바탕으로 비용을 계산하는 로직을 구현합니다. 이 로직은 MCP(Model Context Protocol) 브리지나 내부 통신 래퍼(Wrapper)에서 호출됩니다.
// src/cost/calculator.rs
use super::model::PricingRegistry;
#[derive(Debug, Clone)]
pub struct UsageStats {
pub model: String,
pub input_tokens: u32,
pub output_tokens: u32,
}
pub struct CostCalculator {
registry: PricingRegistry,
}
impl CostCalculator {
pub fn new(registry: PricingRegistry) -> Self {
Self { registry }
}
pub fn calculate(&self, usage: &UsageStats) -> f64 {
if let Some(pricing) = self.registry.get(&usage.model) {
let input_cost = (usage.input_tokens as f64 / 1000.0) * pricing.input_price_per_1k;
let output_cost = (usage.output_tokens as f64 / 1000.0) * pricing.output_price_per_1k;
input_cost + output_cost
} else {
0.0
}
}
}
에이전트 통신 프로토콜과의 통합
ZeroClaw의 에이전트들은 파일 기반 아키텍처나 IPC를 통해 통신합니다. 이 과정에서 ‘비용’ 정보도 함께 전달되도록 메시지 구조를 확장해야 합니다.
기존 통신 패킷에 cost 필드를 추가하는 방식이 가장 깔끔합니다.
// src/agent/message.rs
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct AgentMessage {
pub id: String,
pub content: String,
pub timestamp: i64,
// 추가된 비용 필드
#[serde(skip_serializing_if = "Option::is_none")]
pub cost_incurred: Option<f64>,
}
impl AgentMessage {
pub fn with_cost(mut self, cost: f64) -> Self {
self.cost_incurred = Some(cost);
self
}
}
이제 메인 에이전트 루프에서 메시지를 처리할 때, 이 비용을 누적하여 전체 세션의 비용을 추적할 수 있습니다.
// src/agent/executor.rs
use crate::agent::message::AgentMessage;
use crate::cost::{CostCalculator, UsageStats};
use std::sync::Arc;
use tokio::sync::RwLock;
pub struct AgentRuntime {
calculator: CostCalculator,
total_cost: Arc<RwLock<f64>>,
}
impl AgentRuntime {
pub async fn process_llm_request(&self, model: &str, prompt: &str) -> Result<String, String> {
// 1. LLM API 호출 (가상의 함수)
let response = call_llm_api(model, prompt).await?;
// 2. 사용량 메트릭 추출 (API 응답 헤더나 바디에서 파싱)
let usage = UsageStats {
model: model.to_string(),
input_tokens: response.usage.input_tokens,
output_tokens: response.usage.output_tokens,
};
// 3. 비용 계산
let cost = self.calculator.calculate(&usage);
// 4. 전체 비용 업데이트
let mut total = self.total_cost.write().await;
*total += cost;
drop(total);
// 5. 로그 출력 (모니터링 대시보드로 전송 가능)
println!("[Cost Monitor] Model: {}, Input: {}, Output: {} | Cost: ${:.6}",
model, usage.input_tokens, usage.output_tokens, cost);
Ok(response.text)
}
}
실제 적용 및 효과
이 시스템을 적용한 후, [Discord Decision MCP] 아키텍처에서 의사결정을 내릴 때 소요되는 비용을 실시간으로 확인할 수 있게 되었습니다.
- 사례 1: 긴 문서 요약 작업 시, 모델을 GPT-4o에서 Claude 3.5 Haiku로 변경하여 비용을 약 70% 절감했습니다.
- 사례 2: 특정 에이전트가 무한 루프에 빛을 때 비용이 급격히 상승하는 것을 감지하고, 자동으로
kill_switch를 동작시키는 안전장치를 마련했습니다.
결론
Infracost가 인프라 코드를 검사하는 것처럼, 에이전트 런타임 수준에서도 비용에 대한 인지 기능(Cost Awareness)은 필수적입니다. Rust의 안전한 타입 시스템과 간결한 코드로, ZeroClaw 플랫폼 자체에 비용 최적화 계층을 통합함으로써 더 효율적인 멀티 에이전트 시스템을 운영할 수 있게 되었습니다.
다음 포스트에서는 이렇게 수집된 비용 데이터를 시각화하는 대시보드 구축기(Grafana + Prometheus)를 다루어 보겠습니다.
감사합니다.
참고 링크: