Prompt Regression Tests: AI 워크플로우가 깨지는 것을 방지하세요
I’m ready to translate the article for you, but I need the full text you’d like translated. Could you please paste the content (or the portion you want translated) here? I’ll keep the source line, formatting, markdown, and any code blocks exactly as they are while translating the rest into Korean.
소개
코드를 배포한다면 이미 회귀 테스트를 하고 있는 겁니다. 무언가를 변경하면 무엇이 깨졌는지 알고 싶죠. 프롬프트 기반 워크플로도 같은 대우를 받아야 합니다.
프롬프트가 인프라가 되는 순간(코드‑review 프롬프트, PRD‑to‑tasks 워크플로, 고객‑support 분류기, 글쓰기 린터 등), 여러분은 그것을 조정하게 됩니다. 그리고 조정할 때마다 전형적인 놀라움을 겪게 되죠:
- “왜 갑자기 장황해졌나요?”
- “왜 JSON 스키마를 따르지 않게 되었나요?”
- “예전엔 잡았던 엣지 케이스가 왜 빠졌나요?”
이것이 프롬프트 회귀입니다: 프롬프트, 모델, 온도, 툴 목록, 혹은 단지 주변 컨텍스트를 변경했을 때 AI 워크플로가 흐트러집니다.
이를 방지하는 실용적인 방법은 프롬프트 회귀 테스트를 소량 구축하는 것입니다. 화려하지 않지만, 놀라울 정도로 효과적입니다.
What to Test (and What Not)
Your goal is not “the exact same words every time.” Your goal is:
- Structure stays valid – JSON이 파싱되고, 필수 필드가 존재합니다.
- Critical behaviors stay true – 실제 버그를 잡고, 올바른 명확화 질문을 하며, 당신의 제약을 준수합니다.
- Failure modes stay acceptable – 입력이 모호할 때는 질문하고, 답을 알 수 없을 때는 모른다고 말합니다.
So don’t snapshot whole outputs as strings unless you need to. Prefer invariants.
Examples of Good Invariants
- Output is valid JSON with a fixed schema.
- Contains at least N issues with severity ≥ “high” when the sample contains obvious defects.
- Does not mention forbidden things (e.g., “as an AI…”, internal system notes).
- Produces bullet points, not paragraphs, for the “summary” section.
Step 1: 작은 “골든 세트” 입력 만들기
8–20개의 테스트 케이스부터 시작하세요. 의도적으로 다양하게 만드세요:
| 카테고리 | 설명 |
|---|---|
| 정상 흐름 | 깨끗한 입력 → 깨끗한 출력. |
| 경계 사례 | 빈 문자열, null과 유사한 자리표시자, 누락된 필드. |
| 적대적 | 프롬프트 주입 시도 (“이전 지시 무시…”) 입력 내부. |
| 실제로 본 실패 사례 | 지난 주에 놓친 버그가 여기 포함됩니다. |
예시 – “PRD → 작업” 프롬프트의 경우, 다음을 포함하세요:
- 간결한 PRD.
- 지저분한 PRD.
- 모순된 요구사항이 있는 PRD.
- 제약 조건을 누락한 PRD(따라서 어시스턴트가 질문해야 함).
2단계: 평가 루브릭 정의 (명시적으로)
좋은 것이 무엇인지 적어 두세요. 간결하게.
코드‑리뷰 프롬프트 루브릭
- 기능 결함을 먼저 찾는다.
- 보안 문제가 있으면 지적한다.
- 버그 위험이 없는 한 스타일을 지나치게 파고들지 않는다.
- 최소한의 실용적인 수정안을 제시한다.
라이팅‑린터 프롬프트 루브릭
- 문체 규칙을 강제한다 (짧은 문장, 불필요한 말 피하기).
- 수동태를 표시한다.
- 비판만이 아니라 대안을 제시한다.
요령: 가능한 경우 테스트가 이 루브릭 항목들을 기계적으로 확인하도록 하세요.
Step 3: Rubric을 체크로 전환하기
Deterministic Checks (먼저 수행)
- JSON 파싱.
- 스키마 검증.
- 금지 문구.
- 필수 헤딩.
- 최대 길이.
예상 항목의 ≈ 60 % 정도를 결정론적 체크로 변환하면 대부분의 오류를 잡을 수 있습니다.
Minimal Node.js Harness Example
import assert from "node:assert";
/* ---------- Helpers ---------- */
function mustParseJson(text) {
try {
return JSON.parse(text);
} catch (e) {
throw new Error("Output is not valid JSON");
}
}
function assertHas(obj, path) {
const parts = path.split(".");
let cur = obj;
for (const p of parts) {
assert.ok(cur && p in cur, `Missing field: ${path}`);
cur = cur[p];
}
}
/* ---------- Main check ---------- */
export function runChecks(outputText) {
const out = mustParseJson(outputText);
// Required fields
assertHas(out, "summary");
assertHas(out, "issues");
assert.ok(Array.isArray(out.issues), "issues must be an array");
// Forbidden phrases
const forbidden = [/as an ai/i, /system prompt/i];
for (const re of forbidden) {
assert.ok(!re.test(outputText), `Forbidden phrase: ${re}`);
}
return true;
}
Fuzzy Checks (범위 제한)
퍼지 체크는 사람들이 자주 걸리는 부분입니다. 과도하게 설계하지 마세요.
패턴
- 키워드 기대치 – 입력에 명백한 SQL‑인젝션 문자열이 포함된 경우, 리뷰에 “injection” 또는 “parameterized”가 언급되어야 합니다.
- 점수 구간 – 두 번째 패스(또는 가벼운 휴리스틱)를 실행해 출력물을 루브릭 항목별로 1–5점으로 평가합니다.
- Diff 허용 범위 – 문장 전체가 아니라 카테고리를 비교합니다(예: 정확한 문장이 아니라 “Found 3 high‑severity issues”와 같은 형태).
LLM 기반 점수를 사용하고 싶다면, 엄격한 루브릭과 낮은 temperature를 적용한 판사 역할로 다루세요.
Step 4: 프롬프트 인터페이스 잠금
대부분의 프롬프트 회귀는 입력을 변경하면서 그 사실을 인식하지 못해서 발생합니다. 코드와 함께 버전 관리되는 단일 프롬프트‑인터페이스 객체를 만들세요:
- 모델 이름
- 온도(Temperature)
- 시스템 메시지
- 사용자‑프롬프트 템플릿
- 도구 정의(있는 경우)
- 출력 스키마
그런 다음 이러한 항목이 변경될 때마다 테스트를 실행합니다.
간단한 규칙
prompt/v1/system.md
prompt/v1/user.md
prompt/v1/schema.json
tests/fixtures/*.json
이렇게 하면 프롬프트 변경 사항을 코드처럼 검토할 수 있습니다.
단계 5: CI에서 테스트 실행 (정말로)
워크플로우가 중요하다면 CI에 넣으세요. 두 가지 실용적인 옵션이 있습니다:
옵션 A – “오프라인‑우선” 테스트 (빠르고 저렴함)
- 결정론적 검사만 수행합니다.
- 실제 모델 호출은 소수만 (또는 전혀) 사용합니다.
주로 포맷과 가드레일에 신경 쓸 때 좋습니다.
옵션 B – “실시간 모델” 테스트 (현실적)
- 실제 호출을 10–30회 실행합니다.
- 작은 예산 한도를 사용합니다.
- 의미 있는 회귀가 있을 때만 실패합니다.
일반적인 접근 방식은 작은 허용 오차를 두는 것입니다:
| 검사 | 실패 유형 |
|---|---|
| JSON must parse | hard fail |
| Required fields present | hard fail |
| Rubric score below threshold | soft fail (warn) unless it drops dramatically |
이렇게 하면 불안정한 실패를 방지하면서도 실제 드리프트를 잡아낼 수 있습니다.
구체적인 예시: 코드‑리뷰 프롬프트 테스트
(원본 예시의 나머지 부분은 여기서 계속됩니다 – 필요에 따라 자세한 테스트 케이스, CI 구성 및 샘플 실행 결과를 삽입하세요.)
경계 버그를 잡기 위한 검토 프롬프트
고정 입력 (작고 의도적으로 결함이 있는 경우):
- 타임존 변환 버그
- 널(null) 체크 누락
- SQL에 대한 의심스러운 문자열 연결
테스트에서는 다음을 검증할 수 있습니다:
- 최소 하나의 이슈가 타임존 또는 날짜 연산을 언급함
- 최소 하나의 이슈가 null/undefined 처리에 대해 언급함
- 최소 하나의 이슈가 인젝션 위험을 언급함
- 이슈에
severity와confidence필드가 포함됨
정확한 exact wording을 확인하는 것이 아니라, 여러분이 의존하는 워크플로우가 여전히 작동하는지를 확인하는 것입니다.
숨겨진 장점: 프롬프트를 안전하게 리팩터링할 수 있음
작은 테스트 스위트라도 하나라도 만들면, 프롬프트 수정이 두렵지 않게 됩니다. 다음을 할 수 있습니다:
- 지시문을 더 구체화
- 장황함을 줄임
- 포맷을 변경
- 모델을 교체
- 도구를 추가
…등을 추측 없이 할 수 있습니다.
몇 분 안에 “작은 개선”이 여러분이 중요하게 생각하던 동작을 실수로 삭제했는지 여부를 알 수 있습니다.
빠른 시작 체크리스트
- 매주 사용하는 프롬프트 하나를 선택하세요.
- 10개의 픽스처를 작성하세요 (실제 사례를 복사/붙여넣기).
- 5개의 불변 조건을 작성하세요 (JSON/스키마/금지 구문/필수 헤딩).
- 하나의 퍼지 체크를 추가하세요 (키워드 기대치).
- 다음 프롬프트 조정 전후에 실행하세요.
프롬프트는 엔지니어링입니다. 중요하게 다루세요.
피드백 요청
이미 프롬프트 테스트가 있다면, 가장 가치 있다고 생각하는 불변량에 대해 알려 주세요.