# 프롬프트 동적 관리 시스템 계획

**날짜**: 2025-12-25  
**최종 보강**: 2026-03-03  
**작성자**: happybell80, Claude  
**상태**: 완료 (2026-03-10 기준)

**원칙 참조**:
- `book/300_architecture/311_backend_coding_principles.md` (계층 분리, DB state 경유)
- `book/300_architecture/312_writing-principles.md`
- `book/300_architecture/315_테스트_원칙.md` (TDD)
- `book/300_architecture/313_Gemini_프롬프트_설계_원칙.md`

**관련 문서**:
- [프롬프트 DB 폐루프 및 하드코딩 잔존 전수 조사 리서치](../research/260319_프롬프트DB_폐루프_및_하드코딩_잔존_전수조사_리서치.md)
- [자기개선루프 프롬프트DB 23로컬24 통합실행기록](./260304_자기개선루프_프롬프트DB_23로컬24_통합실행기록.md)
- [프롬프트DB 응답생성 폐루프 미연결](../troubleshooting/260310_프롬프트DB_응답생성_폐루프_미연결.md)
- [프롬프트 DB 부분 도입 상태와 하드코딩 프롬프트 잔존](../troubleshooting/260311_prompt_db_partial_adoption_and_hardcoded_prompts.md)

---

## 1. 목표

하드코딩 프롬프트를 DB 정책으로 전환해 다음을 달성한다.
- 무중단 프롬프트 수정
- 사용자/로빙/작업별 개인화
- A/B 실험 기반 자동 개선
- 안전한 자동 승격 + 즉시 롤백

## 1-1. 문제 분리 원칙
- `폐루프`와 `전면 DB화`는 같이 가야 하지만 같은 문제는 아닙니다.
- `폐루프`는 `DB의 활성 프롬프트가 실제 응답 생성 전에 반영되는가`의 문제입니다.
- `전면 DB화`는 `운영 프롬프트 다수가 DB 버전 관리 경로로 이관됐는가`의 문제입니다.
- 순서는 `폐루프`를 먼저 닫고, 그 다음 `전면 DB화`를 닫는 것으로 둡니다.

---

## 2. 현재 상태 요약 (2026-03-19 갱신)

- 하드코딩 프롬프트: **24건** 확인 (P1: 3건, P2: 8건, P3: 13건). 상세 인벤토리·gap 분석은 [전수 조사 리서치](../research/260319_프롬프트DB_폐루프_및_하드코딩_잔존_전수조사_리서치.md) §3~5 참조.
- DB 골격(테이블·API·추적): 구현 완료. 운영 프롬프트 DB 적재: **0건**.
- 폐루프: 미연결. 주입 방식은 리서치 §6에서 확정 (`process_request()` 진입 시 `context` 주입, 기존 RAG 검증 패턴 재사용).
- 현재 문제:
  - 프롬프트 수정 시 코드 배포 필요
  - DB 버전을 바꿔도 실제 응답에 반영 안 됨
  - 회귀 시 신속 롤백이 어려움

---

## 3. 범위

### 포함
- `rb8001` LLM 경로의 시스템/작업별 프롬프트
- 감정 기반 동적 규칙(톤/길이/금지 규칙)
- A/B 실험, 메트릭 수집, 자동 승격 정책

### 제외
- 모델 자체 교체/튜닝
- 감정 분류기 모델 재학습
- 인프라 토폴로지 변경

## 3-1) 로컬 개발자 실행 경계 (필수)

### 로컬 개발자가 할 수 있는 것
- 프롬프트 DB 스키마/Repository/Service/API 구현
- `/api/message` 경로의 프롬프트 버전 선택/이벤트 자동 기록 코드 구현
- 단위 테스트/로컬 통합 테스트 작성 및 실행
- 실험 게이트/승격 조건 로직 문서화 및 코드 반영

### 로컬 개발자가 할 수 없는 것 (운영 확인 필요)
- 23 게이트웨이 프록시 경로/메서드 운영 반영 확정
- 운영 DB 실적재/지표 집계 기반 자동 승격 최종 판정
- 실사용 트래픽 기준 품질/안전/비용 게이트 충족 판정
- 운영 경유 폐루프 E2E 최종 승인

