Documents
Home>Documents>AI>Agent>Geny

Prompt Management System: Dynamic Loading and Custom Prompts

5 min readFeb 8, 2026Feb 22, 2026

Prompt Management System – Dynamic Loading and Custom Prompts

Running multiple sessions in Claude Control quickly makes it clear that prompts need to be managed systematically. This post covers building a system that separates system prompts, task-specific prompts, and shared instructions into discrete files and loads them dynamically.

Prompt Directory Structure

prompts/
├── system/
│   ├── default.md          # 기본 시스템 프롬프트
│   ├── code_review.md      # 코드 리뷰 전용
│   └── refactoring.md      # 리팩토링 전용
├── tasks/
│   ├── frontend.md         # 프론트엔드 작업 지시
│   ├── backend.md          # 백엔드 작업 지시
│   └── testing.md          # 테스트 작업 지시
└── templates/
    ├── bug_fix.md           # 버그 수정 템플릿
    └── feature_add.md       # 기능 추가 템플릿

Implementing the Prompt Loader

from pathlib import Path
from string import Template
from typing import Dict, Optional
import yaml

class PromptManager:
    def __init__(self, prompts_dir: str):
        self.base_dir = Path(prompts_dir)
        self._cache: Dict[str, str] = {}
        self._metadata: Dict[str, dict] = {}

    def load_prompt(self, path: str, variables: dict = None) -> str:
        cache_key = f"{path}:{hash(frozenset((variables or {}).items()))}"
        if cache_key in self._cache:
            return self._cache[cache_key]

        file_path = self.base_dir / path
        if not file_path.exists():
            raise FileNotFoundError(f"프롬프트 파일 없음: {path}")

        content = file_path.read_text(encoding="utf-8")

        # YAML 프론트매터 파싱
        if content.startswith("---"):
            parts = content.split("---", 2)
            if len(parts) >= 3:
                self._metadata[path] = yaml.safe_load(parts[1])
                content = parts[2].strip()

        # 변수 치환
        if variables:
            content = Template(content).safe_substitute(variables)

        self._cache[cache_key] = content
        return content

    def get_system_prompt(self, role: str = "default") -> str:
        return self.load_prompt(f"system/{role}.md")

    def render_template(self, template_name: str, **kwargs) -> str:
        return self.load_prompt(f"templates/{template_name}.md", kwargs)

Per-Session Prompt Configuration

Each session can be configured with a different system prompt.

class SessionPromptConfig:
    def __init__(self, prompt_manager: PromptManager):
        self.pm = prompt_manager

    async def configure_session(self, session_id: str, config: dict):
        system_prompt = self.pm.get_system_prompt(
            config.get("role", "default")
        )
        task_prompt = ""
        if "task_type" in config:
            task_prompt = self.pm.load_prompt(
                f"tasks/{config['task_type']}.md"
            )
        combined = f"{system_prompt}\n\n{task_prompt}"
        await self._apply_to_session(session_id, combined)

Hot Reloading

The system also automatically invalidates the cache whenever a prompt file changes, using the watchdog library.

from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

class PromptWatcher(FileSystemEventHandler):
    def __init__(self, prompt_manager: PromptManager):
        self.pm = prompt_manager

    def on_modified(self, event):
        if event.src_path.endswith(".md"):
            self.pm._cache.clear()
            print(f"프롬프트 캐시 갱신: {event.src_path}")

Wrapping Up

Moving prompt management to a file-system-based layout unlocks version control. You can track prompt change history with Git and share prompts with teammates without friction. The dynamic loading is particularly useful — prompts can be updated without restarting the server.

Tags
prompt managementdynamic loadingtemplateAgentClaude