OpenClaw 에이전트에 무결성 검사(Sanity-Check) 레이어를 추가하여 3일 만에 6개의 침묵하는 실패(Silent
요약
OpenClaw 에이전트 운영 중 발생하는 '침묵하는 실패(Silent Failures)'를 방지하기 위해 무결성 검사(Sanity-check) 레이어를 구축한 사례를 소개합니다. 에이전트가 스스로를 검증하지 않도록 별도의 Watchdog 스크립트를 통해 API 빈 응답 및 속도 제한 문제를 잡아내는 방법을 다룹니다.
핵심 포인트
- 에이전트의 '성공' 신호가 실제 작업 완료를 보장하지 않음
- 에이전트 외부에서 실행되는 독립적인 무결성 검사 레이어 필요
- API의 빈 200 OK 응답과 Rate-limit 문제를 감지하는 방법
- AI 모델이 아닌 단순 스크립트를 통한 불변량(Invariants) 검증
3일. 6개의 침묵하는 실패. 하나의 작은 감시(Watchdog) 스크립트.
나는 불편한 사실을 인정하기 전까지 약 4개월 동안 나의 OpenClaw 에이전트를 실행해 왔다: 그 실행 중 상당수가 아무런 유용한 결과물도 내지 못한 채 조용히 진행되고 있었고, 나는 전혀 모르고 있었다는 점이다. 에이전트가 충돌(Crash)했기 때문이 아니다 — 충돌은 발생하지 않았다. 잘못된 작업에 성공했기 때문이다. 도구 호출(Tool call)이 빈 페이로드(Payload)와 함께 200 OK를 반환했다. 크론 잡(Cron job)이 할당량 제한(Quota wall)에 걸렸음에도 동일한 죽은 토큰으로 계속 재시도했다. 브라우저 자동화(Browser automation) 단계에서 타임아웃(Timeout)이 발생하자, 에이전트는 이를 "완료"로 처리했고, 절반만 렌더링된 페이지를 "완료된 보고서"라며 내 편지함으로 보냈다.
이 오류 중 그 어떤 것도 오류로 로그(Log)에 남지 않았다. 대시보드에서는 모두 성공적인 실행처럼 보였다. 그래서 나는 무결성 검사(Sanity-check) 레이어 — 각 주요 워크플로(Workflow)가 끝난 후 실행되는 작은 감시(Watchdog) — 를 추가했고, 썰물 때 떠오르는 시체들처럼 실패 사례들이 수면 위로 드러나기 시작하는 것을 지켜보았다.
내가 무엇을 만들었는지, 무엇을 잡아냈는지, 그리고 이것 없이는 절대 발견하지 못했을 네 가지 패턴에 대해 설명하겠다.
감시자: 60줄짜리 무결성 검사 패스(Sanity-check pass)
아이디어는 간단하다. 사소하지 않은 모든 워크플로(크론 잡, 서브 에이전트 턴, 또는 예약된 작업) 이후에, 에이전트 자체의 "완료" 신호만으로는 신뢰할 수 없는 몇 가지 불변량(Invariants)을 작은 스크립트가 확인한다. 만약 어떤 검사라도 실패하면, 감시자(Watchdog)가 텔레그램(Telegram) 알림을 보낸다. 모든 것이 통과되면 침묵을 유지한다.
핵심 코드는 다음과 같다. 이를 scripts/agent-watchdog.sh에 저장하고, 워크플로를 마치는 모든 크론(Cron)이나 도구에서 호출하라:
#!/usr/bin/env bash
# agent-watchdog.sh — OpenClaw 워크플로를 위한 실행 후 무결성 검사
# 사용법: agent-watchdog.sh <workflow-name> <log-file>
...
이 스크립트는 의도적으로 단순하게 설계되었다. AI를 사용하지 않는 5가지 패턴 검사로 구성되며, 1초 미만으로 실행된다. 핵심은 에이전트가 자신의 작업 자체를 검증해서는 안 된다는 것이다. 방금 출력을 생성한 바로 그 모델이 그 출력이 실제인지 여부를 결정해서는 안 된다.
3일 동안 실제로 잡아낸 것들
저는 이 기능을 제 환경에서 가장 자주 실행되는 네 가지 워크플로우(workflow)에 연결했습니다: 아침 리서치 스윕(research sweep), 일일 DEV.to 포스팅 슬롯, 소셜 미디어 참여 봇, 그리고 주간 시스템 보고서 크론(cron) 작업입니다. 72시간 이내에, 이 감시 장치(watchdog)는 여섯 가지의 서로 다른 침묵하는 실패(silent failures)를 찾아냈습니다:
1. 제3자 API로부터의 빈 200 응답. 제 리서치 스윕은 부동산 데이터 API를 호출하고 있었는데, 이 API는 쿼리에 일치하는 항목이 없을 때 200 OK 상태 코드와 함께 {"results": []}를 반환합니다. 에이전트는 무언가를 찾아달라는 요청을 받았음에도 불구하고, 이를 "결과 없음, 임무 완수"로 처리했습니다. 워크플로우 자체에 "최소 결과 수" 단언(assertion)을 추가하기 전까지, 감시 장치의 빈 200 체크는 매일 아침 작동했습니다.
2. 성공처럼 보였던 속도 제한(Rate-limit). 참여 봇은 제 포스트에 달린 새로운 댓글을 가져오기 위해 매시간 DEV.to API를 호출합니다. 지난 화요일, API가 Retry-After 헤더와 함께 429 상태 코드를 반환하기 시작했습니다. 하지만 응답 본문(response body)은 여전히 JSON으로 파싱되었고, 에이전트는 기쁘게도 "0개의 댓글을 가져옴"이라고 기록하고 다음 단계로 넘어갔습니다. 결과적으로, 실제로 반응이 오고 있던 스레드에서 3일 동안 답글을 놓치게 되었습니다.
3. 크론(Cron) 작업이 건너뛰어졌으나 에이전트가 인지하지 못함. 제 크론 작업 중 하나는 특정 조건에서 {"fire": false}를 반환하는 trigger.script 게이트와 함께 cron.add를 사용합니다. 크론 스케줄러는 지시된 대로 실행을 성실히 건너뛰었지만, 에이전트의 로깅 레이어(logging layer)는 작업 자체가 건너뛰어졌다는 사실은 기록하지 않고, 단지 가장 최근의 도구 호출(tool call)이 성공했다는 사실만을 기록했습니다. 대시보드상으로는 모든 것이 정상인 것처럼 보였습니다.
4. "완료"로 처리된 브라우저 자동화 타임아웃. 제 주간 보고서에는 대시보드 스크린샷이 포함됩니다. 브라우저 도구에는 30초의 기본 타임아웃(timeout)이 설정되어 있습니다. 대시보드가 느려졌을 때 스크린샷은 빈 화면으로 돌아왔고, 에이전트는 빈 이미지를 보고서에 그대로 삽입했습니다. 감시 장치는 도구 출력 단언(tool-output assertion)을 확인하여 이를 잡아냈지만, 저는 제 자체 래퍼(wrapper)에 별도의 "이미지 바이트 크기 > 0" 체크를 추가해야 했습니다.
5. 서브 에이전트(Sub-agent)가 캐시된 응답을 반환함. 제가 생성한 서브 에이전트는 매시간 업데이트되는 소스로부터 최신 데이터를 가져와야 했습니다. 하지만 제가 잘못 설정한 캐싱 헤더(caching header) 때문에 14시간 동안 동일한 페이로드(payload)를 계속 반환했습니다. 와치독(watchdog)의 "도구 출력 존재(tool output present)" 체크는 통과했습니다. 즉, 응답이 비어 있지 않았기 때문입니다. 하지만 **내용(content)**은 오래된 것이었습니다. 저는 이 문제를 잡아내기 위해 타임스탬프(timestamp) 체크를 추가했습니다.
6. 에이전트가 도구가 고장 났을 때 메모리에서 답변함. 가장 창피했던 사례입니다. 저의 일일 DEV.to 포스팅 슬롯은 포스트가 실제로 게시되었는지 확인하기 위해 DEV.to API를 호출해야 했습니다. API 호출이 잘못된 URL이나 리다이렉트 체인(redirect chain) 등의 이유로 소리 없이 실패했는데, 에이전트는 에러를 발생시키는 대신 학습 데이터(training data)를 기반으로 그럴듯하게 들리는 "포스트가 게시되었습니다!"라는 메시지를 생성했습니다. 포스트는 게시되지 않은 상태였습니다. 와치독이 이를 직접 잡아내지는 못했지만, "API URL이 정규 패턴(canonical pattern)과 일치하는가"에 대한 후속 체크가 이를 잡아냈습니다.
제가 절대 찾아내지 못했을 네 가지 패턴
3일간의 데이터를 분석한 결과, 실패 사례들은 네 가지 형태로 군집화되었습니다. 만약 여러분이 OpenClaw 에이전트를 구축하거나 운영하고 있다면, 다음은 제가 주의 깊게 살펴본 침묵하는 실패(silent-failure)의 전형적인 유형들입니다:
| 패턴 | 양상 | 저렴한 탐지 방법 |
|---|---|---|
| 빈 성공(Empty success) | 본문이 비어 있는 200 OK, [], 또는 {} | Grep으로 HTTP/.* 200 .* \{?\s*\}?$ 검색 |
| ... |
여기서 얻은 메타 레슨(meta-lesson)은 제가 계속해서 재학습하고 있는 것입니다: 스스로를 재검증(double-check)하지 않는 에이전트에게는 이를 대신해 줄 다른 무언가가 필요하다는 것입니다. 또 다른 에이전트가 아닙니다. 또 다른 에이전트 역시 동일한 조건에서 동일한 방식으로 실패할 것입니다. 단순한 스크립트, 정규 표현식(regex), 혹은 Bash 원라이너(one-liner)가 필요합니다. 즉, 감시 대상이 가진 실패 모드(failure modes)를 공유하지 않는 무엇인가가 필요합니다.
내가 배운 점
가장 큰 깨달음은 단일 실패 사례가 아니라 바로 그 _비율(ratio)_이었습니다. 제가 세심하게 모니터링하고 있다고 생각했던 시스템에서, 4개의 워크플로우(workflows)에 걸쳐 3일 동안 6개의 침묵하는 실패가 발생했습니다. 이는 와치독을 도입하기 전 제 OpenClaw 실행의 침묵하는 실패율이 대략 50%에 달했다는 것을 의미하며, 대시보드가 초록색으로 표시된다는 이유만으로 저는 이 시스템을 "대체로 잘 작동한다"라고 부르고 있었다는 뜻입니다.
그 이후로 저는 제가 신경 쓰는 모든 크론 (cron) 작업에 와치독 (watchdog)을 추가했습니다. 대화형 메인 세션 (interactive main session)에는 굳이 적용하지 않았습니다. 제가 현장에 있다면, 제가 바로 와치독이니까요. 하지만 워크플로 (workflow)가 관리되지 않는 상태가 되는 순간, 무결성 검사 (sanity-check) 단계를 거치게 됩니다.
이 글에서 한 가지만 얻어가신다면 다음과 같습니다. 당신이 가장 오래 신뢰해 온 워크플로를 선택하여, Bash 스크립트에 5개의 체크 항목을 추가한 뒤 일주일 치 로그에 대해 실행해 보십시오. 결과로 나오는 숫자가 무엇이든, 그 숫자에 당신이 시스템에 대해 갖는 신뢰도를 곱해 보십시오. 그 결과값은 아마도 당신이 원치 않는 방향으로 틀려 있을 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기