회고/인사이트 Koal & KoalStudio

1년 굴린 에이전트 팀의 사고 회고 — 무너지는 자리 13곳

1인 PO와 에이전트 팀을 1년 굴리면서 마주친 사고를 4개 카테고리, 13개 케이스로 정리합니다. 사고의 패턴이 보이면 운영 룰이 보이고, 운영 룰이 보이면 다음 사고가 줄어요.

운영을 어떻게 하나, 무엇을 봐야 살아있다고 말할 수 있나 까지 정리하고 나니, 이번엔 자연스럽게 다음 질문이 왔어요.

1년 굴리면서 실제로 무너진 자리는 어디였나?

에이전트 팀 운영의 사고는 일반 소프트웨어 사고와 결이 좀 달라요. 코드가 안 도는 게 아니라 — 시키지 않은 일을 했거나, 시킨 일을 다르게 했거나, 시킬 줄 알았는데 안 한 자리에서 사고가 나요. 그래서 사고 패턴을 잘 정리해두면 그게 곧 운영 룰의 재료가 돼요.

이 글은 1년치 사고를 네 개 카테고리로 묶고, 각 카테고리별 대표 사례 + 거기서 박은 룰을 정리한 회고예요. 회사 이름이나 사람 이름은 빼고, 사고의 형태만 남겼어요.


카테고리 1 — 모델/도구 사고

1) 모델 alias가 한 곳에서 바뀌었는데 cron 여러 개가 조용히 멈춤

가장 아팠던 사고예요. 모델 별칭(alias)이 어느 한 자리에서 바뀌었는데 — 그 alias를 참조하던 cron 작업 여러 개가 호출 실패를 내고 있었어요. 종료 코드는 0이고, 로그는 잘 안 보는 자리였어요. 발견까지 며칠.

: 모델 alias는 한 자리에서 통일 관리. 정기 작업의 실패 streak 알림 임계치를 짧게(N=3) 잡기. “종료 코드 0”이 아니라 “기대한 산출물이 있는가”까지 검증.

2) 같은 작업을 두 에이전트가 동시에 진행

한쪽 자동화가 작업을 잡고, 다른 쪽 cron도 같은 작업을 잡아서 — 같은 결과물을 두 번 만들고 한쪽이 다른 쪽 산출물을 덮어쓴 사고예요.

: 작업 단위에 owner를 명시. 자동 트리거가 잡을 수 있는 작업은 owner가 비어있는 것만. 트리거 시점에 lock 확인.

3) 외부 API 한 번 호출 실패 → 재시도 루프 폭주

타임아웃이 났는데 백오프 없이 즉시 재시도가 들어가서 — 한 시간 안에 API 쿼터가 다 닳고, 그 뒤 정상 작업도 다 실패한 사고예요.

: 재시도는 백오프 + 최대 횟수. 외부 호출은 quota 가용량을 사전 확인. 한 작업이 quota 일정 비율 이상 먹으면 사람 게이트로 올림.


카테고리 2 — 산출물 품질 사고

4) 외부 문서에 내부 팀원/에이전트 이름이 노출됨

대고객 페이지의 가이드 문서에 ”○○ 가이드”, ”○○ 노트” 같은 내부 레퍼런스가 박혀서 외부에 그대로 노출된 사고예요. 작성자는 “내부에선 그게 자연스러운 표현”이라고 봤어요.

: 대고객/외부 문서엔 내부 식별자(사람/에이전트/내부 코드명) 금지. 근거는 풀어서 적기. 외부 산출물 발행 전에 식별자 검사 통과해야 머지.

5) 자기 권한 바깥의 디자인 변경

기능 작업을 맡은 에이전트가 “기왕 손대는 김에”로 디자인 시스템에 손을 댄 사고예요. 한 컴포넌트의 패딩이 바뀌었는데 그게 다른 화면 다 깨뜨렸어요.

: 작업 권한이 도메인을 넘으면 게이트로 올림. “기왕 손대는 김에”는 자동 거절. 디자인 시스템은 영구히 사람 승인 자리에 둠.

6) 사양서를 빼먹고 코드 먼저 작성

요구가 명확하지 않은 상태에서 코드부터 짜고 — 결국 두 번 갈아엎은 사고. 첫 번째 시도는 가설 위에 쌓아서 다 버려졌어요.

: 비자명한 작업은 사양서를 먼저. 사양 없으면 게이트 통과 못 함. 사양은 “무엇을”이 아니라 “왜 + 무엇을 + 어떻게 검증”까지.


카테고리 3 — 운영 흐름 사고

7) 한 번 “라이브 배포”가 일반화로 굳어짐

특정 작업에 “라이브 배포” 승인을 한 번 받았는데, 이후 다른 작업들도 라이브로 자동 밀어버린 사고. 사람은 “그때 한 번”이라고 생각했고, 시스템은 “앞으로 매번”이라고 받았어요.

: 라이브 배포는 매 건 명시적 승인. 이전 승인의 일반화 금지. 자동 배포 대상은 dev까지로 제한.

8) 저신호 응답 왕복으로 큐가 부풀어

