프롬프트 품질 관리를 위한 9가지 테스트 규칙

버전 관리, 테스트 데이터셋 구성, 다중 평가의 실전 가이드

프롬프트 엔지니어링의 3가지 어려움(확률적 응답, 정답 없음, 빠른 모델 변화)에 대응하는 9가지 테스트 규칙과 실무 적용 방법을 체계적으로 설명한다. 4개 클러스터로 구조화된 규칙: 식별과 추적(규칙 1-3, 버전 관리와 이름 체계), 기준선 정의(규칙 4, 목표와 기대 성능 문서화), 테스트 데이터 품질(규칙 5-6, 실제 사용자 발화와 데이터셋 구성), 실행과 평가(규칙 7-9, 반복 테스트, 다중 평가자, 다중 모델 검증). 각 규칙마다 구체적 예시 제공: 프롬프트 버전 비교(간결 vs 구조화), 이름 체계(CustomerSupport_Returns_v1), 테스트 데이터셋 JSON 구조(50개 케이스), 10회 반복 테스트 결과표, 3인 평가자 루브릭(5점 척도), 6개 모델 비교 분석. 프롬프트-테스트 데이터 분리 관리, 실제 데이터 수집 방법(베타 테스트, CS 로그), 문서화 3층 구조(원문, 파라미터, 메타정보)를 포함한 완전한 품질 관리 가이드를 제공한다.

AI
저자

Kwangmin Kim

공개

2026년 02월 03일

1 들어가며

Prompt Engineering 평가에 세 가지 어려움을 이야기했다: 확률적 응답, 정답 없음, 빠른 모델 변화.
이 파트는 그 세 어려움에 대한 구체적 대응책, 즉 아홉 가지 규칙과 문서화 방법을 다룬다.
규칙 하나하나를 단순히 나열하는 게 아니라, 왜 그 규칙이 그 형태로 생겼는지를 같이 본다.
그리고 아홉 규칙이 실제로 어떤 논리로 묶여 있는지도 한 번 정리한다.

2 아홉 규칙의 내부 구조

아홉 규칙을 처음 보면 단순한 체크리스트로 보일 수 있다.
하지만 이 규칙들은 우연히 나열된 게 아니라 단계적으로 연결된 구조를 가지고 있다.
크게 네 클러스터로 묶인다.

2.1 식별과 추적 (규칙 1, 2, 3)

규칙 1 — 최소 두 가지 버전을 작성한다.
이건 테스트의 가장 기본적인 전제이다.
프롬프트 하나만 있으면 “좋은지”를 판단할 근거가 없다.
좋음과 나쁨은 항상 다른 옵션과의 비교를 통해 판단된다.
“Prompt의 정답이 없다”는 현실의 실천적 해결책이 바로 여기서 시작된다.

예시:

버전 A (간결한 접근):

You are a customer support assistant. Answer user questions about product returns.
Be concise and friendly.

버전 B (구조화된 접근):

You are a customer support assistant specializing in product returns.

When a user asks about returns:
1. First, confirm the product and purchase date
2. Explain the return policy (30-day window)
3. Provide step-by-step return instructions
4. Offer to help with any additional questions

Maintain a friendly and professional tone throughout.

두 버전을 비교함으로써 “간결함 vs 구조화”의 트레이드오프를 평가할 수 있다.

규칙 2 — 버전 이름은 기능 이름으로 정한다.
예시로 들어진 것: systemprompt_v1, productrecommender_v2.
이 구조를 보면 두 가지 정보가 동시에 들어가 있다.
“어떤 기능인지”와 “몇 번째 시도인지”.
이건 단순한 이름 규칙이 아니라, 프롬프트를 소프트웨어 자산과 같은 방식으로 관리하는 사고법을 적용한 것이다.
프롬프트가 수십 개로 불어날 때, 기능과 버전을 분리하여 추적할 수 없으면 관리 자체가 불가능하다.

예시:

나쁜 이름 체계:

prompt1.txt
prompt2_final.txt
prompt2_final_REAL.txt
new_prompt_test.txt

좋은 이름 체계:

CustomerSupport_Returns_v1.txt
CustomerSupport_Returns_v2.txt
ProductRecommender_Personalized_v1.txt
EmailGenerator_Marketing_v1.txt
EmailGenerator_Marketing_v2.txt

이렇게 하면: - 기능별로 그룹화 가능 (CustomerSupport, ProductRecommender, EmailGenerator) - 버전 히스토리 추적 가능 (v1, v2, v3…) - 팀원 간 소통 명확 (“CustomerSupport_Returns_v2 테스트해봤어?”)

규칙 3 — 기능이 세분화될 때 이름도 그 구조를 반영한다.
예시: FinancialAnalysis_Revenue_v1, FinancialAnalysis_Expense_v1.
규칙 2에서 했던 기본 구조 위에, 같은 도메인 안에서 세부 카테고리가 분기될 때의 이름을 다룬다.
이 예시를 들은 건 단순히 이름의 형식을 보여주려는 게 아니다.

프롬프트가 성장하면서 단일 기능에서 세분화로 전이되는 경로를 암시하는 것이다.
처음엔 FinancialAnalysis_v1 하나로 시작해서, 나중엔 Revenue와 Expense로 분기한다.
그리고 그 분기가 이름 구조에 바로 보이는 것.

예시:

초기 단계 (통합 프롬프트):

FinancialAnalysis_v1.txt
FinancialAnalysis_v2.txt

성장 단계 (기능 세분화):

FinancialAnalysis_Revenue_v1.txt
FinancialAnalysis_Revenue_v2.txt
FinancialAnalysis_Expense_v1.txt
FinancialAnalysis_CashFlow_v1.txt
FinancialAnalysis_ProfitMargin_v1.txt

더 깊은 세분화:

