Place2Page의 프롬프트는 처음에는 그냥 문자열처럼 보였다. 하지만 실험이 늘어나자 진짜 문제는 좋은 문구를 찾는 것이 아니라 어떤 입력에서 어떤 후보안이 왜 이겼는지 다시 설명할 수 있는가였다. 그래서 임시로 바꾼 문구를 한 번의 요청으로 흘려보내지 않고 실험 묶음, 후보안, 판단 결과, 저장된 스냅샷, 기본값 반영으로 이어지는 기록 체계를 만들었다.
요약
- 문제: 프롬프트 실험을 복붙으로 관리하면 어떤 문구가 어떤 결과를 만들었는지 빠르게 추적하기 어려워졌다.
- 판단: 프롬프트 변경도 "좋아 보인다"가 아니라 기준안, 후보안, 판단 결과, 반영 여부로 남겨야 했다.
- 해결: 실험 묶음, 후보안, 판단 결과, 스냅샷, 기본값 반영 흐름을 하나의 기록 체계로 묶었다.
- 배운 점: 프롬프트도 코드처럼 실험, 기록, 비교, 반영 흐름이 있어야 운영 가능한 자산이 된다.
상황
Place2Page는 장소 정보를 받아서 랜딩페이지를 생성한다. 사용자는 Google Maps나 Naver Map URL을 넣고, 서비스는 장소 정보와 사진, 영업시간, 메뉴, 리뷰 같은 데이터를 바탕으로 HTML 페이지를 만든다.
처음에는 프롬프트를 고치는 일이 단순했다. 문구 하나를 바꾸고, 생성 결과를 몇 개 보면서 더 나아졌는지 판단하면 됐다. 그런데 시간이 조금 지나자 문제가 생겼다.
어떤 프롬프트로 만든 결과였는지 기억이 흐려졌다. A안이 좋았던 것 같은데, 정확히 어떤 문구였는지 다시 찾기 어려웠다. B안을 조금 수정한 B-안이 있었는데, 그게 실제 기본값에 반영된 건지 실험으로만 끝난 건지도 애매했다.
프롬프트 실험이 많아질수록 복붙과 기억에 의존하는 방식은 한계가 분명했다.
문제
프롬프트 실험에서 가장 위험한 상태는 "좋아 보인다"에서 멈추는 것이다.
예를 들어 어떤 프롬프트가 더 좋아 보였다고 해도, 아래 질문에 답하지 못하면 운영하기 어렵다.
- 어떤 장소 입력으로 실험했는가?
- 기준안은 무엇이었고, 바꾼 후보안은 무엇이었는가?
- 왜 이겼다고 판단했는가?
- 실패한 케이스는 무엇인가?
- 나중에 같은 프롬프트를 다시 선택할 수 있는가?
- 이 프롬프트가 지금 기본값인가?
프롬프트는 문자열이라 쉽게 바꿀 수 있다. 그래서 더 쉽게 흩어진다. 실험 결과가 코드 diff처럼 남지 않으면, 나중에는 "그때 그 문구"를 찾는 일이 디버깅이 된다.
처음 생각한 가설
처음에는 프롬프트 텍스트를 파일로 빼면 충분할 줄 알았다. 파일에 있으면 git diff가 남고, 리뷰도 할 수 있기 때문이다.
하지만 실제 실험은 파일만으로 끝나지 않았다. 같은 장소 입력을 다시 생성하거나, 임시 문구를 적용하거나, 같은 입력에 대해 여러 후보안을 비교해야 했다. 즉, 실험의 단위는 파일 하나가 아니라 "어떤 입력에 어떤 프롬프트를 적용했고, 그 결과를 어떻게 평가했는가"였다.
그래서 프롬프트 파일 관리보다 한 단계 위에 실험 관리가 필요했다.
확인한 증거
이 문제는 단순히 "나중에 관리하기 어려울 것 같다"는 느낌만은 아니었다. 실험을 반복하다 보니 기록해야 하는 표면이 자연스럽게 드러났다.
같은 문제는 하나의 실험 묶음으로 관리하고, 그 안에서 기준안과 후보안을 나눴다. 생성 결과를 본 뒤에는 어떤 후보안이 통과했고, 어떤 후보안은 보류하거나 다시 작업해야 하는지 판단 결과를 함께 남겼다.
더 중요한 단서는 테스트에 있다. 임시 프롬프트를 적용해 생성하더라도, 그 문자열이 요청 payload 안에만 남고 사라지지 않도록 했다. 테스트는 변경된 프롬프트가 스냅샷으로 저장되고, 실제 생성 요청은 raw 문자열 대신 저장된 스냅샷을 참조하는지 확인한다.
Experiment: copy quality check
Candidate: stricter grounding rule
Saved prompt snapshot exists
Generation request references the saved snapshot즉, 실험 문구가 요청 한 번짜리 문자열로 사라지는 것이 아니라 다시 선택하고 비교할 수 있는 기록으로 남는다.
나중에 현재 기본값을 확인했을 때도 같은 구조가 필요했다. 파일에 남은 기본 프롬프트와 실제 선택된 프롬프트가 다를 수 있기 때문이다. 이 확인이 없었다면 "파일은 그대로인데 왜 결과가 바뀌었나"를 설명하기 어려웠다.
해결한 방식
실험 단위를 명시했다.
- 실험 묶음: 하나의 문제를 검증하는 단위
- 후보안: 같은 문제 안에서 비교할 프롬프트 또는 설정
- 판단 결과: 결과에 대한 현재 판단
- 메모: 왜 그렇게 판단했는지 남기는 기록
예를 들어 "지도 embed가 있는데도 외부 지도 링크가 중복으로 나오는 문제"를 본다면 그 문제를 하나의 실험 묶음으로 잡는다. 그 안에서 기준안과 더 엄격한 후보안을 나누고, 결과를 보면서 통과, 재작업, 보류 같은 판단을 붙인다.
여기서 중요한 것은 프롬프트 스냅샷이었다.
임시 프롬프트를 넣고 실행하면, 그 문자열을 단순히 request payload에만 남기지 않는다. 기준이 되는 프롬프트를 복제하고, 바뀐 부분만 새 스냅샷으로 묶는다. 이후 생성 요청은 raw string이 아니라 저장된 스냅샷을 참조한다.
이렇게 하면 실험 당시의 프롬프트를 다시 열 수 있다. 수정할 수도 있고, 다른 입력에 다시 적용할 수도 있고, 괜찮으면 기본값으로 반영할 수도 있다.
왜 반영 흐름이 필요했나
프롬프트 실험에서 "이게 제일 나은 것 같다"는 순간이 와도 그걸 실제 기본값으로 바꾸는 흐름이 따로 있어야 한다.
그냥 파일을 고치거나 환경 변수를 바꾸면, 현재 무엇이 기본값인지 확인하기 어려워진다. 반대로 검증된 프롬프트 스냅샷을 기본값으로 반영하면, 다음 생성부터 그 기준이 적용되고 파일의 기본값은 fallback으로 남는다.
이 구조의 장점은 명확했다.
- 파일에 있는 기본 프롬프트와 운영 중인 기본 프롬프트를 구분할 수 있다.
- 검증된 실험 스냅샷을 기본값으로 반영할 수 있다.
- 다시 문제가 생기면 어떤 변경이 반영됐는지 추적할 수 있다.
- 현재 기본값을 별도로 확인할 수 있다.
프롬프트가 코드처럼 배포된다면, 배포 상태도 코드처럼 확인할 수 있어야 한다.
배운 점
이 작업을 하면서 프롬프트 엔지니어링을 보는 관점이 조금 바뀌었다.
처음에는 좋은 프롬프트 문장을 찾는 일이 핵심이라고 생각했다. 물론 문장 자체도 중요하다. 하지만 제품 안에서 더 중요한 것은 그 문장이 어떻게 실험되고, 어떤 기준으로 선택되고, 언제 기본값이 되었는지 추적할 수 있는가였다.
프롬프트 실험은 메모장에 남기면 쉽게 사라진다. 반대로 실험 묶음, 후보안, 판단 결과, 스냅샷, 반영 흐름으로 관리하면 운영 자산이 된다.
이제 Place2Page에서 프롬프트를 바꿀 때는 단순히 "프롬프트를 수정했다"고 말하지 않는다. 어떤 문제를 보고, 어떤 가설을 세웠고, 어떤 후보안이 어떤 이유로 이겼는지 같이 남긴다.
다음에는 이렇게 판단한다
프롬프트 실험을 다시 할 때는 문구가 마음에 드는지보다, 나중에 같은 결정을 재현할 수 있는지 먼저 본다. 기록되지 않은 prompt win은 운영 자산이 아니라 기억에 가까워진다.
앞으로 프롬프트 실험을 할 때는 최소한 아래는 남긴다.
- 문제 상황
- 실험 입력
- 기준안
- 후보안
- 판단 결과
- 실패한 케이스
- 기본값 반영 여부
프롬프트는 쉽게 바꿀 수 있어서 더 엄격하게 기록해야 한다. 복붙으로 시작할 수는 있지만, 운영은 복붙으로 오래 버티기 어렵다.
