13.6.2 프롬프트 내 제약 조건 주입(System Prompt Constraints) 기법
거대하고 복잡한 엔터프라이즈 환경에서 시스템이 거대 언어 모델(LLM)에게 데이터 추출 지시를 내릴 때, 그 프롬프트(Prompt)는 결코 인간 대 인간 사이의 부드러운 ’부탁’이나 구어체 대화가 되어서는 안 된다. 그것은 한 치의 오차도 허용되지 않는 엄격한 법률적 **‘계약서(Contract)’**이자 기계적 **‘명령어 세트(Instruction Set)’**처럼 건조하고 날카롭게 작성되어야만 한다.
파이프라인 후방에 배치된 Pydantic 1단계 강타입 오라클(13.3절)이 스키마 위반을 확인하고 때려잡는 무자비한 그물망(Net)이라면, 전방의 시스템 프롬프트(System Prompt)에 주입하는 **‘제약 조건(Constraints)’**은 LLM 에이전트가 애초에 그 그물망에 걸려들 멍청한 짓을 하지 않도록 모델의 어텐션 신경망에 강제로 룰을 각인시키는 공학적 최면(Hypnosis)과 같다.
만약 후방 오라클 객체 스키마에 invoice_date 필드가 반드시 YYYY-MM-DD 형태여야만 한다고 엄격하게 정의되어 있다고 가정해 보자. 이를 전방의 프롬프트에서 막연하고 게으르게 “날짜 필드를 잘 찾아서 JSON으로 뽑아줘” 라고만 지시한 뒤 기적을 파라고 Pydantic 하나에만 방어망을 전적으로 의존하면 어떻게 될까?
백이면 백, 자유도가 높은 LLM은 그날의 확률적 기분이나 원본 문서의 텍스트 생김새에 휘둘려 January 1, 2023이나 2023.01.01, 혹은 01-01-23 같은 수천 가지의 더러운 변종 텍스트를 내뱉게 되고, 오라클은 쉴 새 없이 ValidationError를 격발시키며 인프라를 다운시킬 것이다.
따라서 최고 수준의 방어적 시스템 프롬프트는 후방 시스템 오라클이 요구하는 깐깐한 제약 조건들을 **거울처럼 정확하게 반사(Mirrored)**하여 모델의 컨텍스트 창(Context Window) 맨 앞단에 선제적이고 강력하게 주입(Injection) 해야만 한다.
1. 정형 스키마와 프롬프트 제약의 동기화(Synchronization) 주입
비정형 LLM 파이프라인 아키텍트들이 흔히 저지르는 가장 치명적인 실수는 “파이썬 코드 단에 적혀 있는 백엔드 제약 조건“과 “자연어 프롬프트에 적혀 있는 프론트 제약 조건“을 각기 다른 개발자가 파편적으로 관리하여, 프로젝트가 커짐에 따라 그 둘 사이의 **규칙 동기화가 서서히 어긋나는 것(Rule Drift)**이다. 이를 공학적으로 틀어막으려면 프롬프트를 반드시 구조적인 템플릿(Template)으로 디자인하고, 강압적이고 명시적인(Explicit) 제약 조건 블록을 박아 넣어야 한다.
[SYSTEM PROMPT]
당신은 거대 엔터프라이즈의 백엔드 트랜잭션을 책임지는 최고 수준의 재무 문서 데이터 역공학(Reverse Engineering) 에이전트다.
아래의 [절대 추출 규칙]을 단 하나라도 위반할 경우, 시스템 파이프라인은 치명적인 크래시(Crash)를 일으키며 트랜잭션은 즉시 폐기될 것이다.
[추출 규칙 및 제약 조건 - Constraints]
1. 날짜 포맷 강제 정규화 (Date Format Enforcement):
- 원본 이미지 문서에 "Feb 3rd, 2023" 또는 "03/02/23" 이라 기괴하게 적혀 있어도,
당신은 반드시 ISO 8601 표준 포맷인 `YYYY-MM-DD` 로만 뇌 내부에서 미리 변환하여 반환해라.
- 예시: "2023-02-03" (O) / "23/02/03" (X) / "Feb 3rd" (X)
2. 화폐 기호 배제 및 타입 캐스팅 준비 (Currency Symbol Stripping):
- `total_amount`를 비롯한 모든 금액 필드에는 절대로 달러 기호(`$`)나 원화 기호(`₩`), 단위 콤마(`,`)를 포함시키지 마라.
- 오직 파이썬 float() 함수로 즉시 캐스팅(Casting)이 가능한 순수한 수치형 스칼라만 반환해라.
- 예시: "1234.56" (O) / "$1,234.56" (X) / "1,234" (X)
3. 대소문자 강제 정규화 (Case Normalization):
- `vendor_name`과 주소 필드는 본래 문서가 소문자나 섞어서 썼든 상관없이 무시하고,
전부 대문자 영문(UPPERCASE)으로 강제 정규화하여 출력해라.
4. 어레이 깊이 제한 (Array Boundary Cap):
- 구매 품목(Line Items) 배열은 시스템 OOM 방지를 위해 최대 50개를 초과하여 추출할 수 없다.
50개를 넘어가는 항목은 과감히 버리고 거기서 배열을 닫아라.
2. 네거티브(Negative) 프롬프팅의 역설과 명시적 돌파 전략
초보 프롬프트 엔지니어들은 에러 방지를 위해 흔히 “절대 기호를 넣지 마라(Do NOT add symbols)” 또는 “오타를 내지 마라” 위주의 부정적(Negative) 프롬프트를 남발하곤 한다. 그러나 트랜스포머 아키텍처 기반의 LLM은 유명한 “코끼리를 절대 생각하지 마라“라는 심리적 역설(Pink Elephant Paradox)처럼, 하지 말라는 단어 그 자체의 토큰 어텐션(Attention)에 쏠려 도리어 금지된 행동을 환각으로 생성해 내는 부작용 경향이 매우 강하다.
방어적 제약 조건 주입의 진정한 공학적 핵심은, 단순히 금지를 지시하는 것이 아니라 위 프롬프트 예시처럼 **“그렇다면 금지 대신 정확히 어떻게 행동해야 하는가(Do this specific format instead)”**를 긍정문의 명시적인 수식과 극단적인 튜토리얼 예시(O/X)로 강력하게 정의하여 모델의 시야를 좁혀주는 데에 있다.
이와 같이 후방 시스템 오라클이 런타임에 잔인하게 요구할 그 깐깐한 제약 조건들을, 미리 자연어의 탈을 씌워 LLM 모델 프롬프트 전두엽에 쐐기처럼 확실하게 박아버리는 통제 전략.
이것이 제대로 먹혀들면 생성된 JSON 데이터가 1단계 구문 오라클에 부딪혀 산산조각 나는 아찔한 파이프라인 사고율(Error Rate)을 무려 90% 이상 드라마틱하게 곤두박질치게 만들 수 있다. 이는 곧 엔터프라이즈가 매월 지불해야 하는 쓸데없는 재호출 API 토큰 요금(Cost)의 폭발적 절약이자, 결제 트랜잭션 처리 속도의 혁신적인 성능(Latency) 단축을 의미하는 위대한 첫걸음이다.