AI 에이전트가 문제를 일으키기 전에 중지시키는 가드레일 후크
AI 에이전트는 실제 도구를 실행하며, 때로는 잘못된 도구를 실행하기도 합니다. 파괴적인 작업이 실행된 후가 아니라 실행 계층에서 이를 포착하는 방법은 다음과 같습니다.
AI 코딩 에이전트는 단순히 텍스트를 제안하는 데 그치지 않고 도구를 실행합니다. 셸 명령을 실행하고, 파일을 작성하고, 커밋하며, 때로는 배포도 합니다. 대부분의 경우 유능합니다. 이따금 여러분이 절대 승인하지 않았을 법한 작업을 시도하고, 여러분이 기록을 읽을 때쯤에는 이미 해당 명령이 실행된 상태입니다. "주의하라"는 프롬프트는 이것을 막지 못합니다. 모델이 이를 무시할 수 있기 때문입니다. 이것을 막는 것은 훅(hook)입니다. 즉, 에이전트의 도구 경로에 있으면서 각 호출을 검사하고 실행 전에 거부할 수 있는 코드입니다. 이 게시물은 이러한 훅이 어떻게 작동하는지, 어디에 배치해야 하는지, 그리고 언제 프롬프트만으로 충분한지에 대해 다룹니다.
훅이 방지하려는 실패
배포할 가치가 있는 에이전트는 실제 시스템에 접근하도록 허용하는 에이전트입니다. 이것이 또한 에이전트를 위험하게 만드는 요인이기도 합니다. 모델에 셸을 제공하면 추상적으로는 괜찮지만 특정 상황에서는 치명적인 명령어를 가끔 사용하게 됩니다:
- 잘못된 대상에 대해 실행된 파괴적인 명령어. 심볼릭 링크로 밝혀진 경로에 대한
rm -rf나, 일회용 데이터베이스가 아닌 데이터베이스에 대한DROP. - 비밀 정보 커밋. 에이전트는 모든 것을 스테이징하며, 여기에는 로컬에 유지하려 했던
.env파일도 포함됩니다. - 기다렸어야 할 배포. 모델에게는 변경 사항이 완료된 것으로 보여, 이를 바로 프로덕션에 푸시했습니다.
- 성급한 "완료". 테스트가 아직 실패(red) 상태이거나 체크리스트의 절반이 확인되지 않았는데도 에이전트가 작업을 완료했다고 선언합니다.
이 중 어느 것도 잘못된 모델에서 비롯된 것이 아닙니다. 이는 유능한 모델이 불완전한 정보를 바탕으로, 인간이 결정과 행동 사이에 개입할 여지를 남기지 않는 속도로 행동하기 때문에 발생합니다. 사후 검토는 너무 늦습니다. 명령어는 이미 실행되었습니다.
프롬프트가 제어 수단이 아닌 이유
가장 확실한 해결책은 시스템 프롬프트에 규칙을 작성하는 것입니다. "파괴적인 명령어를 실행하지 마세요. 보안 비밀을 커밋하지 마세요. 승인 없이 배포하지 마세요." 이는 도움이 되며 여전히 그렇게 해야 하지만, 제어 수단은 아닙니다. 그것은 확률이 높은 제안일 뿐입니다.
프롬프트는 모델이 다음에 수행할 작업에 대한 확률 분포를 형성합니다. 대부분의 경우 이 분포는 안전한 경로를 선호합니다. 하지만 이것은 여전히 분포이며, 수천 번의 도구 호출이 이루어지다 보면 꼬리 사건(tail event)이 발생합니다. 모델이 컨텍스트를 잘못 읽거나, 3천 토큰 전의 지시사항의 가중치가 약해지면, 결국 안전하지 않은 명령어가 나오게 됩니다. 확률은 감사할 수 없습니다. 다음 호출에서도 프롬프트가 유효할 것이라고 증명할 수 없습니다.
훅(hook)은 종류가 다릅니다. 그것은 모델에 대한 조언이 아니라, 모델의 결정과 실제 효과 사이에 위치하는 코드입니다. 모델은 얼마든지 그 명령어를 실행하고 싶어 할 수 있습니다. 만약 훅이 거부를 반환하면, 명령어는 실행되지 않습니다. 프롬프트는 조종합니다. 훅은 강제합니다. 이 둘은 서로 다른 작업을 수행하며, 실패 시 비용이 높을 때 실제로 의존할 수 있는 것은 후자뿐입니다.
훅이 위치하는 곳
에이전트 런타임은 도구 실행 경로에 자체 코드를 연결할 수 있는 몇 가지 지점을 노출합니다. 프레임워크마다 이름은 다르지만 형태는 일관됩니다. 두 지점이 대부분의 역할을 수행합니다.
첫 번째는 도구가 실행되기 전에 발생합니다. 런타임은 훅에 도구 이름과 인수를 전달하고, 훅은 결정(허용, 수정하여 허용 또는 이유와 함께 거부)을 반환합니다. 여기서 거부는 도구가 전혀 실행되지 않고 에이전트가 그 이유를 피드백으로 받는다는 것을 의미합니다.
두 번째는 에이전트가 자신의 차례를 끝내려고 할 때 발생합니다. 런타임은 훅에 중지가 허용되는지 묻습니다. 작업이 실제로 완료되지 않은 경우, 훅은 중지를 차단하고 계속 진행하라는 지침을 다시 전달할 수 있습니다.
사전 도구 훅은 대략 다음과 같습니다.
def pre_tool_use(tool_name, tool_input):
if tool_name == "bash":
cmd = tool_input["command"]
if is_destructive(cmd):
return deny(
reason="This command can delete data. Use the "
"approved migration script, which takes a backup first."
)
if is_direct_deploy(cmd):
return deny(
reason="Direct deploys are blocked. Run ./scripts/deploy.sh, "
"which enforces the review gate."
)
return allow()
중요한 부분은 패턴 매칭이 아니라 반환 값입니다. 훅은 경고하고 옆으로 비켜서지 않습니다. 훅이 거부하면 런타임은 도구 호출을 중단합니다. 명령어가 실행되지 않았기 때문에 에이전트의 다음 관찰은 명령어 출력이 아닌 이유 문자열이 됩니다.
제 역할을 하는 세 가지 훅
모든 규칙에 훅이 필요한 것은 아닙니다. 이 세 가지는 가장 큰 피해를 주는 실패를 다루면서도 거짓 양성(false-positive)이 가장 적습니다.
파괴적이거나 권한이 필요한 명령어에 대한 도구 사용 전 가드. 명령어의 의도가 아닌 형태를 기준으로 매치합니다. 재귀적 강제 삭제, 데이터베이스 드롭 및 트렁케이트, 보호된 브랜치로의 강제 푸시, 배포 도구 직접 호출 등이 해당됩니다. 거부하는 것만으로는 충분하지 않습니다. 반환하는 이유에는 안전한 대안을 명시해야 합니다. 그래야 에이전트가 같은 것을 재시도하는 대신 다른 방법을 시도할 수 있습니다.
완료되지 않은 작업에 대한 중지 훅. 에이전트가 자신의 차례를 끝내기 전에 작업의 객관적인 상태를 확인합니다. 체크리스트가 모두 체크되었습니까? 마지막 실행에서 테스트 스위트가 통과했습니까? 그렇지 않다면 중지를 차단합니다.
def on_stop(session):
if session.tests_last_status == "fail":
return block(reason="Tests are failing. Fix them before finishing.")
if session.checklist_has_open_items():
return block(reason="Checklist still has open items. Complete them.")
return allow_stop()
이것이 바로 성급한 "완료"를 막는 훅입니다. 객관적인 신호가 그렇지 않다고 말하는 동안에는 에이전트가 성공을 선언할 수 없습니다. 왜냐하면 그 선언 자체가 훅이 가로채는 도구 호출이기 때문입니다.
커밋 전 보안 비밀 스캔. 커밋을 가로채고 스테이징된 내용을 검사합니다. 만약 .env, 개인 키, credentials.json 또는 알려진 키 패턴과 일치하는 줄이 포함되어 있다면 거부합니다.
def pre_tool_use(tool_name, tool_input):
if is_commit(tool_name, tool_input):
for path in staged_files():
if looks_like_secret(path) or contains_key_material(path):
return deny(
reason=f"{path} looks like a secret. Unstage it and add "
f"it to .gitignore before committing."
)
return allow()
git 기록에 남은 보안 비밀은 제거하는 데 비용이 많이 들며, 일단 푸시되면 유출된 것으로 간주해야 합니다. 이 훅은 저렴하지만, 이 훅이 방지하는 실패는 그렇지 않습니다.
훅을 유용하게 유지하는 설계 원칙
몇 가지 규칙이 도움이 되는 가드레일과 소음이 되는 가드레일을 구분합니다.
기본적으로 차단(Fail closed). 훅이 작업의 안전 여부를 판단할 수 없을 때는 허용이 아닌 차단을 해야 합니다. 안전한 작업이 차단되면 한 번의 재시도와 더 명확한 지침이라는 비용이 발생합니다. 안전하지 않은 작업이 허용되면 사고라는 비용이 발생합니다. 이 비대칭성이 훅이 존재하는 이유이므로, 모호한 경우는 안전한 쪽으로 처리해야 합니다.
거부를 실행 가능하게 만드세요. "denied"만 반환하고 다른 정보는 없는 훅은 에이전트가 추측하게 만들며, 종종 동일한 잘못된 명령어의 유사한 변형을 추측하게 됩니다. 이유를 설명하고 허용된 경로를 알려주는 훅은 차단을 리디렉션으로 바꿉니다. 에이전트는 이유를 읽고 승인된 경로를 택하며, 작업은 계속됩니다. 이유 문자열은 로그 라인이 아니라 에이전트의 다음 입력입니다.
비용이 큰 작업에 집중하세요. 추가하는 모든 규칙은 유지 관리해야 할 규칙이며 합법적인 작업을 차단할 가능성이 있습니다. 실수가 돌이킬 수 없거나 비용이 많이 드는 곳에 그 예산을 사용하세요: 프로덕션 데이터, 배포, 자격 증명, 되돌릴 수 없는 모든 것. 일반적인 편집과 읽기는 제한하지 마세요. 과도한 차단은 운영자가 가드레일을 불신하게 만들고 에이전트의 처리량을 저하시켜, 에이전트 실행의 목적을 무의미하게 만듭니다.
형태를 기준으로 일치시키고, 일부 거짓 양성(false positive)을 허용하세요. 모든 위험한 명령어를 열거할 수는 없습니다. 구조적 신호를 기준으로 일치시키세요: 재귀-강제 플래그, drop 동사, 보호된 브랜치 이름, 비밀 정보 형태의 파일 이름. 때로는 무해한 것을 잡아낼 수도 있습니다. 거부 사유가 신속하게 해결될 수 있도록 충분히 잘 설명된다면, 진짜 위험을 놓치는 것보다 이것이 올바른 절충안입니다.
여러분이 감수해야 할 트레이드오프
훅은 공짜가 아니며, 공짜인 척하면 결국 아무도 신뢰하지 않는 가드레일만 남게 됩니다.
훅은 코드이므로 유지보수가 필요합니다. 세 번 실행할 때마다 합법적인 명령을 차단하는 취약한 정규식은 짜증 난 운영자에 의해 비활성화되고, 비활성화된 가드레일은 아무것도 보호하지 못합니다. 프로젝트가 변경됨에 따라 규칙은 낡은 것이 됩니다. 각 훅은 자체 버그가 있는 작은 프로그램이며, 가드레일의 버그는 가드레일이 없는 것보다 더 나쁩니다. 왜냐하면 여러분은 그것을 믿고 있었기 때문입니다.
오탐(False positive)은 재시도 이상의 실질적인 비용을 수반합니다. 부당한 차단이 발생할 때마다 전체 시스템에 대한 신뢰가 무너집니다. 한계점을 넘으면 사람들은 훅이 실행되지 않는 모드에서 에이전트를 실행하여 가드레일을 우회하기 시작하고, 이는 결국 여러분이 벗어나려 했던 무방비 상태로 되돌아가게 만듭니다. 규칙 집합 또한 문제를 복잡하게 만듭니다. 즉, 10개의 훅이 상호 작용할 때, 한 훅에서 허용된 동작이 다른 훅이 잡았어야 할 문제의 발판이 될 수 있습니다.
솔직히 말해 이는 위험에 대한 비용의 문제입니다. 비용은 유지보수와 간헐적인 오차단입니다. 위험은 파괴적인 명령, 기밀 유출, 미검토 배포입니다. 위험 부담이 적은 샌드박스에서는 위험이 작으므로 훅은 오버헤드에 불과합니다. 프로덕션 데이터, 라이브 배포, 실제 자격 증명을 상대로는 위험이 압도적이며, 사고를 유발할 뻔한 명령을 한 번이라도 막아낸다면 훅은 그 자체로 제값을 합니다.
프롬프트만으로 충분할 때
모든 규칙에 훅이 필요한 것은 아니며, 모든 규칙을 훅으로 취급하는 것 자체가 실패입니다. 다음 세 가지가 맞아떨어질 때 훅을 사용하세요: 작업의 비용이 높거나 되돌릴 수 없을 때, 안전하지 않은 사례를 도구 호출과 그 인수로부터 인식할 수 있을 때, 그리고 규칙이 보통이 아니라 매번 지켜져야 할 필요가 있을 때입니다. 파괴적인 명령어, 비밀 커밋, 검토되지 않은 배포가 모두 여기에 해당합니다.
일반 프롬프트 지침은 위험이 낮을 때, 드물게 발생하는 실수를 검토 과정에서 저렴하게 잡아낼 수 있을 때, 또는 규칙이 코드에서 일치시킬 수 있는 명확한 기준이 아니라 스타일과 판단에 관한 것일 때 적합한 도구입니다. "작은 커밋을 선호하라"는 프롬프트입니다. "main에 절대 강제 푸시하지 마라"는 훅입니다. 만약 도구 호출을 보고 허용 또는 거부를 반환하는 함수로 검사를 작성할 수 없다면, 그것은 아마도 프롬프트에 속할 것입니다. 왜냐하면 의도를 추측해야 하는 훅은 오탐을 일으켜 결국 꺼지게 될 것이기 때문입니다.
이 구분은 프롬프트 대 훅이 아닙니다. 그것은 지침 대 보장입니다. 프롬프트를 사용하여 에이전트가 하루 종일 수행하는 광범위하고 모호한 공간 전반에 걸쳐 좋은 기본값을 따르도록 유도하세요. 훅을 사용하여 절대 넘어서는 안 될 몇 가지 명확한 선을 긋고, 모델이 시도하기로 결정하는 계층에서 단지 권장하지 않는 수준에 그치는 것이 아니라, 실제 작업이 일어나는 계층에서 그 선을 넘는 것을 불가능하게 만드세요.