### 완료 판정 규칙
- 로컬 완료: 코드/테스트/문서 기준 충족
- 운영 완료: 서버 반영 + 운영 경유 E2E + 지표 검증 완료
- 운영 증거 없이는 "프롬프트 DB화 완료"로 보고하지 않는다

## 3-2. 자기개선 루프 계획과의 운영 원칙 (설계 병행, 실행 분리)
- 설계는 병행:
  - 프롬프트 DB화와 자기개선 루프 DB를 `run_id` 기준으로 함께 설계한다.
- 실행은 분리:
  - 1차 릴리스는 루프 수집/판정 골격을 우선 적용한다.
  - 프롬프트 자동 승격/롤백은 기준선 데이터 확보 후 2차로 적용한다.
- 운영 모드:
  - 초기 기본값은 `자동 제안 + 사람 승인`으로 고정한다.
- RAG 적용 원칙:
  - RAG는 필수 선행조건이 아니다.
  - 1차는 반성/정책 업데이트 보조 용도로만 제한 적용한다.
  - 프롬프트 승격/롤백의 최종 판정은 기존 정량 지표와 게이트 규칙을 우선한다.
- 연결 문서:
  - [자기개선 루프 DB/서비스 구현 실행계획](./260303_자기개선루프_DB_구현_실행계획.md)

---

## 4. 데이터 모델 (최소 스키마)

### 4.1 `prompt_templates`
- purpose: 프롬프트 논리 단위(식별자)
- columns:
  - `id`, `template_key` (unique)  
  - `prompt_type` (`system`, `chat`, `extract`, `calendar_confirm`, ...)
  - `scope_level` (`global`, `robeing`, `task`, `user`)
  - `scope_id` (nullable)
  - `is_enabled`
  - `created_at`, `updated_at`

### 4.2 `prompt_versions`
- purpose: 템플릿 버전 이력
- columns:
  - `id`, `template_id`, `version`
  - `content` (TEXT/JSONB)
  - `variables_schema` (JSONB)
  - `status` (`draft`, `candidate`, `active`, `retired`)
  - `created_by`, `change_reason`, `created_at`

### 4.3 `prompt_experiments`
- purpose: 실험 라우팅
- columns:
  - `id`, `experiment_key`, `template_key`
  - `control_version_id`, `treatment_version_id`
  - `traffic_ratio` (예: 90:10)
  - `guardrail_config` (JSONB)
  - `status` (`running`, `paused`, `completed`)
  - `started_at`, `ended_at`

### 4.4 `prompt_events`
- purpose: 요청 단위 결과 로그
- columns:
  - `id`, `request_id`, `template_key`, `version_id`, `variant`
  - `execution_mode` (`safe_prod`, `strict_observe`)
  - `user_id`, `robeing_id`, `task_type`
  - `latency_ms`, `tokens_in`, `tokens_out`
  - `feedback_signal` (up/down/reask/abandon)
  - `created_at`

### 4.5 `prompt_metrics_daily`
- purpose: 일별 집계
- columns:
  - `date`, `template_key`, `version_id`, `variant`
  - `success_rate`, `reask_rate`, `fallback_rate`
  - `avg_latency_ms`, `avg_tokens`, `feedback_score`

---

## 5. 런타임 병합 규칙

### 5.1 해석 순서 (낮음 → 높음 우선순위)
1. Global
2. Robeing
3. Task
4. User
5. Runtime modifier(감정/리스크 규칙)

### 5.2 병합 원칙
- 문자열 덮어쓰기 금지, 섹션 단위 병합(`role`, `constraints`, `output_format`)
- 금지 규칙(예: 민감 라벨 직접 노출 금지)은 항상 최종 승자
- 해석 실패 시 처리 규칙:
  - `safe_prod`: 즉시 fallback (최근 stable active 버전)
  - `strict_observe`: fallback 금지, 오류 노출 + 구조화 로그 기록