FinancialAnalysis_Revenue_Quarterly_v1.txt
FinancialAnalysis_Revenue_Yearly_v1.txt
FinancialAnalysis_Expense_OperatingCosts_v1.txt
FinancialAnalysis_Expense_Marketing_v1.txt

이 구조의 이점: - 도메인 계층(FinancialAnalysis) → 기능(Revenue) → 세부기능(Quarterly)이 이름에서 바로 보인다 - 새로운 기능 추가 시 어디에 배치해야 할지 명확하다 - 폴더 구조 없이도 파일 이름만으로 조직화가 가능하다

2.2 기준선 정의 (규칙 4)

규칙 4 — 각 버전의 목표와 기대 성능을 문서화한다.
클러스터 하나에서 버전을 여러 개 만들었다.
그런데 여러 개의 버전 중 어떤 게 “나은지”를 판단하려면, 각 버전이 무엇을 목표로 하는지가 미리 정의되어 있어야 한다.
목표가 없으면 비교 자체가 방향을 잃는다.
이건 규칙 1의 확장이다. 버전이 여러 개라면, 각 버전의 존재 이유가 명확해져야 한다.

예시:

프롬프트: ProductRecommender_Personalized_v2

목표: - 사용자의 구매 이력과 선호도를 기반으로 3개의 상품 추천 - 추천 이유를 각 상품마다 1-2문장으로 설명 - 응답 시간 3초 이내

기대 성능: - 정확도: 추천 상품 중 최소 1개 이상이 사용자의 관심 카테고리와 일치 (80% 이상) - 다양성: 3개 추천이 서로 다른 카테고리에서 선택됨 (100%) - 형식 준수: JSON 형식 오류 없음 (100%) - 안전성: 재고 없는 상품 추천 안 함 (100%)

v1과의 차이점: - v1은 설명 없이 상품명만 나열 → v2는 추천 이유 추가 - v1은 같은 카테고리 중복 가능 → v2는 다양성 보장

테스트 결과 기준: - Pass: 위 4가지 기대 성능 모두 충족 - Fail: 하나라도 기준 미달

이렇게 문서화하면 테스트 후 “이 버전은 목표를 달성했는가?”를 명확히 판단할 수 있다.

2.3 테스트 데이터의 품질 (규칙 5, 6)

규칙 5 — 작위적인 문장을 사용하지 않는다. 실제 사용자 발화와 실제 데이터를 사용한다.
“작위적인 문장”이란 테스트를 위해 직접 만든 문장을 말한다.
예를 들어 “부산의 맛있는 음식 추천해줘”라고 자신이 쓴 테스트 문장과,
실제 사용자가 앱에서 입력한 “부산 갔는데 뭘 먹어야 돼”는 완전히 다르다.
후자쪽이 더 불완전하고, 맥락이 빠지고, 오타가 있을 수 있다.
그리고 바로 그 불완전함이 실제 환경에 해당된다.
테스트 데이터가 실제 환경과 달라지면, 테스트 결과는 현실을 반영하지 못한다.
ML에서 학습 데이터와 평가 데이터 간의 분포 차이가 모델 성능을 왜곡하는 것과 동일한 논리이다.

예시:

나쁜 예 (작위적 테스트 데이터):

"서울에서 부산까지 가는 방법을 알려주세요."
"상품 반품 절차를 상세히 설명해주세요."
"이번 달 매출 보고서를 작성해주세요."

→ 문법 완벽, 맥락 명확, 정중한 표현

좋은 예 (실제 사용자 발화):

"서울 부산 어케가요"
"반품하고싶은데요"
"이번달 매출은요?"
"환불 요청했는데 연락이 엄ㅂ어요" (오타)
"저번에 주문한거요 그거" (불완전한 맥락)
"아 답답하네 진짜 반품 어떻게 하는거에요" (감정 표현)

→ 오타, 생략, 맥락 부족, 감정 포함

실제 데이터 수집 방법: - 베타 테스트 기간 동안 수집한 실제 사용자 입력 - 고객 지원 채팅 로그 (개인정보 제거 후) - A/B 테스트 과정에서 기록된 사용자 쿼리 - 사내 직원들의 자유 형식 입력 (시뮬레이션)

실제 데이터로 테스트하면 프롬프트의 실전 성능을 정확히 파악할 수 있다.

규칙 6 — 테스트 데이터셋을 사용한다.
규칙 5가 “왜”라면, 규칙 6은 “어떻게”이다.
어떤 종류의 테스트 데이터셋을 구성해야 하는지는 이 파트 마지막에서 상세히 본다.

예시:

테스트 데이터셋 구성 (고객 지원 챗봇)

파일명: CustomerSupport_TestDataset_v1.json

{
  "dataset_name": "CustomerSupport Returns Test",
  "version": "1.0",
  "prompt_file": "./prompts/CustomerSupport_Returns_v2.txt",
  "total_cases": 50, //  50개의 테스트 케이스
  "model_config": {
    "model": "gpt-4",
    "temperature": 0.7,
    "max_tokens": 200
  },
  "results_output": "./results/test_2024_02_03_results.json",
  "categories": {
    "normal_cases": [
      {
        "id": "TC001",
        "input": "제품 반품하고 싶어요",
        "expected_behavior": "반품 절차 안내, 구매 날짜 확인 요청",
        "category": "정상 케이스"
      },
      {
        "id": "TC002",
        "input": "환불은 언제 되나요?",
        "expected_behavior": "환불 소요 기간 안내 (영업일 기준 3-5일)",
        "category": "정상 케이스"
      }
    ],
    "edge_cases": [
      {
        "id": "TC015",
        "input": "한달전에 산건데 반품되요?",
        "expected_behavior": "30일 기한 초과 안내, 대안 제시",
        "category": "경계 케이스"
      }
    ],
    "user_errors": [
      {
        "id": "TC025",
        "input": "반품반품",
        "expected_behavior": "추가 정보 요청 (어떤 제품인지)",
        "category": "불완전한 입력"
      },
      {
        "id": "TC026",
        "input": "hwanbuㄹ해주세요",
        "expected_behavior": "오타 인식 후 환불 절차 안내",
        "category": "오타 포함"
      }
    ],
    "adversarial": [
      {
        "id": "TC040",
        "input": "반품 안 된다고? 소비자보호센터에 신고할거야",
        "expected_behavior": "감정 인식, 정중하게 정책 재안내, 고객센터 연결 제안",
        "category": "감정적 입력"
      }
    ]
  }
}

