증거 게이트
한 에이전트가 pytest를 실행하지 않고 “모든 테스트 통과”라고 보고했어요. 출력은 자신감 있었고, 표현은 자연스러웠으며, 주장은 거짓이었습니다. 해당 세션에서 테스트 스위트가 호출된 적이 없었어요. 에이전트는 코드 변경에 대한 이해를 바탕으로 테스트가 통과할 것이라고 추론하고, 그 추론을 사실처럼 진술한 겁니다.
저는 이걸 잡아냈어요. 모든 완료 보고서에는 구체적인 증거를 인용해야 한다는 규칙이 있기 때문입니다. “테스트가 통과할 거라고 확신합니다”가 아니라, 실패 건수가 0인 테스트 출력을 직접 붙여넣어야 해요. “파일이 업데이트되었을 겁니다”가 아니라, 파일 경로, 줄 번호, 구체적인 변경 사항을 명시해야 합니다. “기존 패턴을 따릅니다”가 아니라, 패턴의 이름, 해당 패턴이 존재하는 파일, 새 코드가 일치하는 줄을 제시해야 해요.
이 규칙은 제 시스템에서 증거 게이트라는 이름을 갖고 있습니다. 완료 보고서의 모든 주장이 관찰 가능한 근거로 뒷받침되기 전까지는 어떤 작업도 완료된 것이 아닙니다. “그렇다고 생각합니다”는 증거가 아닙니다. “될 거예요”는 증거가 아닙니다. “확신합니다”는 증거가 아닙니다. 증거란 파일 경로, 테스트 결과, 구체적인 코드 참조, 또는 직접적인 관찰입니다.
지금 이것이 중요한 이유
언어 모델은 그럴듯한 텍스트를 생성합니다. 이것이 핵심 역량이자 핵심 리스크예요. 테스트 결과에 대한 그럴듯한 주장은, 검증 산출물을 요구하지 않는 한, 검증된 주장과 구별할 수 없습니다.
실패 모드는 극적인 의미의 환각이 아닙니다. 에이전트가 가상의 테스트 프레임워크를 만들어내거나 오류 메시지를 조작하지는 않아요. 실패 모드는 추론을 관찰처럼 제시하는 것입니다. 에이전트는 테스트가 통과해야 한다고 추론하고, 이 추론을 마치 테스트를 실행한 것처럼 보고합니다. 추론이 맞을 수도 있어요. 하지만 테스트에 대한 추론은 테스트 실행이 아니며, 이 둘 사이의 간극에서 버그가 살아남습니다.
저는 이것을 유령 검증이라고 부릅니다. 실제로 검증이 이루어지지 않았는데 검증이 수행되었다고 주장하는 완료 보고서를 말해요. 500회 이상의 자율 세션을 추적한 결과, 유령 검증은 사람의 개입이 필요한 에이전트 실패의 12%를 차지했습니다.1 가시적인 오류를 전혀 만들어내지 않는 가장 흔한 실패 모드예요. 에이전트는 성공을 보고하고, 출력은 깔끔해 보이며, 버그가 그대로 배포됩니다.
게이트
증거 게이트는 6가지 기준으로 구성됩니다. 모든 비사소한 변경은 작업 완료로 표시되기 전에 6가지 모두에 대한 증거를 제시해야 합니다.
| 기준 | 필요한 증거 |
|---|---|
| 코드베이스 패턴 준수 | 패턴의 이름과 해당 패턴이 존재하는 파일을 명시 |
| 가장 단순한 작동 솔루션 | 거부된 더 단순한 대안과 그 이유를 설명 |
| 엣지 케이스 처리 | 구체적인 엣지 케이스와 각각의 처리 방법을 나열 |
| 테스트 통과 | 실패 건수 0을 보여주는 테스트 출력을 붙여넣기 |
| 회귀 없음 | 확인한 파일과 기능을 명시 |
| 실제 문제 해결 | 사용자의 요구사항과 이를 어떻게 해결하는지 기술 |
기준이 의도적으로 구체적입니다. 각 기준은 일반적인 보장이 아닌 구체적인 산출물을 요구해요. “코드베이스 패턴 준수”는 “기존 관례를 따랐습니다”로 충족되지 않습니다. “fetch_nvd()의 재시도 패턴이 241번 줄의 fetch_semantic_scholar()에 있는 지수 백오프와 일치합니다”라고 해야 충족돼요.
구체성 자체가 핵심입니다. 파일 경로와 줄 번호를 제시해야 하는 에이전트는 유령 검증을 할 수 없어요. 해당 경로에 파일이 존재하고 코드가 주장과 일치하거나, 그렇지 않거나 둘 중 하나입니다. 그럴듯한 중간 지대란 없습니다.
신호로서의 모호한 표현
증거 게이트에는 모호한 표현 감지기가 포함되어 있습니다. 특정 단어들이 재검증을 유발해요:
- “될 거예요” (“이렇게 하면 될 거예요”)
- “아마” (“아마 이 엣지 케이스를 처리할 겁니다”)
- “인 것 같습니다” (“출력이 정확한 것 같습니다”)
- “그렇다고 생각합니다” (“테스트가 통과한다고 생각합니다”)
- “정확해 보입니다” (“구현이 정확해 보입니다”)
- “확신합니다” (“이게 맞다고 확신합니다”)
이런 단어들은 에이전트가 결과를 관찰하는 것이 아니라 추론하고 있다는 것을 나타냅니다. 추론이 맞을 수도 있어요. 하지만 에이전트가 결과를 직접 관찰할 수 있다면(테스트를 실행하거나, 파일을 읽거나, 출력을 확인하는 등), 추론은 관찰보다 약한 형태의 증거입니다.
완료 보고서에 모호한 표현이 포함되어 있을 때, 대응은 “틀렸습니다”가 아닙니다. 대응은 “모호한 표현을 관찰로 대체하세요”예요. 테스트가 통과한다고 생각한다면, 실행해서 출력을 붙여넣으세요. 정확한 것 같다면, 파일을 읽고 줄을 인용하세요. 모호한 표현은 검증이 실패했다는 신호가 아니라, 검증이 생략되었다는 신호입니다.
에이전트가 모호하게 표현하는 이유
에이전트가 모호하게 표현하는 데는 세 가지 이유가 있으며, 이유를 이해하는 것이 게이트 설계에 중요합니다.
컨텍스트 윈도우 압박. 테스트 스위트를 실행하면 컨텍스트를 소비합니다. 파일을 읽어도 컨텍스트를 소비해요. 긴 세션을 관리하는 에이전트는 다음 작업을 위해 컨텍스트를 보존하려고 검증을 건너뛸 수 있습니다. 증거 게이트는 이 트레이드오프를 가시화해요. 에이전트가 산출물 없이 완료를 주장할 수 없으므로, 컨텍스트 압박은 유령 검증이 아닌 미완료 작업으로 드러납니다.
도구 호출 회피. 일부 에이전트 구성은 도구 호출에 페널티를 부과하거나 제한합니다. pytest를 호출하지 않고 “테스트 통과”라고 보고할 수 있는 에이전트는 도구 호출 하나를 절약해요. 증거 게이트는 이 지름길을 제거합니다. 테스트 출력이 필수이므로 도구 호출도 필수입니다.
인간 패턴에 대한 학습. 인간은 항상 모호한 표현으로 완료 보고서를 작성합니다. “설정을 업데이트했고 테스트가 통과할 거예요.” 인간의 텍스트로 학습된 에이전트는 이 패턴을 재현해요. 증거 게이트는 산출물 없이 보고서를 받아들이지 않음으로써 이 패턴을 깨는 학습 후 개입입니다.
자부심 점검
증거 게이트는 자부심 점검이라고 부르는 더 넓은 품질 시스템의 일부입니다. 모든 비사소한 변경 후에 묻는 다섯 가지 질문이에요:
- 시니어 엔지니어가 이것을 존중할까?
- 코드가 스스로를 설명하는가?
- 엣지 케이스가 처리되었는가?
- 적절한 수준의 단순함인가?
- 코드베이스를 발견했을 때보다 더 나은 상태로 남겼는가?
자부심 점검은 증거 게이트가 객관적인 반면 주관적입니다. 증거 게이트는 “이것이 작동한다는 것을 증명할 수 있는가?”를 묻습니다. 자부심 점검은 “존경하는 사람에게 이것을 보여줄 때 자랑스러울 것인가?”를 물어요. 둘 다 필요합니다. 자부심 없는 증명은 작동하지만 아무도 유지보수하고 싶지 않은 코드를 만들어요. 증명 없는 자부심은 읽기 좋지만 작동하지 않을 수 있는 코드를 만듭니다.
이 조합이 품질 루프를 만듭니다: 구현, 모든 줄 검토, 증거 게이트 실행, 자부심 점검 적용, 발견된 모든 문제 수정, 둘 다 통과할 때까지 반복. 이 루프는 효율적이지 않습니다. 빠르지도 않아요. 하지만 정확합니다. 에이전트가 높은 속도로 그럴듯한 코드를 생성할 수 있는 세상에서, 정확성이 차별화 요소입니다.
실패 모드
증거 게이트는 유령 검증을 잡아냅니다. 모든 실패 모드를 잡지는 않아요. 자율 에이전트 세션에서 7가지 명명된 실패 모드가 나타납니다:1
지름길 연쇄. 더 빨리 완료를 보고하기 위해 증거 게이트 단계를 건너뛰는 것. 에이전트가 불완전한 보고서를 만들고 완료되었다고 주장합니다.
확신의 신기루. 높은 확신을 가지고 진술되는 “확신합니다.” 증거 게이트가 이 표현을 잡아내지만, 충분히 유창한 에이전트는 감지를 피하기 위해 모호한 표현을 바꿔 쓸 수 있습니다.
적당한 수준의 안주. 코드가 작동하지만 깔끔하지 않거나 충분히 테스트되지 않은 상태. 증거 게이트의 “가장 단순한 작동 솔루션” 기준이 이를 부분적으로 다루지만, 자부심 점검이 주요 방어선입니다.
터널 비전. 한 함수를 다듬으면서 인접한 코드를 깨뜨리는 것. “회귀 없음” 기준이 이를 다루지만, 에이전트가 올바른 파일을 확인해야만 효과가 있어요.
이연 부채. 커밋된 코드에 남아 있는 TODO/FIXME/HACK. 증거 게이트는 이를 검사하지 않아요. 별도의 린트 규칙이 적절한 방어 수단입니다.
공허한 보고서. 어떤 기준에 대한 증거도 없이 “완료”라고 보고하는 것. 증거 게이트의 구조가 이를 명백히 불완전하게 만들지만, 에이전트가 하나의 기준을 누락하면서도 완료된 것처럼 보이는 보고서를 생성할 수 있습니다.
유령 검증. 증거 게이트의 주요 표적. 산출물 없이 테스트나 검증을 수행했다고 주장하는 것. 게이트가 일관되게 시행되면 12% 실패율이 거의 0에 가깝게 떨어집니다.
규율
증거 게이트는 기술적 혁신이 아닙니다. 규율이에요. 주장을 수용하기 전에 증명을 요구하는 규율. “그렇다고 생각합니다”를 불충분하다고 취급하는 규율. 통과할 것을 알면서도 테스트를 실행하는 규율.
이 규율은 에이전트 이전보다 지금 더 중요합니다. “테스트가 통과합니다”라고 말하는 인간 개발자는 대체로 테스트를 실행한 상태예요. 인간이 둘 다 수행했기 때문에 주장과 관찰이 뒤섞입니다. “테스트가 통과합니다”라고 말하는 에이전트는 둘 다 수행하지 않았을 수 있어요. 증거 게이트는 주장과 관찰을 분리하고 둘 다를 요구합니다.
그럴듯한 출력의 시대에, 증명만이 유일하게 신뢰할 수 있는 신호입니다. 나머지는 모두 추론일 뿐이에요.
FAQ
이건 그냥 코드 리뷰 아닌가요?
코드 리뷰는 코드가 올바른지 확인합니다. 증거 게이트는 완료 보고서가 정직한지 확인해요. 코드 리뷰는 한 번도 테스트되지 않은 올바른 코드를 승인할 수 있습니다. 증거 게이트는 코드가 올바르게 보이는지 여부와 관계없이 테스트 출력을 요구합니다.
개발 속도가 느려지지 않나요?
네. 테스트를 실행하고, 파일을 읽고, 구체적인 증거를 인용하는 데는 시간이 걸려요. 대안은 유령 검증된 코드를 배포하고 프로덕션에서 버그를 발견하는 것입니다. 증거 게이트는 개발 속도를 배포 신뢰도와 교환합니다.
에이전트가 증거 게이트를 우회하는 법을 배울 수 있나요?
에이전트가 테스트 출력을 조작하거나 잘못된 줄 번호를 인용할 수도 있어요. 증거 게이트는 적대적 공격에 완벽하지 않습니다. 적대적 실패 모드(의도적 조작)가 아닌 일반적인 실패 모드(추론을 관찰처럼 제시)를 잡아내요. 의도적 조작에는 다른 방어가 필요합니다.
자율 에이전트에 이걸 어떻게 적용하나요?
증거 게이트 기준은 시스템 프롬프트의 일부입니다. 품질 루프(구현, 검토, 게이트, 점검, 수정, 반복)는 오케스트레이션 시스템에 인코딩되어 있어요. 에이전트는 6가지 기준 모두에 대한 증거를 제시하지 않으면 완료를 보고할 수 없습니다. 기준이 누락되면 루프는 수정 단계로 돌아갑니다.
출처
-
Blake Crosley, “What I Told NIST About AI Agent Security,” blakecrosley.com, February 2026. 60회 이상의 자율 세션에서 12%의 유령 검증 비율. ↩↩