---

## 6. 자동 개선 정책

### 6.1 실험 지표
- 1차 품질 지표:
  - `reask_rate` (낮을수록 좋음)
  - `negative_feedback_rate` (낮을수록 좋음)
  - `task_success_rate` (높을수록 좋음)
- 2차 비용/성능 지표:
  - `avg_latency_ms`
  - `avg_tokens`
  - `fallback_rate`

### 6.2 자동 승격 조건(예시)
- 최소 샘플 수 충족 (예: n >= 500)
- treatment가 control 대비:
  - `reask_rate` 10% 이상 개선
  - `negative_feedback_rate` 악화 없음
  - `latency_ms` 15% 이상 악화 없음
- guardrail 위반 0건

### 6.3 자동 롤백 조건
- 15분 윈도우 기준 `fallback_rate` 급증
- `negative_feedback_rate` 임계치 초과
- 5xx/파싱 실패 연쇄 발생

### 6.4 운영 모드
- 기본: **자동 제안 + 사람 승인 후 승격**
- 완전 자동 승격은 초기 1개월 관찰 후 제한 적용

---

## 7. 구현 단계

### Phase 1: 인벤토리/스키마/TDD
- ~~하드코딩 프롬프트 인벤토리 확정(파일/함수/용도)~~ → **완료** (2026-03-19, 24건. [리서치 §3](../research/260319_프롬프트DB_폐루프_및_하드코딩_잔존_전수조사_리서치.md))
- `prompt_templates`, `prompt_versions` 생성 → **완료** (`prompt_management_repository.py`)
- Repository + Service 뼈대 구현 → **완료** (`prompt_db_endpoint.py`, `message_tracking_service.py`)
- 테스트: 스키마 생성/버전 조회/활성 버전 선택 → **완료**

### Phase 2: 읽기 경로 전환 (safe fallback)
- `llm_service.py`의 `process_request()` 진입 시 DB 활성 프롬프트 조회 → `context['system_instruction']` + `context['skip_default_prompt']` 주입. 상세는 [리서치 §6](../research/260319_프롬프트DB_폐루프_및_하드코딩_잔존_전수조사_리서치.md).
- 캐싱: 모듈 레벨 dict, TTL 60초. 상세는 [리서치 §9](../research/260319_프롬프트DB_폐루프_및_하드코딩_잔존_전수조사_리서치.md).
- 기존 하드코딩은 fallback로 유지 (`safe_prod` 모드)
- 테스트:
  - DB 성공 시 DB 프롬프트 사용
  - DB 실패 시 하드코딩 fallback
  - `strict_observe`에서 DB 실패 시 fallback 없이 오류/로그 노출 검증

### Phase 3: 병합/개인화/감정 modifier
- scope 병합 및 변수 치환
- 감정/리스크 modifier를 DB 규칙으로 부분 이관
- 테스트:
  - 병합 우선순위
  - 금지 규칙 우선 적용

### Phase 4: 실험/메트릭/자동 승격
- `prompt_experiments`, `prompt_events`, `prompt_metrics_daily` 구현
- 실험 라우터 + 승격/롤백 워커 구현
- 테스트:
  - 라우팅 결정 일관성
  - guardrail 위반 시 자동 중단/롤백

---

## 8. 수용 기준 (Definition of Done)

- 하드코딩 의존 프롬프트 80% 이상 DB 전환
- 핵심 UX 경로(chat/summarize/extract/calendar_confirm) DB 관리
- 실험 대시보드에서 버전별 지표 확인 가능
- 자동 롤백 시나리오 리허설 1회 이상 통과
- 장애 시 서비스 무중단 fallback 확인
- `strict_observe` 경로에서 의도적 실패가 로그/메트릭으로 누락 없이 수집되는지 검증

---

## 9. 위험과 대응

- 위험: 잘못된 프롬프트 배포로 UX 악화
  - 대응: candidate → canary → full 단계적 승격
- 위험: 과도한 개인화로 톤 불안정
  - 대응: immutable safety constraints 최종 적용
