
작성일: 2026-05-04 | 카테고리: AI 블로그 / 운영 사례 | 태그: OpenClaw, Heartbeat, API Cost, LLM 운영
😱 도입부 — “어, 이 그래프 왜 이래?”
2026년 5월 2일 밤. 평소처럼 LLM API 사용량 대시보드를 무심코 열었다가 눈을 의심했다.
qwen3.6-plus 모델이 정확히 30분마다 호출되고 있었다. 그것도 24시간 내내, 쉬지 않고.
주말 사이에 아무 작업도 돌리지 않았는데 말이다. 게다가 곁다리로 kimi-k2.5까지 3초 간격으로 덩달아 찍히고 있었다. 마치 시계 태엽처럼, 30분마다 두 모델이 순차적으로 호출되는 패턴. 5일 동안 이 낭비가 지속되고 있었다는 사실을 깨닫는 데는 10분도 걸리지 않았다.
오늘은 이 Heartbeat 과잉 현상을 발견하고 분석하고 해결한 전 과정을 낱낱이 공유한다. 나처럼 AI 에이전트 플랫폼을 운영 중인 분들이라면 반드시 체크해야 할 포인트다.
🔍 증상 발견 — 대시보드 속 규칙적인 스파이크
최초 발견: 5월 2일 API 사용 로그
API 대시보드를 시간축으로 펼쳐보니 다음과 같은 패턴이 적나라하게 드러났다.
## 실제 로그 타임라인 (5월 2일 발췌)
2026-05-02T00:00:02Z kimi-k2.5 HEARTBEAT_OK 120ms
2026-05-02T00:00:05Z qwen3.6-plus HEARTBEAT_OK 105ms
2026-05-02T00:30:01Z kimi-k2.5 HEARTBEAT_OK 118ms
2026-05-02T00:30:04Z qwen3.6-plus HEARTBEAT_OK 98ms
2026-05-02T01:00:03Z kimi-k2.5 HEARTBEAT_OK 125ms
2026-05-02T01:00:06Z qwen3.6-plus HEARTBEAT_OK 102ms
...
## — 하루 48회 × 2모델 = 총 96회 API 호출 발생
육안으로 보기에도 30분 간격이 너무 깔끔했다. 크론잡치고는 간격이 짧고, 그렇다고 실사용자 요청으로 보기엔 응답 코드가 전부 HEARTBEAT_OK였다.
• 응답 99%가 HEARTBEAT_OK → 실제 작업은 발생하지 않음
• 하루 96회 × 5일 = 약 480회 무의미한 API 콜
• 유의미한 처리는 전체의 1% 미만
📊 데이터 분석 — 5일간 누적 통계
디스커버리 결과를 표로 정리하면 훨씬 더 선명해진다.
| 항목 | 상세 | 비고 |
|---|---|---|
| 발생 기간 | 2026-04-29 ~ 2026-05-03 (5일) | 발견은 5월 2일 |
| 원인 세션 | da181611 (main 에이전트 텔레그램) | Gateway heartbeat 대상 |
| kimi-k2.5 호출 | HEARTBEAT_OK 18회 (9.9%) | 타임아웃 2회 포함 |
| qwen3.6-plus 호출 | HEARTBEAT_OK 163회 (90.1%) | 04/29 14:57 이후 완전 장악 |
| 호출 패턴 | 매 30분: kimi → 3초 후 qwen | 듀얼 모델 호출 |
| 총 호출 수 | 약 480회 (5일 × 96회/일) | 99% 무의미 |
듀얼 호출 패턴 상세
흥미로운 점은 kimi-k2.5에 타임아웃이 발생한 시점(04/29 01:48, 02:24) 이후, 04/29 14:57을 기점으로 qwen3.6-plus가 실질적인 heartbeat 처리 모델로 완전히 전환되었다는 사실이다.
## 듀얼 호출 시퀀스 (매 30분 반복)
[00분:00초] → kimi-k2.5 호출 (default_model)
[00분:03초] → qwen3.6-plus 호출 (fallback/sub)
[00분:07초] → HEARTBEAT_OK 응답
## 타임아웃 이벤트 (kimi-k2.5)
2026-04-29 01:48 UTC — LLM idle timeout 120s
2026-04-29 02:24 UTC — LLM idle timeout 120s
🧪 원인 분석 — OpenClaw Heartbeat 메커니즘
문제의 근원은 OpenClaw Gateway의 Heartbeat 폴링이었다.
Heartbeat 동작 방식
- Gateway는 기본적으로 30분마다 main 에이전트 세션에 heartbeat 폴링을 보낸다.
- 에이전트는
HEARTBEAT.md파일을 확인한다. - 파일에 수행할 작업이 있으면 → LLM을 호출하여 작업 수행
- 파일이 비어있으면 →
HEARTBEAT_OK응답만 반환 (이상적으로는 LLM 호출 없이)
그런데 왜 LLM이 호출됐을까?
핵심 이슈는 여기에 있었다.
- main 세션에 default_model (kimi-k2.5)과 fallback 모델 (qwen3.6-plus)이 동시에 바인딩되어 있었고, heartbeat 폴링 시 두 모델이 모두 호출되는 듀얼 호출 패턴이 발생했다.
HEARTBEAT.md파일이 대부분 비어 있었음에도 불구하고, 하트비트 자체가 LLM 호출을 트리거하는 구조였다. 즉, 빈 파일 확인조차 모델 호출을 거치는 셈.- 이 현상이 5일간 방치되면서 API 비용이 조용히 누수되고 있었다.
Heartbeat는 백그라운드 작업 분배에 유용한 기능이지만, HEARTBEAT.md가 비어 있을 때도 LLM이 호출되는지 반드시 확인해야 한다. 특히 다중 모델이 바인딩된 세션에서는 예상치 못한 N배 호출이 발생할 수 있다.
🛠️ 해결 방안 — 단계별 대응
1단계: 즉시 응급 조치 — HEARTBEAT.md 비우기
가장 먼저 할 일은 heartbeat가 작업을 물고 늘어지지 않도록 하는 것이다.
# HEARTBEAT.md를 빈 상태로 유지
echo "" > /root/.openclaw/workspace/news_editor/HEARTBEAT.md
# 크론탭으로 주기적 초기화 (매 시간)
0 * * * * truncate -s 0 /root/.openclaw/workspace/news_editor/HEARTBEAT.md
2단계: Heartbeat 간격 조정
openclaw.json 설정 파일에서 heartbeat 간격을 늘려 호출 빈도를 낮춘다.
// openclaw.json (Gateway 설정)
{
"heartbeat": {
"enabled": true,
"heartbeatIntervalMs": 3600000, // 30분 → 1시간으로 조정
"maxRetries": 1
}
}
3단계: 단일 모델 강제
세션 설정에서 fallback 모델을 제거하거나, heartbeat 전용 경량 모델을 명시적으로 지정한다.
// session config 예시
{
"sessionId": "da181611",
"heartbeatModel": "kimi-k2.5", // heartbeat 전용 모델 지정
"disableFallbackForHeartbeat": true // 듀얼 호출 방지
}
4단계: Heartbeat 완전 비활성화 검토
만약 heartbeat가 반드시 필요하지 않은 환경이라면, 과감하게 비활성화하는 것도 방법이다.
// openclaw.json
{
"heartbeat": {
"enabled": false // 완전 비활성화
}
}
5단계: 모니터링 체계 구축
사후 대응만으로는 부족하다. 비정상 호출 패턴을 실시간 감지하는 스크립트를 만들어두자.
#!/bin/bash
# heartbeat_monitor.sh — 비정상 호출 패턴 감지
# 크론: */10 * * * * /path/to/heartbeat_monitor.sh
LOG_FILE="/var/log/openclaw/heartbeat.log"
THRESHOLD=10 # 10분 내 3회 이상 HEARTBEAT_OK → 알림
COUNT=$(grep "HEARTBEAT_OK" "$LOG_FILE" | \
awk -v date="$(date -u -d '10 min ago' +%Y-%m-%dT%H:%M)" '$0 > date' | wc -l)
if [ "$COUNT" -gt "$THRESHOLD" ]; then
echo "[ALERT] 과도한 heartbeat 감지: ${COUNT}회 / 10분"
# 여기에 Slack/Discord/Telegram 알림 연동
fi
📋 운영자 체크리스트
- API 대시보드 주기적 점검 — 최소 주 1회, 모델별 호출 횟수·패턴 확인
- Heartbeat 로그 분리 저장 — HEARTBEAT_OK 로그를 별도 파일로归档하여 이상 탐지
- 세션별 바인딩 모델 감사 — default_model + fallback 중복 여부 확인
- heartbeatIntervalMs 명시적 설정 — 기본값(30분)을 맹신하지 말고 의도적으로 조정
- 비용 알림 임계값 설정 — 일일 API 호출 한도 초과 시 즉시 알림
- HEARTBEAT.md 주기적 초기화 자동화 — 크론으로 빈 파일 유지
✍️ 마무리 — 교훈 요약
이번 사례를 통해 얻은 교훈은 단순하지만 강력하다.
- 기본값을 맹신하지 말라. OpenClaw의 30분 heartbeat 기본값도, 듀얼 모델 바인딩도 “의도하지 않은 동작”을 만들어낼 수 있다.
- 대시보드를 정기적으로 들여다봐라. 5일간이나 방치된 이유는 “아무도 안 봤기 때문”이다. 최소 주 1회 사용량 리뷰는 필수다.
- HEARTBEAT_OK ≠ 무료. heartbeat 확인 자체가 LLM API 콜을 유발하는 구조라면, 빈 heartbeat도 비용을 발생시킨다.
- 모니터링은 사후약방문이 아니라 예방접종이다. 이상 패턴을 감지하는 스크립트 하나가 장기적 비용 누수를 막아준다.
