장시간 AI 작업에서 가장 무서운 것은 실패 자체가 아니다. 침묵이다.
사람은 집안일도 하고, 장도 보고, 식사까지 끝내고 돌아왔는데 AI는 여전히 “작업 중”일 수 있다. 이때 가능한 해석은 두 가지다.
- 거대한 테스트를 성실하게 돌리고 있다.
- 몇 시간 전에 어딘가에서 멈췄다.
로딩 표시만으로는 둘을 구분하기 어렵다. commit이 한동안 없다고 해서 멈춘 것도 아니다. 테스트, 조사, 비교, 생성, 외부 대기에서는 정상적으로도 결과물이 한동안 나오지 않을 수 있다.[1]
그래서 장시간 AI에는 지능과 별개로 관측 가능성(observability) 이 필요하다.
1. 결론――몇 분 이상 걸릴 수 있는 작업은 현재 상태를 밖으로 보여 주는 편이 좋다
장시간 에이전트는 마지막 완료 보고만으로 부족하다. 중간 상태를 사람이 읽을 수 있어야 한다.[1]
매분 “살아 있습니다!”라고 외칠 필요는 없다. 대신 다음을 알 수 있어야 한다.
- 지금 무엇을 하는가
- 무엇이 끝났는가
- 무엇이 남았는가
- 테스트 중인가
- 막힌 것이 있는가
- 마지막 생존 확인은 언제인가
- 마지막 실제 변경은 언제인가
실무 설정의 한 예로, 눈에 보이는 결과가 약 10분 동안 없으면 Heartbeat를 갱신하고, Heartbeat가 20분 넘게 오래되면 “정지”가 아니라 STALE_UNKNOWN으로 읽을 수 있다.[1]
침묵은 먼저 “모른다”이지, 곧바로 “실패했다”가 아니다.
2. commit 기록만으로는 왜 부족한가
Git commit은 실제 변경의 증거로는 훌륭하지만 현재 살아 있는지를 보여 주기에는 약하다.
1시간짜리 regression test가 정상 실행 중이어도 commit은 0개일 수 있다. 반대로 Heartbeat용 의미 없는 파일을 10분마다 commit하면 기록만 지저분해진다.[1]
따라서 다음을 분리해야 한다.
Heartbeat = 현재 상태
Material change = 코드·계약·산출물·검증이 실제로 바뀐 증거
lastHeartbeatAt과 lastMaterialChangeAt을 따로 두면 “30분간 commit은 없지만 5분 전에 TESTING Heartbeat가 있었다”는 상태를 정확히 이해할 수 있다.[1]
필요한 것은 commit 수가 아니라 침묵의 의미를 읽을 수 있는 정보다.
3. 최소한 무엇을 기록해야 할까?
장시간 run에는 다음 항목이 유용하다.[1]
runId: 작업의 고유 IDstate: RUNNING / BLOCKED / COMPLETED / STALE_UNKNOWN 등phase: READING / IMPLEMENTING / TESTING / VERIFYING 등workingOn: 지금 실제로 하는 일startedAtlastHeartbeatAtlastMaterialChangeAtbranch / baseSha / headSha / PRcompletedMilestones / remainingMilestonesblockerstestsnextCheckpoint
그러면 6시간짜리 작업도 단순한 “6시간짜리 검은 상자”가 아니라,
IMPLEMENTING → TESTING → INTEGRATING → VERIFYING
으로 읽힌다.
아무리 똑똑해도 검은 상자는 검은 상자다. 오래 보면 꽤 불안하다.
4. 특히 테스트 중 Heartbeat가 중요하다
긴 테스트는 멈춘 AI와 모습이 거의 같다.
긴 테스트 전에 다음을 남기자.[1]
phase = TESTING- 테스트 이름
- 범위
- 고정된 총량이 있다면
84 / 127같은 실제 숫자 - 현재 failure 발생 여부
설계나 디버깅에 “82% 완료” 같은 분위기 숫자는 피하는 편이 좋다. 남은 18%가 무엇인지 아무도 모른다.
비율은 분모가 객관적으로 고정되어 있을 때만 의미가 있다.
- 84 / 127 tests
- 3 / 5 acceptance gates
“구현 82%”는 AI 버전의 “거의 다 왔어”다. 위치를 물어보면 아직 집일 수도 있다.
5. “멈췄나?”는 단계적으로 읽는다
Heartbeat freshness를 다음처럼 분류하는 실무 예가 있다.[1]
| 경과 시간 | 해석 |
|---|---|
| 0–10분 | CURRENT |
| 10–20분 | HEARTBEAT_OVERDUE |
| 20분 초과 | STALE_UNKNOWN |
STALE_UNKNOWN은 FAILED가 아니다.
이후에는 다음 순서로 확인한다.
- 현재 진행 기록
- Check / Status
- branch / PR
- 최신 commit
- 마지막에만 추정
Heartbeat는 오래됐는데 직후에 PR이나 commit이 새로 생겼다면 “작업은 했지만 상태 업데이트를 잊었다”일 가능성이 있다.
AI도 일은 했는데 일지는 안 쓴 사건이 생긴다.
6. 피해야 할 진행 관리
main에 Heartbeat 전용 commit을 쌓지 않는다
히스토리를 더럽히고 충돌과 불필요한 CI/deploy를 만들 수 있다. Issue comment, Check, Status, 비운영 ops 영역 같은 가벼운 가변 surface가 낫다.[1]
Heartbeat마다 새 댓글을 만들지 않는다
1 run = 1개의 가변 기록이 읽기 쉽다. 10분마다 새 댓글이 생기면 진행 확인이 고고학이 된다.[1]
침묵을 즉시 실패로 판단하지 않는다
Heartbeat가 오래됐을 뿐이면 STALE_UNKNOWN. 실패는 명시적인 실패 증거가 있을 때 사용한다.[1]
비밀과 원문 로그를 쓰지 않는다
API key, token, password, private URL, 원문 사적 대화, 개인정보, 기밀 파일 내용은 진행 로그에 넣지 않는다.[1]
관측 시스템 장애 때문에 안전한 본 작업까지 멈추지 않는다
진행 API가 실패하면 다른 sink로 fallback하고 독립적으로 계속할 수 있는 작업은 계속한다.[1]
7. 실용 템플릿
State: RUNNING
Phase: TESTING
Run ID: agent-20260916-long-task
Started: 10:00
Last heartbeat: 14:05
Last material change: 13:42
Branch: feat/long-task
Head SHA: abc1234
PR: #123
Working on:
- regression tests
Completed:
- runtime implementation
- contract update
Remaining:
- regression completion
- merge verification
- production readback
Blockers:
- none
Tests:
- 84 / 127 passed so far
- no failure observed
Next checkpoint:
- finish regression, then integration
이 정도만 보여도 “멈췄나?”가 “아, 테스트 중이구나”로 바뀐다.
진행 표시의 목적은 AI를 재촉하는 것이 아니다. 사람이 불필요하게 재시작하거나 끼어들거나 같은 지시를 중복으로 보내는 일을 줄이는 것이다.
8. 정리――장시간 AI는 “똑똑함”과 “보임”을 따로 설계한다
장시간 AI에서는 다음 구분이 필요하다.[1]
- commit 없음 ≠ 정지
- Heartbeat 있음 ≠ 실제 변경 진행
- Heartbeat 오래됨 ≠ 실패
- blocker 하나 ≠ 전체 정지
- 테스트 중 침묵 ≠ 잠듦
AI가 6시간 일할 수 있다면 사람이 6시간 화면을 지켜볼 필요는 없다.
더 좋은 설계는 확인하러 왔을 때 현재 위치를 바로 알 수 있는 것이다.
이상적인 장시간 에이전트는 계속 말하는 AI가 아니다.
조용히 일하되, 들여다보면 어디까지 왔는지 보이는 AI다.
- docs/article-pipeline/agent-work-observability.md — internal operational design for long-running AI progress observability, heartbeat cadence, stale classification, testing visibility, privacy, concurrency, and terminal conditions. Updated 2026-09-15

