delegate-job
자율 에이전트 비동기 위임 및 MQTT 이벤트 관찰 아키텍처 명세서
시스템 아키텍처 명세
자율 에이전트 위임 모델은 동시성 간섭이 배제된 단방향 이벤트 스트림 구조를 띱니다. 아래 탭을 전환하며 상호작용 가능한 다이어그램들을 확인하실 수 있습니다.
1. 이 스킬이 하는 일
delegate-job 스킬은 복잡한 자율 에이전트 오케스트레이션(작업 위임) 흐름을 단 한 줄의 명령어로 단순화합니다.
자동화 범위
1. Job ID 신규 발급 및 독자적인 샌드박스 레지스트리 레코드 생성
2. 이벤트 전송 유실 방지를 위해 에이전트 시작 전에 백그라운드 수신기를 선(先) 실행
3. 격리된 tmux 인터랙티브 터미널 세션을 열어 작업 위임
4. 에이전트가 발행한 실시간 상태 이벤트를 감지하여 NDJSON 포맷 감사 로그(Audit Log) 축적
5. 최종 성공(completed) 또는 실패(error) 감지 시 자동으로 자원 반환 및 모니터링 해제
2. 설치 & 사전조건
이 스킬은 POSIX 표준 개발 규격을 준수하며, 파이썬 기반의 백그라운드 구독 유틸리티와 통신 드라이버가 필요합니다.
사전 시스템 사양
| 인프라 항목 | 기본 요건 | 활용 세부 영역 |
|---|---|---|
| OS | macOS / Linux (Windows 환경인 경우 WSL 가상 환경 권장) | 런타임 커널 지원 |
| Runtime | Python 3.9+ | Registry / Publisher / Subscriber 연동 |
| Multiplexer | tmux (미설치 시 wrapper 기동 시 경고) | 인터랙티브 모드 세션 격리 기동 |
| Library | paho-mqtt ≥ 2.0 (VERSION2 콜백 API 지원 사양) | MQTT 브로커 통신을 위한 로컬 파이썬 모듈 |
스킬 저장소 파일 디렉터리 구성
~/.hermes/skills/autonomous-ai-agents/delegate-job/
├── SKILL.md # Claude 에이전트가 해석하는 프롬프트 가이드 지침
├── USER_MANUAL.html # 사용자 분석용 HTML 지침서 (본 문서)
├── README.md # 간략한 프로젝트 정보
├── job-protocol.md # MQTT JSON 페이로드 규격 및 스키마 명세
├── registry.md # 레지스트리 레코드의 속성 값 정의 및 락 매커니즘
├── mqtt-broker-setup.html # PoC → TLS 운영 브로커 컷오버 가이드 (HTML)
├── delegate-job # 오케스트레이션 역할을 하는 Bash wrapper script
└── scripts/
├── mqtt_common.py # 브로커 연결 빌드, 파일 락, 감사 로그 공통 모듈
├── registry.py # Job 등록/수정을 관장하는 CLI 헬퍼 라이브러리
├── publish_event.py # 구동 중인 에이전트가 이벤트를 쏠 때 쓰는 송신기
└── job_subscriber.py # 위임 작업 종료 시점까지 백그라운드에서 감시하는 수신기
3. Quick Start (작업 위임)
단일 통합 명령어를 활용하여 즉시 위임 환경을 기동하거나, 빌드 파이프라인 연동 시 단계별 조작이 용이하도록 파이썬 직접 구동을 지원합니다.
delegate-job submit 명령을 통하여 백그라운드 수신기를 구동하고 에이전트를 안전하게 격리 기동합니다.
delegate-job submit \
--agent claude-code \
--prompt "정렬 문제 10개를 만들어 sort_problems.md로 저장" \
--workdir /path/to/project \
--agent-session tmux:demo \
--timeout 600 --idle-timeout 120
위임 시작 시 콘솔 실시간 로그 (stdout 예시)
registered job: 2971fbf8
subscriber pid: 34311 (log: .hermes/jobs/2971fbf8.subscriber.out)
agent launched in tmux session: demo (attach with: tmux attach -t demo)
subscriber output:
2026-06-19T13:16:00Z job=2971fbf8 seq=1 started Job 2971fbf8 started
2026-06-19T13:18:20Z job=2971fbf8 seq=2 completed saved sort_problems.md
subscriber exit code: 0
/path/to/project/.hermes/delegate_job_logs/2971fbf8 # Audit log 디렉터리 경로 반환
의존 관계 설정 및 디버깅을 위해 단계를 세분화하여 파이썬 런타임 스크립트를 기동할 수 있습니다.
PY=.venv/bin/python
SKILL=~/.hermes/skills/autonomous-ai-agents/delegate-job/scripts
# 1) 작업 정보 레코드 생성
JID=$($PY "$SKILL/registry.py" register \
--prompt "정렬 파일 구성" --agent claude-code --agent-session tmux:demo \
--timeout 600 --idle-timeout 120)
# 2) 백그라운드 수신기 우선 실행 (순서 의존성 주의)
$PY "$SKILL/job_subscriber.py" --job "$JID" --timeout 600 --idle-timeout 120 &
# 3) 시작(started) 상태 신호 수동 전송
$PY "$SKILL/publish_event.py" --job "$JID" --event started
# 4) 에이전트 구동 과정... (에이전트가 직접 completed/error 신호를 쏘도록 지시)
# 5) 조회 및 로그 상태 검증
$PY "$SKILL/registry.py" get --job "$JID"
$PY "$SKILL/registry.py" logs "$JID"
$PY "$SKILL/registry.py" logs --list
4. Job Lifecycle
각 위임 작업은 독립적인 분기가 적용되어 라이프사이클을 돌며 수신기의 exit code를 결정 짓습니다.
프로세스 반환 코드(Exit Code) 사양
| 기동 스크립트 | Exit Code | 반환 의미 및 트리거 사유 |
|---|---|---|
job_subscriber.py |
0 | 에이전트로부터 작업을 성공적으로 마쳤다는 completed 이벤트 수신 완료 |
job_subscriber.py |
1 | 작업 수행 실패 또는 처리 예외로 에이전트가 error 이벤트를 송출한 경우 |
job_subscriber.py |
2 | 설정한 허용 제한 시간(timeout_sec) 또는 정체 제한(idle_timeout_sec) 임계치 도달 |
publish_event.py |
0 | MQTT 브로커 대상 QoS 1 발행 성공 및 PUBACK 응답 수신 완료 |
publish_event.py |
1 | 필수 파라미터 누락, 존재하지 않는 JID 참조 등 호출 설정 오류 |
publish_event.py |
2 | 브로커 접속 실패 또는 네트워크 결함으로 지수 백오프 기반 3회 재시도 유실 |
5. 이벤트 프로토콜
메시지 전송은 지연 및 간섭을 피하기 위해 1 Job당 1 전용 토픽을 준수합니다.
MQTT 통신 토픽 규격
python/mqtt/jobs/<job_id>/events
메시지 페이로드 스키마 명세
전송하는 모든 데이터는 UTF-8 규격의 JSON 오브젝트 포맷을 따릅니다.
{
"schema_version": 1,
"seq": 7, // 동일 JID 내 단조 증가하는 정수형 카운터
"job_id": "abc12345",
"event": "started", // started | permission_required | progress | completed | error
"timestamp": "2026-06-19T09:32:00Z", // ISO-8601 UTC 규격 시각
"detail": "데이터베이스 쿼리 아티팩트 보관 완료",
"data": { // 확장 요구사항 반영을 위한 옵션 메타 딕셔너리
"auth_token": "a1b2c3d4..."
}
}
Event Catalogue
| 이벤트명 | 의무 여부 | 발행 유발 시점 | detail 기술 방식 |
|---|---|---|---|
started |
필수 (seq=1) | 에이전트가 작업을 할당받아 수행에 착수할 때 | "Job <id> started" |
permission_required |
옵션 | 에이전트가 시스템 파일 수정 등 사용자 승인이 필요할 때 | "needs to write sort_problems.md" |
progress |
옵션 | 중간 단계 수행 결과 알림 | "creating problem 5/10" |
completed |
종단 필수 | 위임 요구 명세를 온전히 이행하고 정상 종료할 때 | "saved sort_problems.md" |
error |
종단 필수 | 수행 도중 예외가 발생하거나 에이전트가 실패 종료될 때 | "internal exception occurred" |
방어적 메시지 파싱 규칙 (Defensive Parsing)
공개 인터넷 브로커(HiveMQ 등)는 제3자의 데이터 변조가 손쉽게 가능하므로, job_subscriber.py는 아래 검증을 거쳐 맞지 않는 유해 메시지를 무시하도록 설계되었습니다.
- JSON 구문 포맷 에러 감지 시 무시 및 폐기
schema_version속성이 구독자 지원 범위(1)와 불일치 시 기각- 수집 대기 중인 대상 JID가 아닌 엉뚱한
job_id를 가진 페이로드 차단
6. Audit Log 자동 기록
작업 실행 주기 동안 실시간 상태 흐름을 추적할 수 있도록 .hermes/delegate_job_logs/<job_id>/ 폴더에 3종의 감사 추적 파일이 자동 축적됩니다.
감사 기록 파일 정의
| 감사 로그 파일 | 포맷 규격 | 기록 내용 및 정보 범위 |
|---|---|---|
meta.json |
JSON Object | 최초 작업 할당 시점의 프롬프트 전문, 타임아웃 구성, 브로커 접속 스냅샷 |
events.ndjson |
NDJSON | 생성, 송신, 수신, 상태 전이의 단계별 세부 이력이 발생 시간순으로 한 줄씩 적재 |
status.json |
JSON Object | 현재 시점의 최종 런타임 결과값 저장 (속도 개선을 위한 포인트 필터용) |
시간 흐름별 감지 포인트
| 기록 액션 시점 | events.ndjson 저장 포맷 | 쓰기 명령의 수행 주체 |
|---|---|---|
| Job 등록 완료 시 | registered (메타 데이터 구성 및 status.json 상태 pending 선 생성) |
registry.py |
| 작업 상태 변경 시 | status_changed (이전 상태 → 변경 상태 흐름) |
job_subscriber.py / registry.py |
| 에이전트 송신 완료 | published (송출 타임스탬프와 페이로드 스냅샷 포함) |
publish_event.py |
| 수신기 수신 완료 | received (수신 감지 성공 시각 및 세부 속성) |
job_subscriber.py |
💡 운영 전환 검증 가이드: 위 종단 검증 시나리오는 공개 PoC 브로커를 활용한 테스트입니다. 사설 운영 브로커로 전환하여 이를 검증하고자 한다면, MQTT 브로커 셋업 가이드 →의 연동 검증 시나리오 절차를 확인하십시오.
로그 쓰기 트랜잭션 격리
감사 로그 아티팩트 파일 쓰기는 메인 에이전트 동작 및 MQTT 발행 스레드를 보호하기 위해 철저한 Best-effort 방식으로 감싸져 실행됩니다. 파일 쓰기 중 동시성 경합 시 try/except 처리 및 OS fcntl 락, logger.warning 백업 처리를 통해 메인 통신 채널을 보장합니다.
7. 명령어 Reference
통합 오케스트레이션 및 상태 점검을 지원하는 CLI 명령어입니다.
| 서브 커맨드 | 상세 역할 설명 | 기본 사용 기법 |
|---|---|---|
submit |
작업 등록, 수신기 실행, 에이전트 tmux 기동 단계를 일괄 지시합니다. | delegate-job submit --agent claude-code --prompt "..." |
status |
레지스트리 레코드를 질의하여 작업의 세부 구성 정보를 조회합니다. | delegate-job status --job <id> |
list |
저장소에 보관된 작업 내역을 목록 형태로 일괄 조회합니다. | delegate-job list |
verify |
에이전트가 추출한 작업 산출물 아티팩트의 무결성 검증 스크립트를 작동시킵니다. | delegate-job verify --job <id> --validate ./validate.sh |
wait |
위임된 작업이 종단(completed/error) 상태를 맞이할 때까지 동기 대기합니다. | delegate-job wait --job <id> |
logs |
감사 로그 이력을 판독하여 타임라인 순으로 가공된 수행 기록을 출력합니다. | delegate-job logs <id> 또는 delegate-job logs --list |
8. 종단 Smoke 검증 결과
실제 로컬 호스트 및 공개 HiveMQ 브로커 상에서 정상 구동된 종단 검증 데이터(job cb32569f)의 정합성 리포트입니다.
검증 재현 시나리오 명령어
# 1) human 에이전트 용 임시 smoke 작업 생성
JID=$(.venv/bin/python skills/delegate-job/scripts/registry.py \
--registry-dir .hermes/jobs register \
--prompt "smoke: flatten+resplit" --agent human --agent-session tmux:flatten-smoke \
--timeout 60 --idle-timeout 30 | tail -1)
# 2) 백그라운드 이벤트 감시 모니터 기동
.venv/bin/python skills/delegate-job/scripts/job_subscriber.py \
--registry-dir .hermes/jobs --job "$JID" --timeout 60 --idle-timeout 30 &
# 3)started 라이프사이클 이벤트 강제 발행
.venv/bin/python skills/delegate-job/scripts/publish_event.py \
--registry-dir .hermes/jobs --job "$JID" --event started \
--detail "flatten smoke started"
# 4) completed 종단 이벤트 강제 발행
.venv/bin/python skills/delegate-job/scripts/publish_event.py \
--registry-dir .hermes/jobs --job "$JID" --event completed \
--detail "flatten smoke done"
# 5) 모니터 프로세스가 completed 접수 후 자동으로 자원 회수 종료 확인
wait %1
실제 생성된 로그 아티팩트 명세 (job cb32569f)
events.ndjson 감사 기록 전문:
{"event": "registered", "status": "pending", "agent": "human", "agent_session": "tmux:flatten-smoke", "topic_prefix": "python/mqtt/jobs/cb32569f", "timestamp": "2026-06-19T04:09:50Z", "logged_at": "2026-06-19T04:09:50.846Z"}
{"event": "received", "source_event": "started", "seq": 1, "topic": "python/mqtt/jobs/cb32569f/events", "timestamp": "2026-06-19T04:10:03Z", "detail": "flatten smoke started", "logged_at": "2026-06-19T04:10:11.548Z"}
{"event": "published", "source_event": "started", "seq": 1, "topic": "python/mqtt/jobs/cb32569f/events", "retain": false, "timestamp": "2026-06-19T04:10:03Z", "detail": "flatten smoke started", "payload": {"schema_version": 1, "seq": 1, "job_id": "cb32569f", "event": "started", "timestamp": "2026-06-19T04:10:03Z", "detail": "flatten smoke started", "data": {}}, "logged_at": "2026-06-19T04:10:12.555Z"}
{"event": "status_changed", "from": "pending", "to": "running", "timestamp": "2026-06-19T04:10:12Z", "logged_at": "2026-06-19T04:10:12.558Z"}
{"event": "received", "source_event": "completed", "seq": 2, "topic": "python/mqtt/jobs/cb32569f/events", "timestamp": "2026-06-19T04:10:13Z", "detail": "flatten smoke done", "logged_at": "2026-06-19T04:10:16.927Z"}
{"event": "published", "source_event": "completed", "seq": 2, "topic": "python/mqtt/jobs/cb32569f/events", "retain": true, "timestamp": "2026-06-19T04:10:13Z", "detail": "flatten smoke done", "payload": {"schema_version": 1, "seq": 2, "job_id": "cb32569f", "event": "completed", "timestamp": "2026-06-19T04:10:13Z", "detail": "flatten smoke done", "data": {}}, "logged_at": "2026-06-19T04:10:17.932Z"}
{"event": "status_changed", "from": "running", "to": "completed", "timestamp": "2026-06-19T04:10:17Z", "logged_at": "2026-06-19T04:10:17.935Z"}
status.json 실시간 결과 캐시 스냅샷:
{
"job_id": "cb32569f",
"status": "completed",
"updated_at": "2026-06-19T04:10:17Z"
}
- 이벤트 정합성 검증 완료: 등록 → 구독 → 2회 발행 후 수신기가 정상 복귀 코드 0을 반환하며 종료
- 감사 로그 누락 방지 확인:
events.ndjson파일 내 시간순 정합 기록 확인 완료 - 데이터 캐싱 정합성 검증: 레지스트리의 status와 감사 로그 내 status.json 캐시 값이 완성 동기화됨
9. 자주 빠지는 함정
자율 에이전트 비동기 위임 환경 구성 및 운영 셋업 중에 봉착하기 쉬운 결함 시나리오입니다.
① 오래된 Job ID의 에이전트 프롬프트 하드코딩 에러
사유: 에이전트 기동 시 무작위 ID가 새로이 발급됩니다. 과거 세션의 ID를 에이전트 기동 프롬프트에 고정 지정해 주면, 에이전트가 다른 Job ID 채널로 이벤트를 쏘고, 감시 수신기는 무반응 타임아웃(exit 2)에 처하게 됩니다.
→ 해결책: 래퍼 실행 프롬프트에 --job "$JOB_ID" 옵션을 넘겨 신규 JID 값을 주입 하십시오.
② 수신기 실행 전 이벤트 선 송출 유실 결함
사유: MQTT 브로커 통신의 일반 비영속화 메시지는 구독 대기 중인 채널이 없으면 소멸합니다. 수신기를 백그라운드에 올리지 않은 상태에서 에이전트가 이벤트를 쏘면 데이터가 소실되어 프로세스가 정지합니다.
→ 해결책: 래퍼 submit 동작을 쓰거나, completed 발행 시 MQTT retain=true 처리를 병행하십시오.
③ tmux 인터랙티브 모드에서의 세션 명칭 중복 간섭
사유: --agent-session에 고정 문자열(예: tmux:claude)을 기입한 채 복수 작업을 동시 실행하면, tmux 세션이 이미 점유 상태여서 프로세스가 기동 실패를 반환합니다.
→ 해결책: 위임 건수마다 UUID 또는 JID 기반 고유 세션명을 매핑 기동하십시오.
④ 공개 테스트 브로커 보안 위협 요소
사유: HiveMQ 공개 브로커는 누구든지 메시지를 구독/발행 가능하므로 스니핑 및 인젝션 해킹 위협에 완전 노출되어 있습니다.
→ 해결책: 내부망에 전용 Mosquitto 브로커를 올리고 TLS 8883 및 Bearer auth_token 인증 로직을 전개하십시오.
⑤ detail / data 속성 내 보안 자격 키 노출 위험
사유: MQTT 전송 페이로드의 detail 및 data에 로컬 절대 경로, 액세스 토큰 등을 여과 없이 주입하면, 타인에게 시스템 정보가 노출됩니다.
→ 해결책: 전송 값은 추상화된 이력 텍스트만 싣고, 기밀 파라미터는 로컬 Registry 레코드를 통해서만 참조 하십시오.
10. 운영 Broker 전환
보안 통제 및 격리가 요구되는 비즈니스 인프라로 이관하기 위한 독립 브로커 구성 기법입니다. 스크립트 수정 없이 환경변수 조율만으로 이행됩니다.
환경 주입 파일 (mqtt.env 예시)
export MQTT_BROKER=mqtt.internal.example.com
export MQTT_PORT=8883
export MQTT_TLS=1
export MQTT_CA_CERTS=/etc/ssl/certs/internal-ca.pem
export MQTT_USERNAME=hermes-operator
export MQTT_PASSWORD=secure_token_key
# 환경 주입
source mqtt.env
# 위임 submit 스크립트 재시동 (스크립트 소스코드 수정 무)
delegate-job submit --agent claude-code --prompt "운영 서버 이관 점검"
우선순위 역전 장애 (Broker Precedence Pitfall)
공통 모듈인 broker_config_from_job()는 환경 변수 로드 후 레지스트리 JSON 파일의 broker.* 속성을 덮어씌워 합성(Merge)합니다.
즉, 쉘 터미널 상에 새로운 MQTT_BROKER 호스트 환경변수를 주입하더라도, 이미 등록되어 구동 대기 중이던 과거 레지스트리 레코드 파일 내부에 HiveMQ PoC 브로커 주소가 하드코딩 되어 있다면, 변경된 주소가 무시되어 과거 브로커로 접속하게 됩니다. 반드시 신규 작업을 다시 등록(register)하거나, 레코드 JSON 파일(.hermes/jobs/<id>.json)을 수동 편집하여 broker.host 항목을 수동 업데이트한 후 구동 하십시오.