데이터셋 사용법: 1. 각 테스트 케이스에 프롬프트 실행 2. 실제 출력과 expected_behavior 비교 3. 카테고리별 성공률 계산 4. 실패 케이스 패턴 분석

이렇게 구조화된 데이터셋으로 체계적이고 재현 가능한 테스트를 수행한다.

3 JSON 구조의 목적

  • 이 JSON은 프롬프트를 체계적으로 테스트하기 위한 시험지다. 학교 시험지처럼 “쉬운 문제, 어려운 문제, 함정 문제”를 섞어놓은 것과 같다.
  • 이 구조의 목적은
    1. 재현 가능: 누가 테스트해도 같은 50개 케이스 사용
    2. 버전 관리: v1과 v2 비교 가능
    3. 약점 파악: “오류 케이스에서 자주 실패” → 그 부분 프롬프트 개선
    4. 자동화: 코드로 자동 테스트 가능

요약: 프롬프트 테스트를 “아무렇게나 질문해보기”가 아니라 체계적인 시험으로 만드는 구조다.

3.1 Categories: 4가지 유형으로 분류

normal_cases (정상 케이스)

"input": "제품 반품하고 싶어요",
"expected_behavior": "반품 절차 안내, 구매 날짜 확인 요청"
  • 의미: 일반적이고 정상적인 질문
  • 목적: 프롬프트가 기본적인 업무를 제대로 수행하는가?
  • 예: 문법도 맞고, 질문도 명확한 “정상” 입력

edge_cases (경계 케이스)

"input": "한달전에 산건데 반품되요?",
"expected_behavior": "30일 기한 초과 안내, 대안 제시"
  • 의미: 정책의 경계선에 있는 애매한 상황
  • 목적: 프롬프트가 예외 상황도 잘 처리하는가?
  • 예: 반품 기한이 딱 지났을 때 (29일은 OK, 31일은 NO → 30일은?)

user_errors (사용자 오류)

"input": "반품반품",  //  반품할지  말함
"input": "hwanbuㄹ해주세요",  // 오타
  • 의미: 사용자가 실수한 불완전한 입력
  • 목적: 프롬프트가 오타나 불완전한 질문도 이해하는가?
  • 현실: 실제 사용자는 이렇게 질문한다

adversarial (적대적 입력)

"input": "반품  된다고? 소비자원에 신고할거야",
"expected_behavior": "감정 인식, 정중하게 정책 재안내"
  • 의미: 화난 고객, 공격적 표현
  • 목적: 프롬프트가 감정적 상황도 잘 대응하는가?
  • 중요: 프롬프트가 싸우거나 무시하지 않고 정중하게 응대하는지 확인

3.2 각 테스트 케이스의 구조

{
  "id": "TC001",  // 고유 번호 (Test Case 001)
  "input": "제품 반품하고 싶어요",  // 사용자가 입력한 
  "expected_behavior": "반품 절차 안내, 구매 날짜 확인 요청",  // 프롬프트가 해야  
  "category": "정상 케이스"  // 어떤 유형인지
}

id: 나중에 “TC025 실패했어요”라고 소통 가능
input: 프롬프트에 넣을 입력
expected_behavior: 정답은 아니지만 “이런 방향으로 답해야 한다”는 기준
category: 분류 (통계 낼 때 “정상 케이스 100% 성공, 오류 케이스 70% 성공” 이런 식으로)

3.3 실전 사용법 예시

# 1. JSON 파일 로드
test_dataset = load_json("CustomerSupport_TestDataset_v1.json")

# 2. 각 케이스 실행
for case in test_dataset["categories"]["normal_cases"]:
    prompt_output = run_prompt(case["input"])
    
    # 3. 결과 평가
    if check_behavior(prompt_output, case["expected_behavior"]):
        print(f"✓ {case['id']} PASS")
    else:
        print(f"✗ {case['id']} FAIL")

# 4. 통계
정상 케이스 성공률: 95%
경계 케이스 성공률: 80%
오류 케이스 성공률: 70%  ← 여기가 약점!

3.4 대규모 서비스 (GPT, Claude, Bard 수준) 예시

{
  "categories": {
    "normal_cases": {},
    "edge_cases": {},
    "user_errors": {},
    "adversarial": {},
    "safety_critical": {  // 추가: 안전성 테스트
      "hate_speech": [],
      "personal_info_leakage": [],
      "harmful_instructions": []
    },
    "performance": {  // 추가: 성능 테스트
      "long_context": [],
      "multi_turn": [],
      "latency_test": []
    },
    "multilingual": {  // 추가: 다국어 테스트
      "korean": [],
      "english": [],
      "code_switching": []  // 한영 섞어쓰기
    },
    "domain_specific": {  // 추가: 도메인별
      "medical": [],
      "legal": [],
      "finance": []
    }
  }
}
  • OpenAI는 수만 개의 테스트 케이스를 사용한다고 알려짐
  • 카테고리가 20~30개 이상
  • 자동화된 평가 시스템 + 사람 평가 병행
project/
├── prompts/
│   ├── customer_support/
│   │   ├── returns_v1.txt
│   │   ├── returns_v2.txt
│   │   └── shipping_v1.txt
│   └── product_recommendation/
│       └── personalized_v3.txt
│
├── test_data/
│   ├── customer_support_returns.json
│   ├── customer_support_shipping.json
│   └── product_recommendation.json
│
├── test_configs/
│   ├── daily_regression.yaml
│   └── pre_deploy_validation.yaml
│
└── test_results/
    ├── 2024-02-01/
    ├── 2024-02-02/
    └── 2024-02-03/

3.5 클러스터 넷: 실행과 평가, 호환성 (규칙 7, 8, 9)

규칙 7 — 같은 테스트 도구로 최소 열 번 이상 생성한다.
“확률적 응답”의 직접적인 대응이다.
LLM은 같은 프롬프트를 받아도 다른 결과를 줄 수 있으므로, 한 번의 결과로는 신뢰할 수 없다.
“최소 열 번”이라는 숫자는 최소한의 표본 크기를 의미한다.
그리고 “같은 테스트 도구”가 강조되는 건, 변수 통제의 문제이다.
“도구가 달라지면 실행 환경 자체가 달라져서 결과를 비교할 수 없다”는 뜻이다.
참고로 “열 번”이라는 수준은 나중 파트에서 정량적 테스트로 넘어간다면 N > 100 수준으로 올라간다.
규칙 7은 최소한의 기준이고, 진짜 신뢰할 수 있는 수준은 더 높다.

예시:

테스트 시나리오: - 프롬프트: ProductRecommender_v2 - 입력: “20대 여성, 최근 운동화 구매 이력” - 도구: OpenAI Playground (GPT-4, temperature=0.7)

10회 실행 결과 기록:

실행 추천 1 추천 2 추천 3 응답 시간 형식 오류
1 요가매트 레깅스 물병 2.3s 없음
2 러닝화 스포츠브라 양말 2.1s 없음
3 레깅스 헬스장가방 물병 2.5s 없음
4 요가매트 레깅스 물병 2.2s 없음
5 운동복 물병 양말 2.4s 없음
6 레깅스 운동복 헬스장가방 2.3s 없음
7 물병 요가매트 레깅스 2.6s 없음
8 러닝화 양말 물병 2.1s 없음
9 레깅스 스포츠브라 물병 2.2s 없음
10 헬스장가방 요가매트 운동복 2.4s 없음

분석: - 가장 자주 추천된 항목: 레깅스(6회), 물병(7회), 요가매트(4회) - 일관성: 모두 운동/피트니스 카테고리 (100% 일관) - 다양성: 총 9가지 서로 다른 제품 제안 (충분한 다양성) - 응답 시간: 평균 2.3s (목표 3s 이내 달성) - 형식 오류: 0건 (안정성 확보)

결론:
10회 반복 테스트를 통해 프롬프트가 일관되게 관련 제품을 추천하며, 형식 오류 없이 안정적으로 작동함을 확인했다.
단, 한 번만 테스트했다면 이런 패턴과 안정성을 파악할 수 없었을 것이다.

규칙 8 — 최소 세 명의 작업 관계자가 평가에 참여한다.
“정답이 없다”는 현실에서, 평가는 불가피하게 평가자의 주관을 포함한다.
한 사람의 판단만으로는 그 주관이 편향이 되는지, 유효한 시각이 되는지 구분이 안 된다.
세 명 이상이면 단일 편향의 영향이 줄어든다.
이건 학술 논문의 동료 심사(peer review) 구조와 동일한 논리를 프롬프트 평가에 적용한 것이다.

예시:

평가 시나리오: - 프롬프트: CustomerSupport_Returns_v2 - 테스트 케이스: “한달전에 산건데 반품되요?” - 응답: “구매하신 지 30일이 지나 반품 기한이 지났습니다. 하지만 제품에 하자가 있다면 AS 접수가 가능합니다. 제품 상태를 확인해주시겠어요?”

평가자별 채점 (5점 척도 루브릭):

평가 기준 평가자 A (PM) 평가자 B (CS팀장) 평가자 C (개발자) 평균
정확성 5 5 5 5.0
친절함 4 5 3 4.0
명확성 5 4 5 4.7
대안 제시 4 5 4 4.3
총점 4.5 4.75 4.25 4.5

평가자별 코멘트:

  • 평가자 A (Product Manager): “정책을 명확히 전달하면서도 대안(AS)을 제시해 좋다. 친절함은 조금 더 개선 가능.”
  • 평가자 B (Customer Support 팀장): “실제 고객 응대 관점에서 매우 적절. ’하지만’으로 연결해 부정적 정보를 완화한 점이 좋다.”
  • 평가자 C (개발자): “논리적으로 정확하고 명확하다. 다만 ’확인해주시겠어요?’가 다소 형식적으로 느껴질 수 있음.”

합의 결과: - 전체 평가: Pass (평균 4.5/5.0) - 개선 제안: “확인해주시겠어요?” → “어떤 문제가 있으신가요?”로 더 공감적인 표현 검토

단일 평가자의 위험: 만약 평가자 C만 평가했다면 “친절함 3점”이 유일한 점수가 되어 실제보다 낮게 평가될 수 있었다.
세 명의 평가로 균형 잡힌 시각을 확보했다.

규칙 9 — 다양한 언어 모델 버전을 사용하여 테스트한다.
세 번째 어려움, “LLM은 빠르게 변한다”의 실천적 대응이다.
하나의 모델에서만 잘 작동하는 프롬프트는 그 모델이 업데이트되거나 더 이상 사용되지 않을 때 바로 취약해진다.
다양한 모델에서 테스트하는 건, 프롬프트의 내재적 품질을 확인하는 것이다.
특정 모델의 특성에 의존하는 게 아니라, 프롬프트 자체의 논리와 구조가 충분히 강한지를 판단한다.

예시:

테스트 대상 프롬프트: EmailGenerator_Marketing_v3

프롬프트 내용:

You are a marketing email writer. Create a promotional email for {{PRODUCT_NAME}} 
that highlights its key benefits and includes a clear call-to-action.

Email should be:
- Professional yet friendly tone
- 150-200 words
- Include subject line
- End with a CTA button text suggestion

다중 모델 테스트 결과:

모델 버전 성공률 평균 길이 톤 일관성 CTA 포함 비고
GPT-3.5 turbo 85% 175 단어 높음 100% 때때로 과도하게 캐주얼
GPT-4 standard 95% 180 단어 매우 높음 100% 가장 안정적
GPT-4 turbo 90% 165 단어 높음 95% CTA 가끔 누락
Claude 3 Opus 90% 190 단어 높음 100% 더 형식적 톤 경향
Claude 3 Sonnet 80% 160 단어 중간 90% 길이 제약 덜 준수
Gemini 1.5 Pro 75% 140 단어 중간 85% 짧고 직설적, 제목 생략 경향

분석:

  1. 모델 간 성능 차이:
    • GPT-4 standard에서 가장 높은 성공률 (95%)
    • Gemini 1.5 Pro에서 가장 낮은 성공률 (75%)
    • 20%p 차이 → 프롬프트가 특정 모델에 과최적화되지 않았음
  2. 일관된 약점:
    • 모든 모델에서 때때로 150단어 미만 생성 (특히 Gemini)
    • CTA 누락이 가장 빈번한 문제점
  3. 개선 방향:
    • CTA 요구사항을 더 명시적으로 강조
    • “MUST include” 같은 강제 키워드 추가 검토
    • 길이 제약을 “Aim for 150-200 words (minimum 150)” 으로 명확화

결론:
이 프롬프트는 대부분의 주요 모델에서 75% 이상 작동하므로 모델 독립적이다.
하지만 Gemini에서의 낮은 성능은 프롬프트 개선 여지를 시사한다.
만약 GPT-4에서만 테스트했다면, Gemini에서의 문제를 사전에 발견하지 못했을 것이다.

4 프롬프트 평가 어려움에 대한 연결선

아홉 규칙이 왜 이런 형태인지 정리하면, 세 어려움과 규칙 사이에 직접적인 대응이 있다.
- 확률적 응답이라는 문제 -> 규칙 1과 규칙 7로 대응.
- 정답 없다는 문제 -> 규칙 4와 규칙 8로 대응.
- 모델이 빠르게 변한다는 문제 -> 규칙 2, 3과 규칙 9로 대응.

그리고 규칙 5와 6은 세 어려움 모두에 공통적으로 작용하는 현실성 보장 장치이다.
테스트 데이터 자체가 현실과 달라지면, 어떤 규칙을 아무리 잘 적용해도 의미가 없다.

5 문서화: 텍스트 이상의 것

규칙 4에서 “문서화하다”라고 했는데, 이 문서화가 구체적으로 무엇을 의미하는지를 살펴본다.

5.1 프롬프트 원문 보존

프롬프트 텍스트 블록의 구성 요소
- Introduction - Response template - Ending

[Introduction]
You are a customer support assistant specializing in product returns.

[Response template]
When a user asks about returns:
1. First, confirm the product and purchase date
2. Explain the return policy (30-day window)
3. Provide step-by-step return instructions

[Ending]
Maintain a friendly and professional tone throughout.

이건 당연하다고 생각하기 쉽지만, 프롬프트를 ChatGPT에 붙여넣고 사용하는 사람은 이 원문(LLM에 실제로 입력하는 프롬프트 텍스트 전체)를 별도로 저장하지 않는 경우가 많다.

많은 사람이 ChatGPT 창에 이런 프롬프트를 입력하고 사용하지만, 그 텍스트를 별도 파일로 저장하지 않는다는 것이다. 나중에 “어떤 프롬프트로 테스트했지?”라고 되돌아볼 때, 원문이 없으면 정확한 재현이 불가능하다.

5.2 모델 파라미터

프롬프트 원문만 저장하면 충분한가? 아니다. LLM 실행 설정값도 함께 기록해야 한다.

예시로 보면: - model=GPT-3.5-turbo (어떤 모델을 사용했는지) - max_tokens=200 (최대 응답 길이) - temperature=0.5 (응답의 랜덤성 정도) - frequency_penalty=1 (반복 단어 억제) - presence_penalty=1 (새로운 주제 도입 촉진)

왜 이것들을 기록해야 하나?

같은 프롬프트라도 temperature=0.5temperature=0.8은 완전히 다른 결과를 만든다.

실제 예시:

프롬프트: “3가지 여행지를 추천해줘”

temperature=0.2 (낮음, 일관적):

1. 제주도 - 자연 경관과 해변
2. 부산 - 도시와 바다의 조화
3. 경주 - 역사 문화 유적

temperature=0.9 (높음, 창의적):

1. 울릉도 - 신비한 섬
2. 담양 죽녹원 - 대나무 숲길
3. 강원도 정선 - 레일바이크

같은 질문, 같은 프롬프트인데 파라미터만 달라져도 추천 목록이 완전히 바뀐다.

문제 상황:

1달 후 팀원이 “저번에 테스트한 그 프롬프트 다시 돌려봤는데 결과가 달라요”라고 한다.
프롬프트 원문만 있고 파라미터 기록이 없다면? 재현 불가능하다.

어떤 설정으로 돌렸는지 모르니, 결과가 다른 이유가: - 프롬프트 문제인지 - 파라미터 문제인지 - 모델 업데이트 때문인지

구분할 수 없다. 이게 재현가능성(reproducibility)을 위해 파라미터를 기록하는 이유다.

5.3 프롬프트의 메타 데이터 템플릿

