
AI 구현의 '누락 유형' 측정 — Codex와 Opus에 동일한 issue를 전달했을 때 (안 A)
요약
AI 모델이 코드를 구현할 때 요구사항을 완벽히 반영하지 못하고 일부를 놓치는 '누락 유형'을 측정하는 실험을 소개합니다. Opus와 Codex를 대상으로 동일한 이슈를 전달하여 구현 누락 패턴을 분석하고, 모델의 추론 능력 문제인지 사양서의 문제인지 구분하는 방법론을 다룹니다.
핵심 포인트
- 보수 PR의 역산을 통해 객관적인 구현 누락 체크리스트를 생성함
- Opus와 Codex에 동일한 프롬프트를 제공하여 구현 성능을 비교함
- 누락 패턴 분석을 통해 모델의 추론 품질과 사양서의 명확성을 구분함
- 에러가 발생하지 않는 '누락 유형'의 위험성과 질적 분석의 중요성 강조
Opus에게 구현을 맡기고 있으면, "기능은 작동하는데 요구사항이 누락되는" 현상이 반복해서 발생했다.
버그가 아니다. 에러가 발생하지 않는다. 데모도 제대로 작동한다. 하지만 나중에 확인해보면, 지정했을 터인 동작의 일부가 구현되어 있지 않다. 우리는 이것을 "누락 유형(抜け型)"이라고 부르고 있다.
원인에 대한 가설은 두 가지였다.
- Opus의 추론 품질 문제 (복잡한 요구사항을 망라하지 못함)
- issue의 사양서 문제 (암묵적인 전제가 너무 많아서 모델이 감지하지 못함)
이 두 가지는 의미가 완전히 다르다. 모델의 문제라면 교체를 검토할 수 있다. 사양서의 문제라면 정교화하면 끝날 일이다.
이를 구분하고 싶었다.
실험 디자인
보수 PR의 역산
실험의 평가 기준을 어떻게 만들지가 먼저 문제가 되었다. "AI가 작성한 코드를 인간이 읽고 채점하는" 방식은 주관이 너무 많이 개입된다.
그래서 사용한 것이 보수 PR(Pull Request)의 역산이다.
"구현 → 누락 발견 → 보수 PR"이라는 구조를 가진 issue에서는, 보수 PR의 차이분(diff)이 "당시 구현이 놓친 요구사항 그 자체"가 된다. 이 차이분을 역산하면 객관적인 체크리스트를 만들 수 있다.
재료로 선택한 것은 과거에 누락이 발생했던 3가지 기능이다.
- 기능 A: 고유명사 사전 (음성 인식에 어구 주입) -
- 기능 B: AI 보정 UI (텍스트 변환 후 오타 검출 패널) -
- 기능 C: 저확도 어구 편집 UI (신뢰도가 낮은 어구의 하이라이트 및 수정) -
각각 체크리스트를 준비했다 (N = 15 / 16 / 11).
cold-start 재현
각 issue의 "구현 커밋의 부모 커밋"으로부터 브랜치를 다시 생성했다. 당시 개발자가 착수했을 때와 동일한 코드베이스를 재현한다.
입력의 동일화
Codex와 Opus에 완전히 동일한 프롬프트를 전달한다. issue 본문과 착수 전의 조사 코멘트만을 추출한 세트다. 체크리스트(정답)는 전달하지 않는다.
안 A의 결과
안 A는 "이력의 Opus vs Codex" 비교다.
Opus의 열은 과거의 구현 이력으로부터 확정되어 있다. 보수 PR이 존재한다는 것은 당시의 구현이 해당 요구사항을 놓쳤음을 의미한다. Codex에게는 새롭게 구현하도록 시켰다.
| 기능 | 전체 요구사항 N | Opus 누락 | Codex 누락 |
|---|---|---|---|
| 기능 A (고유명사 사전) | 15 | 3 | 1 (경미) |
| ... | 합계 | 15 | 1 |
| 42 |
Opus 15개 누락, Codex 1개 누락.
"누락 유형"의 질적 분석
숫자보다, 어떻게 누락되었는가가 본질이라고 생각한다.
Opus의 누락에는 3가지 패턴이 있었다.
패턴 1: 신규 파라미터의 "전 경로 수평 전개" 누락 (기능 A)
고유명사 사전의 주 경로(최초 업로드 시의 어구 주입)는 완벽하게 구현했다. 문제는 그 이후였다.
이 시스템에는 작업(job)의 재실행 경로가 3가지 있다. ① 실패한 작업의 수동 재실행, ② 타임아웃 시의 자동 재시도, ③ 폴백(fallback)을 경유한 재시도. 3가지 모두에서 어구를 전달하는 것을 잊었다. 각 경로가 서로 다른 레이어(프론트엔드 API · 백엔드 Python · UI 컴포넌트)에 흩어져 있기 때문에, 각각에 수평 전개할 필요가 있었다.
issue에는 "재실행 시에도 어구를を引き継ぐ(계승)할 것"이라고 적혀 있지 않았다. "기능을 추가하면 전 경로에 적용하는 것이 당연하다"라는 암묵적인 전제 때문에 누락되었다.
실질적인 피해는 크다. 최초의 텍스트 변환에서는 고유명사 보조가 작동하는데, 재실행하는 순간 소리 없이 사라진다. 에러가 발생하지 않기 때문에 알아채기 어렵고, 나중에 조사하면서 발견되었다.
패턴 2: "다중 레이어의 접속" 연결 누락 (기능 B)
"A가 B를 호출하는" UI는 완성되었다. 하지만 "B의 엔드포인트 자체를 만드는" 한 단계 아래의 레이어가 누락되었다.
백엔드의 API 엔드포인트와 프론트엔드의 루트, 2개 레이어가 미작성 상태로 종료되었다. 호출을 해도 404가 반환된다. UI는 완성된 것처럼 보이지만 실제로는 아무 일도 일어나지 않는다.
issue에는 "UI를 구현한다"라고 되어 있었지만 "그 UI가 호출하는 서버 측도 만든다"는 적혀 있지 않았다. 접속의 양 끝을 맞추는 책임이 암묵적인 전제가 되어 있었다.
패턴 3: 스코프의 후반부 포기 (기능 C)
issue는 백엔드 처리 · 데이터베이스 · 프론트엔드의 3개 레이어 모두를 명시적으로 요구하고 있었다. 백엔드와 DB는 완벽히 구현하여 종료했다. 프론트엔드에는 손을 대지 않았다.
패턴 1·2와 달리, 이것은 암묵적인 전제조차 아니다. 명시되어 있던 스코프 (scope)의 후반부를 그대로 포기했다.
3개 패턴에 공통되는 구조
주 경로 (happy path)를 완성시킨다
↓
데모가 동작한다
...
Opus는 "기능을 만드는" 작업은 해낼 수 있었다. 해내지 못한 것은 "그 기능이 도달해야 할 전체 범위 (surface area)를 망라하는" 작업으로, 명시되지는 않았지만 당연하게 여겨지는 업무였다.
Codex 구현의 특징
Codex의 유일한 누락은 기능 A의 "메트릭 (metrics) 기록의 입도(granularity) 부족"이다. 단어 수를 수치로 확인하기는 어렵지만 기능은 완전히 동작한다. 중요도는 낮으며, 애초에 issue 본문에 기재되지 않은 요구사항이었다.
기능 A에서 Codex가 작성한 코드를 보면, 사전 단어를 컴포넌트 스코프 (component scope)에서 한 번 산출하고 클로저 (closure)를 통해 모든 경로에서 참조하는 구조를 선택했다. Opus가 범했던 "호출 누락"이라는 클래스의 버그를 설계로 구조적으로 제거한 것이다.
성실한 유보
안 A의 수치를 보고 "Codex로 갈아타자"라고 판단하기에는 이르다.
입력 품질의 차이라는 교란 요인이 있다.
Codex에는 증류된 프롬프트 (distilled prompt, issue 본문 + 착수 전 코멘트만)를 전달했다. 반면 Opus의 사례는 당시 실제 개발 과정에서 여러 커밋에 걸친 점진적 구현과 피드백 루프의 산물이다. "Codex의 누락이 적다"는 결과의 일부는 "입력이 정리되어 있었기 때문"일 수 있으며, 순수한 모델 차이와 분리할 수 없다.
기타 유의 사항:
- n = 3은 소표본이다. 3개의 경향이 우연일 가능성을 부정할 수 없다.
- Opus 사례는 cold-start가 아닌 이력이 있는 상태이므로 조건이 비대칭적이다.
- 측정의 함정: 하나의 실험에서
git add -A가 툴 파일 80만 행을 포함해 버렸다. 착수 지점의.gitignore에 제외 행이 설정되지 않았던 것이 원인으로, 구현 능력과는 무관하지만 오래된 base에서 실험하면 이런 노이즈가 혼입된다.
왜 아직 결론이 나지 않는가
결정적인 테스트는 안 B다.
안 B에서는 준비된 문맥이 없는 깨끗한 Opus 세션에 Codex와 동일한 증류된 프롬프트만을 전달하여 구현하게 한다. 입력 조건을 동일하게 맞춘 후 결과를 비교한다.
- 안 B의 Opus도 누락이 적다면 $\rightarrow$ 당시 누락의 주된 원인은 입력의 암묵적 전제. 교체가 아니라 사양 (specification) 정교화가 정답.
- 안 B의 Opus가 여전히 누락된다면 $\rightarrow$ 주된 원인은 모델. 그때 비로소 이행 (migration)이 선택지가 된다.
안 A는 "어디에 문제가 있는지"를 좁히기 위한 예비 결과다. 다만 안 A 단독으로도 말할 수 있는 것이 있다.
Opus의 결점을 알면 활용법이 바뀐다
Opus의 강점은 "주 경로의 설계와 구현"에 있다. 문제는 "surface area의 망라"라는 암묵적인 업무를 스스로 완수하지 못하는 경우가 있다는 점이다.
이 결점의 정체를 알게 되면 사용법이 바뀐다.
- "이 파라미터가 통과해야 할 모든 경로를 열거해서 확인해달라"고 구현 후에 명시적으로 지시한다.
- issue에 surface area를 작성한다 ("이 기능이 접촉해야 할 모든 레이어 (layer)"를 열거한다).
- "구현 $\rightarrow$ 리뷰 $\rightarrow$ 보수"의 2단계 구성을 전제로 플로우 (flow)를 설계한다 (한 번에 완성되기를 기대하지 않는다).
"Claude 최강"이라는 기사는 많다. 하지만 내가 실제로 사용하며 느끼는 것은, 최강이라기보다 "무엇을 잘하고 무엇에 암묵적 전제가 약한지를 아는 사람만이 제대로 다룰 수 있는" 도구라는 감각이다.
Codex가 앞섰다는 결과가 나왔다. 그것은 그것대로 솔직하게 적는다. 하지만 동시에 Opus의 누락 패턴을 이해할 수 있었던 것은 수확이었다고 생각한다. "왜 누락되는가"를 알면 대책은 "교체"뿐만이 아니기 때문이다.
안 B의 결과가 나오면 후속 글을 쓰겠습니다.
안 A의 실험 설계, 체크리스트, 각 기능의 판정 상세 내용은 사내 문서에 기록되어 있습니다. 본 기사는 공개 가능한 범위로 추상화되었습니다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기