쓴 글공개

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

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

손실 88.3분

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

두 종류의 실패

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

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

왜 그런가

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

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

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

그래서 무엇이 잡아내나

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

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

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

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

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

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

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

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

반대편도 있습니다

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

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

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

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

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

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

88.3분을 잃은 방식

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

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

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

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

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

게임 QA 서버와 게임 제작 팀은 단계 전이를 전부 다른 에이전트의 검수 판정으로 막고 있었다. 산출물이 실제로 존재하는지, 크기가 맞는지 확인하는 지점은 검색해도 0건이었다.

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

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

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

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

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

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

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

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

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

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

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

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

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

그래도 넘기지 않기로 한 것

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

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

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

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

못 한 것

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

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

판정

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

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