에이전트의 핵심 규칙을 15개에서 9개로 줄인 방법: 왜 적중률(Hit Rate)만으로는 어떤 규칙을 폐기할지 결정할 수 없는가
요약
에이전트 거버넌스 시스템의 규칙을 최적화하는 과정에서 적중률(Hit Rate) 지표만으로 규칙을 폐기할 때 발생하는 위험성을 경고합니다. 탐지 로직의 결함으로 인해 규칙이 실행되지 않는 것처럼 보이는 '고장 난 탐지기' 문제를 지적하며, 단순 실행 횟수가 아닌 신호 품질의 중요성을 강조합니다.
핵심 포인트
- 낮은 적중률이 반드시 규칙의 무용성을 의미하지는 않음
- 탐지 로직(Detector)의 결함이 데이터 왜곡을 초래할 수 있음
- 규칙이 실행되었는가가 아니라 실행될 수 있는지를 확인해야 함
- 단순 실행 횟수 기반의 자동 폐기 방식은 위험함
나는 에이전트의 거버넌스 시스템(governance system)에서 29개의 규칙을 거의 삭제할 뻔했다.
그 규칙들의 적중률(hit rates)은 0에 가까웠다. 어떤 것들은 수십 번의 세션 동안 한 번도 실행되지 않았다. 내가 가진 모든 지표에 따르면, 그것들은 컨텍스트 예산(context budget)만 소비할 뿐 실수를 한 번도 가로채지 못하는 무거운 짐이었다.
한 스크립트가 나를 멈춰 세웠다. 그 스크립트는 내 대시보드가 던지는 질문과는 다른 질문을 던졌다. "규칙이 실행되었는가?"가 아니라 "규칙이 실행될 수 있는가?"를 물었다.
29개의 탐지기(detectors)가 고장 나 있었다. 규칙들이 쓸모없는 것이 아니었다. 그것들은 잘못된 명령, 폐기된 도구 이름, 혹은 몇 달 전에 형식이 변경된 신호를 찾느라 눈을 감고 실행되고 있었던 것이다.
그 아찔한 순간은 신호 품질(signal quality)에 관한 것이었지, 내가 최종적으로 어떤 규칙을 강등시켰는지에 관한 것이 아니었다. 별개로, 나는 핵심(Core) 레이어를 15개 규칙에서 9개로 재구조화했다. 7개는 강등되었고, 1개는 승격되었으며, 6개의 슬롯이 확보되었다. 어려운 점은 재구조화가 아니었다. 어떤 폐기 신호(retirement signals)를 신뢰할 수 있는지 파악하는 것이었다.
뻔한 접근 방식 (하지만 작동하지 않는 방식)
규칙을 자동으로 폐기하고 싶다면, 가장 뻔한 신호는 각 규칙이 얼마나 자주 실행되는지를 나타내는 _적중률(hit rate)_이다. 낮은 적중률 = 유용하지 않음 = 폐기. 실행 횟수순으로 정렬하여 하위 N개를 잘라낸다. 끝.
나도 거의 그렇게 만들 뻔했다. 그러다 두 명의 독자가 왜 이 방식이 실패하는지 지적해 주었다.
Mike (jugeni)는 문제 전체를 재정의하는 댓글을 남겼다:
"고장 난 탐지기에서 감소한 카운트는 규칙이 사용되지 않는다는 증거가 아니라, 탐지기가 실행될 기회조차 없었다는 증거이다."
그리고 dipankar_sarkar는 다음과 같이 덧붙였다:
"두 가지 오류의 비용은 대칭적이지 않다."
이 두 댓글은 적중률을 오도하게 만드는 세 가지 함정을 설명한다. 나는 단순한 감쇠 곡선(decay curve)을 계획할 때 이 세 가지를 모두 놓쳤다.
적중률을 오도하게 만드는 세 가지 함정
함정 1: 고장 난 탐지기
당신의 규칙이 "커밋하기 전에 테스트가 통과하는지 확인하라"고 말한다고 가정하자. 탐지 로직은 테스트 실행 단계를 찾는다. 에이전트가 테스트를 실행했다면 규칙이 실행된다. 만약 실행하지 않았다면, 규칙은 침묵을 지킨다.
이제 탐지 로직 (detection logic)이 고장 났다고 상상해 보십시오. 잘못된 명령어를 찾고 있거나, 명령어 형식이 변경되었거나, 도구 이름 (tool name)이 업데이트되었을 수 있습니다. 탐지기 (detector)가 실행되지만 아무것도 찾지 못하고, "위반 사항 없음 (zero violations)"이라고 보고합니다. 당신의 대시보드에는 초록색 체크 표시가 뜹니다. 해당 규칙은 전혀 실행되지 않는 것처럼 보입니다.
하지만 탐지기는 눈이 먼 상태입니다. 규칙은 자신의 임무를 수행할 기회조차 갖지 못한 것입니다.
제가 제 규칙들에 대해 자동화된 점검을 수행했을 때, 29개의 규칙에서 탐지기가 고장 나 있었습니다. 비활성 상태처럼 보였지만 실제로는 눈을 감은 채 실행되고 있었던 29개의 규칙이었습니다.
저는 이 글을 쓰는 동안 이 사실을 뼈아프게 배웠습니다. 제 소스 노트에는 2주 전 변경 로그 (changelog) 항목에 "고장 난 탐지기 5개"라고 인용되어 있었습니다. 하지만 오늘 라이브 스크립트를 실행했을 때, 결과는 29개였습니다. 탐지 범위 (detection coverage)는 확장되었지만, 문서 (documentation)는 업데이트되지 않았던 것입니다. 저는 오래된 스냅샷 (stale snapshot)을 믿고 있었으며, 이는 정확히 제가 설명하고 있는 함정에 빠진 것이었습니다.
이것이 첫 번째 함정입니다: 적중 횟수 0회(zero hits)는 "규칙이 사용되지 않음"을 의미할 수도 있고, "탐지기가 고장 남"을 의미할 수도 있습니다. 적중률 (hit rate)만으로는 그 차이를 구분할 수 없습니다.
함정 2: 고위험 작업 (The High-Consequence Operation)
어떤 규칙들은 되돌릴 수 있는 것들을 보호합니다. "일관된 변수 명명 규칙을 사용하십시오." 만약 이 규칙을 폐기하여 명명 규칙이 일관되지 않게 된다면, 이름을 바꾸는 것으로 해결할 수 있습니다. 비용은 들겠지만 복구는 가능합니다.
반면, 다른 규칙들은 되돌리기 어려운 것들을 보호합니다. "운영 데이터 (production data)를 삭제하지 마십시오." "고객에게 실제 이메일을 보내지 마십시오." "인프라에 비용을 지출하지 마십시오." 만약 이러한 규칙 중 하나를 "거의 실행되지 않는다"는 이유로 폐기했는데, 딱 한 번 실행된다면, 당신은 롤백 (rollback)을 할 수 없을지도 모릅니다.
이 비대칭성 (asymmetry)이 바로 함정입니다. 100번의 세션 중 1번 실행되는 규칙은 적중률 측면에서 폐기 후보처럼 보입니다. 하지만 그 규칙이 고위험 작업 (high-consequence operation)을 보호하고 있다면, 잘못되었을 때의 비용은 "불편함"이 아닙니다. 그것은 쉽게 고칠 수 없는 피해입니다.
dipankar_sarkar의 공식화는 제 생각보다 더 날카로웠습니다:
"두 가지 오류의 비용은 대칭적이지 않다."
그의 제안은 다음과 같습니다: 가역성 (reversibility)을 이진 분류 (binary classification)로 취급하는 것입니다. 알 수 없거나 결과가 중대한 (high-consequence) 작업에는 절대 자동 폐기되지 않는 규칙을 부여합니다. 빈도 기반의 결정은 권고 (advisory) 및 스타일 규칙에만 적용합니다.
솔직히 말씀드리면, 이 분류는 현재 부분적으로 수동으로 이루어지고 있습니다. 저는 도구 클래스 (tool class)를 보수적인 1차 대리 지표 (proxy)로 사용합니다. 쓰기 (write), 지출 (spend), 전송 (send) 또는 변형 (mutate)을 수행하는 작업은 기본적으로 고위험으로 취급됩니다. 이것이 해당 작업이 말 그대로 가역 불가능하다는 것을 증명하는 것은 아닙니다. 쓰기 작업은 트랜잭션 (transactional) 방식일 수 있고, 외부 요청은 멱등성 (idempotent)을 가질 수도 있습니다. 도구 클래스는 위험의 대리 지표일 뿐, 증거가 아닙니다. 그리고 규칙이 없는 상태에서 작업을 재현 (replaying)하며 복구 불가능한 효과가 발생하는지 관찰함으로써 가역성을 도출해내는 이상적인 방식은 아직 구현되지 않았습니다.
함정 3: 내가 아직 해결하지 못한 것
앞선 두 가지 함정은 제 데이터에서 관찰되었습니다. 세 번째는 제가 아직 운영 환경 (production)에서 검증하지 못한 설계상의 위험입니다.
어떤 규칙이 "데이터베이스에 카드 토큰을 저장하지 마시오"라고 명시되어 있다고 가정해 봅시다. 이 규칙은 Stripe를 사용하고 있었고, 토큰을 저장하면 PCI 준수 범위 (PCI compliance scope)에 포함되기 때문에 작성되었습니다. 이 규칙은 에이전트가 카드 데이터를 쓰려고 할 때마다 실행됩니다.
2년 후, 귀하가 다른 결제 프로세서로 전환합니다. 규칙은 여전히 "카드 토큰을 저장하지 마시오"라고 말합니다. 하지만 그 이유인 PCI 범위는 더 이상 동일한 방식으로 적용되지 않을 수 있습니다. 혹은 다르게 적용될 수도 있습니다.
이 규칙의 트리거 키 (trigger key)는 엔티티 ("card_token 컬럼")이지, 제약 조건 ("우리 DB의 카드 데이터는 우리를 PCI 범위에 포함시킨다")이 아닙니다. 제약 조건은 변했지만 트리거가 변하지 않으면, 규칙은 실행되지 않아야 할 때 실행되거나 실행되어야 할 때 침묵하게 됩니다.
이것이 세 번째 함정입니다: 적중률 (hit rate)은 트리거가 실행되었는지를 측정할 뿐입니다. 제약 조건이 여전히 유효한지에 대해서는 아무것도 말해주지 않습니다. 규칙이 완벽한 적중률을 가질 수 있어도, 잘못된 것을 보호하고 있을 수 있습니다.
제가 이 문제를 해결했다고 주장하지는 않겠습니다. 엔티티 (Entities)보다는 제약 조건 (Constraints)에 규칙을 맞추는 것이 제가 지향하는 설계 원칙입니다. 현재로서는 트리거 (Triggers)가 특정 엔티티를 참조하는 규칙들을 식별할 수 있을 뿐이며, 아직 체계적인 해결책을 가지고 있지 않음을 인정합니다.
파이프라인 (그리고 누락된 것)
세 가지 함정을 이해하고 나면, 해결 구조는 자연스럽게 따라옵니다. 단순히 적중률 (Hit rate)에 따라 규칙을 폐기해서는 안 됩니다. 폐기 결정을 내리기 전에 각 함정을 걸러내야 합니다.
제가 구축한 것과 구축하지 못한 것은 다음과 같습니다.
필터 1: 탐지기 상태 확인 (Check Detector Health)
어떠한 적중률 신호도 신뢰하기 전에, 탐지기 (Detector)가 실제로 작동하고 있는지 확인합니다. 자동화된 스크립트는 모든 규칙을 다음 세 가지 상태 중 하나로 분류합니다:
- detector_healthy: 탐지기가 작동하며 판결 (Verdicts)을 생성함
- detector_broken: 탐지기가 실행은 되지만 판결을 생성할 수 없음 - 잘못된 것을 찾고 있거나, 명령 형식 (Command format)이 변경됨
- rule_genuinely_unused: 탐지기는 정상적이지만, 해당 상황이 발생하지 않아 규칙이 전혀 실행되지 않음
마지막 카테고리에 속하는 규칙만이 폐기하기에 안전한 후보입니다. detector_broken 규칙은 제외됩니다 - 이 규칙들이 쓸모없기 때문이 아니라, 이들의 적중률 신호를 신뢰할 수 없기 때문입니다.
확인된 229개의 규칙 중 (시스템에는 총 289개가 있지만, 아카이브된 규칙과 탐지기 메타데이터가 없는 규칙은 이 확인 대상에서 제외됨): 48개는 정상(healthy), 29개는 고장(broken), 152개는 실제로 사용되지 않음(genuinely unused)으로 나타났습니다.
참고: "정상 (healthy)"은 탐지기가 실행될 수 있고 내부적으로 일관된 판결을 생성할 수 있음을 의미합니다. 이것이 의미론적 커버리지 (Semantic coverage)를 증명하는 것은 아닙니다. 하나의 명령 경로만 확인하는 탐지기도 다른 시나리오를 놓치면서 "정상"일 수 있습니다.
필터 2: 고위험 규칙 제외 (Exclude High-Consequence Rules)
필터 1을 통과한 규칙(탐지기가 작동하며, 규칙이 실제로 거의 실행되지 않는 경우)에 대해, 해당 규칙이 어떤 종류의 작업을 보호하는지 확인합니다.
고위험 작업(보수적인 대리 지표로서 write, spend, send, mutate)을 보호하는 규칙은 자동 폐기 대상에서 제외됩니다. 이 규칙들은 적중률과 관계없이 유지됩니다.
이것은 가중치가 부여된 점수가 아니라 이진 게이트 (binary gate)입니다. dipankar_sarkar의 주장은 가중치를 부여하는 것(예: "중요도 = 0.8")이 단지 위험을 규칙에서 가중치로 옮길 뿐이라는 것이었습니다. 이진 태그 (binary tag)는 자동화 대상이 될 수 있는 규칙의 집합을 좁히는 역할을 할 뿐, 분류 자체를 정확하게 만드는 것은 아닙니다.
이 분류는 부분적으로 수동으로 이루어집니다. 도구 클래스 (Tool class)가 대부분의 사례를 다루지만, 예외적인 사례 (edge cases)는 인간의 검토가 필요합니다. 그리고 인간의 판단 역시 규칙과 마찬가지로 부패합니다.
구축되지 않은 것: 빈도 감쇠 (Frequency Decay)
필터 1과 필터 2를 거치고 나면, 건강한 탐지기 (detectors)를 가지고 있으며 가역적 작업 (reversible operations)을 보호하는 규칙들이 남습니다. 이러한 규칙들의 경우, 적중률 (hit rate)은 덜 오해의 소지가 있는 신호가 됩니다.
현재 제가 가진 것은 이진 결정입니다. 판결 분포 (verdict distribution)와 연결 필터링 (linkage filtering)을 기반으로, 규칙은 강등 후보이거나 아니거나 둘 중 하나입니다. 연속적인 감쇠 곡선 (continuous decay curve)은 없습니다.
dipankar_sarkar가 제안했던 방식 — 세션당 적중 횟수에 약 0.9를 곱하고 임계값 미만이면 강등하는 방식 — 은 구현되지 않았습니다. 제가 가진 것은 보호된 확인 (guarded check)이지, 감쇠 알고리즘 (decay algorithm)이 아닙니다. 빠져 있는 조각은 빈도 감쇠 (frequency decay) 그 자체입니다.
이 차이점을 명확히 하고 싶습니다: 필터들은 구축되어 실행 중입니다. 감쇠 메커니즘 (decay mechanism)은 계획 단계에 있습니다. 다음에 설명할 핵심 구조 재편 (Core restructuring)은 감쇠 알고리즘이 아닌 필터의 출력값을 사용했습니다.
실제로 일어난 일: 두 가지 별개의 사건
같은 주에 일어난 두 가지 일을 분리해야 합니다. 왜냐하면 초기 기사에서 이 둘을 혼동했기 때문입니다.
사건 1: 파이프라인이 17개의 태스크 (Task) 규칙을 아카이브했습니다. 수명 주기 확인 (lifecycle check)을 통해 28개의 성능 저하 후보를 식별했습니다. 연결 필터 (linkage filter)가 11개(충돌 그룹 내 규칙, 안전 필수 규칙, 알려진 함정 추적 규칙)를 제거하여 17개가 남았습니다. 이 17개의 태스크 규칙은 아카이브되었습니다. 즉, 사용은 가능하지만 기본적으로 로드되지 않는 퇴역 계층 (retired tier)으로 이동되었습니다. 감사 로그 (audit log)에 각 작업이 기록되었습니다. 롤백 테스트 (rollback test)는 통과했습니다.
이벤트 2: Core를 15개에서 9개로 재구조화했습니다. 이는 별도의 반수동적 (semi-manual) 결정이었습니다. 7개의 Core 규칙은 Task로 강등되었고, 1개의 Task 규칙은 Core로 승격되었습니다. 파이프라인 (pipeline)이 후보 신호 (candidate signals)를 제공했지만, 최종적인 Core 재구조화는 파이프라인 출력에 의해 결정된 것이 아니라, 파이프라인 출력에 기반한 인간의 판단 (human judgment call)이었습니다.
강등된 Core 규칙들은 발생 빈도가 낮았습니다. 즉, 실행이 프로토콜 문서 (protocol documents)에 구조화된 OS 흐름 (flow) 전용 규칙, 관찰된 샘플 내에서 기록된 실패 (misses)가 없는 규칙, 그리고 이제는 네이티브 도구 호출 (native tool calling)이 처리하는 플랫폼 특정적 규칙들이었습니다. 승격된 규칙은 여러 도메인에 걸쳐 40번의 관찰된 세션 동안 기록된 실패가 없었습니다.
저는 파이프라인이 Core 재구조화를 일으켰다고 주장하는 것이 아닙니다. 파이프라인이 신뢰할 수 없는 신호들을 걸러냈고, 저는 가공되지 않은 적중 횟수 (raw hit counts)만 있을 때보다 더 나은 정보를 바탕으로 재구조화 결정을 내렸다고 말하는 것입니다.
내가 아직 해결하지 못한 네 가지 간극 (Gaps)
이것은 흐름 제약 (flow constraint)이지, 권한이 격리된 게이트 (permission-isolated gate)가 아닙니다. 스크립트는 제가 호출할 때 실행됩니다. 에이전트 (agent)는 이론적으로 스크립트를 수정할 수 있습니다. 이것은 에이전트가 체크를 우회할 수 없는 시스템이 아닙니다. 에이전트가 스스로에게는 실행하지 않는 체크를 제가 실행하는 시스템입니다.
파이프라인이 무겁습니다. 여러 개의 스크립트, 감사 로그 (audit logs), 롤백 메커니즘 (rollback mechanisms) 등이 포함됩니다. 대부분의 설정에는 이것이 과잉 설계 (over-engineered)되었을 수 있다는 점을 인지하고 있습니다. 규칙이 20개라면 이것이 필요하지 않겠지만, 200개라면 필요할 수도 있습니다.
빈도 감쇠 (Frequency decay) 기능이 구축되지 않았습니다. 빠진 부분은 슬라이딩 윈도우 (sliding-window) 또는 지수 감쇠 (exponential decay) 알고리즘입니다. 현재 제가 가진 것은 판결 분포 (verdict distribution)에 기반한 이진 결정 (binary decision)입니다. 제 경험상 수동으로 눈을 가늘게 뜨고 확인하는 것보다는 낫지만, 정의된 비교 대상에 대해 이 주장을 측정해 보지는 않았습니다.
가역성 분류 (Reversibility classification)가 부분적으로 수동입니다. 도구 클래스 (Tool class)는 거친 대리 지표 (coarse proxy)입니다. 예외 사례 (edge cases)에는 인간의 검토가 필요합니다. 이상적인 방식인 리플레이 기반 유도 (replay-based derivation)는 구현되지 않았습니다.
하나의 체크로 시작하라
이러한 이점을 얻기 위해 파이프라인까지 구축할 필요는 없습니다. 가장 가치 있는 행동은 대부분의 사람들이 건너뛰는 바로 그것입니다: 당신의 디텍터(detector)가 실제로 작동하고 있는지 확인하는 것입니다.
CLAUDE.md, AGENTS.md, 또는 .cursorrules에 있는 규칙 중 관찰된 적중률(hit)이 0이거나 0에 가까운 각 규칙에 대해 다음을 질문하십시오:
- 무엇이 이 규칙을 트리거(trigger)하는가? 특정 신호(signal)를 명시하십시오. 명령(command), 파일 패턴(file pattern), 또는 동작(behavior) 등이 될 수 있습니다.
- 그 신호가 여전히 탐지 가능한가? 만약 규칙이 이름이 변경된 도구(tool) 이름, 더 이상 사용되지 않는(deprecated) 명령, 또는 형식이 변경된 패턴을 찾고 있다면, 디텍터(detector)가 고장 난 것입니다.
- 디텍터가 고장 났다면 어떤 일이 발생하는가? 규칙이 비활성 상태인 것처럼 보입니다. 대시보드에는 "위반 사항 0건"이라고 표시됩니다. 하지만 그 규칙은 당신을 보호하기로 되어 있었으나, 그러지 못하고 있는 것입니다.
현재 제가 수행한 점검에서는 229개의 규칙 중 29개가 디텍터 고장(detector-broken)으로 분류되었습니다. 이 비율을 다른 규칙 시스템에 일반화할 수는 없습니다. 하지만 "내 디텍터가 실제로 작동하고 있는가?"라고 질문하는 이 연습 자체는 20개 규칙의 하위 집합을 대상으로 약 30분 정도 소요되었습니다. 결과는 상황에 따라 다를 수 있습니다.
더 나아가고 싶다면, 규칙들이 무엇을 보호하는지에 따라 분류해 보십시오:
- 이 규칙이 되돌리기 쉬운 것(naming, formatting, style)을 보호하는가?
- 아니면 되돌리기 어려운 것(데이터 삭제, 외부 호출, 비용, 상태 변이(state mutation))을 보호하는가?
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기