프롬프트 원문과 파라미터 외에 프롬프트의 메타데이터도 함께 기록한다: - 프롬프트 유형 (음식 추천, 고객 지원, 코드 생성 등) - 제목 (CustomerSupport_Returns_v2) - 설명 (어떤 상황에서 사용하는가) - 기대 결과 (중요!) - 작업 과정 (설계 의도와 결정 이유) - 기타 메모

# 프롬프트 메타데이터 파일 예시
---
prompt_id: "CustomerSupport_Returns_v2" # 프롬프트 고유 ID 또는 제목
prompt_type: "고객 지원" # 프롬프트 유형 
created_date: "2026-02-01"
author: "홍길동"

description: | # 설명
  이커머스 고객 지원 챗봇. 
  제품 반품 관련 문의에 응답한다.
  30일 반품 정책 기반.

expected_results: # 기대 결과
  - 반품 정책을 명확히 설명 (30일 이내)
  - 구매 날짜와 제품 정보 확인 요청
  - 단계별 반품 절차 안내
  - 응답 시간 3초 이내
  - 친절하고 전문적인 톤 유지

design_decisions: | # 작업 과정 기록
  2026-02-01: 구조화된 단계 제시 방식 채택
  - 이유: 사용자가 따라하기 쉽고 누락 없음
  - 대안: 산문체 설명 방식 (거부, 너무 길어짐)
  
  Q&A 형식은 고려했으나 채택 안 함
  - 이유: 모든 경우의 수를 커버 불가

notes: | # 기타 메모
  - 실제 반품 처리는 별도 시스템 연동 필요
  - 해외 배송은 이 프롬프트 범위 밖
  - v1 대비 개선: "대안 제시" 추가 (AS, 교환)
---

여기서 “기대 결과”가 핵심이다.

나쁜 기대 결과 (모호함):

이 프롬프트는 음식 추천을 한다.

이건 그냥 기능 설명이다. 테스트 후 “성공했나?”를 어떻게 판단할까? 애매하다.

좋은 기대 결과 (구체적 기준):

1. 정확히 3개의 음식을 추천해야 한다
2. 3개는 서로 다른 카테고리여야 한다 (중복 불가)
3. 각 추천마다 이유를 1-2문장으로 설명한다
4. 특수문자나 이모지가 포함되면 안 된다
5. 응답 시간은 3초 이내여야 한다

이제 테스트 결과를 볼 때:

프롬프트 응답:

1. 삼계탕 - 보양식으로 좋습니다
2. 냉면 - 여름에 시원하게
3. 비빔밥 🍚 - 다양한 야채

평가: - ✓ 3개 추천 (Pass) - ✓ 서로 다른 카테고리 (Pass) - ✓ 이유 설명 (Pass) - ✗ 이모지 포함 (🍚) (Fail) - ✓ 2.1초 응답 (Pass)

결과: Fail (기준 4 미달)

이렇게 구체적인 기대 결과가 있으면: - 테스트 후 “통과/실패”를 명확히 판단 가능 - 어떤 부분이 문제인지 바로 파악 (이모지 제거 필요) - 주관적 판단 배제 (누가 봐도 Fail)

기대 결과가 없으면? “음, 괜찮은 것 같은데?” 같은 애매한 평가만 남는다.

작업 과정 기록: 설계 중 고려한 이슈와 의사결정이 들어간다.

예시 상황:

음식 추천 프롬프트를 만들 때 두 가지 선택지가 있었다:

A안: 실시간으로 맛집 리뷰 사이트를 검색해서 추천
B안: 사전에 정리된 음식 목록에서 추천

팀은 B안을 선택했다. 작업 과정 기록:

의사결정 기록 (2026-02-01)
-----------------------------------
문제: 실시간 검색 vs 사전 목록 중 선택
결정: 사전 목록 방식 채택

이유:
1. 실시간 검색은 응답 시간이 5-10초로 느림 (목표 3초 초과)
2. 외부 API 의존성으로 안정성 저하 위험
3. 검색 결과 품질이 프롬프트로 제어 불가

트레이드오프:
- 장점: 빠른 응답(2초), 안정적 작동
- 단점: 최신 맛집 반영 안 됨 (월 1회 수동 업데이트)

대안 검토:
- 향후 캐싱 방식으로 실시간 검색 재검토 가능
  • 왜 이런 기록이 중요한가?
    • 소프트웨어 엔지니어링의 ADR(Architecture Decision Record) 개념 때문이다.
    • 결정만 남기는 게 아니라 왜 그렇게 결정했는지를 함께 남긴다
    • 가령, 3개월 후 새로운 팀원이 “왜 실시간 검색 안 하나요? 그게 더 좋지 않나요?”라고 물어본다.
  • 작업 과정 기록이 있으면:
    • → “응답 시간 3초 목표 때문에 포기했어요. 대신 월 1회 목록 업데이트로 보완합니다”
    • → 명확한 답변, 논의 5분 종료
  • 작업 과정 기록이 없으면:
    • → “음… 그때 뭔가 이유가 있었던 것 같은데…”
    • → 다시 회의 소집, 같은 논의 반복, 1시간 낭비
  • 나중에 프롬프트를 수정할 때, 이 기록을 보면:
    • “실시간 검색을 추가하려면 응답 시간 목표를 조정해야 하는구나”
    • “캐싱 방식이 좋은 대안일 수 있겠다”

같은 이슈를 반복해서 고민하지 않고, 이전 맥락 위에서 발전시킬 수 있다.

6 테스트 데이터셋: 약점 지도 그리기

  • 규칙 5와 6에서 “실제 데이터를 사용하라”고 했다.
  • 그런데 어떤 실제 데이터를 골라야 하나?
  • 무작위로 100개 뽑으면 될까? 아니다. 전략적으로 구성해야 한다.

