Skip to content

[FEATURE] llmgen 프롬프트 외부 파일 런타임 로드 — 코드 재배포 없이 수정 가능 (이슈 #149 후속) #171

Description

@juhy0987

어떤 기능인가요?

현재 internal/crawler/parser/rule/llmgen/prompt.go 에 system / user 프롬프트가 Go 상수로 하드코딩 되어 있습니다. 프롬프트는 LLM 응답 품질의 가장 큰 lever 인데, 한 줄만 수정해도 다음이 모두 필요합니다:

  1. 코드 변경 → PR 생성 → 리뷰 → CI → 머지
  2. 바이너리 재빌드
  3. 전체 서비스 재배포

운영자 (또는 LLM ops 담당) 이 프롬프트를 자주 튜닝해야 하는 특성상 본 cycle 은 과도합니다. 본 이슈는 프롬프트를 외부 파일에서 런타임에 로드 하여 코드 재배포 없이 수정 가능하게 합니다.

목표 vs 현재 상태

현재 본 이슈 후
프롬프트 위치 Go 상수 (prompt.go:31-71) 외부 파일 (예: scripts/prompts/page.txt / list.txt / system.txt)
수정 시 필요 코드 변경 + PR + 재빌드 + 재배포 파일 수정 + 프로세스 재기동 (또는 reload trigger)
Git 추적 코드 diff 로 변경 확인 scripts/prompts/ 의 파일 diff 로 확인 (동일하게 git 관리)
검증 CI 의 build/test 시작 시 파일 존재 + non-empty 검증, 형식 검증 (placeholder 치환 가능 여부)

무엇을 하나요?

1. 디렉토리 구조 (scripts/prompts/)

이미 빈 디렉토리 + .gitkeep 만 존재. 본 이슈에서 다음 파일 추가:

scripts/prompts/
├── system.txt          # 모든 호출 공통 system prompt
├── page.user.txt       # TargetTypePage 용 user prompt 템플릿
└── list.user.txt       # TargetTypeList 용 user prompt 템플릿

user prompt 는 placeholder ({{HOST}}, {{TARGET_TYPE}}, {{HTML}}, {{MAX_HTML_BYTES}}) 를 포함하고 런타임에 치환.

2. PromptLoader 추상화

// internal/crawler/parser/rule/llmgen/loader.go (신규)

type PromptLoader interface {
    System() string
    UserForTarget(targetType storage.TargetType) (string, error)
}

// FilePromptLoader: 시작 시 1회 read + in-memory cache
type FilePromptLoader struct { ... }
func NewFilePromptLoader(dir string) (*FilePromptLoader, error)

// EmbedPromptLoader: 현재 하드코딩 프롬프트의 fallback (file 부재 시)
type EmbedPromptLoader struct{}

3. config 추가

// pkg/config/config.go LLMConfig
PromptDir string  // LLM_PROMPT_DIR (default "scripts/prompts")
  • LLM_PROMPT_DIR=\"\" 또는 디렉토리 부재 → EmbedPromptLoader fallback (graceful)
  • 디렉토리 존재 + 일부 파일 누락 → fail-fast (배포 누락 가시화)

4. BuildPrompt 변경

현재 prompt.go:25-29BuildPrompt 함수를 PromptLoader 의존성으로 변경:

func BuildPrompt(loader PromptLoader, host string, t storage.TargetType, html string) (system, user string, err error) {
    // loader.System() + loader.UserForTarget(t) 에서 placeholder 치환
}

5. Reload 정책 (선택)

본 이슈 1차 scope: 프로세스 재기동 시에만 reload. 운영자 워크플로:

  1. scripts/prompts/page.user.txt 수정 + commit + push
  2. 운영 환경에서 git pull + make restart

향후 (별도 이슈로 분리 가능):

  • SIGHUP 으로 reload — 단일 인스턴스 무중단 갱신
  • 또는 /admin/llm/prompts/reload API endpoint
  • 또는 file watcher (fsnotify)

본 이슈에서는 "파일 로드 인프라" 까지만. 동적 reload 는 별도 이슈로 추적 권장.

6. 테스트

  • FilePromptLoader: 정상 로드 / 파일 부재 / 빈 파일 / placeholder 미포함
  • EmbedPromptLoader: 항상 동일 프롬프트 반환
  • BuildPrompt: placeholder 치환 정확성 + HTML 잘림 동작 보존
  • Generator 통합: 외부 파일 vs embed 양쪽으로 정상 LLM 호출

의존 / 연관

영향 / 위험

효과

  • 프롬프트 튜닝 cycle 단축 — LLM 응답 품질 개선이 운영 액션으로 가능
  • 프롬프트가 git 추적되어도 코드 변경과 분리 → 리뷰 부담 ↓
  • A/B 테스트 가능 — 환경변수로 디렉토리 swap

위험

  • 잘못된 프롬프트 배포: 시작 시 placeholder 검증 + 형식 검증으로 1차 방어. 그래도 LLM 응답 품질 저하는 사후 metric 으로만 발견 가능
  • 파일 부재 / 권한 오류: fail-fast 또는 graceful fallback 정책 — 본 이슈에서 명시 결정 필요
  • 여러 인스턴스 일관성: 재기동 타이밍 차이로 일시적으로 다른 프롬프트 사용 가능. 무중단 reload 정책 도입 시 더 심각해짐 (별도 이슈)

롤백

  • LLM_PROMPT_DIR=\"\" 설정 → 즉시 EmbedPromptLoader (현재 하드코딩) 동작
  • 또는 본 PR revert

참고

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions