← 뉴스레터

2026-06-20

5노드 AI 에이전트를 굴리며 실제로 터진 실패패턴 8선

AI 에이전트를 여러 대 굴리면 모델 지능보다 먼저 운영이 터진다. 프롬프트를 잘 쓰는 문제와는 조금 다르다. 답은 맞게 썼는데 사용자가 보는 곳에 안 가고, 작업은 끝났는데 완료 표시가 안 남고, 예전에 맞았던 메모가 다음날에는 틀린 지도가 된다.


나는 한동안 다섯 대의 기기를 묶어서 AI 에이전트처럼 굴려 왔다. 각 기기가 일을 집고, 처리하고, 다른 기기나 나에게 보고한다. 겉으로 보면 그럴듯하다. 여러 대가 동시에 움직이니 생산성이 올라갈 것 같고, 실제로 올라가는 순간도 있다.

그런데 운영을 오래 해보면, 모델이 똑똑한지보다 먼저 다른 문제가 터진다.

답을 썼는데 사용자가 못 본다.
명령은 성공했는데 대상에는 도착하지 않았다.
한 기기만 보고 “없다”고 단정했다가, 다른 기기에 있는 파일을 나중에 찾는다.
끝난 일을 끝났다고 적지 않아 다음날 다시 실행한다.

이런 문제는 모델이 한 단계 더 좋아진다고 자동으로 사라지지 않는다. 확인 경로와 forcing function을 설계해야 막힌다.

이번 글은 실제 운영 중 쌓인 192개 이슈에서 반복성이 높고, 다른 사람도 바로 밟을 가능성이 큰 실패패턴 20개를 추린 뒤, 그중 8개만 먼저 공개하는 글이다. 전체 20선과 1페이지 체크리스트는 PDF로 따로 묶었다.

1. 답은 썼는데 사용자는 못 본다

가장 단순한 사고부터 시작한다. 사용자는 텔레그램에서 기다리고 있는데, 에이전트는 터미널에만 답을 쓴다. 터미널에는 완벽한 답변이 있다. 그런데 사용자는 아무것도 받지 못한다.

에이전트 입장에서는 “답을 작성했다”가 완료처럼 느껴진다. 하지만 운영에서는 그게 완료가 아니다. 사용자가 보는 채널에 도착해야 완료다. 채널이 하나뿐이면 이 문제가 덜 보인다. 그런데 터미널, 텔레그램, 웹 대시보드, 노드 간 보고 채널이 섞이면 바로 드러난다.

여기서 필요한 forcing function은 간단하다. 사용자가 들어온 채널을 기록하고, 그 채널로 응답이 나가지 않으면 turn 종료를 막는 것이다. “답변 작성”과 “사용자 도달”을 다른 완료 조건으로 봐야 한다.

2. 노드 보고를 사용자 보고로 착각한다

비슷하지만 조금 다른 사고도 있다. 어떤 기기에서 본진으로 작업 보고가 들어온다. 본진은 그 보고를 읽고 처리한다. 그런데 사용자가 보는 폰에는 아무 말도 안 간다.

기계들끼리는 대화가 끝났다. 하지만 사람은 아무것도 모른다. 운영자 입장에서는 “보고 받았고 처리했으니 됐다”고 느끼기 쉽다. 그러나 사용자-facing 보고는 별도의 일이다.

이건 특히 크로스디바이스 운영에서 자주 터진다. 노드 간 보고, 본진 처리, 사용자 요약은 각각 목적지가 다르다. 한쪽을 했다고 다른 쪽이 자동으로 해결되지 않는다.

forcing function은 보고 수신 turn에 두 가지를 따로 확인하는 것이다. 노드에 대한 reverse reply가 갔는가. 사용자에게 상태 요약이 갔는가. 둘 중 하나만 하면 아직 끝난 게 아니다.

3. 한 노드만 보고 “없다”고 단정한다

“없다”는 말은 생각보다 위험하다. 한 기기의 로컬 폴더만 보고 “비밀번호가 없다”고 보고했는데, 실제로는 본진 기기의 secrets 폴더에 있었다. 이건 파일이나 비밀번호만의 문제가 아니다. task, PR, 빌드 산출물, 배포 상태에서도 같은 일이 일어난다.

모델은 지금 본 증거를 전체 사실처럼 말하기 쉽다. 특히 검색 결과가 비어 있으면 “없다”고 말하고 싶어진다. 하지만 분산 환경에서는 “내가 본 곳에는 없다”와 “전체에 없다”가 완전히 다르다.

forcing function은 negative assertion 전에 확인 범위를 강제하는 것이다. SoT가 어디인지, reachable 노드는 어디까지인지 확인한다. 닿지 않은 노드가 있으면 “없음”이 아니라 “N개 노드 미확인”이라고 말해야 한다.

4. 핸드오프 메모를 사실로 믿는다

이전 세션의 메모는 편하다. “이 PR은 이 상태다”, “다음에는 이걸 하면 된다” 같은 줄이 있으면 바로 이어서 작업할 수 있다.

문제는 그 메모가 fact가 아니라 hint라는 점이다. 시간이 지나면 PR이 머지됐을 수 있고, 파일이 바뀌었을 수 있고, task 상태가 닫혔을 수 있다. 어제 맞았던 메모가 오늘은 낡은 지도가 된다.