- 위험: 지표 왜곡/샘플 편향
  - 대응: 최소 샘플 수 + 시간/사용자 분산 조건

---

## 10. 운영 권고안 (초기 4주)

### 10.1 운영 모드
- 기본 모드: `자동 제안 + 사람 승인 후 승격`
- 완전 자동 승격은 안정화 4주 후, low-risk 템플릿부터 제한 허용

### 10.2 배포/승격 절차
- 단계 고정: `draft -> candidate -> canary(10%) -> full`
- canary 승격 최소 조건:
  - 최소 샘플 수 `n >= 500`
  - 최소 관찰 시간 `>= 24h`
  - guardrail 위반 0건

### 10.3 실시간 가드레일 (권장 임계치)
- `reask_rate`가 control 대비 10%p 이상 악화 시 즉시 중단
- `negative_feedback_rate`가 control 대비 5%p 이상 악화 시 롤백
- `fallback_rate`가 15분 윈도우에서 2배 이상 급증 시 롤백
- 5xx/파싱 실패 연쇄 발생 시 즉시 kill-switch

### 10.4 DB 운영 권장
- `prompt_events`는 일 단위 파티셔닝 권장
- 보관 주기: 원천 이벤트 30~90일, 집계 메트릭 장기 보관
- 필수 인덱스:
  - `(template_key, version_id, created_at)`
  - `(experiment_key, status)`
  - `(date, template_key, version_id)` on daily metrics
- 권한 분리:
  - 편집 권한(템플릿 수정)과 승격 권한(활성화/롤백) 분리

### 10.5 관측/알람
- 대시보드 3축:
  - 품질: reask, feedback, task_success
  - 성능: latency, tokens
  - 안정성: fallback, parse_fail, 5xx
- 알람 윈도우 이중화:
  - 15분 단기(즉시 대응)
  - 1시간 중기(추세 확인)

### 10.6 복구/감사
- 승격마다 `change_reason`, `approver`, `experiment_key` 필수 기록
- 월 1회 롤백 드릴 수행 및 결과 문서화

### 10.7 폴백 규율 (핵심)
- 원칙: 폴백은 사용자 보호용 최후 수단이며, 개선 신호를 가리는 기본 경로가 되어서는 안 된다.
- 이중 운영 모드:
  - `safe_prod`: 일반 사용자 트래픽, 제한적 폴백 허용
  - `strict_observe`: 카나리/내부 검증 트래픽, 폴백 금지(오류를 노출해 개선 포인트 확보)
- 필수 로그 스키마(폴백/실패 공통):
  - `request_id`, `template_key`, `version_id`, `variant`
  - `route`, `fallback_reason`, `upstream_error_code`, `upstream_error_message`
  - `latency_ms`, `created_at`
- 운영 규칙:
  - `strict_observe` 비율은 최소 5~10% 유지
  - 폴백 비율이 기준치 초과 시 실험 승격 금지
  - 로그 없는 폴백 경로는 배포 금지

---

## 11. 체크리스트 (누락 방지)

### 설계
- [x] 실제 하드코딩 인벤토리 목록화 완료 (2026-03-19, 24건 확인. [전수 조사 리서치](../research/260319_프롬프트DB_폐루프_및_하드코딩_잔존_전수조사_리서치.md) §3)
- [ ] scope 우선순위 충돌 규칙 정의
- [ ] 안전 제약(금지 규칙) 최종 우선순위 명시

### 구현
- [ ] Repository/DDL/Service 동시 반영
- [ ] fallback 경로(코드/DB) 모두 테스트
- [ ] 실험 라우팅 deterministic 보장
- [ ] `safe_prod` vs `strict_observe` 모드별 실패 처리 테스트
- [ ] 실패 이벤트의 구조화 로그 스키마(`execution_mode` 포함) 검증

### 운영
- [ ] 승격/롤백 가드레일 임계값 합의
- [ ] 대시보드/알람 연결
- [ ] 장애 훈련(롤백 drill) 수행
- [ ] `strict_observe` 트래픽 비율(최소 5~10%) 운영 중인지 확인
- [ ] 폴백 이벤트 필수 로그 스키마 누락 없는지 점검
- [ ] 로그 없는 폴백 경로가 없는지 배포 전 검증

