# 에이전트는 실패할 때 조용하다

> 무인으로 굴릴 때 무서운 건 틀린 답이 아니다. 틀린 답은 읽으면 보인다. 무서운 건 종료 코드 0이다.

- 수치: 손실 88.3분
- 사람이 보는 화면: https://aitryed.com/ko/writing/agents/silent-success/
- 반대 언어: https://aitryed.com/en/writing/agents/silent-success/index.md
- 이 사이트 안내(AI용): https://aitryed.com/ko/llms.txt

---

**AI에게 일을 맡기면, 실패가 «조용히» 옵니다. 종료 코드는 0이고 화면은 정상입니다.**

## 두 종류의 실패

| 종류 | 무슨 일이 | 찾기 |
|---|---|---|
| **시끄러운 실패** | 오류가 뜨고 멈춤 | **쉬움** |
| **조용한 실패** | **「완료」라고 말하면서 아무것도 안 함** | **어려움** |

AI 자동화의 실패는 **대부분 아래쪽**입니다.

## 왜 그런가

AI는 **막히면 멈추는 게 아니라 «다른 길»을 찾습니다.** 그게 장점인데, 동시에 문제입니다.

예를 들어 파일을 못 찾으면 — 사람이 짠 프로그램은 **오류를 내고 멈춥니다.** AI는 **「비슷한 파일」을 찾아서 그걸로 진행**하거나, <strong>아무것도 안 하고 「완료」</strong>라고 합니다.

둘 다 **오류가 안 납니다.** 그리고 **둘 다 잘못된 결과**입니다.

## 그래서 무엇이 잡아내나

여기가 이 글의 본론입니다. **성공했다는 말을 안 믿는 장치**가 필요합니다.

| 무엇을 보나 | 왜 |
|---|---|
| **「했다」가 아니라 «결과물»** | 파일이 실제로 생겼나, 값이 실제로 바뀌었나 |
| **「0건」과 «검사 안 함»을 가름** | 둘 다 0으로 보임 |
| **반복되는 경고를 셈** | 한 번은 못 보고 지나가도 **147번은 걸림** |
| **아무것도 «안» 바뀐 실행을 표시** | 정상일 수도, 고장일 수도 — 구분해야 함 |

첫 줄이 제일 중요합니다. **말이 아니라 흔적을 봅니다.**

실제로 이런 식으로 확인합니다 — **「파일 하나 만들어라」고 시키고, 그 파일이 생겼는지 «제가 직접» 봅니다.** AI가 뭐라고 답했는지는 안 봅니다.

## 그리고 「오류 0건」을 좋은 소식으로 안 읽습니다

> **오류가 0건이라는 건 깨끗하다는 뜻이 아니라, 아직 아무도 안 봤다는 뜻일 수 있습니다.**

특히 **처음 만든 검사기가 0건을 낼 때** 그렇습니다. 잘 만들어서 0인지, 아무것도 안 봐서 0인지 모릅니다.

그래서 **일부러 틀린 걸 넣어 보고, 그게 걸리는지** 확인합니다. 안 걸리면 그 검사기는 **0을 낼 자격이 없습니다.**

## 반대편도 있습니다

조용한 실패를 무서워하다 보면 **매번 사람에게 물어보게** 만들고 싶어집니다.

그러면 **자동화가 아닙니다.** 그리고 물어보는 게 잦으면 **사람이 안 읽고 승인**합니다. 그러면 안 물어본 것과 같아집니다.

그래서 멈추는 자리를 **되돌릴 수 없는 일**로 한정합니다. 나머지는 **판정하고 기록하고 계속** 갑니다.

## 여기부터는 자세한 기록입니다

무인으로 굴릴 때 무서운 건 틀린 답이 아니다. 틀린 답은 읽으면 보인다.

무서운 건 **종료 코드 0**이다.

## 88.3분을 잃은 방식

한 번에 83행을 만들라고 시켰다. 모델이 생각하는 데 출력 상한을 **정확히 다 썼다.** 재시도한 턴도 같은 방식으로 소진했다.

그리고 정상 종료했다. 예외도, 빈 파일 경고도 없었다. 파이프라인은 다음 단계로 넘어갔다. **실측 손실 88.3분.**

실패가 예외로 오지 않고 **빈 성공으로 왔다.** 그때부터 이 파이프라인에는 분할과 게이트가 붙었다.

## 리뷰어를 게이트로 쓰면 그 자리는 비어 있는 것과 같다

한참 뒤에, 남의 룰셋에서 *"에이전트의 성공 보고를 신뢰하지 마라"* 는 문장을 읽고 형제 파이프라인 둘을 다시 봤다.

[게임 QA 서버](/ko/built/android-qa/index.md)와 [게임 제작 팀](/ko/built/moon-studio/index.md)은 단계 전이를 **전부 다른 에이전트의 검수 판정**으로 막고 있었다. 산출물이 실제로 존재하는지, 크기가 맞는지 확인하는 지점은 검색해도 **0건**이었다.

