AI 변형 파이프라인 — 1제 → 3제 증식 + 검수 자동화
목적
외주 강사 납품분 1,500제(initial pool) → AI 변형 3배 증식 → 검수 통과분 ~4,500제(final pool) 확보. P0 베타 출시 시 학생당 콘텐츠 풀이 충분하도록.
자가 검토 (작성자 점검)
본 파이프라인의 안전 장치:
- 수학적 오류 차단: LLM 셀프 체크 + 사람 5% 샘플 + 학생 시범 풀이 3중
- 저작권 보호: 변형은 발주처 자산 (계약서 §8) — 강사에게 사전 통지
- 비용 관리: Haiku 4.5 사용 (Sonnet 대비 1/12) → 4,500제 변형 비용 ~10만원 추정
1. 변형 종류 (3가지)
각 원본 1문항당 다음 3가지 변형 1개씩 생성:
Type V1: 수치 치환 (Number Substitution)
가장 안전한 변형. 정답·풀이 알고리즘 동일, 숫자만 변경.
예시:
- 원본:
1.45 + 2.78 = ?→ 변형:2.36 + 3.59 = ? - 원본:
1/2 + 1/3 = ?→ 변형:1/4 + 1/5 = ?
위험: 변형 후 결과가 학년 학습 범위를 벗어나는 경우 (예: 초3에 약분 결과 등장)
Type V2: 맥락 치환 (Context Substitution)
일상 맥락 변경. 수학 구조는 동일.
예시:
- 원본: 피자 6조각 중 2조각 → 변형: 케이크 6조각 중 2조각
- 원본: 종이학 5마리/일 → 변형: 책 5쪽/일
위험: 학년·문화 부적절 맥락 (예: 초1에 주식·환율)
Type V3: 표현 치환 (Representation Substitution)
문제 표현 형식 변경. 수학 구조 동일.
예시:
- 원본: 객관식 → 변형: 단답형 (정답을 직접 입력)
- 원본: 표 형식 → 변형: 산문 형식 (“종이학을 매일 5마리씩…”)
- 원본: 수치 → 변형: 도형 (분수를 그림으로)
위험: 표현 변형 시 난이도가 미세하게 바뀜 → 난이도 재검수 필요
2. 파이프라인 단계
[원본 1제 from RFP 강사 납품분]
↓
[Step 1: 모델 선택 — Haiku 4.5]
↓
[Step 2: 프롬프트 주입 — 변형 종류 V1/V2/V3]
↓
[Step 3: 변형 결과 생성 (1제 → 3제)]
↓
[Step 4: LLM 셀프 검증 — Sonnet으로 수학 정확성 체크]
↓
[Step 5: 자동 형식 검증 — 부록 E 11~15, 41~50번 자동화 가능 항목]
↓
[Step 6: 통과율 ≥ 95% → DB 저장 / 미만 → 인간 검수 큐]
↓
[Step 7: 5% 샘플 인간 검수 (강사 또는 발주처)]
↓
[Step 8: 베타 학생 시범 풀이 → 정답률·시간 측정 → 이상 발견 시 변형 폐기]
3. 프롬프트 (Step 2 — Haiku)
시스템 프롬프트
당신은 초등 수학 콘텐츠 변형 전문가입니다.
원본 문항 1개를 입력받아 동일한 학습 목표를 가진 변형 문항 3개를 생성합니다.
변형 종류 1개씩 균형 있게 생성하세요:
- V1 수치 치환: 숫자만 변경, 풀이 알고리즘 동일
- V2 맥락 치환: 일상 맥락 변경, 수학 구조 동일
- V3 표현 치환: 표현 형식 변경, 학습 목표 동일
원본 문항의 다음 메타데이터를 모두 유지:
- unit_id, grade, domain, difficulty, type
변형 후 다음을 보장:
1. 수학적 정확성 — 정답 도달 가능
2. 학년 적합성 — 해당 학년 학습 범위 내
3. 일상 맥락 적절성 — 학년·문화 적합
4. 보기 (객관식의 경우) 5개 모두 distinct + 학생 실수 패턴 1개 이상
출력은 JSON 형식:
{
"variants": [
{ "type": "V1", "id": "<생성 ID>", ...원본과 동일 필드 },
{ "type": "V2", "id": "<생성 ID>", ... },
{ "type": "V3", "id": "<생성 ID>", ... }
]
}
유저 프롬프트 (예시)
원본 문항:
```yaml
id: "E5-1-2-MID-MC-007"
unit_id: "E5-1-2"
unit_path: "초5 > 1학기 > 약수와 배수"
domain: "NUM"
grade: "E5"
difficulty: "MID"
type: "MC"
body_md: |
12의 약수를 모두 구하시오.
choices:
- "1, 2, 3, 4, 6"
- "1, 2, 3, 4, 6, 12"
- "2, 3, 4, 6"
- "1, 12"
- "1, 2, 3, 6, 12"
answer_index: 1
solution_md: |
12를 나누어떨어지게 하는 자연수가 약수입니다.
$12 = 1 \times 12 = 2 \times 6 = 3 \times 4$
따라서 약수는 1, 2, 3, 4, 6, 12.
tags: ["약수", "자연수"]
estimated_time_sec: 60
위 원본을 변형하여 V1 (수치 치환), V2 (맥락 치환), V3 (표현 치환) 1개씩 생성하세요.
### 변형 ID 명명 규칙
원본 ID에 `-V{1|2|3}` 접미사:
- 원본: `E5-1-2-MID-MC-007`
- 변형: `E5-1-2-MID-MC-007-V1`, `-V2`, `-V3`
---
## 4. LLM 셀프 검증 (Step 4 — Sonnet)
Haiku로 생성한 결과를 Sonnet으로 검증. Haiku가 빠르고 저렴하지만 수학 오류 가능성이 약간 더 높음.
### Sonnet 검증 프롬프트
다음 변형 문항이 수학적으로 정확한지 검증하세요.
[변형 문항 입력]
검증 항목 (RFP 부록 E 영역 2): 11. 정답이 수학적으로 옳음 — 직접 풀어 도달 가능? 12. 풀이 해설이 정답을 도출 — 단계별 논리 일관? 13. 풀이 단계 중 계산 오류 없음? 14. 객관식 답이 보기 중 정확히 1개? 15. LaTeX 수식이 의도한 의미를 표기?
각 항목에 PASS / FAIL 표기 + 실패 시 사유 명시.
추가 검증:
- 학년 학습 범위 위반 여부
- 보기 분포 자연스러움 (객관식)
- estimated_time_sec 적절성
JSON 출력: { “verdict”: “PASS” | “FAIL”, “checks”: [{ “id”: 11, “result”: “PASS”/“FAIL”, “reason”: ”…” }, …], “additional”: { “out_of_scope”: false, … } }
### 통과 기준
- 영역 2 (5항목) **모두 PASS** → 자동 통과
- 1건이라도 FAIL → 인간 검수 큐로 이관
---
## 5. 자동 형식 검증 (Step 5 — Code-based)
LLM 호출 없이 코드로 검증할 수 있는 항목 (부록 E 영역 5):
```python
def validate_format(variant):
checks = {
# 메타데이터
"id_regex": bool(re.match(r"^E[1-6]-[12]-[1-6]-(LOW|MID|HIGH)-(MC|SA|DE)-[0-9]{3}-V[1-3]$", variant["id"])),
"unit_exists": variant["unit_id"] in known_unit_ids,
"domain_match": variant["domain"] == unit_to_domain[variant["unit_id"]],
# 길이
"body_under_1000": len(variant["body_md"]) <= 1000,
"solution_under_2000": len(variant["solution_md"]) <= 2000,
# 객관식 구조
"mc_5_choices": len(variant.get("choices", [])) == 5 if variant["type"] == "MC" else True,
"answer_index_in_range": 0 <= variant.get("answer_index", -1) <= 4 if variant["type"] == "MC" else True,
# 시간 추정
"time_in_range": time_range_for(variant["grade"], variant["difficulty"])[0] <= variant["estimated_time_sec"] <= time_range_for(...)[1],
# 태그
"tags_3_to_7": 3 <= len(variant.get("tags", [])) <= 7,
# YAML 인코딩
"valid_utf8": is_valid_utf8(variant),
}
return all(checks.values()), checks
6. 인간 검수 큐 (Step 7)
다음 조건에 해당하는 변형은 자동 통과 X, 인간 검수 큐로:
- Step 4 LLM 셀프 검증 FAIL
- Step 5 자동 형식 검증 FAIL
- 무작위 5% 샘플 — 모든 단계 통과해도 5% 검수 (RFP §5 3단계와 동일)
- 고난이도 (HIGH) 변형 — 100% 인간 검수 (실수 비용이 큼)
- 서술형 (DE) 변형 — 100% 인간 검수 (rubric 검증 필요)
검수자 인터페이스 권장
- 원본 vs 변형 좌우 비교 뷰
- 검수 결과: PASS / FAIL / EDIT (수정 후 PASS)
- 검수 시간 기록 → SLA 관리 (1건 평균 90초 목표)
7. 베타 학생 시범 풀이 (Step 8)
베타 출시 후 변형 문항이 풀이될 때 메트릭 자동 수집:
| 지표 | 임계값 | 위반 시 |
|---|---|---|
| 정답률 | < 20% 또는 > 95% | 변형 폐기 또는 난이도 재분류 |
| 평균 풀이시간 | > estimated × 2 또는 < estimated × 0.3 | estimated_time_sec 갱신 |
| 학생 신고 | ”이 문제 이상해요” 버튼 | 인간 재검수 큐 |
| 보기 선택 분포 (MC) | 정답에 < 30% 집중 | 보기 재구성 |
자동 폐기되는 변형은 Question.status = 'archived'. 학습지 추천에서 제외.
8. 비용 추정
Haiku (Step 2 변형 생성)
- 입력: 원본 1제 + 시스템 프롬프트 ≈ 1,500 토큰
- 출력: 변형 3제 + 메타 ≈ 3,000 토큰
- Haiku 4.5 가격: 1.25/M 출력
- 1제당 비용: 0.00375 = $0.0041 ≈ 6원
- 1,500제 변형: ~9,000원 (= 약 1만원)
Sonnet (Step 4 셀프 검증)
- 입력: 변형 1제 + 검증 프롬프트 ≈ 2,000 토큰
- 출력: PASS/FAIL JSON ≈ 500 토큰
- Sonnet 4.6 가격: 15/M 출력
- 1제당 비용: 0.0075 = $0.0135 ≈ 19원
- 4,500 변형 검증: ~85,500원 (= 약 9만원)
인간 검수 비용
- 5% 샘플 + HIGH·DE 100% ≈ 500제
- 1제당 90초 × 외주 강사 시급 30,000원 = 750원
- 500제 검수: ~37만원
총 변형 파이프라인 비용
≈ 1만 + 9만 + 37만 = 약 47만원
이는 단위 경제 v0.1 §2.5 추정에 추가 — 일회성 비용이라 단가에 분산 불필요.
9. 운영 체크리스트
P0 출시 전:
- Haiku·Sonnet API 키 획득 + 비용 limit 설정
- 변형 생성 스크립트 (Python) 작성
- 자동 형식 검증 코드 (Step 5) 작성
- 인간 검수 인터페이스 (Notion DB 또는 simple web app)
- 베타 메트릭 수집 (Supabase RLS·Edge Function)
P0 출시 후 (운영):
- 매주 변형 통계 (생성·통과·폐기 건수)
- 매월 비용 모니터링 (LLM API 사용량)
- 분기별 프롬프트 개선 (실패 패턴 분석)
10. PRD v0.2 영향
본 파이프라인 결정으로 PRD v0.1 §2 데이터 모델에 다음 추가:
Question (기존)
...
source: enum(self_made, ai_variant, public_link)
source_question_id?: int ← AI 변형의 원본 (이미 있음)
variant_type?: enum(V1, V2, V3) ← 신규
ai_check_passed: boolean ← 신규 (Step 4 통과 여부)
human_reviewed: boolean ← 신규 (Step 7 통과 여부)
status: enum(draft, active, archived) ← 확장 (기존 deprecated 추가)
beta_metrics: jsonb? ← 신규 (정답률·풀이시간·신고)
PRD v0.2에 본 파이프라인 §2~§7을 §3.5(콘텐츠 풀 운영)로 통합 권장.