---

## 12. 2회 점검 기록 (2026-03-03)

### 1차 점검에서 추가한 누락 항목
- 자동 승격 조건(샘플 수/가드레일)
- 자동 롤백 조건
- 이벤트/메트릭 테이블 분리
- 현재 하드코딩 인벤토리 규모 반영

### 2차 점검에서 확정한 항목
- scope 병합 우선순위 명시
- immutable safety constraints 최종 우선 적용 명시
- DoD/운영 체크리스트/리허설 항목 추가

---

## 13. 운영 기본값 표 (초기값)

| 항목 | 기본값 | 비고 |
|---|---:|---|
| canary 비율 | 10% | high-risk 템플릿은 5% |
| 최소 샘플 수 | 500 | 승격 전 필수 |
| 최소 관찰 시간 | 24h | 승격 전 필수 |
| reask_rate 가드레일 | +10%p | control 대비 악화 시 중단 |
| negative_feedback_rate 가드레일 | +5%p | control 대비 악화 시 롤백 |
| fallback_rate 가드레일 | 2x | 15분 윈도우 급증 시 롤백 |
| 오류 가드레일 | 5xx/파싱 실패 연쇄 | 즉시 kill-switch |

---

## 14. DDL/롤백 SQL 예시

### 14.1 생성 DDL (요약)
```sql
CREATE TABLE IF NOT EXISTS prompt_templates (
  id BIGSERIAL PRIMARY KEY,
  template_key TEXT NOT NULL UNIQUE,
  prompt_type TEXT NOT NULL,
  scope_level TEXT NOT NULL,
  scope_id TEXT,
  is_enabled BOOLEAN NOT NULL DEFAULT TRUE,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS prompt_versions (
  id BIGSERIAL PRIMARY KEY,
  template_id BIGINT NOT NULL REFERENCES prompt_templates(id),
  version INT NOT NULL,
  content JSONB NOT NULL,
  variables_schema JSONB,
  status TEXT NOT NULL,
  created_by TEXT,
  change_reason TEXT,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE(template_id, version)
);

CREATE TABLE IF NOT EXISTS prompt_experiments (
  id BIGSERIAL PRIMARY KEY,
  experiment_key TEXT NOT NULL UNIQUE,
  template_key TEXT NOT NULL,
  control_version_id BIGINT NOT NULL REFERENCES prompt_versions(id),
  treatment_version_id BIGINT NOT NULL REFERENCES prompt_versions(id),
  traffic_ratio JSONB NOT NULL,
  guardrail_config JSONB NOT NULL,
  status TEXT NOT NULL,
  started_at TIMESTAMPTZ,
  ended_at TIMESTAMPTZ
);

CREATE TABLE IF NOT EXISTS prompt_events (
  id BIGSERIAL PRIMARY KEY,
  request_id TEXT NOT NULL,
  template_key TEXT NOT NULL,
  version_id BIGINT NOT NULL REFERENCES prompt_versions(id),
  variant TEXT NOT NULL,
  user_id TEXT,
  robeing_id TEXT,
  task_type TEXT,
  latency_ms INT,
  tokens_in INT,
  tokens_out INT,
  feedback_signal TEXT,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS prompt_metrics_daily (
  date DATE NOT NULL,
  template_key TEXT NOT NULL,
  version_id BIGINT NOT NULL REFERENCES prompt_versions(id),
  variant TEXT NOT NULL,
  success_rate DOUBLE PRECISION,
  reask_rate DOUBLE PRECISION,
  fallback_rate DOUBLE PRECISION,
  avg_latency_ms DOUBLE PRECISION,
  avg_tokens DOUBLE PRECISION,
  feedback_score DOUBLE PRECISION,
  PRIMARY KEY (date, template_key, version_id, variant)
);

CREATE INDEX IF NOT EXISTS idx_prompt_events_template_ver_time
  ON prompt_events(template_key, version_id, created_at);
CREATE INDEX IF NOT EXISTS idx_prompt_experiments_key_status
  ON prompt_experiments(experiment_key, status);
CREATE INDEX IF NOT EXISTS idx_prompt_metrics_daily_lookup
  ON prompt_metrics_daily(date, template_key, version_id);
```