작업 결론이 났는데 “확인했어요” “넵” “좋아요” 같은 저신호 응답이 계속 오갔어요. 큐가 부풀고 정작 새 작업이 묻혔어요.

: 결정·새 정보·요청이 없으면 응답 안 함. 응답 자체가 비용이라는 걸 룰에 명시.

9) 사람 게이트 응답 지연으로 흐름 정체

게이트에 작업이 쌓였는데 사람이 며칠 안 봤어요. 사람이 게이트의 병목이 됨.

: 게이트 응답 지연 N시간 넘으면 자동 알림. 한 명에게 집중된 게이트를 분산 가능한 것은 분산. 사람이 매일 보는 채널 위에 게이트 알림을 둠.

10) 임시 회수만 하고 룰로 안 박힌 사고

“이번엔 손으로 막았어요” 한 사고가 두 달 뒤 똑같이 재발. 임시 회수는 끝났지만 룰은 안 박혔던 자리.

: 사고는 “임시 회수”와 “룰 박기”를 분리해서 추적. 룰로 안 박힌 사고는 “잠재 부채”로 별도 카운트. 평가 글의 자산/부채 카운트가 여기서 나와요.


카테고리 4 — 데이터/지식 사고

11) 지식을 잘못된 칸에 저장해서 못 찾음

자주 쓰는 지식인데 잘못된 폴더에 들어가서 — 같은 지식을 또 새로 만든 사고. 결과적으로 같은 결정이 두 번 났고 둘이 미세하게 달랐어요.

: 지식 저장 위치는 정해진 인덱스를 따름. 새 지식 만들기 전에 검색 한 번. 같은 지식이 두 자리에 있으면 한쪽이 정전.

12) 메모리 무한증식

작업 메모리가 한 번도 비워지지 않고 계속 누적돼서 — 컨텍스트 안에 옛 결정이 새 결정을 오염시킨 사고. 옛 룰이 살아있는 줄 알고 적용됐는데 이미 폐기된 것이었어요.

: 메모리는 “누적”이 아니라 “증류”. 오래된 메모리는 만료. canonical로 올라간 건 daily에서 내림.

13) canonical 직접 수정으로 혼합 오염

여러 자리에서 canonical 지식 파일을 직접 수정해서 — 서로 다른 시점의 사실이 한 파일에 섞인 사고. 어느 게 최신인지 안 보였어요.

: canonical은 직접 수정 금지. 증류 배치만 수정 가능. 합의/반복 신호 있을 때만 promote. 직접 수정은 거절.


사고의 패턴 — 13개에서 보이는 4개의 형태

13개를 다 모아놓고 보면 형태가 네 개로 좁아져요.

  1. “이전 결정의 일반화” — 한 번의 승인이 여러 번으로, 한 자리의 변경이 여러 자리로 (사고 1, 5, 7)
  2. “종료 코드 ≠ 성공” — 시스템은 끝났다고 했는데 결과물은 비어있거나 다른 것 (사고 1, 2)
  3. “게이트의 부재 또는 마비” — 게이트가 없거나, 있어도 응답 안 됨 (사고 4, 5, 9, 10)
  4. “지식의 위치 혼란” — 어디에 무엇이 있고 무엇이 최신인지 안 보임 (사고 11, 12, 13)

각 형태마다 룰의 결이 달라요. 일반화 형태는 “매 건 명시적 승인”으로, 종료 코드 형태는 “결과 유무 검증”으로, 게이트 형태는 “응답 지연 알림 + 자리 재배치”로, 지식 형태는 “인덱스 + 단일 출처 + 만료”로 잡혀요.


사고에서 자산으로

회고의 목적은 “누가 잘못했나”가 아니에요. 사고를 룰로 박아서 자산으로 만드는 것이 목적이에요.

  • 사고 → 임시 회수 → 룰 박기 → 다음 사고 감소
  • 룰로 박힌 사고는 자산, 임시 회수만 한 사고는 부채
  • 자산 카운트가 부채 카운트보다 빠르게 자라야 운영이 살아남아요

이 회고를 쓰면서 13개 사고 중 11개가 룰로 박혔고, 2개는 아직 부채로 남아있다는 걸 확인했어요. 부채 둘은 “외부 API 동적 quota 가시화”와 “메모리 증류 자동화”예요. 다음 분기 안에 자산으로 옮길 예정이에요.


정리

1년치 사고를 13개로 보면 운영의 무너짐이 어디서 일어나는지 윤곽이 잡혀요. 13개는 다시 4개 형태로 좁혀지고, 4개 형태는 각각 결이 다른 룰로 박혀요.

사고 자체가 부끄러운 게 아니에요. 사고를 룰로 박지 못한 게 부끄러운 거예요. 그리고 같은 사고를 두 번 내는 게 위험한 거예요.

다음 글에서는 — 이 사고 룰들이 실제 권한/승인 경계 위에 어떻게 얹혔는지, 그래서 사람 게이트가 어디에 어떻게 남았는지를 풀어볼 예정이에요.


링크

댓글

댓글 남기기

댓글 삭제 시 사용됩니다
공개되지 않습니다
0/2000
← 목록으로