이 글을 읽으면 알 수 있는 것
- Claude Code 스케줄 에이전트가 서로 직접 호출할 수 없는 구조적 한계와 우회법
- 파일 기반 릴레이 패턴으로 에이전트 간 통신을 구현하는 방법
- validate_data_integrity의 JSON 출력 구조화 설계
- 에이전트의 이중 모드(스케줄/대화형) 설계와 위험 등급 분류 기준
- ETL → 무결성 검증 → 전문가 분석 → 비즈니스 브리핑의 4단 파이프라인 아키텍처
- 대안 3가지를 비교하고 “스케줄 체인 + 파일 릴레이”를 선택한 근거
문제: 스케줄 에이전트는 서로를 모른다
Claude Code의 스케줄 에이전트(claude schedule)는 cron처럼 지정된 시간에 독립적으로 실행됩니다. 각 에이전트는 자기 프롬프트만 받고, 자기 작업만 하고, 종료합니다.
문제는 이겁니다. ETL 파이프라인에서 여러 단계의 분석이 필요한데, 에이전트 A가 에이전트 B를 호출하는 API가 없습니다.
에이전트 A (데이터 검증)
↓ ??? 어떻게 전달?
에이전트 B (비즈니스 분석)
↓ ??? 어떻게 전달?
Telegram 알림 (파운더에게 도착)
서로 직접 호출이 불가능한 에이전트들을 어떻게 릴레이처럼 협업시킬 것인가. 이것이 이 글의 핵심 질문입니다.
해법: 파일이 메시지 버스다
에이전트 간 통신에 별도 메시지 큐나 데이터베이스가 필요하지 않았습니다. 파일 시스템이 곧 메시지 버스입니다.
ETL (cron)
↓ .etl-results/latest.json 작성
validate_data_integrity (Django command)
↓ .etl-results/integrity-check.json 작성
data-validator 에이전트 (스케줄)
↓ .etl-results/dv-analysis.txt 작성
etl_daily_report (Django command)
↓ Telegram 메시지 발송
각 단계의 출력이 다음 단계의 입력이 됩니다. latest.json은 ETL 실행 결과, integrity-check.json은 무결성 검증 결과, dv-analysis.txt는 전문가 수준의 위험 분석입니다.
핵심 원칙은 단순합니다.
- 각 단계는 정해진 경로에 파일을 쓴다
- 다음 단계는 그 파일을 읽는다
- 파일이 없으면 해당 분석을 건너뛴다 (graceful degradation)
구현 1 — validate_data_integrity: 기계가 읽을 수 있는 검증 결과
기존 validate_data_integrity 커맨드는 사람이 읽는 텍스트만 출력했습니다. 에이전트가 이 결과를 해석하려면 구조화된 데이터가 필요합니다.
—format=json 옵션 추가
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument(
'--format', choices=['text', 'json'], default='text',
help='Output format'
)
parser.add_argument(
'--output', type=str, default=None,
help='Write results to file instead of stdout'
)
CHECK별 구조화된 결과
각 무결성 검증 항목을 일관된 구조로 출력합니다.
{
"timestamp": "2026-03-31T04:15:00+09:00",
"checks": [
{
"check_id": "CHK-001",
"name": "product_count_consistency",
"label": "Product count matches across sources",
"passed": true,
"message": "1,604,283 products in DB, 1,604,283 in ETL log"
},
{
"check_id": "CHK-007",
"name": "orphan_ingredients",
"label": "No orphan ingredient references",
"passed": false,
"message": "Found 23 ingredients referencing non-existent substances"
}
],
"summary": {
"total": 12,
"passed": 11,
"failed": 1
}
}
check_id는 사람이 참조할 때 쓰고, name은 코드에서 필터링할 때 씁니다. label은 에이전트가 리포트에 인용할 때 자연어 설명으로 사용합니다. 이렇게 세 종류의 식별자를 두는 게 과잉 설계처럼 보이지만, 실제로 사용처가 다릅니다.
구현 2 — data-validator 에이전트: 이중 모드 설계
data-validator.md는 Claude Code의 에이전트 프롬프트 파일입니다. 이 에이전트에는 두 가지 실행 모드를 설계했습니다.
Mode 1: 스케줄 모드
## Mode 1 — Scheduled (autonomous)
트리거: etl_daily.sh에서 validate_data_integrity --format=json 실행 후 자동 호출
입력: .etl-results/integrity-check.json
출력: .etl-results/dv-analysis.txt
### 분석 절차
1. integrity-check.json을 읽는다
2. 각 CHECK의 passed/failed를 확인한다
3. failed 항목에 대해 위험 등급을 평가한다
4. 종합 소견을 dv-analysis.txt에 작성한다
Mode 2: 대화형 모드
## Mode 2 — Interactive (on-demand)
트리거: 메인 세션에서 "data-validator에게 물어봐" 형태로 호출
입력: 사용자가 지정한 파일 또는 직접 질문
출력: 대화 내에서 직접 응답
같은 에이전트가 새벽에는 자율 분석, 낮에는 문답 형태로 동작합니다. 프롬프트 하나로 두 모드를 지원하는 이유는, 도메인 지식(검증 기준, 위험 판단 로직)을 한 곳에서 관리하기 위함입니다.
위험 등급 분류 기준
### Risk Assessment
- **GREEN**: 모든 CHECK 통과. 정상 운영.
- **YELLOW**: 1~2개 CHECK 실패, 서비스 영향 없음.
예) 마이너 카운트 불일치, 비활성 레코드 참조
- **RED**: 핵심 CHECK 실패, 서비스 영향 가능.
예) product count 불일치, 활성 제품의 ingredient 누락
GREEN/YELLOW/RED 분류는 단순하지만, 뒤에 나올 etl_daily_report에서 후속 조치 권장을 자동 생성하는 데 핵심 입력이 됩니다.
구현 3 — ceo-briefing 검토와 기각
Polsia(AI가 운영하는 1인 회사)에서 영감을 받아 CEO 브리핑 에이전트도 설계했습니다. 이 에이전트의 역할은 “DV 분석을 흡수해서 비즈니스 관점으로 레이어링”하는 것이었습니다.
설계했던 분석 방법론
## 분석 원칙: Trust but verify
1. data-validator의 dv-analysis.txt를 읽는다
2. 동료의 분석을 신뢰하되, 원본 데이터(integrity-check.json)와 교차 검증한다
3. 기술적 소견을 비즈니스 임팩트로 변환한다
- "orphan ingredients 23건" → "사용자 검색 시 23개 제품 성분 정보 누락 가능"
4. 권장 조치를 우선순위와 함께 제시한다
“Trust but verify”는 흥미로운 패턴이었습니다. 에이전트 B가 에이전트 A의 분석을 그대로 복사하는 게 아니라, 원본 데이터로 돌아가서 한 번 더 확인합니다. 에이전트 간 교차검증이 자동으로 발생하는 구조입니다.
기각 이유
그러나 현재 규모에서 CEO 에이전트는 기각했습니다. 이유는 단순합니다.
- Claude Code 메인 세션이 이미 오케스트레이터 역할을 하고 있다. 파운더가 직접 대화하는 메인 세션이 곧 CEO의 판단 레이어다.
- Telegram 알림의 수신자가 파운더 1명이다. 경영진에게 요약 보고하는 상황이 아니라, 기술을 이해하는 사람이 직접 받아보는 상황이다.
- 레이어가 하나 더 추가되면 latency와 토큰 비용이 증가한다. 파일 릴레이 단계가 늘어날수록 전체 파이프라인 실행 시간이 길어진다.
대신, CEO 에이전트가 하려던 “비즈니스 해석” 역할은 etl_daily_report의 룰 기반 권장 시스템으로 흡수했습니다. AI 에이전트보다 if-else가 적합한 영역이었습니다.
구현 4 — etl_daily_report: 모든 것을 모으는 최종 조립기
etl_daily_report 커맨드는 파이프라인의 마지막 단계입니다. 앞 단계들의 출력을 모두 흡수해서 Telegram 메시지로 조립합니다.
—integrity-json 옵션
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument(
'--integrity-json', type=str, default=None,
help='Path to integrity check JSON from validate_data_integrity'
)
파일 경로를 명시적으로 받는 이유는, 하드코딩된 경로 대신 etl_daily.sh가 경로를 주입하도록 설계했기 때문입니다. 파이프라인 오케스트레이터(쉘 스크립트)가 파일 경로를 관리하고, 각 단계는 자기가 받은 경로만 봅니다.
_build_agent_recommendations(): 룰 기반 권장
def _build_agent_recommendations(self, integrity_data, dv_analysis):
"""무결성 결과 + DV 분석을 기반으로 후속 조치를 권장한다."""
recommendations = []
if integrity_data:
failed = [c for c in integrity_data['checks'] if not c['passed']]
for check in failed:
if check['check_id'] in ('CHK-001', 'CHK-002'):
recommendations.append({
'priority': 'HIGH',
'action': f"[{check['check_id']}] 핵심 카운트 불일치 — 즉시 확인 필요",
'detail': check['message']
})
else:
recommendations.append({
'priority': 'LOW',
'action': f"[{check['check_id']}] {check['label']} — 모니터링 권장",
'detail': check['message']
})
if dv_analysis:
# DV 에이전트의 위험 등급을 파싱
if 'RED' in dv_analysis:
recommendations.insert(0, {
'priority': 'CRITICAL',
'action': 'DV 에이전트 RED 판정 — 파이프라인 점검 필요',
'detail': dv_analysis[:200]
})
return recommendations
이 함수가 CEO 에이전트를 대체합니다. AI가 매번 토큰을 소비하며 “이 수치는 비즈니스적으로…”를 생성하는 대신, 룰 기반으로 즉시 분류합니다. CHK-001(제품 카운트)과 CHK-002(성분 매핑)는 핵심 지표이므로 HIGH, 나머지는 LOW. DV 에이전트가 RED를 내렸으면 CRITICAL.
대안 비교: 왜 “스케줄 체인 + 파일 릴레이”인가
세 가지 방식을 검토했습니다.
A안: EC2에서 Claude CLI 직접 호출
# etl_daily.sh에서 직접
claude --agent data-validator --input .etl-results/integrity-check.json
claude --agent ceo-briefing --input .etl-results/dv-analysis.txt
기각 이유: EC2 인스턴스에 Claude CLI를 설치하고 인증을 유지해야 합니다. t3.micro의 1GB RAM에서 Claude CLI + Docker 앱을 동시에 돌리면 OOM 위험이 높습니다. 또한 CLI 세션 관리(인증 토큰 갱신 등)가 추가 부담입니다.
B안: 스케줄 체인 + 파일 릴레이 (선택)
cron → etl_daily.sh → validate_data_integrity → (파일) → DV 에이전트 → (파일) → etl_daily_report → Telegram
선택 이유: 각 단계가 독립적이고, 실패해도 다음 단계가 graceful하게 동작합니다. 파일이 없으면 해당 섹션을 건너뛸 뿐, 전체가 죽지 않습니다. EC2에는 쉘 스크립트만 돌면 되고, 에이전트는 Claude Code 스케줄로 별도 실행됩니다.
C안: 수동 운영
매일 아침 → 직접 로그 확인 → 직접 판단 → 직접 알림
기각 이유: 솔로 파운더가 매일 아침 로그를 수동 확인하는 건 지속 가능하지 않습니다. 하루 빠뜨리면 무결성 이슈를 놓칩니다.
| 기준 | A. EC2 CLI | B. 스케줄 체인 | C. 수동 |
|---|---|---|---|
| EC2 리소스 부담 | 높음 (CLI + Docker) | 낮음 (쉘만) | 없음 |
| 실패 격리 | 낮음 (CLI 크래시 시 전체 중단) | 높음 (단계별 독립) | - |
| 유지보수 | CLI 인증 관리 | 파일 경로 관리 | 시간 비용 |
| 확장성 | 에이전트 추가 용이 | 에이전트 추가 용이 | 한계 있음 |
인프라: etl_daily.sh 9단계 파이프라인
B안의 실체는 etl_daily.sh 쉘 스크립트입니다. 기존 ETL 스크립트를 9단계 파이프라인으로 확장했습니다.
#!/bin/bash
# /home/ec2-user/scripts/etl_daily.sh
RESULTS_DIR="/home/ec2-user/.etl-results"
LOG_DIR="/home/ec2-user/logs"
DATE=$(date +%Y%m%d)
LOG="$LOG_DIR/etl_$DATE.log"
mkdir -p "$RESULTS_DIR" "$LOG_DIR"
# --- Stage 1~4: ETL 실행 ---
echo "[$(date)] Stage 1: ETL i1250 start" >> "$LOG"
docker exec checkeat-app \
python manage.py import_i1250 --resume >> "$LOG" 2>&1
echo "[$(date)] Stage 2: ETL c002 start" >> "$LOG"
docker exec checkeat-app \
python manage.py import_c002 --resume >> "$LOG" 2>&1
# --- Stage 5: ETL 결과 수집 ---
echo "[$(date)] Stage 5: Collect ETL results" >> "$LOG"
docker exec checkeat-app \
python manage.py collect_etl_results \
--output /app/.etl-results/latest.json >> "$LOG" 2>&1
# --- Stage 6: 무결성 검증 (기계) ---
echo "[$(date)] Stage 6: Data integrity check" >> "$LOG"
docker exec checkeat-app \
python manage.py validate_data_integrity \
--format=json \
--output /app/.etl-results/integrity-check.json >> "$LOG" 2>&1
# --- Stage 7: DV 에이전트 분석 대기 ---
# DV 에이전트는 Claude Code 스케줄로 별도 실행.
# 이 단계에서는 DV 분석 파일 존재 여부만 확인.
echo "[$(date)] Stage 7: Check DV analysis" >> "$LOG"
DV_FILE="$RESULTS_DIR/dv-analysis.txt"
if [ -f "$DV_FILE" ]; then
echo " DV analysis found" >> "$LOG"
DV_FLAG="--dv-analysis $DV_FILE"
else
echo " DV analysis not found, skipping" >> "$LOG"
DV_FLAG=""
fi
# --- Stage 8: 리포트 생성 + Telegram 발송 ---
echo "[$(date)] Stage 8: Generate report" >> "$LOG"
docker exec checkeat-app \
python manage.py etl_daily_report \
--integrity-json /app/.etl-results/integrity-check.json \
$DV_FLAG >> "$LOG" 2>&1
# --- Stage 9: 정리 ---
echo "[$(date)] Stage 9: Cleanup" >> "$LOG"
# 어제 분석 파일 삭제 (오늘 것만 유지)
find "$RESULTS_DIR" -name "*.txt" -mtime +1 -delete
find "$RESULTS_DIR" -name "*.json" -mtime +1 -delete
echo "[$(date)] Pipeline complete" >> "$LOG"
docker-compose 볼륨 공유
.etl-results 디렉토리가 호스트와 컨테이너 간에 공유되어야 합니다.
services:
app:
image: checkeat-app
volumes:
- etl-results:/app/.etl-results
# ...
volumes:
etl-results:
driver: local
driver_opts:
type: none
device: /home/ec2-user/.etl-results
o: bind
호스트의 /home/ec2-user/.etl-results와 컨테이너의 /app/.etl-results가 같은 디렉토리를 가리킵니다. Django 커맨드가 컨테이너 안에서 쓴 JSON을 호스트의 쉘 스크립트가 읽을 수 있고, Claude Code 스케줄 에이전트가 쓴 분석 파일을 다시 컨테이너 안의 Django 커맨드가 읽을 수 있습니다.
에이전트 팀 구성
파이프라인 외에도, 개발 과정 전반에서 활용하는 에이전트 팀이 있습니다.
| 에이전트 | 역할 | 주요 사용 |
|---|---|---|
| planner | 기획→기술 명세 번역 | 기능 설계 시 요구사항을 기술 태스크로 분해 |
| qa-detective | 버그 탐정 | 코드 리뷰 시 엣지케이스 공격 |
| data-validator | 데이터 무결성 검증 | ETL 파이프라인 (스케줄) + 수동 검증 (대화형) |
교차검증 패턴이 핵심입니다. planner가 명세를 쓰면 qa-detective가 공격하고, data-validator가 데이터 관점에서 검증합니다. 메인 세션이 이들의 의견을 종합해서 최종 판단합니다.
CEO 에이전트를 별도로 두지 않은 이유를 다시 정리하면: 메인 세션이 이미 오케스트레이터입니다. 에이전트는 전문 분야의 깊은 분석을 담당하고, 종합 판단은 도메인을 가장 잘 아는 파운더 + 메인 세션이 합니다. 현재 규모(1인 운영, 에이전트 3~4개)에서 오케스트레이터를 에이전트로 분리하면 레이어만 하나 늘어나는 오버엔지니어링입니다.
전체 아키텍처 요약
새벽 3:00 cron → etl_daily.sh 시작
├── Stage 1~4: ETL 실행 (import_i1250, import_c002, ...)
├── Stage 5: collect_etl_results → latest.json
├── Stage 6: validate_data_integrity → integrity-check.json
│
새벽 4:00 Claude Code Schedule → data-validator 에이전트
├── integrity-check.json 읽기
├── 위험 평가 (GREEN/YELLOW/RED)
└── dv-analysis.txt 쓰기
│
새벽 5:00 cron → etl_daily.sh (Stage 7~9)
├── Stage 7: dv-analysis.txt 존재 확인
├── Stage 8: etl_daily_report (모든 파일 흡수 → Telegram)
└── Stage 9: 정리
아침 7:00 파운더 기상 → Telegram에서 리포트 확인
시간 간격이 통신 프로토콜입니다. Stage 6 이후 DV 에이전트에게 1시간의 작업 시간을 줍니다. DV 에이전트가 늦거나 실패해도, Stage 7에서 파일 부재를 감지하고 해당 섹션 없이 리포트를 생성합니다.
한계와 솔직한 상태
이 시스템은 설계하고 구현했지만, 아직 실전 운영에서 검증되지 않았습니다. 솔직하게 알려진 한계를 정리합니다.
에이전트 간 실시간 피드백 불가
DV 에이전트가 RED를 판정해도, etl_daily_report가 실행되기 전까지 아무 일도 일어나지 않습니다. 긴급 상황에 대한 즉시 알림이 없습니다. 현재는 시간 간격(1시간)에 의존하는데, 이것이 실제 운영에서 충분한지는 돌려봐야 압니다.
파일 기반 통신의 취약점
- 파일 쓰기 실패: 디스크 풀, 권한 문제 등으로 중간 파일이 안 쓰이면 다음 단계가 빈 입력으로 동작합니다.
- 동시성: etl_daily.sh와 DV 에이전트가 같은 파일을 동시에 접근할 가능성이 있습니다. 현재는 시간 간격으로 회피하지만, lock 파일 같은 명시적 동기화는 없습니다.
- 디버깅: 파이프라인 중간에 문제가 생기면, 각 단계의 파일을 하나씩 열어봐야 합니다. 중앙화된 로그나 트레이싱이 없습니다.
아직 답을 모르는 질문들
- DV 에이전트의 토큰 소비가 일별로 얼마나 되는가?
- GREEN이 계속되면 DV 에이전트를 건너뛰는 최적화가 가능한가?
- 파이프라인이 길어질수록(단계 추가) 전체 실행 시간이 어떻게 늘어나는가?
이 질문들은 실제로 파이프라인을 돌리기 시작하면 답이 나올 것입니다.
교훈
- 파일 시스템은 가장 단순한 메시지 버스다. 에이전트 간 통신에 Kafka나 Redis가 필요하지 않다. 규모가 작을 때 파일 릴레이는 충분하다.
- Graceful degradation을 기본으로 설계한다. 중간 단계가 실패해도 전체가 죽지 않게 “파일이 없으면 건너뛴다”를 모든 단계에 적용.
- AI 에이전트와 룰 기반을 섞어 쓴다. 위험 등급 분류(GREEN/YELLOW/RED)처럼 판단 기준이 명확한 영역은 if-else가 AI보다 빠르고 저렴하고 예측 가능하다.
- 오케스트레이터를 에이전트로 만들지 않는다(현재 규모에서는). 메인 세션이 오케스트레이터, 전문 에이전트는 전문 분석. 역할이 명확하면 레이어를 줄인다.
- 구현보다 검증이 어렵다. 파이프라인을 만드는 건 며칠이면 되지만, 실전에서 안정적으로 동작하는지 확인하는 건 훨씬 오래 걸린다. 이 시스템도 아직 그 과정에 있다.