### 14.2 롤백 SQL (요약)
```sql
-- 1) active 버전 되돌리기(예: status 전환 정책 사용 시)
-- UPDATE prompt_versions SET status='active' WHERE id=:stable_version_id;
-- UPDATE prompt_versions SET status='retired' WHERE id=:bad_version_id;

-- 2) 실험 즉시 중단
-- UPDATE prompt_experiments SET status='paused', ended_at=NOW() WHERE experiment_key=:exp_key;

-- 3) 필요 시 인덱스/테이블 제거 (개발환경 한정)
-- DROP INDEX IF EXISTS idx_prompt_events_template_ver_time;
-- DROP TABLE IF EXISTS prompt_metrics_daily, prompt_events, prompt_experiments, prompt_versions, prompt_templates;
```

---

## 15. RACI (책임 체계)

| 역할 | 책임 |
|---|---|
| Author | 프롬프트 초안 작성, 변경 사유 기록 |
| Reviewer | 품질/톤/안전 제약 검토 |
| Approver | canary/full 승격 승인 |
| Operator | 알람 대응, kill-switch, 롤백 실행 |

운영 원칙:
- 편집 권한과 승격 권한은 분리한다.
- 야간/주말 온콜 Operator를 지정한다.

---

## 16. 운영 캘린더

- 일일:
  - 15분/1시간 알람 리뷰
  - 전일 대비 품질/성능/안정성 지표 점검
- 주간:
  - 실험 결과 리뷰
  - candidate -> canary -> full 승격 회의
- 월간:
  - 롤백 드릴 1회
  - 가드레일 임계값 재평가

---

## 17. 변경 이력 템플릿

```text
change_id:
template_key:
from_version:
to_version:
experiment_key:
change_reason:
expected_impact:
approver:
applied_at:
rollback_target_version:
post_check_result:
```

---

## 18. 장애 런북 (요약)

1. 알람 감지: 가드레일 위반 확인(15분/1시간 동시 확인)  
2. 즉시 차단: experiment `paused` + kill-switch 적용  
3. 안정화: stable active 버전 강제 전환  
4. 검증: 핵심 API smoke + 주요 UX 시나리오 3건 확인  
5. 사후 분석: 원인/영향/재발 방지 항목 기록

운영 목표:
- 장애 감지 후 15분 내 안정 버전 복귀

---

## 19. 일정 추정 (체크용)

### 19.1 빠른 현실 기준
- 30분: 설계/문서 확정, 작업 분할, 체크리스트 고정 (구현 완료 불가)
- 4~6시간: MVP 골격 구현
  - 스키마/Repository/기본 조회·병합
  - `safe_prod`/`strict_observe` 분기 연결
  - 핵심 단위 테스트 일부
- 1~2일: 기능 완료 + 테스트 통과
  - LLM 경로 연동(`llm_service`, `gemini_handler`)
  - fallback/strict_observe 동작 검증
  - TDD + 핵심 E2E
- 4~5일: 운영 안정화 포함
  - canary(5~10%) 관측
  - 가드레일 임계치 보정
  - 롤백 드릴 수행

### 19.2 마일스톤
- M1 (반나절): DB 스키마 + Repository 초안
- M2 (1일): 런타임 병합 + 모드 분기 동작
- M3 (2일): 테스트/검증 완료, canary 시작 준비
- M4 (4~5일): canary 관측 + 운영 기준 확정

---

## 참고 문서
- `book/300_architecture/311_backend_coding_principles.md`
- `book/300_architecture/312_writing-principles.md`
- `book/300_architecture/315_테스트_원칙.md`
- `book/300_architecture/313_Gemini_프롬프트_설계_원칙.md`
- `journey/troubleshooting/250806_happybell80_동적프롬프트구현.md`
