Fable 5 일기 시리즈 — ① 지시에서 목표로 · ② 만드는 쪽 ≠ 검사하는 쪽 (이 글)

지난 글을 발행하고, 스스로 읽어 보다 걸린 대목이 있었다. 마지막에 “만드는 쪽과 검사하는 쪽을 분리하라”고 규칙처럼 적어 놓고는, 정작 “그래서 그걸 어떻게 세팅하는데?”를 하나도 안 적었더라. 독립된 곳에서 검증하게 만든다는 게 말은 멋진데, 실제로는 뭘 분리하라는 건지, 채점 기준을 별도 파일로 빼야 하는 건지, Claude Code에서 어디에 뭘 두라는 건지 — 그 구체가 비어 있었다. 오늘 일기는 그 빈칸을 채우는 글이다.

한눈에: 무엇을 분리하는가?

flowchart LR
    subgraph BAD["❌ 자기 채점 (같은 문맥)"]
        M1["만든 에이전트"] --> S1["자기가 만든 걸<br/>자기가 채점"]
        S1 --> R1["'다 잘 됐다'<br/>(같은 맹점을 공유)"]
    end
    subgraph GOOD["✅ 분리된 검증"]
        M2["만드는 쪽<br/>(Maker)"] --> ART["산출물 + 고정된 채점 기준(rubric)"]
        ART --> C2["검사하는 쪽<br/>(Checker)<br/>독립 문맥·독립 주체"]
        C2 --> R2["O/X + 어디가 왜 부족한지"]
        R2 -->|"X면 피드백"| M2
    end
    classDef bad fill:#ffe3e3,stroke:#e03131,color:#a01418;
    classDef good fill:#d3f9d8,stroke:#2f9e44,color:#1d6b2c;
    class M1,S1,R1 bad;
    class M2,ART,C2,R2 good;

왼쪽이 함정이다. 만든 놈이 자기가 만든 걸 채점하면, 만들 때 가졌던 맹점을 채점할 때도 똑같이 가지고 있다. “나는 잘했다”에 도달하기 쉽다. 오른쪽이 오늘 세팅할 구조다. 만드는 쪽과 검사하는 쪽 사이에 고정된 채점 기준을 두고, 검사는 다른 자리에서 한다.

왜 자기가 자기를 못 채점하나?

이건 성격이 나빠서가 아니라 구조의 문제다. LLM은 자기 출력을 자기가 비평하는 데 약하다는 게 여러 곳에서 확인됐다(Anthropic 엔지니어링 블로그도, Fable 5로 루프를 돌린 Lance Martin도 같은 얘기를 한다). 이유를 그림으로 그리면 단순하다.

flowchart TD
    CTX["하나의 문맥 창(context)"] --> D1["'이 방향이 맞다'는 전제"]
    D1 --> BUILD["그 전제 위에서 산출물 생성"]
    BUILD --> SELF["같은 창에서 '검토'"]
    SELF --> BIAS["전제를 의심할 근거가<br/>창 안에 없음 → 통과 판정"]
    classDef w fill:#fff3bf,stroke:#e67700,color:#8a5a00;
    class CTX,BIAS w;

핵심은 “같은 창 안에는 자기 전제를 뒤집을 재료가 없다”는 것이다. 그래서 채점자는 처음부터 다른 창에서 시작해야 한다. 만드는 과정을 못 본 채, 오직 산출물과 기준만 받아서, “이게 기준을 충족하나?”만 본다. 그래야 “왜 이렇게 만들었는지”에 물들지 않는다.

‘독립된 검증’은 정확히 무엇을 분리하나?

막연히 “따로 검사해”가 아니다. 분리할 축이 세 개다.

flowchart TD
    SEP["독립된 검증 = 세 가지 분리"] --> A["① 문맥 분리<br/>(context)<br/>만들 때 대화를 안 봄"]
    SEP --> B["② 주체 분리<br/>(agent)<br/>다른 에이전트/세션이 채점"]
    SEP --> C["③ 기준 분리<br/>(rubric)<br/>채점 기준을 고정된 산출물로"]
    A --> WHY["→ 세 개가 다 있어야<br/>'적대적으로 반증'이 가능"]
    B --> WHY
    C --> WHY
    classDef s fill:#d0ebff,stroke:#1971c2,color:#0b4a8f;
    class A,B,C,WHY s;
  • 문맥 분리: 검사하는 쪽은 만들 때의 대화·시행착오를 보지 않는다. 산출물만 본다.
  • 주체 분리: 만든 세션이 아니라 다른 에이전트(혹은 새 세션)가 채점한다.
  • 기준 분리: 채점 기준이 채점자 머릿속(혹은 만든 쪽 머릿속)에 있으면 안 된다. 밖에 고정돼 있어야, 만드는 쪽도 검사하는 쪽도 같은 자를 쓴다.

그래서 채점 기준을 별도 .md 파일로 만들어야 하나?

내가 가장 궁금했던 질문이라 정직하게 답을 갈라 적는다. 둘을 구분하면 헷갈리지 않는다.

