본문 바로가기

실전 원칙

자동화에 판단을 맡길 때 '긴급도'와 '실행 권한'을 반드시 분리해야 하는 이유

문제: '급하다'와 '자동으로 고쳐라'를 한 덩어리로 묶는 실수

AI에게 "알아서 판단하고 조치하라"고 맡기고 싶은 순간이 온다. 개선 아이디어를 수집하고, 우선순위를 매기고, 급한 건 알려주고, 나아가 스스로 고치게 하는 자가발전 루프다. 이때 가장 흔한 설계 실수는 '긴급도 판단'과 '자동 실행 권한'을 하나의 결정으로 묶는 것이다.
"이 문제는 A등급(가장 급함)이다" → "그러니 바로 자동 수정한다"로 이어지는 흐름이 대표적이다. 언뜻 합리적으로 보인다. 급한 문제니까 빨리 고치는 게 맞지 않나? 하지만 급하다는 것과 AI가 손대도 안전하다는 것은 전혀 다른 질문이다. 급한 결제 로직 오류는 급하면서도 절대 AI가 자동으로 건드려서는 안 되는 코드다.

실제 개발 기록(개선 루프 1단계, improvement_judge.py)에서 얻은 원칙은 명확하다. 자가발전 루프의 안전 열쇠는 "무엇을 자동화하는가"를 두 축으로 쪼개는 것이다. 하나는 긴급도(얼마나 빨리 알려야 하는가), 다른 하나는 자율화 등급(자동으로 코드에 손대도 되는가, 돈에 닿는가). 이 글은 그 분리를 어떻게 실제 코드 구조로 만드는지 다룬다.


왜 위험한가: 긴급도를 LLM에 맡기면 실행 권한도 흔들린다

두 축을 섞으면 이런 사고가 난다. LLM이 긴급도를 판단하는데, 긴급도가 곧 실행 트리거이므로 LLM의 오탐 한 번이 코드 자동 수정으로 직결된다. LLM은 확률적으로 답을 만드는 도구라 같은 입력에도 다른 등급을 낼 수 있고, 프롬프트 인젝션이나 맥락 오해로 엉뚱한 걸 '급함'으로 올릴 수 있다.
여기서 핵심 원칙 하나. 위험도(자동 수정 허용 여부) 판단을 절대 LLM에 맡기지 않는다. 위험도는 코드로 고정한다. 즉 경로와 키워드 같은 결정론적 규칙으로 "이 파일은 돈에 닿으니 자동 수정 금지"를 못박아 둔다. 그러면 LLM이 A(최고 긴급)로 판정하더라도, 돈 경로에 걸린 문제는 자동 수정이 일어나지 않고 사람에게만 알림이 간다.

긴급도(A/B/C)LLM 2모델 교차알림 속도알림 오탐/누락 (되돌리기 쉬움)
자율화 등급코드(경로·키워드)자동 수정 허용 여부돈·데이터 손상 (되돌리기 어려움)

표의 마지막 열이 왜 두 축을 나눠야 하는지 요약한다. 알림이 틀리면 텔레그램 하나 더 오는 정도지만, 실행 권한이 틀리면 결제·배포 코드가 멋대로 바뀐다. 피해 규모가 다른 두 결정을, 신뢰도가 다른 두 판단자에게 맡기는 것이 분리의 본질이다.


확인 절차: 긴급도는 2모델 합의로, 권한은 결정론으로

실제 구현에서 긴급도 판정은 GPT와 Claude 두 모델의 교차 판정으로 만들었다. 만드는 놈과 채점하는 놈을 분리하고, 서로 다른 모델로 다양성을 확보하는 루프 엔지니어링 원칙을 그대로 옮긴 것이다. 합의 규칙(consensus())은 다음과 같다.

# 두 모델의 등급을 합의로 착지시키는 규칙
# A ⟺ 둘 다 A     (알림은 만장일치일 때만)
# C ⟺ 둘 다 C     (폐기도 만장일치일 때만)
# 그 외          → B
# A ↔ C 강한 이견 → B + conflict 태그

이 규칙의 의도는 오탐과 신호 유실을 동시에 막는 것이다. 알림(A)은 두 모델이 모두 급하다고 해야만 울린다 → 오탐 감소. 폐기(C)도 둘 다 무시해도 된다고 해야만 버린다 → 중요 신호 유실 방지. 한쪽이라도 애매하면 B로 착지시켜 사람이 볼 수 있게 남긴다.

