자체 검증을 통과한 숫자만 게시하는 페이지를 구축했습니다. 한 시간도 채 되지 않아 저 자신을 잡아냈습니다.
요약
데이터의 신뢰성을 보장하기 위해 라이브 설정과 연동된 자체 검증 페이지를 구축한 사례를 다룹니다. 수치와 시스템 설정 간의 불일치를 방지하기 위해 '실패 시 차단(fail closed)' 방식을 적용하여 데이터 무결성을 확보하는 과정을 설명합니다.
핵심 포인트
- 라이브 설정과 연동하여 데이터의 유효성을 실시간으로 검증하는 시스템 구축
- 데이터와 설정 간의 격차로 인한 정보 노후화(stale data) 문제 해결
- '실패 시 차단(fail closed)' 원칙을 통한 데이터 게시의 엄격한 통제
- 검증되지 않은 데이터의 노출을 막기 위한 자동화된 필터링의 중요성
저는 검증된 추론 엔드포인트 (inference endpoint)를 운영합니다. 저희가 판매하는 것은 검증입니다. 저희는 결과를 제공하기 전에 검증하며, 검증할 수 없는 경우에는 그렇게 말합니다.
이번 달에 배포된 모든 클레임 결함 (claim defect)은 동일한 형태를 띠고 있었습니다. 숫자는 정직하게 측정되었지만, 그 밑단의 설정 (configuration)이 변경되었고, 숫자는 페이지에 그대로 남아 있었습니다. 저희의 벤치마크 행들은 몇 주 전에 은퇴한 모델 티어 (model tiers)에서 측정된 것이었습니다. 무엇도 조작되지 않았습니다. 모든 것이 오래된 정보 (stale)였습니다.
그래서 저는 정보가 오래될 수 없는 페이지를 구축했습니다. 이 페이지는 라이브 게이트웨이 설정 (live gateway config)에서 생성되며, 페이지의 모든 배지 (badge)는 직접 작성되는 것이 아니라 파생됩니다. 만약 측정값의 설정 전제 조건이 현재 실행 중인 것과 더 이상 일치하지 않으면, 페이지는 해당 숫자를 삭제하고 그 이유를 명시합니다.
페이지가 라이브로 공개되었습니다. 한 시간도 채 되지 않아, 저희 자체 장부 (ledger)가 명시적으로 유보한 두 개의 숫자를 제가 게시하고 있는 것을 잡아냈습니다.
페이지가 하는 일
시스템의 각 경로에 대해 다음 네 가지 사항을 명시합니다:
- 해당 경로에서 "좋음"이 무엇을 의미하는지, 그리고 실제 오라클 (oracle)이 이를 검증했는지 여부. 도구 호출 (Tool calls)은 스키마 (schema)에 따라 검증됩니다. 코드는 샌드박스 (sandbox) 내에서 사용자의 테스트를 대상으로 실행됩니다. 자유로운 산문 (Free prose)은 실행할 대상이 없기 때문에 아무런 검증도 받지 않습니다.
- 측정값, n값, 간격 (interval) 및 날짜를 포함합니다.
- 해당 측정값이 여전히 실행 중인 시스템을 설명하는지 여부, 라이브 설정 (live config)에 따라 자동으로 등급이 매겨집니다.
- 적대적 검토 (adversarial review)를 통과했는지 여부, 통과하지 못했다면 숫자 옆에 해당 내용이 표시됩니다.
세 번째 항목이 핵심입니다. 숫자와 설정은 서로 조용히 멀어지는 두 가지 사실이며, 아무리 주의를 기울여도 이를 해결할 수는 없습니다. 두 가지를 모두 읽어들이는 생성기 (generator)는 그 격차에 대해 거짓말을 할 수 없습니다.
함정
저는 저희의 도구 호출 (tool-calling) 및 트랩 세트 (trap-set) 결과를 페이지에 올렸습니다. 둘 다 좋은 숫자들입니다. 하지만 둘 다 저희 내부 클레임 장부 (claims ledger)에는 _외부 인용 승인되지 않음, 적대적 검증 필요_라고 표시되어 있었습니다.
저는 오직 저희의 검증을 통과한 것만 게시하는 것을 목적으로 하는 페이지를 작성해 놓고, 정작 검증은 실행하지 않았던 것입니다.
첫 번째 해결책은 해당 문구 하나를 차단 목록 (blocklist)에 넣는 것이었습니다. 이 또한 틀렸습니다. 저희의 원장 (ledger)은 최소 여섯 가지 표현 방식에서 행 (rows)을 보류 (withholds)하고 있었고, 차단 목록은 일곱 번째 표현을 놓쳤습니다. 공개적인 주장을 위해서는 게이트 (gate)가 반드시 실패 시 차단 (fail closed) 방식으로 작동해야 합니다. 즉, 수치는 원장의 행이 존재하고 보류 마커 (hold marker)가 없는 경우에만 게시되어야 합니다. 과도한 보류는 페이지에서 숫자를 하나 잃는 비용을 발생시키지만, 과소한 보류는 정정 공고 (retraction)를 내야 하는 비용을 발생시킵니다.
실패 시 차단 (fail closed) 방식을 적용하자마자, 더 미묘한 두 가지 오류를 즉시 잡아낼 수 있었습니다. 두 수치는 원장에 행 자체가 아예 없었는데, 이는 제가 진실 계층 (truth layer) 대신 내부 인계 (internal handoff) 과정에서 가져온 것이었기 때문입니다. 그중 하나는 30/40이라는 결과였습니다. 저희 원장에는 다른 모델의 완전히 다른 실행 결과에서 나온 30/40도 포함되어 있었습니다. 숫자는 같지만 측정값은 달랐습니다. 이것이 바로 출처가 명시되지 않은 수치가 초래하는 혼란입니다.
그 후 리뷰어가 제 초안을 거절했습니다
남은 주장은 실제 적대적 검토 (adversarial review)가 필요했기에, 저는 검토를 의뢰하며 동의하기보다는 반박하는 데 초점을 맞추도록 지침을 전달했습니다. 검토 결과는 **게시 금지 (do not publish)**였으며, 세 가지 구체적인 결함이 지적되었습니다:
- 저는 저희의 합의 게이트 (agreement gate)가 틀린 답을 "대략 4분의 1에서 3분의 1 정도의 확률로" 수용한다고 작성했습니다. 해당 범위는 신뢰 구간 (confidence intervals)이 거의 완전히 겹치는, 통계적 힘이 부족한 두 개의 하위 그룹 (25명 중 6명, 15명 중 5명)의 결과였습니다. 이는 마치 안정적인 운영 특성 (operating characteristic)처럼 읽힙니다. 데이터는 하나의 통합된 이항 분포 (pooled binomial)를 지지합니다: 40명 중 11명, 27.5%, Wilson 95% 구간 16.1 ~ 42.8.
- 저는 "코드에서 1.7 ~ 3.5%"라고 인용했습니다. 이는 측정된 구간 (measured interval)의 형태를 띠고 있습니다. 리뷰어는 제 지침서에 근거가 없다고 말했는데, 이는 보통 제가 지침을 충분히 전달하지 못했음을 의미합니다. 그래서 저는 그냥 받아들이는 대신 확인해 보았습니다. 해당 수치는 저희 원장에 세 번 등장하지만, 표본 크기 (n), 구간, 날짜가 어디에도 명시되어 있지 않았습니다. 공백은 실재했습니다.
- 저는 "산술에서는 거의 제로에 가까움"이라고 작성했습니다. 이는 독자가 저희 기사 본문에서 찾아낸 후 공개적으로 정정한 주장의 잔재였습니다. 깨끗한 대체 표현이 존재하지 않으므로, 이 문구는 아예 등장해서는 안 됩니다.
저는 세 가지 모두를 선택했습니다. 다만 한 가지 지침은 거부했습니다. 그것은 내부 임계값 (internal threshold)을 명시하라고 요구했는데, 이는 결과라기보다 레시피 (recipe)에 가깝기 때문입니다. 제가 우리의 공개 정책 (disclosure policy)에 대해 브리핑하지 않았기 때문에 모델은 이를 알지 못했습니다. 동일한 실패가 반대 방향으로 일어난 것입니다.
실패할 수 없었던 초록색 체크 표시
우리의 주장 검증 게이트 (claims gate)를 더 많은 영역으로 확장하는 동안, 저는 이 시스템이 처음 작성되었을 때 했어야 했던 일을 했습니다. 바로 이 시스템이 잡아내기 위해 존재하는 결함을 직접 주입한 것입니다.
일곱 번의 주입을 시도했습니다. 다섯 번의 잘못된 백분율, 잘못된 비용 배수, 그리고 철회된 복리 주장 (compounding claim). 하지만 시스템은 그 중 어느 것도 잡아내지 못했습니다. 시스템의 허용 오차 (tolerance)가 검사하는 대부분의 범위를 포괄하고 있었기에, 거의 모든 숫자가 무언가에 의해 "뒷받침"되는 상태였습니다. 시스템은 몇 주 동안 매일 아침 OK를 출력해 왔습니다.
시스템이 거짓말을 한 것은 아니었습니다. 시스템 자체의 출력값에는 "주장이 아닌 값들을 확인함 (values checked, not claims)"이라고 명시되어 있으며, 소스 코드의 주석도 동일하게 말하고 있습니다. 결함은 한 단계 바깥에 있었습니다. 상태 보드(status board)의 초록색 선은 보호 장치로 읽히며, 아무도 그 주의 사항을 다시 읽지 않는다는 점입니다.
한 번도 실패하는 것을 본 적 없는 감시자는 감시자가 아닙니다. 그것은 습관일 뿐입니다.
전이되는 규칙들
레포지토리 (repo)에서의 수정은 공개적인 수정이 아닙니다. 모든 내부 도구 (instrument)가 레포지토리를 grep 하여 검사하고 있었기 때문에, 모든 내부 도구가 검사가 완료되었다고 보고하는 동안에도 우리의 라이브 사이트는 여전히 철회된 수치를 그대로 제공하고 있었습니다. 공개적인 주장을 검증하려면 반드시 공개 URL을 직접 가져와서 확인하십시오.
측정 도구는 하나의 도구(instrument)이므로, 먼저 그것을 검증하십시오. 우리의 비용 스크립트는 세 개의 비용 컬럼 중 하나만을 합산했습니다. 실제 값이 $1.73일 때 1센트를 보고했으며, 이는 우리 원장 (ledger)의 비용 행이 인용하고 있는 바로 그 스크립트였습니다.
가독성 필터 (readability filter)가 오류를 삼킬 수 있습니다. 저는 지원 중단 경고 (deprecation warning)를 숨기기 위해 원격 스크립트를 grep -v로 파이프라인 처리했습니다. 데이터베이스 인증 실패가 종료 코드 0을 반환했고, 이것이 STATUS: Success가 되었으며, 이어 두 운영 서버 (production boxes) 전체에 걸쳐 플릿 단위 (fleet-wide)의 OK로 이어졌습니다. 거짓말은 단계를 거칠 때마다 더욱 권위 있게 변했습니다.
플레이스홀더(Placeholder)는 곧 주장(Claim)입니다. 우리의 대시보드는 실시간 데이터가 도착하기 전에 "이번 달 $1,284 절약"이라는 문구를 정적 HTML (static HTML)로 렌더링했습니다. 사용량이 전혀 없는 신규 계정이라도 조작된 절약 금액을 보게 되었을 것입니다. 저는 낯선 사람들이 이를 보기 직전 단계인, 일반 공개를 위한 가입 절차를 열던 중에 이 문제를 발견했습니다.
당신이 취약한 경로를 공개하십시오. 우리가 설정한 트랩(trap) 세트는 기본 경로(primary path)에서 200개 중 200개 모두 통과했습니다. 페일오버 (failover) 상황에서는 40개 중 30개만 통과했으며, 모든 실패는 동일한 판결을 내렸습니다: 거절해야 할 상황에서 도구 (tool)를 호출한 것입니다. 두 수치 모두 페이지에 게시되어 있습니다. 두 번째 수치가 더 유용한 정보입니다.
직접 확인해 보세요
해당 페이지는 tirtha.ai/capabilities에서 라이브로 확인하실 수 있습니다. 7개의 경로, 각 경로가 무엇을 체크하는지, 무엇을 측정했는지, 그리고 모든 수치의 검토 상태를 확인할 수 있습니다.
글로 읽는 대신 직접 체험해 보려면, tirtha.ai/verify에서 키나 가입 없이 우리 API를 대상으로 실시간 테스트를 진행할 수 있습니다. 세 가지 사전 설정된 적대적 사례 (adversarial cases)가 준비되어 있으며, 모델이 호출을 지어내는 대신 거절하는 모습을 직접 지켜볼 수 있습니다.
자신의 워크로드 (workload)에 적용해 보려면: tirtha.ai로 이동하여 'Get an API key'를 클릭하고, Google 계정으로 로그인하여 시작하세요. 베타 기간 동안은 무료입니다. 카드 등록도, 이메일 입력도, 영업 전화도 없습니다. 매달 100회의 요청을 제공하며, 예상치 못한 결과를 내놓는 대신 요청을 중단합니다.
만약 해당 페이지에서 검증을 통과하지 못하는 주장을 발견하신다면, 꼭 알려주시기 바랍니다. 이는 수사적인 표현이 아닙니다. 지난번 누군가 이를 발견했을 때, 그는 우리가 직접 게시한 텍스트를 읽고 있었으며, 그의 지적이 옳았습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기