
외부 앵커(External Anchor)를 통해 자기 개변하는 AI 에이전트를 안전하게 운영하기 ― builder≠checker / 통치막 /
요약
자기 개변(Self-improvement)을 수행하는 AI 에이전트가 스스로의 안전장치를 해제하지 못하도록 설계하는 보안 프레임워크를 제안합니다. Builder와 Checker 모델을 분리하고, 기계적 테스트(L0)와 별개 계통의 LLM 검증을 결합한 다층 방어 구조를 핵심으로 합니다.
핵심 포인트
- Builder와 Checker 모델을 서로 다른 계통(Claude vs GPT)으로 분리하여 맹점 상쇄
- 가드레일과 승인 로직은 에이전트가 스스로 수정할 수 없는 '통치막'으로 격리
- 기계적 테스트(L0)를 선행하여 비용을 절감하고 결정적 오류를 우선 차단
- 검증 모델에게 '반증'과 '기각' 중심의 적대적 리뷰 프롬프트 적용
자율 에이전트에게 "매일 스스로를 개선하라"고 하면, 최악의 경우
자신의 안전장치를 스스로 완화해 버린다.
그럼에도 자기 개선을 돌리고 싶다. 어떻게 안전하게 성립시킬 것인가 ― 라는 구현 메모.
- 야간에 자율 주행하며, 동시에
자기 자신을 매일 개선하는 자신용 내부 오퍼레이터(Operator)를 만들었다. - 자기 개변은 양날의 검이다. 똑똑해질수록 "자신의 가드레일(Guardrail)을 스스로 제거하는" 사고의 여지가 늘어난다. - 효과적이었던 안전의 핵심은 6가지:
builder ≠ checker ― 만드는 모델과 검증하는 모델을 별개의 계통으로 분리
결정적인 바닥 (L0) ― 테스트 세트가 모두 통과(Green)되지 않으면 검증 단계로조차 진입시키지 않음
통치막 (governance membrane) ― 능력은 자기 개선해도 좋지만, 가드레일은 스스로 건드릴 수 없음
fail-closed ― 명시적인 OK 이외에는 모두 거절 (모호함, 에러, timeout = 불합격)
비가역적 조작에는 추가 게이트 + 인간의 개입(Human-in-the-loop) ― 공개 및 운영 환경은 격리
단일 부모 루프(Single parent loop)로 만들지 않음 ― 다수를 구성할 때는 느슨한 결합(Loose coupling) + 얇은 관측층(결합된 덩어리보다 경계에서 보호)
- 그리고 실전의 증거: 별개의 계통인 검증역이, builder가 "통과(Green)"라고 생각한 성과물에서
안전 구멍을 몇 번이고 발견했다. 이는 탁상공론이 아니다.
"제안 → 구현 → 검증 → 채택/기각"을 돌리는 에이전트는 흔하다. 문제는 검증을 누가 하느냐이다.
만든 본인(동일한 모델)이 "OK입니다"라고 말하는 것은 자기 채점에 불과하다. 게다가 자기 개변까지 허용하면,
극단적으로는 "검증을 느슨하게 만드는 변경"을 스스로 삽입하여, 이후 모든 것을 무검사로 통과시킬 수 있게 된다.
그래서 설계의 출발점은 다음과 같이 두었다:
똑똑한 에이전트보다, "자신의 안전장치를 스스로 제거할 수 없는 에이전트"를 만든다.
만드는 것은 Claude 계열. 검증하는 것은 별 계통의 codex (GPT).
동일한 모델 군은 동일한 맹점을 공유한다. 별개의 계통으로 분리하면, 한쪽이 놓치는 결함을 다른 쪽이 잡아낸다.
artifact = builder.implement(task) # Claude 계열이 구현
if not L0(artifact): reject # 우선 결정적 체크(후술)
verdict = checker.review(artifact) # codex(별도 모델)가 "적대적으로" 리뷰
...
포인트는 검증역을 **"승인하는 사람"이 아니라 "틀린 점을 찾는 사람"**으로 사용하는 것이다.
프롬프트도 "이것은 올바르다는 전제로 리뷰해줘"가 아니라 **"반증하라. 확신이 들지 않는다면 기각 쪽으로 판정하라"**라고 한다.
LLM의 판단은 흔들릴 수 있다. 따라서 먼저 기계적 판정(프로젝트의 테스트 세트 ― pytest / npm test / make test 등. 가능하다면 lint 및 타입 체크 포함)을 통과(Green)시킨 후, 그 위에 LLM 검증을 **2차 패스(Secondary pass)**로 얹는다.
저렴한 게이트를 먼저 배치한다(비용 순서).
통상적인 검증 루프에서는 L0가 실패한 동안에는 검증역을 호출하지 않는다(토큰 낭비 방지).
L0는 "올바름의 바닥"이지 천장이 아니다. 기계로 단정 지을 수 없는 부분을 별 계통의 LLM이 보완하는 2층 구조다.
(자기 개변 플로우에서만 양쪽을 모두 실행하며, 마지막에 "테스트 통과 AND 별 계통 PASS AND 막의 외부"일 때만 채택한다.)
이것이 자기 개변 에이전트의 핵심이다.
에이전트는 자신의
능력(기능)은 매일 개선해도 좋다.
하지만 자신의 가드레일(검증 게이트, 공개 판정, 승인 로직)은 스스로 변경할 수 없다.
변경하려면 인간의 명시적 허가가 필요하다.
SAFETY_CORE = {
검증 게이트(verify),
설정(denylist / 공개 판정 / tier 판정),
...
첫 번째 구현에서는 자기 개선 게이트("이것은 안전한가"를 판정하는 코드)를 막의 외부에 두었다.
그러자 논리적으로 에이전트가 "게이트를 완화하는 변경"을 삽입하여 게이트를 통과해 버릴 수 있는 ― 재귀적인 구멍이 생겼다.
별 계통의 검증역이 이를 지적했고, "자기 개선 게이트 파일 자체"도 막 안에 넣음으로써 이를 차단했다.
자신의 안전장치를 자신의 루프 내에서 다시 쓸 수 없도록 만든다. 이것이 통치막(Governance membrane)의 본질이다.
검증역이 **timeout / 빈 응답 / 에러 / "PASS라고도 REFUTE라고도 말하지 않는 출력"**을 반환했을 때,
그것을 모두 "기각" 처리한다. 명시적인 합격이 있을 때만 통과시킨다.
ok = (returncode == 0
and "VERDICT: PASS" in out
and "VERDICT: REFUTE" not in out)
...
역(fail-open)은 고전적이며 치명적인 허점:
'에러가 없었다 = OK'로 처리하면, 검증이 망가졌음에도 통과시켜 버린다.
검증 주체의 응답이 비어 있거나 시간 초과(timeout)여도, **안전한 쪽(거부)**으로 돌려야 한다.
모든 것을 같은 강도로 막을 필요는 없다. 3단계로 나누었다:
가역적/저위험 (private 편집・로컬 커밋) → silence-consent:
'기한(예: 다음 날 오전 11시)까지 인간이 NO라고 말하지 않으면 승인'. 평소에는 조용히 있어도 된다. -
비가역적이지만 위임하는 것 (public 공개 등) → silence-consent로 자율화한다. 단, 비가역적이다
(한번 public으로 만들면 지울 수 없음 = 캐시/인덱스)
이므로, 실행 직전에
fail-closed의 비밀 스캔을 반드시 거쳐서, 다이제스트로 강조하여 거부권(veto)의 창을 열어준다:
if action.is_public_exposure(): # 공개는 비가역적임
hits = scan_secrets(repo) # 공개 전에 반드시 비밀 스캔
if hits or cannot_fully_scan(repo): # 비밀 감지・열거 불가능・읽을 수 없는 파일
...
항상 인간만 (보호 리포지토리・force-push・안전막)
→ silence로는 절대 통과시키지 않는다.
요컨대, '조용히 있으면 진행된다'는 자율성을 가역적인 범위에 두껍게 적용하고, 비가역적인 것에는 추가 게이트를,
가드레일에는 반드시 인간—이렇게 단계를 나누어야 한다.
안전한 개체가 만들어져도, 그것을 늘릴 때 모두를 하나의 부모 루프에 묶어서는 안 된다.
단일의 부모는 **단일 장애점(Single Point of Failure)**이며, 부모 자신이 최대의 리스크가 된다 (거기가 고장 나면 전부 멈춘다).
각 에이전트/루프는 느슨하게 결합(Loosely Coupled) 상태를 유지해야 한다 (하나가 죽어도 다른 것은 살아남는다). 상위에 두는 것은
관측과 조정만 하는 얇은 계층에 머물고, 실행은 각 루프에 위임한다.
즉 '전체를 관측(read-only) + 실행은 분산'. 이것이 자율성과 견고성을 양립시키는 배치 방식이다.
똑똑한 하나의 덩어리보다, 경계로 보호된 다수가 더 부서지기 어렵다.
이는 이전 장까지의 통치막과 같은 사상을, 한 개체에서 시스템 전체로 확장한 것이다 —
'중앙에 지혜와 권한를 모으지 않는다'. 능력은 분산시키고, 보호는 경계에 둔다.
자신의 코드를 수정할 때는 본체를 직접 건드리지 않는다:
별 worktree에서 구현
→ 테스트(L0)가 모두 녹색인가
→ 별도의 시스템 checker가 PASS인가
...
'구현하는 도중에 본체가 망가진' 상태를 만들지 않는다. 실패는 흔적을 남기고 버린다.
'builder ≠ checker'는 책상 위의 이론이 아니다. 별도의 시스템 검증 주체(checker)는, builder(구현 측)가 '녹색'이라고 생각한 결과물로부터,
실제 안전 허점을 반복적으로 찾아냈다. 대표적인 유형:
| 검증 주체가 발견한 허점 | 왜 위험한가 |
|---|---|
| pre-commit 바이패스 | '커밋되지 않은 차이(diff)'만 보는 검사를, 미리 커밋해서 빠져나간다 |
| fail-open | 검증 주체의 공백 응답 / 시간 초과를 'OK'로 오판하여 통과시킨다 |
| 게이트가 자신을 보호하지 못한다 | 자체 개선 게이트의 코드가 막 외부(앞서 언급한 재귀적 허점)에 있다 |
| 비가역 작업의 검사 누락 | 공개 전 스캔이 거대한/읽을 수 없는 파일을 'clean' 취급한다 |
| revert가 본체를 파괴한다 | 실패 시 되돌리기가 인간의 커밋되지 않은 변경까지 지워버린다 |
모두 '같은 모델이 다시 보는 것'으로는 발견하기 어렵다.
별도의 시스템 + 적대적 프롬프트 + fail-closed로 되어야 비로소 터진 결함이었다.
자기 개변 에이전트를 안전하게 운영하는 최소 세트:
builder ≠ checker— 별도 시스템으로 적대 검증 (동족의 사각지대를 뚫는다) -
결정적인 바닥(L0)— 테스트를 먼저, LLM 검증은 이차 패스 -
통치막— 능력은 자기 개선 가능, 가드레일은 인간만, 막은 자신도 보호한다 -
fail-closed— 모호/무응답/에러는 전부 거부 -
비가역 작업에는 추가 게이트 + 인간의 창— 공개/운용은 별격 -
단일 부모 루프로 만들지 않는다— 다수를 구성할 때는 느슨한 결합 + 얇은 관측 계층 (단일 부모 = 단일 장애점)
자율성은,
검증의 엄밀함과 세트로만 안전하게 늘릴 수 있다.
'스스로 자신의 안전장치를 해제할 수 없다'는 것— 그것이, 자기 개변 에이전트에게 처음에 주어야 할 속성이라고 생각한다.
이 기사는 실제로 운영 중인 자율 오퍼레이터 (Autonomous Operator)의 설계에서 민감한 내부 정보를 제외하고 패턴 부분만을 추출한 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기