지난 글에서 대화가 길어질 때 무엇을 접고 무엇을 남길지를 정했다면, 어느 경우에도 접히지 않고 매번 맨 앞에 실려 가는 블록이 하나 남는다. 시스템 프롬프트다.
이 블록은 두 가지 점에서 다른 메시지와 다르다. 첫째, 요청 한 번마다 예외 없이 실려 가므로 길이가 곧 고정 비용이다. 둘째, 캐시의 맨 앞이라 여기가 흔들리면 뒤가 전부 무효가 된다. 그래서 시스템 프롬프트 설계는 문장을 잘 쓰는 문제이기 전에 무엇을 어느 자리에 둘지 정하는 문제다.
여기 넣을 것과 넣지 말 것
기준은 하나다 — 요청마다 값이 달라지는가.
| 넣는다 | 넣지 않는다 |
|---|---|
| 역할과 담당 범위 | 이번 질문에만 해당하는 조건 |
| 항상 지켜야 할 규칙 | 검색해 온 문서 |
| 출력 형식과 도구 사용 원칙 | 오늘 날짜, 세션 상태 |
| 판단이 갈릴 때의 우선순위 | 사용자 이름 같은 개인 값 |
| 모르는 것을 만났을 때의 행동 | 자주 바뀌는 정책 표 |
오른쪽 아래 두 줄이 자주 어겨진다. 오늘 날짜와 사용자 이름은 시스템 프롬프트에 넣기 딱 좋아 보이지만, 그 순간 이 블록은 사용자마다 그리고 하루마다 달라지는 문자열이 된다. 캐시는 토큰 단위로 완전히 같아야 적중하므로 날짜 한 줄 때문에 자정마다 전체가 미스가 된다.
이런 값은 앞서 본 상태 블록처럼 뒤쪽에 붙인다. 위치가 뒤라고 무시되지 않는다 — 오히려 마지막 사용자 메시지에 가까울수록 더 잘 지켜진다.
순서가 캐시를 정한다
같은 내용이라도 배열에 따라 캐시 적중률이 완전히 달라진다. 원칙은 덜 바뀌는 것이 위로다.
이렇게 쌓으면 위의 세 층은 배포 전까지 한 글자도 바뀌지 않으므로 모든 사용자, 모든 요청에서 같은 앞부분을 공유한다. 사용자마다 다른 값은 경계 아래에 있어 캐시 대상 밖이지만, 크기가 작아 손해가 적다.
거꾸로 배열하면 어떻게 되는지가 더 중요하다. 맨 위에 "오늘은 2026년 8월 13일입니다" 한 줄을 두면 그 아래 수천 토큰이 전부 매일 새로 계산된다. 한 줄 때문에 전체가 무효가 되는 것이다.
규칙은 금지가 아니라 대체 행동으로 쓴다
내용으로 넘어가면 가장 효과가 큰 습관이 이것이다. 하지 말라는 문장은 그 상황이 왔을 때 무엇을 해야 하는지를 알려 주지 않는다.
- 약한 규칙 — "추측해서 답하지 마세요."
- 강한 규칙 — "근거를 찾지 못하면 '자료에서 확인되지 않습니다'라고 답하고, 어떤 자료를 찾아봤는지 적으세요."
앞쪽은 모델이 규칙을 지키려 할 때 갈 곳이 없다. 답을 안 할 수도 없으니 결국 추측하거나, 어색하게 회피한다. 뒤쪽은 대체 행동이 명시돼 있어 실행할 수 있다.
같은 이유로 "간결하게", "친절하게" 같은 형용사보다 관찰 가능한 조건이 낫다. "세 문단 이내", "코드가 필요하면 먼저 코드를 보이고 설명은 뒤에"처럼 적으면 어겼는지를 평가로 셀 수 있다. 셀 수 없는 규칙은 회귀 평가에도 걸리지 않는다.
길어질수록 덜 지켜진다
규칙을 늘리면 준수율이 함께 올라갈 것 같지만 실제로는 반대 방향으로 꺾이는 지점이 온다. 규칙이 스무 개를 넘어가면 서로 충돌하는 쌍이 생기고, 모델은 그중 무엇을 우선할지 모른다.
그래서 세 가지를 권한다.
우선순위를 명시한다. "안전 규칙 > 형식 규칙 > 문체 규칙"처럼 한 줄만 적어도 충돌 시 행동이 안정된다.
예외를 규칙 옆에 붙인다. 규칙 목록 아래에 예외를 모아 두면 모델이 둘을 연결하지 못한다. 예외는 해당 규칙 바로 다음 줄에 적는다.
안 지켜지는 규칙은 다른 곳으로 옮긴다. 프롬프트로 열 번 시도해도 안 되는 것은 대개 프롬프트로 안 되는 것이다. 출력 형식은 구조화된 출력 기능으로, 금지어는 후처리 검증으로 옮기는 편이 확실하다. 시스템 프롬프트는 지켜지면 좋은 것을 담는 자리이지 반드시 지켜져야 하는 것을 보장하는 장치가 아니다 — 반드시 지켜져야 하는 것은 코드로 막는다.
예시 한 쌍이 문단 열 개보다 낫다
설명으로 잘 전달되지 않는 것이 형식과 어조인데, 이 둘은 짧은 예시 하나로 거의 해결된다.
[예시]
질문: 지난달 환불 건수는?
답변: 확인된 자료가 없습니다. 조회 가능한 범위는 이번 분기이며,
지난달 데이터는 `billing.monthly` 권한이 필요합니다.
이런 예시는 문체·형식·거절 방식을 한꺼번에 보여 준다. 다만 예시에는 비용이 있다. 매 요청 실려 가는 토큰이고, 예시가 많으면 모델이 그 형식에 지나치게 고정된다. 두세 개면 충분하고, 가장 자주 틀리는 경우를 골라 넣는다. 잘하고 있는 것을 예시로 넣는 것은 자리 낭비다.
프롬프트도 코드처럼 다룬다
마지막은 운영 얘기다. 시스템 프롬프트는 배포물인데 자주 배포물처럼 취급되지 않는다. 관리자 화면에서 직접 고치고, 누가 언제 무엇을 바꿨는지 기록이 없고, 되돌릴 방법도 없다.
최소한 이 정도는 갖춰 두는 편이 낫다.
| 갖출 것 | 없으면 |
|---|---|
| 파일로 저장하고 형상 관리 | 어제와 무엇이 달라졌는지 모른다 |
| 버전 문자열을 응답 로그에 남기기 | 문제 재현이 안 된다 |
| 고정된 회귀 평가 묶음 | 한 줄 고쳐 다른 것이 깨진 줄 모른다 |
| 되돌리기 절차 | 나쁜 변경이 오래 남는다 |
세 번째가 핵심이다. 시스템 프롬프트 수정은 겉보기에 한 줄이지만 영향 범위가 전체다. "정중하게"를 "간결하게"로 바꿨더니 거절 문구가 짧아져 사용자가 이유를 알 수 없게 되는 식의 부작용이 흔하다. 그래서 자주 나오는 질문 스무 개 정도를 고정해 두고, 프롬프트를 고칠 때마다 그 스무 개를 다시 돌려 답이 어떻게 달라졌는지 비교한다. 자동 채점까지 갈 것도 없이, 이전 답과 나란히 놓고 눈으로 보는 것만으로도 큰 사고는 걸러진다.
정리하면 좋은 시스템 프롬프트는 짧은 프롬프트가 아니라 자리가 정해진 프롬프트다. 안 바뀌는 것이 위, 바뀌는 것이 아래. 규칙은 금지가 아니라 대체 행동으로. 반드시 지켜져야 하는 것은 프롬프트가 아니라 코드로. 그리고 고칠 때마다 같은 질문 묶음으로 확인한다. 이 넷이 있으면 프롬프트를 손보는 일이 도박이 아니라 배포가 된다.
읽어주셔서 감사합니다. 😊