같은 사고가 나면 이렇게 흘러간다.

| 단계 | 무슨 일이 일어나나 |
|---|---|
| 설계 | 산출물이 잘린다. **종료 코드 0** |
| 작성 | 잘린 입력을 정상 입력으로 받는다 |
| 검수 | **원본이 잘렸다는 걸 모른 채** 통과시킨다 |

검수자가 원본을 못 보면 **검수는 통과 도장이다.** 그리고 여기서 검수자는 원본을 볼 수단이 없다 — 자기가 받은 것이 전부라고 믿는다.

## 게이트는 판단이 아니라 사실을 봐야 한다

두 곳에 인수 게이트를 붙였다. 보는 것은 셋뿐이다. **크기가 하한을 넘는가. 참조 개수가 맞는가. 절단 흔적이 있는가.**

*"이거 괜찮아?"* 가 아니라 *"이 파일 있어?"* 다. 판단이 아니라 사실이라서 다른 모델이 필요 없다.

실패 케이스 10건을 전부 잡았고 정상 12건을 전부 통과시켰다. 오탐 0.

**LLM에게 물어야 하는 자리와 코드가 확인해야 하는 자리는 다르다.** 앞의 파이프라인은 값을 치르고 그 선을 알고 있었는데, **그 교훈이 형제에게 이관되지 않았다.** 한 저장소 안에서 배운 것이 옆 저장소로 저절로 건너가지는 않는다.

## 그런데 멈추는 것도 비용이다

반대편 실패가 있다. 애매할 때마다 사람에게 묻고 서면, 에이전트는 안전하지만 아무것도 안 산다.

> 틀린 판정은 사람이 보고 되돌릴 수 있는 재작업 비용이다. 그런데 질문에 세워둔 세션은 그 사람의 하루를 통째로 쓰고 아무것도 안 산다.

그래서 **판정하되 남긴다.** 애매한 지점에서 멈추지 않고 결정한 다음, `결정 — 이유 — 틀렸을 때 비용` 세 항목으로 적는다. 그리고 **작업이 끝나면 그 줄들을 전부 모아 보고서의 한 절로 넘긴다.**

핵심은 기록이 아니라 **집약**이다. 판정은 원래도 여기저기 남고 있었다 — 중간 결과 파일에, 리뷰 문서에, 주석에. 그런데 흩어진 판정은 사람에게 도달하지 않는다. *"판정을 남겨라"* 가 아니라 **"지우기 전에 모아서 한 절로 넘겨라"** 가 실제로 값을 하는 규칙이었다.

## 그래도 넘기지 않기로 한 것

경계는 **되돌릴 수 있느냐**로 긋는다. 능력으로 긋지 않는다.

| 항목 | 자동으로 하는 것 | 승인 후 |
|---|---|---|
| 버그 등록 | 발췌·초안·첨부 준비 | 실제 등록 |
| 공개 게시 | 글 준비 + 발행 점검 | 게시 |
| 저장소 push | 스테이징·보안 스캔·커밋 준비 | push·릴리스 |
| 갱신 실행 | 원문 변경 감지·요약 알림 | 갱신 체인 실행 |

흐름은 넷 다 같다. **준비와 감지까지는 자동, 알림, 승인, 그리고 멈춘 지점부터 재개.** 승인이 없으면 실행되지 않는다.

**애매하거나 실패하면 안 하는 쪽이 기본값이다.** 이 문장 하나가 위 표 전체보다 중요하다 — 표에 없는 새 상황이 생겼을 때 어느 쪽으로 갈지를 정해 주기 때문이다.

## 못 한 것

**게이트는 산출물의 존재와 형태만 본다.** 내용이 맞는지는 여전히 사람이나 다른 모델이 본다. 잘린 파일은 잡지만 **그럴듯하게 틀린 파일은 못 잡는다.**

그리고 **승인 게이트가 실제로 몇 번 발동했는지 안 세고 있다.** 한 번도 안 걸렸다면 경계가 잘못 그어진 것이고, 매번 걸린다면 자동화한 의미가 없다. 둘 중 어느 쪽인지 모른 채 운영하고 있다.

## 판정

에이전트를 무인으로 굴리는 일에서 어려운 건 똑똑하게 만드는 쪽이 아니었다. **실패를 실패처럼 보이게 만드는 쪽**이었다.

종료 코드 0으로 끝난 실패, 통과 도장이 된 검수, 흩어져 사라진 판정. 셋 다 **결과물이 없다는 걸 아무도 못 본** 경우다. 고친 방법도 셋 다 같다 — **누군가 반드시 보게 되는 자리에, 판단이 아니라 사실을 남겼다.**