무엇파일로 빼나?
채점 기준(rubric)응, 빼는 게 좋다만드는 쪽·검사하는 쪽이 같은 기준을 봐야 함. 파일이면 대화가 길어져도 안 흔들리고, 재사용·버전관리도 된다
검사하는 쪽(checker)파일이 아니라 별도 문맥/에이전트채점은 ‘행위’라 파일이 아니라 독립 실행 주체로 존재. (에이전트 정의를 파일로 둘 순 있지만, 그건 checker의 ‘설정’이지 checker 자체는 아님)

정리하면, “별도 .md로 만들어야 하나?”의 답은 ‘채점 기준은 그렇다’이다. 완료 정의(Definition of Done)를 rubric.md(혹은 작업 프롬프트 안의 고정 섹션)로 박아 두고, 검사는 그 파일을 읽는 독립 에이전트에게 시킨다. 채점 기준을 파일로 빼는 순간, “잘 됐나?”라는 주관적 질문이 “이 목록을 다 통과했나?”라는 체크 가능한 질문으로 바뀐다.

Claude Code에서는 구체적으로 어떻게 배선하나?

추상론만 하면 안 되니까, 실제로 쓰는 네 가지 세팅을 난이도 순으로 적는다.

flowchart LR
    L1["① 가장 단순<br/>rubric.md + 검증 서브에이전트<br/>'이 기준으로 적대적으로 검증해'"] --> L2["② 지속형<br/>.claude/agents/verifier.md<br/>검증 전용 에이전트 정의"]
    L2 --> L3["③ 워크플로<br/>maker 단계 → verify 단계<br/>판정을 스키마로 강제"]
    L3 --> L4["④ 가장 독립적<br/>결정론적 채점기<br/>테스트·비교 스크립트"]
    classDef a fill:#e5dbff,stroke:#7048e8,color:#432b96;
    class L1,L2,L3,L4 a;
  • ① rubric.md + 검증 서브에이전트: 완료 정의를 파일로 적고, 만든 뒤 새 서브에이전트를 띄워 “이 산출물을 rubric.md 기준으로 반증(refute) 시도해”라고 시킨다. 서브에이전트는 자기 문맥 창을 갖기 때문에 ①문맥·②주체가 자동으로 분리된다. 제일 가볍고, 오늘 당장 쓸 수 있다.
  • .claude/agents/verifier.md: 검증만 하는 에이전트를 아예 정의로 박아 둔다. “너는 만들지 않는다. 오직 기준 대비 O/X와 근거만 낸다”는 성격을 고정. 반복 작업에 좋다.
  • ③ 워크플로의 verify 단계: 만드는 단계 다음에 검증 단계를 두고, 판정을 정해진 형식(스키마)으로 강제한다. {통과: true/false, 실패근거: ...} 처럼. 형식이 강제되면 “음 대체로 괜찮은 듯요” 같은 얼버무림이 안 나온다.
  • ④ 결정론적 채점기: 제일 셌다. 사람도 LLM도 아닌 코드가 채점한다. 테스트 스위트, 출력 비교 스크립트, 빌드 경고 카운트. 코드는 합리화를 안 한다. 가능하면 항상 이걸 1순위로 깐다.

한 가지 원칙 — 가능한 만큼 아래로 내려간다. ④로 판정할 수 있으면 ④로, 그게 애매한 영역(문장 품질·설계 타당성)만 ①~③의 LLM 검증에 맡긴다.

목표형 프롬프트의 골격 (5부)

검증을 분리하려면, 애초에 프롬프트가 채점 가능한 형태여야 한다. 절차 번호(1번·2번…)를 지우고, “무엇이 되면 끝인지”를 기계가 O/X로 판정 가능한 조건으로 적는 게 전부다. 골격은 다섯이다 — ①목표 ②완료 정의 ③가드레일 ④검증 방법 ⑤루프 규칙.

## 목표
<도달해야 할 최종 상태 한 문장>
 
## 완료 정의 (Definition of Done)
- [ ] <기계 판정 가능한 조건 — diff=0, 테스트 통과, 빌드 경고 0 등>
 
## 가드레일
- <넘지 말 선 — 수정 금지 파일, 실행 금지 행동>
 
## 검증 방법
- <채점 주체·도구 — 테스트 스크립트, 독립 문맥의 검증 서브에이전트. 자기 채점 금지>
 
## 루프 규칙
- 완료 정의 전부 통과까지 수정-재시도 반복
- 동일 원인 N회 실패 시 로그·가설 정리 후 중단·보고

⚠️ 여기 딱 하나의 함정이 있다. 완료 정의에 “깔끔하게”, “잘”, “적절히” 같은 주관어가 들어가면 루프가 못 돈다. 채점자가 O/X를 낼 수 있는 조건만 쓴다. “코드를 깔끔하게 정리”가 아니라 “dotnet build 경고 0, 순환 복잡도 15 이하”처럼.

예시 — 레거시 VBA를 .NET으로 포팅할 때

