A Coding System That Refuses to Trust Its Own Output
자신이 만든 코드를 스스로 승인하지 않는 코딩 시스템
Derivative는 자연어 요구사항을 실행 가능한 Python으로 바꾸지만, 생성기가 결과를 승인하지는 않습니다. 별도 검증 파이프라인이 격리 실행과 계약·적대적 검사를 거쳐 패키징을 결정하며, 블라인드 평가에서 검증 성공은 0/6이었지만 실패 판정과 불가능 판정은 각각 3/3을 맞혔습니다.
- 주제
AI 요약
Derivative는 코드를 생성하는 일보다, 생성된 구현을 받아들일 근거를 어떻게 마련할지에 초점을 둔 오픈소스 프로젝트입니다. 생성기와 승인 권한을 분리해, 코드를 만든 구성 요소가 스스로 정답을 선언하지 못하게 합니다. 소프트웨어 빌드 파이프라인 Forge는 자연어 요구사항을 구조화된 계약으로 바꾸고, 후보 코드를 생성해 격리 실행한 뒤 독립 검증을 수행합니다. 검증 증거가 충분할 때만 패키징하며, 부족하면 명시적으로 실패를 반환합니다.
요구사항을 계약으로 바꾸고 후보 코드를 격리합니다
요구사항을 곧바로 코드로 옮기지 않고, 먼저 명시적 의무와 제약으로 컴파일합니다. 계약에는 공개 인터페이스, 필요한 파일과 명령, 동작 기준, 영속성·보안·관측성 요구, 테스트 의무, 금지 동작 등이 들어갑니다. 이 계약은 검증 전에 고정합니다. 후보 코드가 나온 뒤 정답의 기준을 바꾸지 못하게 하려는 설계입니다.
생성된 코드는 신뢰하지 않는 소프트웨어로 취급합니다. Forge는 후보를 임시 Docker 샌드박스에서 실행합니다. 네트워크를 차단하고, 루트 파일시스템을 읽기 전용으로 설정하며, 자원과 환경 변수를 제한합니다. 호스트 자격 증명도 물려주지 않습니다. 따라서 모델이 코드를 보고 “작동할 것”이라고 판단하는 대신, 실제 실행 환경에서 무슨 일이 일어났는지를 검증 근거로 삼습니다.
생성과 승인 권한을 분리합니다
검증은 구문·임포트·실행 확인, 요구사항 및 인수 조건 검사, 적대적 검증으로 나뉩니다. 모든 필수 단계가 통과해야 패키징을 승인합니다. 코드가 실행되더라도 요구사항을 충족하지 못하면 빌드에 실패합니다. 결과 상태도 구분합니다. verified는 요구된 실행·계약·적대적 검증을 통과했다는 뜻입니다. validation_failed는 후보는 있지만 패키징을 정당화할 증거가 부족하다는 뜻입니다. infeasible_proven은 원래 제약이 모순임을 증거와 함께 확인했다는 뜻입니다. 샌드박스를 사용할 수 없거나 정책을 위반한 경우는 소프트웨어 요구사항 실패와 별도로 기록합니다.
저자는 verified가 형식적 증명이나 모든 입력에 대한 정확성, 완전한 보안을 뜻하지 않는다고 선을 긋습니다. 해당 리비전에서 Forge가 검사할 수 있는 실행 계약과 증거 기준을 만족했다는 제한된 의미입니다. 검증이 구체적 실패를 찾으면 수정도 시도하지만, 무작정 재생성하지는 않습니다. 실패 서명에 맞춘 수정을 요구하고, 산출물이 실질적으로 바뀌었는지 확인한 뒤 전체 검증을 다시 수행합니다.
결과와 증거를 남기고 블라인드 평가를 유지합니다
빌드 산출물에는 build_spec.json, feasible_plan.json, code_artifact.json, validation_artifact.json, packaged_artifact.json 등이 포함될 수 있습니다. 패키지에는 코드와 테스트, 승인 근거가 함께 남습니다. 따라서 결과 파일뿐 아니라 어떤 계약을 기준으로 평가했는지, 무엇을 실행했는지, 왜 패키징을 허용했는지도 살펴보고 다시 분석할 수 있습니다.
개발 중 반복해서 본 사례와 독립 평가를 구분하려고, Derivative는 동결된 블라인드 벤치마크를 일반 회귀 테스트와 따로 관리합니다. 현재 V11 기준선은 12개 사례입니다. 상태 분류 정확도는 6/12였고, 외부 오라클 기준 Verified@1은 0/6이었습니다. 검증된 소프트웨어를 기대한 여섯 사례 중 외부 오라클을 통과한 결과가 하나도 없었습니다. 반면 validation_failed 사례는 3/3, 불가능 판정 사례도 3/3을 맞혔습니다. 저자는 이를 제품 수준의 코드 생성 성능으로 포장하지 않습니다. 시스템이 외부에서 승인되는 코드를 만드는 능력보다, 근거 없는 성공 판정을 거부하는 능력을 먼저 보여준 결과라고 설명합니다. 동결된 결과는 이후 수정의 회귀 근거로만 재사용하며, 새 블라인드 결과로 바꾸지 않습니다.
범위와 다음 측정 과제
Forge의 현재 범위는 새로 만드는 Python CLI, REST 서비스, 데이터 파이프라인, 라이브러리입니다. 기존 저장소의 임의 편집, 프런트엔드 생성, 다중 언어 지원, 보편적 형식 검증이나 일반적인 소프트웨어 정확성은 주장하지 않습니다. 다음 과제도 지원 범위를 넓히는 일이 아니라, 내부 검증과 독립 외부 오라클의 승인 사이 간격을 줄이는 일입니다. 새 블라인드 사례, 더 나은 요구사항 컴파일, 강화된 검증을 개발 중에 이미 본 적 없는 분포에서 측정하겠다는 계획입니다.
dev.to 반응
- @reidmarlow — 의무 계약을 미리 고정하면 생성기가 기준을 바꾸는 문제는 막을 수 있지만, 가장 까다로운 경계는 요구사항 컴파일러 쪽으로 옮겨갑니다. 처음 구조화된 제약으로 번역할 때 명시되지 않은 가정이나 인터페이스의 경계 조건을 빠뜨리면, 샌드박스 실행은 깔끔하고 증거 묶음도 그럴듯해도 불완전한 명세를 기준으로 빌드를 인증하게 됩니다. 외부 검증 성공률 0/6과 불가능 판정 통과를 함께 공개한 점은 좋은 벤치마크 방식입니다. 많은 에이전트 테스트 환경은 통과율이 보기 좋지 않으면 평가 기준을 슬쩍 낮춥니다.
- @danielecangi — 그 경계는 검증할 가치가 있지만, 아직 가장 까다로운 경계라고 단정하지는 않겠습니다. 동결된 의무 계약이 있으면 원래 요구사항, 컴파일된 의무, 결과 증거를 비교할 안정된 기준점이 생깁니다. 반복 측정에서 가정이나 인터페이스 조건이 누락된다면 그때 구체적인 재설계 과제가 드러납니다. 아직 도달하지 않은 한계를 가정해 설계하기보다, 현재 구조를 밀어붙여 실제로 어디서 깨지는지 증거로 확인하고 싶습니다. 외부 검증 0/6도 기준을 완화하지 않은 채 시스템이 무엇을 입증했고 무엇을 입증하지 못했는지 보여줍니다.
- @arhancanli — 동결된 블라인드 결과를 0/6 검증 성공으로 공개하고, 숫자가 좋아 보일 때까지 조정하지 않은 점을 가장 신뢰합니다. 다만 사례가 6개뿐이면 0/6은 실제 성공률이 대략 46%를 넘는다는 가설만 배제합니다(정확한 95% 경계). 거부 판정 3/3도 실제 거부율이 29% 정도로 낮을 가능성과 양립합니다. 시스템이 신중한지, 아니면 전부 거부하는지는 아직 구분되지 않습니다. 이를 나눌 지표는 오수락률입니다. 외부 오라클이 거부한 후보 중 검증기가
verified로 표시한 비율입니다. 검증 결과가 0개면 이 비율은 정의할 수 없습니다. 생성기 성능이 나아지기를 기다리지 않고 검증기를 직접 시험하려면, 눈에 띄는 테스트를 통과하지만 실제로 틀린 후보를 새 블라인드 세트에 넣어야 합니다. 경계값 오류, 잘못된 날짜 계산, 예외를 삼키는 코드 같은 사례입니다. 적대적 검증의 테스트도 계약에서 나온 요구사항을 따르나요, 아니면 독립적으로 만들었나요? - @glenallen — 생성과 승인을 분리한 점은 좋지만, 승인 근거의 범위도 생성 산출물만큼 꼼꼼히 살펴야 합니다. 검증기가 아는 의무를 모두 만족해도 계약에 들어오지 않은 가정을 어길 수 있습니다. 그래서 검증 성공과 함께 ‘검증 완전성’도 측정할 만합니다. 선언된 의무가 모두 통과했는지만 볼 것이 아니라, 중요한 요구사항이 의무로 제대로 옮겨졌는지도 확인해야 합니다. 동결 계약은 기준이 바뀌는 일을 막지만, 원래 요구사항에서 의무와 검증 증거까지 이어지는 추적 정보가 있으면 누락된 범위를 숨은 가정으로 남기지 않고 관찰할 수 있습니다.
- @aidiveyt — 승인 근거를 어디까지 적용했는지 따져봐야 한다는 점에 공감합니다. 제가 실행한 별도 리뷰 에이전트는 빌드 에이전트가 이미 통과시킨 변경을 117초 동안 검토했습니다. 단위 테스트 50개, 빌드, 린트 변경분, Prettier를 다시 확인하고 중요한 지적 0건과 사소한 지적 6건을 반환했습니다. 그런데 최종 보고서는 E2E를 실행하지 않았고 라이브 서버와 시드 데이터베이스가 필요하다고 표시했습니다. 범위는 솔직하게 밝혔지만, 판정만 보면 실제보다 더 많은 부분을 확인한 듯 읽혔습니다.
- @danielecangi — Derivative가 구분하려는 것도 바로 그 점입니다. 별도 리뷰 에이전트는 유용한 증거를 줄 수 있지만, 녹색 판정은 실제 검사 범위 안에서만 의미가 있습니다. 테스트하지 않은 E2E 경로는 테스트 50개를 다시 실행해도 확인되지 않습니다. Forge는 명시된 계약, 실행한 검증, 기록된 증거를 기준으로 승인하며 외부 오라클의 판정은 별도로 둡니다. 판정이 증거 범위를 정하는 것이 아니라 증거가 판정의 의미를 정해야 합니다.
- @madhabcoder — “생성기가 ‘통과했다’고 말하게 두지 않는다”는 점이 제가 배운 전부입니다. PR을 놀리는 Chrome 확장 프로그램을 만들면서 값싼 방식으로 배웠습니다. 처음에는 같은 모델이 조롱 문구를 쓰고 채점했는데, 변경 목록에도 없는 파일을 소재로 삼은 농담까지 계속 승인했습니다. 별도 검사기(Jev)를 두니 많이 나아졌습니다. 여기에 누군가 아주 간단한 방법을 제안해 지금 추가하고 있습니다. 모델이 판단하기 전에 언급된 파일이 실제 diff에 있는지 일반 검사로 확인하는 방식입니다. Derivative에도 검증 전에 이런 값싼 비모델 검사가 있나요, 아니면 전부 의무 계약을 거치나요?
- @danielecangi — Derivative에는 파일·경로 무결성, 구문, 임포트, 진입점, 인터페이스 계약, 매니페스트, 출처 정보를 확인하는 결정적 비모델 검사도 있습니다. 이 검사는 의무 기반 검사와 적대적 검증을 비롯해 파이프라인 여러 단계에 나뉘어 있습니다. 기사에서 전체 구조를 다 설명하지는 못했습니다. 원칙은 말씀하신 사례와 같습니다. 결정적으로 확인할 수 있는 항목을 모델에게 판단시키지 않는 것입니다.
- @kartik-nvjk — 생성기가 “통과했다”고 말하지 못하게 하고, 검증 증거만 패키징을 승인하는 규칙은 계속 지켜야 합니다. 비슷한 시스템을 만들었을 때 효용은 나중에 드러났습니다. 각 실행의 실패 증거를 저장하고, 오늘의 검증기가 지난주 실패 사례도 잡아내는지 확인하는 방식입니다. Forge는 실패 사례를 생성기 개선에 다시 쓰나요, 아니면 의도적으로 생성과 검증을 독립적으로 유지하나요?
원문: dev.to / 번역·요약: Trawling