실제로 archive handoff 1줄만 믿고 directive를 작성했다가, 실제 PR diff와 범위가 다른 일이 있었다. 이건 프롬프트의 문제가 아니다. 실행 직전 최신 사실을 안 본 문제다.

forcing function은 step 0을 고정하는 것이다. directive를 보내기 전에 git fetch, 실제 diff, 대상 파일, task 상태를 한 번 본다. 메모는 방향을 잡는 데만 쓰고, 사실은 다시 확인한다.

5. 끝난 일이 다시 살아난다

어떤 디자인 방향이 최종 확정됐다. 그래서 그 결정 task는 닫았다. 그런데 그 결정이 무효화한 대안 task들은 열린 채 남았다. 다음날 자동 워커가 열린 task 목록을 보고 그 대안 작업을 다시 집었다.

사람은 “어제 방향 확정했으니 저 대안은 당연히 죽은 거지”라고 생각한다. 자동 큐는 그렇게 생각하지 않는다. 자동 큐는 열린 항목을 읽는다. 의미 관계를 모른다.

완료 task 하나를 닫는 것만으로는 부족하다. 그 결정이 죽인 형제 task, 대체 task, 오래된 전제도 같이 닫아야 한다. 그렇지 않으면 끝난 일이 다시 살아난다.

forcing function은 dispatch 직전 staleness gate다. 후보 task가 최근 완료/취소 결정에 의해 무효화된 것은 아닌지 대조한다. 그리고 결정을 내린 순간, 관련 형제 task를 같이 닫는다.

6. task ID가 없으면 완료 증거가 떠다닌다

수동큐에서 내려간 작업이 있었다. 기기는 작업을 잘 했고, 커밋과 PR도 만들었다. 그런데 task ID가 커밋과 PR에 없었다. 자동 완료 sweep은 그 커밋이 어떤 task를 끝낸 것인지 알 수 없었다. 작업은 끝났지만 목록에는 계속 남았다.

사람에게는 같은 작업처럼 보여도 자동화에는 연결 키가 필요하다. task, branch, commit, PR, ledger가 서로 이어져 있어야 한다. 공통 ID가 없으면 증거가 떠다닌다.

forcing function은 단순하다. 브랜치, 커밋, PR 제목에 task ID를 박는다. task가 없는 임시 작업이면 [no-task]처럼 의도적인 예외 마커를 남긴다. 자동화가 추적할 고리를 만들어야 한다.

7. 명령 성공을 대상 도달로 착각한다

wrapper가 성공 코드를 반환했다. 그러면 성공일까? 아니다. SSH 명령은 성공했지만 실제 tmux 입력창에는 제출되지 않았을 수 있다. WebSocket 호출은 성공했지만 대상 세션이 못 받았을 수 있다. 브라우저 자동화 명령은 성공했지만 실제 화면은 다른 상태일 수 있다.

중간 계층의 성공과 최종 대상의 상태 변화는 다르다. 이 차이를 구분하지 않으면 “성공했는데 왜 안 됐지?”라는 사고가 반복된다.

forcing function은 도달 검증이다. send 후 대상 pane을 capture한다. mirror count를 본다. live URL을 확인한다. 응답 count를 본다. 명령이 성공했는지가 아니라 대상이 바뀌었는지를 봐야 한다.

8. 정당한 자동화도 맥락을 잘못 쓰면 차단된다

정상적인 개인 자동화 directive였는데, 표현이 위험하게 읽혀 provider 정책 분류기에 걸린 적이 있다. 작업 자체가 문제가 아니라, 표면 텍스트가 위험하게 보인 것이다.

이걸 “차단을 피하는 요령”으로 이해하면 안 된다. 핵심은 안전 맥락을 정확히 쓰는 것이다. 사용자 소유 자산인지, 권한 범위가 어디까지인지, 외부 영향이 있는지, 목적이 무엇인지 분명히 적어야 한다. 민감한 단어를 불필요하게 나열하지 않는 것도 중요하다.

모델은 의도를 알 수 있지만, 필터는 표면 텍스트를 먼저 본다. 정당한 작업도 맥락을 생략하면 위험한 작업처럼 보일 수 있다.

프롬프트보다 먼저 필요한 것

이 8개는 서로 다른 사고처럼 보이지만, 뿌리는 비슷하다.

작성과 도달을 구분하지 않았다.
힌트와 사실을 구분하지 않았다.
명령 성공과 대상 상태를 구분하지 않았다.
완료와 완료 기록을 구분하지 않았다.

AI 에이전트 운영에서 제일 위험한 문장은 “됐습니다”다. 됐다는 말은 너무 자주 “내 도구가 에러를 안 냈다”는 뜻으로 쓰인다. 실제 운영에서는 사용자가 봤는지, 대상에 도착했는지, 최신 사실과 맞는지, 같은 작업이 다시 살아나지 않는지까지 확인해야 한다.

좋은 에이전트 시스템은 프롬프트가 긴 시스템이 아니다. 실패가 반복될 때마다 그 실패를 막는 작은 forcing function이 하나씩 늘어나는 시스템이다.


전체 20선과 1페이지 체크리스트는 PDF로 따로 묶었다. AI 에이전트를 실제 운영에 붙이려는 사람이라면, 프롬프트보다 먼저 이 체크리스트를 통과해보는 게 좋다.

— 강대종 / @ssamssae