6.1 테스트 데이터셋 구성의 5가지 원칙

  • 5가지 원칙: 테스트 데이터를 “어떻게 선택할지”의 기준
  • 모든 원칙이 동시에 교차 적용되어야한다. 예를 들어,
    • 원칙 1: “반품 40%, 배송 30%, 환불 20%” 같은 실제 사용 빈도를 테스트 데이터에 반영한다. (이렇게 하면 전체 사용자 질문의 80%를 커버)
    • 원칙 2-5: 각 카테고리 안에서 정상/경계/오류/한계를 섞어라
  • 이 5가지 원칙을 모두 적용해야, 프롬프트의 강점과 약점을 정확히 파악할 수 있다.

6.1.1 대표적이고 빈번한 실제 사용자 발화

  • 가장 자주 일어나는 사용 패턴부터 커버한다.

예시 (고객 지원 챗봇):

실제 사용 통계: - “반품하고 싶어요” 유형: 40% - “배송 조회” 유형: 30% - “환불 문의” 유형: 20% - 기타: 10%

테스트 데이터 50개 구성: - 반품 관련: 20개 (40%) - 배송 관련: 15개 (30%) - 환불 관련: 10개 (20%) - 기타: 5개 (10%)

목적: 실제 사용 빈도를 반영해서, 80% 이상의 실제 상황을 커버한다.

이게 정상 범주의 커버리지다.

6.1.2 다양하고 포괄적인 내용

  • 사용자가 할 수 있는 모든 종류의 질문을 포함한다.

나쁜 예 (다양성 부족):

"반품하고 싶어요"
"제품 반품 가능한가요?"
"반품 절차 알려주세요"
"반품하려고요"

→ 전부 반품만 물어봄. 배송, 환불, 제품 문의는?

좋은 예 (다양성 확보):

- 반품: "반품하고 싶어요"
- 배송: "언제 도착하나요?"
- 환불: "환불 기간은?"
- 제품 정보: "사이즈 어떻게 되나요?"
- 계정 문제: "로그인이 안 돼요"
- 할인/쿠폰: "할인 코드 사용법"

목적:
* 한 유형만 테스트하면, 다른 유형에서 실패할 수 있다.
* 프롬프트가 “반품”에만 특화되어 있고 “배송 조회”는 제대로 못 할 수 있다. * 다양한 유형을 모두 테스트해야 프롬프트의 전체 범위를 확인 가능하다.

6.1.3 예상치 못한 사항 포함

여기서 테스트 성격이 바뀐다.

앞의 1, 2번은 “프롬프트가 잘 작동하는 경우를 확인”하는 것이다.
3번부터는 “프롬프트가 실패할 만한 경우를 의도적으로 찾는” 것이다.

예시:

정상 케이스:

"반품하고 싶어요" → 프롬프트가 잘 처리함

예상치 못한 케이스:

"반품하고 싶은데 영수증을 잃어버렸어요"
"선물받은 건데 반품 되나요?"
"해외 배송인데 반품 비용은요?"

이런 질문들은: - 기본 반품 정책만으로는 답변 어려움 - 예외 상황 처리 능력 테스트 - 프롬프트에 추가 지침이 필요한지 판단 가능

목적: “매뉴얼대로만” 하는 프롬프트는 현실에서 약하다.
예상 밖 상황도 잘 처리하는지 확인한다.

6.1.4 사용자 오류 패턴

핵심: 실제 사용자는 완벽하게 질문하지 않는다.

완벽한 질문 (이론):

"구매한 제품을 반품하고 싶습니다. 절차를 알려주세요."

→ 문법 완벽, 의도 명확

실제 질문 (현실):

"반품반품" (뭘 반품할지 안 말함)
"hwanbuㄹ해주세요" (오타)
"저번에 산거요 그거" (맥락 부족)
"반품 ㅎㅎ" (불명확한 의도)

프롬프트가 깔끔한 질문에만 잘 작동하면: - 실전에서는 자주 실패한다 - “무슨 말인지 모르겠습니다” 같은 쓸모없는 답변만 반복

좋은 프롬프트는: - 오타를 이해한다: “hwanbuㄹ” → “환불”로 인식 - 불완전한 질문에 추가 질문: “어떤 제품을 반품하시나요?” - 맥락 추론: “저번에 산거” → 최근 구매 내역 확인 제안

목적: 오류 패턴을 테스트 데이터에 넣어서, 프롬프트가 관대하게(robustly) 작동하는지 확인한다.

6.1.5 LLM의 한계가 드러난 발화

핵심: 이건 프롬프트 문제가 아니라 모델 자체의 약점이다.

LLM이 약한 영역: - 최신 정보: “2026년 2월 4일 날씨는?” (학습 데이터 이후) - 복잡한 수학: “17 × 23 + 89 ÷ 3 = ?” - 한국어 특이 표현: “그거 있잖아, 그 뭐냐, 그거, 그게 아니라, 아니 내말은, 그게 아니고, 등” - 개인 정보: “내 주문 내역 보여줘” (데이터베이스 연동 필요)

예시 상황:

프롬프트: 고객 지원 챗봇
질문: “오늘 배송 가능한가요?” (오늘 = 2026년 2월 4일)

프롬프트가 아무리 잘 작성되어도: - GPT-4는 학습 데이터가 2023년까지 (최신 날짜 모름) - “오늘”이 언제인지 알 수 없음 - 잘못된 답변 가능성

이걸 테스트 데이터에 왜 포함하나?

목적: 1. 프롬프트 vs 모델 한계 구분 - 실패 이유가 프롬프트 때문인지, 모델 한계 때문인지 파악

  1. 보완책 설계
    • “최신 정보는 외부 API 연동 필요”
    • “날짜 관련 질문은 사전 처리 필요”
  2. 사용자 기대 관리
    • “이런 질문은 현재 모델로 처리 불가” 명시
    • 서비스 범위 명확화

정리:

테스트 데이터셋은 “프롬프트가 잘 작동하는 사례 모음집”이 아니다.
프롬프트의 약점이 어디 있는지 드러내는 지도다.

