프롬프트 엔지니어링 & 가드레일
엔터프라이즈 AI 제품의 프롬프트 설계, 버전 관리, 입출력 가드레일, 안전성 통제를 정리합니다.
모델을 잘 고르는 것보다 프롬프트를 어떻게 관리하느냐가 엔터프라이즈 AI 품질을 더 많이 결정합니다. 프롬프트는 코드의 일부이고, 가드레일은 프롬프트가 실패했을 때의 안전망입니다.
프롬프트 계층 구조
| 계층 | 역할 | 변경 빈도 | 관리 주체 |
|---|---|---|---|
| System prompt | 서비스 정체성, 행동 규칙, 제약 | 낮음 | 플랫폼 팀 |
| Context prompt | 사용자/테넌트별 문맥, 정책 | 중간 | 앱 로직 |
| User prompt | 실제 사용자 입력 | 높음 | 사용자 |
| Tool schema | 도구 설명, 입출력 계약 | 낮음 | 개발 팀 |
System Prompt 설계 원칙
| 원칙 | 설명 | 예시 |
|---|---|---|
| 역할 명시 | 모델의 역할과 경계를 선언 | "당신은 고객지원 상담사입니다. 의료 조언은 하지 않습니다." |
| 행동 제약 | 하면 안 되는 것을 명시적으로 나열 | "가격을 임의로 약속하지 않습니다." |
| 출력 형식 지정 | 응답 구조를 사전에 합의 | "JSON으로 응답합니다. 자유 텍스트를 섞지 않습니다." |
| 에스컬레이션 규칙 | 모르거나 확실하지 않을 때의 행동 정의 | "확실하지 않으면 상담원 연결을 안내합니다." |
| 데이터 경계 | 접근 가능한 정보 범위를 선언 | "제공된 context 밖의 정보를 추측하지 않습니다." |
const systemPrompt = `
당신은 ${companyName}의 고객지원 상담사입니다.
## 규칙
- 제공된 정책 문서와 FAQ만 근거로 답변합니다.
- 가격, 환불, 법적 판단을 임의로 약속하지 않습니다.
- 확실하지 않으면 "상담원 연결을 도와드리겠습니다"로 안내합니다.
- 개인정보를 요청하거나 반복하지 않습니다.
## 출력 형식
- 한국어로 답변합니다.
- 마크다운 형식을 사용합니다.
- 3문장 이내로 핵심을 먼저 전달합니다.
`입력 가드레일
| 가드레일 | 목적 | 구현 위치 |
|---|---|---|
| 길이 제한 | 토큰 비용 제어 | App layer |
| PII 감지 | 개인정보 유출 방지 | App layer |
| Injection 탐지 | prompt injection 차단 | App layer |
| Rate limiting | 남용 방지 | WAF / App layer |
| Content moderation | 유해 입력 차단 | 모델 또는 App |
function validateInput(input: string): { valid: boolean; reason?: string } {
if (input.length > 4000) {
return { valid: false, reason: 'input_too_long' }
}
if (detectPII(input)) {
return { valid: false, reason: 'pii_detected' }
}
if (detectInjectionPattern(input)) {
return { valid: false, reason: 'injection_suspected' }
}
return { valid: true }
}출력 가드레일
| 가드레일 | 목적 | 구현 방식 |
|---|---|---|
| Schema validation | 구조화된 출력 보장 | AI SDK structured output |
| Content filtering | 유해/부적절 출력 차단 | 후처리 필터 |
| Factuality check | hallucination 감소 | source attribution 강제 |
| Action boundary | 허용되지 않은 action 차단 | tool allowlist |
| Confidence threshold | 불확실한 응답 차단 | escalation trigger |
Structured Output으로 출력 계약 강제
엔터프라이즈에서는 자유 텍스트 응답보다 구조화된 출력 계약이 안전합니다.
import { generateText, Output } from 'ai'
import { z } from 'zod'
const ticketClassification = z.object({
category: z.enum(['billing', 'technical', 'account', 'other']),
severity: z.enum(['low', 'medium', 'high', 'critical']),
summary: z.string().max(200),
suggestedAction: z.enum(['auto-resolve', 'assign-agent', 'escalate']),
confidence: z.number().min(0).max(1),
})
const result = await generateText({
model: 'openai/gpt-5-mini',
output: Output.object({ schema: ticketClassification }),
prompt: `다음 고객 문의를 분류하세요: ${ticketText}`,
})
if (result.output.confidence < 0.7) {
await escalateToHuman(result.output)
}프롬프트 버전 관리
| 전략 | 장점 | 적합한 상황 |
|---|---|---|
| 코드 내 상수 | 배포와 함께 버전 | 소수 프롬프트 |
| 별도 파일/모듈 | 리뷰 가능 | 팀 단위 관리 |
| 환경 변수/설정 서비스 | 배포 없이 변경 | 빈번한 조정 |
| A/B 테스트 프레임워크 | 효과 측정 가능 | 성숙한 운영 단계 |
실무 해석
프롬프트를 DB나 CMS에 넣어 실시간으로 바꾸는 것은 편리하지만 위험합니다. 프롬프트 변경은 모델 변경만큼 영향이 크므로, 코드 리뷰와 동일한 수준의 검토를 거치는 편이 안전합니다.
가드레일 체인
입력부터 출력까지 가드레일을 체인으로 연결합니다.
MCP prompts를 활용한 재사용 가능한 프롬프트
MCP의 prompts primitive를 활용하면 반복적인 작업 템플릿을 서버에서 관리할 수 있습니다.
| 용도 | 예시 | 관리 주체 |
|---|---|---|
| 분석 보고서 형식 | "이 데이터를 주간 보고서로 요약해줘" | 운영 팀 |
| 고객 응대 템플릿 | "환불 안내 메시지를 작성해줘" | CS 팀 |
| 코드 리뷰 지침 | "이 PR을 보안 관점에서 검토해줘" | 개발 팀 |
안전성 계층
| 계층 | 통제 | 실패 시 영향 |
|---|---|---|
| 네트워크 | WAF / BotID로 악성 요청 차단 | 서비스 남용 |
| 입력 | PII, injection, length 필터 | 데이터 유출, 조작 |
| 모델 | system prompt 제약, tool allowlist | 의도치 않은 행동 |
| 출력 | schema validation, content filter | 부적절한 응답 |
| 실행 | Sandbox 격리, approval gate | 시스템 침해 |
ADR 스타일 결론
Decision
프롬프트는 코드 자산으로 관리하고, 가드레일은 입력-모델-출력-실행 전 구간에 배치합니다. 구조화된 출력 계약과 confidence threshold를 기본값으로 두어 자유 텍스트 응답의 위험을 줄입니다.
실무 체크리스트
- system prompt에 역할, 제약, 에스컬레이션 규칙이 명시돼 있는가
- 입력 가드레일(길이, PII, injection)이 App layer에 있는가
- 구조화된 출력이 필요한 경로에 schema contract가 있는가
- 프롬프트 변경이 코드 리뷰 프로세스를 거치는가
- confidence threshold 아래 응답에 fallback 경로가 있는가