내가 실제로 해 본 종류의 작업을 일반화해서 옮긴다(회사 고유 모듈명·범위명은 뺐다). 오래된 VBA 계산 모듈을 .NET 서비스로 옮기는 상황이라고 하자.

지시형(예전 습관):

LegacyCalc.bas 열어서 함수 목록 뽑아줘.
CalcService.cs 만들고 첫 함수부터 하나씩 C#으로 옮겨.
엑셀은 EPPlus로...
하나 끝나면 보여줘. 내가 확인하고 다음 거 지시할게.

→ 함수마다 내가 개입한다 = 내가 병목이고, 검사도 결국 내 눈이라 주체 분리가 안 된다.

목표형(전환 후):

## 목표
레거시 VBA 계산 모듈을 .NET(EPPlus) 서비스로 포팅
 
## 완료 정의
- [ ] 모듈의 모든 public 프로시저가 C#으로 구현됨
- [ ] 샘플 입력 3종: VBA 실행 결과 vs C# 출력 셀 값 diff = 0
- [ ] 기존 서비스 회귀 테스트 전부 통과 (회귀 0)
- [ ] dotnet build 경고 0
 
## 가드레일
- 이미 완성된 기존 서비스 파일 수정 금지
- 별도로 관리 중인 특정 이름범위(Name range) 접근 금지
- 신규 NuGet 패키지 추가 금지 (EPPlus만)
 
## 검증 방법
- scripts/compare_output.py 로 셀 값·서식 비교 (결정론적 채점기 = 최우선)
- 포팅 후 독립 문맥의 검증 서브에이전트가 VBA 원본 대비 로직 동등성을 적대적으로 반증
 
## 루프 규칙
- diff ≠ 0 → 차이 셀 좌표 기준 원인 추적 → 수정 → 재실행
- 동일 원인 5회 실패 → 실패 로그 + 가설 정리 후 중단·보고

여기서 검증이 두 겹인 걸 보라. 셀 값 비교(compare_output.py)는 코드가 채점하니 제일 독립적이다. 그리고 그걸로 못 잡는 “로직이 원본과 진짜 같은가”는 독립 문맥의 서브에이전트가 반증한다. 만드는 쪽은 이 둘을 통과할 때까지만 돌고, 나는 맨 끝에 한 번 본다.

예시 — claude.md를 ‘대본’에서 ‘헌법’으로

같은 원리가 claude.md에도 적용된다. 예전엔 여기에 대본을 적었다.

1. 작업 전 반드시 git status 확인
2. 파일 수정 전 백업 생성
3. 함수 하나 고칠 때마다 나에게 보고

지금은 헌법을 적는다 — 스텝이 아니라 완료의 정의와 검증 원칙.

## 완료의 정의 (전 작업 공통)
- 빌드/테스트 미통과 상태에서 '완료' 선언 금지
- 완료 선언 전, 독립 검증 서브에이전트 리뷰 필수
 
## 가드레일
- 비가역 행동(배포·실계정 게시·파일 삭제)은 실행 전 승인 요청
- main 브랜치 직접 커밋 금지
 
## 검증
- C#: dotnet test / Python: pytest — 자기 채점 금지, 채점은 별도 문맥에서
 
## 메모리
- 실패 → 원인 검증 → 규칙화를 통과한 것만 기록 (미검증 메모 축적 금지)

“함수 하나 고칠 때마다 보고”가 사라지고, “완료 선언 전 독립 검증 필수”가 그 자리를 채웠다. 개입을 매 스텝에서 완료의 문턱 하나로 옮긴 것이다.

지금 내 프롬프트가 지시형인지 목표형인지 — 자가진단 3개

  • 절차 번호(1→2→3)가 있으면 지시형, 체크박스 완료 조건이 있으면 목표형.
  • “보여줘/확인받아”가 중간중간 있으면 지시형, 맨 마지막에 한 번만 있으면 목표형.
  • 완료 조건을 스크립트나 검증 에이전트가 O/X로 판정 가능한가 — 불가능하면(주관어가 섞였으면) 루프가 안 돈다.

마무리 — 검사하는 자리에 판단을 둔다

지난 글에서 “조종사에서 설계자로”라고 썼는데, 오늘 한 겹 더 내려가 보니 설계의 핵심은 결국 채점대를 어디에 놓느냐였다. 만드는 쪽은 값싸졌다. 이제 희소한 건 “이게 진짜 됐다고 누가, 무엇으로 판정하는가”다. 그 판정을 만든 놈에게 맡기지 않고, 고정된 기준과 독립된 검사자에게 넘기는 것 — 그게 목표형으로 일하면서도 결과를 계속 믿을 수 있는 유일한 방법이더라.

그래서 요즘 나는 새 작업을 시작할 때 프롬프트보다 rubric을 먼저 쓴다. 무엇이 되면 끝인지를 O/X로 적을 수 있으면, 나머지는 루프가 알아서 한다. 못 적겠으면 — 그건 아직 내가 그 일을 충분히 이해 못 했다는 신호고.

참고자료