장애 처리도 설계에 넣었다. 한쪽 API가 실패하면 최대 B로 제한하고, 둘 다 실패하면 결정론 폴백으로 내려간다. 즉 절대 죽지 않는다. Claude는 anthropic SDK(claude-opus-4-8 기본, env override), GPT는 openai SDK(모델 env 지정)를 쓰고, 키는 공용 정본 .env(RULE-009) 한 곳에서 읽는다. LLM 호출은 유료 크레딧이 나가므로 이 판정 기능 자체가 기본 꺼짐(ENABLE_HUB_LLM_JUDGE, 기본 off) 상태이고, 켰을 때만 상위 N개에 대해 판정·A 알림·등급 태그를 붙인다.

  1. 수집·triage는 결정론 코드(collect.py/propose.py)로만 처리한다. 여기엔 LLM을 넣지 않아 stdlib만으로 돌아간다.
  2. 긴급도가 필요한 상위 문제만 improvement_judge.py로 넘겨 2모델 판정을 받는다.
  3. 합의 결과가 A면 텔레그램 알림, C면 조용히 폐기 후보, 나머지는 B로 표기한다.
  4. 자동 수정 허용 여부는 이 등급과 별개로 코드 규칙(경로·키워드)이 결정한다.
  5. SDK는 지연 import해서 기능이 꺼져 있으면 로드조차 안 한다.

체크리스트: 두 축이 정말 분리됐는지 검증

  • 긴급도 판정 결과가 직접 코드 수정을 트리거하지 않는가? (A라도 알림까지만)
  • 자동 수정 허용 여부를 LLM이 아니라 코드 규칙이 결정하는가?
  • 돈·결제·배포 경로가 경로/키워드로 자동 수정 금지 목록에 고정되어 있는가?
  • LLM 판정에 서로 다른 모델(또는 사람) 최소 2개의 교차 검증이 있는가?
  • API 실패·비용 초과 같은 상황에서 안전한 쪽으로 폴백하는가? (죽거나, 더 위험한 자동 실행으로 빠지지 않는가)
  • 비용이 드는 판정 기능이 기본 꺼짐이고 명시적으로 켜야 동작하는가?
  • 자동으로 손댄 뒤 되돌릴 방법과 사람 승인 게이트가 있는가?

실제 설계 문서(improvement-loop-design.md)에서는 자율화 등급을 3단계로 나눴다. 등급0은 자동 수정, 등급1은 원클릭(사람이 버튼 한 번), 등급2는 영구적으로 사람 게이트다. 그리고 돈에 닿는 경로는 긴급도가 A라도 자동 수정 대상에서 제외된다. 긴급도가 높다고 자율화 등급이 올라가지 않는다는 것이 두 축 분리의 실전적 결론이다.


적용 예시: 내 자동화에 두 축을 넣는 법

메일 자동 응답 봇을 예로 들자. 두 축을 섞은 설계는 "긴급 메일이면 자동으로 답장 발송"이다. LLM이 긴급하다고 오판하면 엉뚱한 답장이 나간다. 두 축을 나눈 설계는 이렇다.

  1. 긴급도(LLM): 메일 내용을 2모델로 판정해 A/B/C. A면 사용자에게 즉시 푸시 알림.
  2. 실행 권한(코드): 발신 주소가 사내 화이트리스트에 있으면 초안까지만 자동 생성(등급1, 사람이 보내기 버튼). 외부·계약·금액 언급 키워드가 있으면 자동 발송 영구 금지(등급2).

이렇게 하면 LLM이 아무리 "이건 급하니 바로 답장"이라고 판단해도, 발송 여부는 코드 규칙이 잡는다. 급한 알림은 빨리 받되, 돈·계약이 걸린 메일은 절대 자동으로 나가지 않는다.
배포 파이프라인도 같다. 로그 이상을 LLM이 A로 올리면 알림은 즉시 가지만, 롤백·배포 명령 실행은 코드가 정의한 승인 절차를 반드시 거친다. 알림 속도는 LLM에게, 손대는 권한은 코드에게가 공식이다.


한계와 주의사항

이 설계는 개선 루프 1단계 실제 구현(테스트 8건)에서 만든 판정·알림까지의 범위다. 자동 수정 단계(등급0/1)는 설계 문서에만 있고 이 기록 시점에는 관찰·알림 전용이었다. 즉 "AI가 자동으로 고친다"는 것은 아직 실행 결과가 아니라 계획이라는 점을 구분해야 한다.
2모델 교차 판정이 오탐을 줄이긴 하지만 없애지는 못한다. 두 모델이 같은 편향을 공유하면 나란히 틀릴 수 있다. 그래서 만장일치일 때만 A/C로 착지시키는 보수적 규칙과, 사람 게이트를 병행한다. LLM 판정을 신뢰도 100%로 다루지 말고, 항상 되돌릴 경로와 승인 단계를 남겨두는 것을 권한다.
또한 LLM 호출은 유료다. 기본 꺼짐으로 두고 상위 소수 항목에만 적용한 이유이기도 하다. 비용·오탐·복잡도를 고려해, 결정론으로 충분히 처리되는 부분(수집·triage)까지 LLM으로 덮지 않는 것이 안전하고 저렴하다. 마지막으로 여기 소개한 규칙과 등급은 특정 환경의 예시이며, 자신의 위험 경로에 맞게 화이트리스트와 금지 키워드를 직접 정의해야 한다.

반응형