5가지 원칙으로 구성하면: - 정상 케이스 커버 (1, 2번) - 경계 상황 발견 (3번) - 오류 내성 확인 (4번) - 모델 한계 파악 (5번)

결과: 프롬프트의 강점과 약점을 정확히 아는 지도를 얻는다.

6.1.6 예시

1. 반품 관련 20개 (빈도 40% 반영 - 원칙 1)
    ├─ 정상: "반품하고 싶어요" (10개)
    ├─ 경계: "31일 지났는데 반품 되나요?" (3개) - 원칙 3
    ├─ 오류: "반품반품" (4개) - 원칙 4
    └─ LLM한계: "오늘 주문 반품 가능?" (3개) - 원칙 5

2. 배송 조회 15개 (빈도 30% 반영 - 원칙 1)
    ├─ 정상: "언제 도착하나요?" (8개)
    ├─ 경계: "해외 배송 기간은요?" (2개) - 원칙 3
    ├─ 오류: "배송ㅇ" (3개) - 원칙 4
    └─ LLM한계: "실시간 위치 추적" (2개) - 원칙 5

3. 환불 문의 10개 (빈도 20% 반영 - 원칙 1)
    ├─ 정상: "환불 절차 알려줘" (5개)
    ├─ 경계: "카드 취소했는데 언제 입금돼?" (2개) - 원칙 3
    ├─ 오류: "hwanbuㄹ" (2개) - 원칙 4
    └─ LLM한계: "내 통장 잔고 확인해줘" (1개) - 원칙 5

4. 기타 카테고리 5개 (다양성 확보 - 원칙 2)
    ├─ 제품 정보: "사이즈 표 어디 있나요?"
    ├─ 계정 문제: "로그인이 안 돼요"
    └─ 할인/쿠폰: "쿠폰 적용이 안 됩니다"
1. 표준화 알고리즘 QnA 40개 (빈도 40% 반영 - 원칙 1)
   * (원칙 2: 알고리즘의 단계별, 유형별 질문을 다양하게 구성)
   ├─ 정상: "A 알고리즘의 2단계 전처리 과정을 설명해줘" (20개)
   ├─ 경계: "입력 데이터에 특수문자가 섞여 있으면 어떻게 처리돼?" (8개) - 원칙 3
   ├─ 오류: "알고리즘 2단계가 뭐더랔ㅋ" (6개) - 원칙 4
   └─ LLM한계: "이 알고리즘의 소스코드 전체를 지금 즉시 자바로 짜줘" (6개) - 원칙 5

2. 데이터 표준화 수행 Agent 40개 (빈도 40% 반영 - 원칙 1)
   * (원칙 2: 엑셀, DB, JSON 등 다양한 데이터 포맷 포함)
   ├─ 정상: "첨부한 엑셀 파일의 컬럼명을 표준 메타데이터로 변환해줘" (20개)
   ├─ 경계: "표준 용어집에 없는 신규 약어는 어떻게 처리할 거야?" (8개) - 원칙 3
   ├─ 오류: (파일 없이) "이거 다 표준으로 바꿔놔" (6개) - 원칙 4
   └─ LLM한계: "우리 회사 내부망 DB에 직접 접속해서 데이터 읽어와" (6개) - 원칙 5

3. 알고리즘 세부 분석 및 설명 Agent 15개 (빈도 15% - 원칙 1)
   ├─ 정상: "이 알고리즘이 기존 BT 도메인 지식과 충돌하는 지점이 어디야?" (8개)
   ├─ 경계: "논문에 나온 수식과 현재 코드의 로직이 다를 때 우선순위는?" (3개) - 원칙 3
   ├─ 오류: "왜 이래?" (단발성 질문) (2개) - 원칙 4
   └─ LLM한계: "이 알고리즘이 10년 뒤에도 유효할지 예측해줘" (2개) - 원칙 5

4. 기타 플랫폼 문의 5개 (나머지 5% - 원칙 1)
   * (원칙 2: 시스템 권한, 로그인, 가이드 문서 위치 등 주변부 질문)
   ├─ "지능형 지식 플랫폼 로그인 오류는 어디에 문의해?"
   ├─ "데이터 표준화 가이드라인 PDF 다운로드 어디서 해?"
   └─ "내 작업 이력 삭제하고 싶어"
그룹 A: 표준화 알고리즘 QnA (40%) - 원칙 1(빈도)
  * 정상(25개): "A 알고리즘의 3단계를 설명해줘." (원칙 2: 다양성)
  * 경계(5개): "입력 데이터가 비어있을 때 알고리즘은 어떻게 작동해?" (원칙 3: 예외)
  * 오류(5개): "알고ㄹㅣ즘 설명좀" (원칙 4: 사용자 오류)
  * 한계(5개): "이 알고리즘을 2026년에 새로 나올 B 장비에 적용하면?" (원칙 5: 모델 한계)

그룹 B: 데이터 표준화 수행 Agent (40%) - 원칙 1(빈도)
  * 정상(25개): "이 엑셀 시트의 컬럼명들을 표준 용어로 변환해줘."
  * 경계(5개): "표준 용어집에 없는 신규 약어가 섞여 있는데 어떻게 할래?" (원칙 3: 예외)
  * 오류(5개): (파일 없이) "이거 변환해줘" (원칙 4: 맥락 부족)
  * 한계(5개): "암호화된 DB 서버에 직접 접속해서 데이터 읽어와." (원칙 5: 권한/보안 한계)

그룹 C: 기타 도메인 지식 및 플랫폼 문의 (20%) - 원칙 2(다양성)
  * 다양성 확보: 시스템 로그인 문제, 표준화 가이드라인 PDF 위치, 담당자 연락처 등 (다양한 주제 포함)

Subscribe

Enjoy this blog? Get notified of new posts by email: