본문 바로가기
Databricks/MLflow

Databricks LLMOps ② Prompt Registry — UC 기반 프롬프트 버전·alias 관리

by 여행을 떠나자! 2026. 7. 6.

📚 시리즈 — Databricks LLMOps

 

 

 

1. 개요

Managed MLflow Prompt Registry는 MLflow 3 기반에서 LLM 프롬프트를 Unity Catalog 에 등록·버전 관리하고, alias(예: dev)로 운영에 연결하는 기능입니다.

프롬프트를 애플리케이션 코드 안에 하드코딩하지 않고 중앙에서 관리하는 것이 핵심 가치입니다.

  • 버전(version) 은 불변(immutable) 스냅샷으로 쌓이고, alias 는 특정 버전을 가리키는 가변(mutable) 포인터라서, 운영 중에도 재배포 없이 프롬프트를 교체할 수 있습니다(자세한 정의는 §2).
  • UI 에서 프롬프트를 조회·수정할 수 있어 비개발자도 협업이 가능합니다.
  • 평가(mlflow.genai.evaluate)·lineage 와도 연결됩니다.

상태: 본 기능은 Beta 입니다. workspace admin 이 Previews 페이지에서 활성화해야 사용할 수 있습니다(§3.1).

 

2. 핵심 개념

개념 설명
Prompt Unity Catalog 안의 명명 엔티티(named entity). 이름은 catalog.schema.prompt_name 3단계
Version 등록할 때마다 자동 증가하는 불변(immutable) 버전 (1, 2, 3 …)
Alias 특정 버전을 가리키는 가변(mutable) 별칭 (이름 자유, 예: dev). 운영은 alias 로만 로드
Template variable {{변수명}} 형태의 이중 중괄호(double-brace) 문법. 예: {{question}}. .format() 으로 주입
Format Text(완성형 단일 문자열) 또는 Chat(role 기반 메시지) 두 형식 저장 가능
  • 프롬프트 URI 형식: prompts:/{catalog}.{schema}.{prompt_name}@{alias} (alias) 또는 prompts:/{...}/{version} (버전 고정).
  • 버전 vs alias 사용 기준: 운영 코드는 버전 번호를 직접 쓰지 말고 alias 를 쓰는 것이 권장됩니다. 그래야 dev alias 만 새 버전으로 옮겨 무중단 교체가 됩니다.
  • Text vs Chat 형식 ≠ 스트리밍: 형식 축(Text/Chat)은 프롬프트 저장 형태이고, 에이전트의 스트리밍 여부와는 무관합니다. 본 가이드 예시는 Text 템플릿 + 스트리밍 에이전트 조합입니다.

Text vs Chat 형식 상세: 두 형식 모두 {{변수}} 자동 추출은 동일하지만(.format() 호출 방식 동일), 반환 타입이 다릅니다.

  • Text — 하나의 완성된 문자열 템플릿입니다. .format()문자열(str) 을 돌려줍니다. system 지시문이나 단일 프롬프트에 적합합니다.

python register_prompt(name=P, template="당신은 화물 어시스턴트입니다. 질문: {{question}}") loaded.format(question="운임이 왜 올랐나요?") # -> "당신은 화물 어시스턴트입니다. 질문: 운임이 왜 올랐나요?" ← 반환 타입: 문자열 1개(str)

  • Chatrole/content 메시지의 리스트를 템플릿으로 저장합니다. .format() 은 변수를 채운 메시지 리스트(list[dict]) 를 돌려주므로 LLM 의 messages 인자에 바로 넣습니다. 역할 분리(system+user)나 few-shot 예시까지 버전 관리하고 싶을 때 적합합니다.

python register_prompt(name=P, template=[ {"role": "system", "content": "당신은 A항공 화물(Cargo) 어시스턴트입니다. 한국어로 답합니다."}, {"role": "user", "content": "{{question}}"}, ]) loaded.format(question="운임이 왜 올랐나요?") # -> [{"role": "system", "content": "당신은 A항공 화물(Cargo) 어시스턴트입니다. 한국어로 답합니다."}, # {"role": "user", "content": "운임이 왜 올랐나요?"}] ← 반환 타입: 메시지 리스트(list[dict])

운영 RAG 에이전트(§5)는 system 문자열만 떼어 직접 messages 를 구성하므로 Text 를 쓰고, 메시지 구조 자체(역할·few-shot)를 버전 관리하려면 Chat 을 택합니다.

 

3. 전제 조건과 활성화

 

3.1 Preview 활성화 (Managed MLflow Prompt Registry)

기능이 꺼져 있으면 등록·로드 호출이 모두 FEATURE_DISABLED 오류로 실패하므로, 아래에서 먼저 활성화합니다.

활성화 (workspace admin):

  1. workspace 우상단 본인 이름 → Settings
  2. 좌측 Previews (= "Manage Databricks previews")
  3. 검색창에 prompt 입력 → "Managed MLflow Prompt Registry" (Beta) 토글 → On
  4. 반영까지 수 분 소요 ("Preview enablement changes take effect in a few minutes")

 

3.2 소프트웨어·실험·권한

항목 요건
MLflow pip install -U "mlflow[databricks]>=3.1.0" (검증 환경 3.14.0)
MLflow Experiment 프롬프트가 연결될 experiment 1개 (mlflow.set_experiment(...))
Unity Catalog 스키마 권한 프롬프트를 저장할 스키마에 CREATE FUNCTION·EXECUTE·MANAGE

권한이 스키마 레벨인 이유: 프롬프트는 UC 안에서 function 류 엔티티로 저장되므로 접근 통제가 스키마 레벨에서 이뤄집니다. 로드의 최소 권한은 카탈로그 USE CATALOG + 스키마 USE SCHEMA·EXECUTE 입니다.

 

4. 빠른 시작 — 등록·로드·변수 주입

 

4.1 UI 에서 조회·관리

메뉴 위치: workspace 좌측 Experiments → 해당 experiment(예 css_cmarket_rag_agent_exp) 진입 → 상단 "Prompts" 탭. (UC 엔티티이므로 Catalog Explorer 의 해당 스키마에서도 확인됩니다.)

UI 에서 가능한 작업:

작업 방법
생성 Prompts 탭 → new prompt 버튼 → Choose 로 UC 스키마 선택 → 이름 입력 → 형식 Text/Chat 선택 → 내용 작성({{변수}} 사용) → commit message → Create
새 버전 프롬프트 옆 new version 버튼 → 수정 → Save (편집 = 새 버전 생성, 기존 버전은 불변)
버전 비교 프롬프트 이름 클릭 → 좌상단 Compare
alias 관리 프롬프트 상세에서 alias 지정/이동

아래 화면은 §4.2 에서 등록한 프롬프트 <catalog>.<schema>.cargo_market_assistant 의 상세 뷰입니다. 좌측 버전 목록(Version 1), 우측 활성 버전(v1)의 alias(@dev)·commit message(cargo cmarket assistant - 초기 버전)·템플릿 본문이 한 화면에 보입니다. 우상단 Create prompt version(새 버전), Compare(버전 비교), alias 편집 진입점도 같은 화면에 있습니다. (Prompts 탭의 프롬프트 목록은 우상단 Select a schema 로 스키마를 골라야 표시됩니다.)

 

4.2 코드로 등록·로드

먼저 대상 스키마에 권한을 부여합니다. 등록·alias 지정은 쓰기 작업이라 CREATE FUNCTION·EXECUTE·MANAGE 가 필요합니다(§3.2). main.default 처럼 권한 없는 스키마면 등록이 실패합니다.

GRANT USE CATALOG ON CATALOG <catalog> TO `<principal>`;
GRANT USE SCHEMA, CREATE FUNCTION, EXECUTE, MANAGE ON SCHEMA <catalog>.<schema> TO `<principal>`;
-- <principal> = 사용자 이메일 또는 Service Principal application-id

이어서 등록·로드:

import mlflow
mlflow.set_tracking_uri("databricks")     # 고정값 — Databricks 호스팅 MLflow tracking 서버 사용
mlflow.set_registry_uri("databricks-uc")  # 고정값 — databricks-uc 로 설정해야 UC 에 등록됩니다

PROMPT = "<catalog>.<schema>.cargo_market_assistant"   # catalog.schema.name

# 1) 등록 — 화물 시장운임(cmarket) 어시스턴트 템플릿 + {{question}} 변수 (이중 중괄호)
p = mlflow.genai.register_prompt(
    name=PROMPT,
    template=(
        "당신은 A항공 화물(Cargo) 시장운임(cmarket) 분석을 돕는 AI 어시스턴트입니다. "
        "제공된 컨텍스트에 근거해 한국어로 답변하고, 근거가 없으면 추측하지 마세요.\n"
        "질문: {{question}}"
    ),
    commit_message="cargo cmarket assistant - 초기 버전",
)
# p.version -> 1, p.variables -> ['question']  (변수 자동 추출)

# 2) 운영 alias 지정
mlflow.genai.set_prompt_alias(PROMPT, alias="dev", version=p.version)

# 3) alias 기준 로드
loaded = mlflow.genai.load_prompt(f"prompts:/{PROMPT}@dev")

# 4) 변수 주입 후 사용
text = loaded.format(question="이번 분기 항공 화물 운임이 오른 이유가 뭔가요?")
# -> "당신은 A항공 화물(Cargo) ... 질문: 이번 분기 항공 화물 운임이 오른 이유가 뭔가요?"

실행 결과 (apne2, dev 환경): 위 코드를 그대로 실행한 결과입니다.

호출 결과
register_prompt(... "...{{question}}") version=1, variables={'question'} (변수 자동 추출)
set_prompt_alias(..., "dev", version=1) dev → v1
load_prompt("prompts:/...@dev") version=1 로 로드
loaded.format(question="이번 분기 항공 화물 운임이 오른 이유가 뭔가요?") "당신은 A항공 화물(Cargo) ... 질문: 이번 분기 ..."

변수가 없는 순수 시스템 지시문(역할·규칙)으로 등록하면 variables=[] 이며, 에이전트는 이를 system 메시지 로 주입합니다. {{question}} 같은 변수가 있는 완성형 템플릿은 .format() 으로 채워 단일 user 메시지 로 보냅니다. 운영 RAG 에이전트(멀티턴)는 전자(시스템 프롬프트) 패턴을 씁니다 — §5 참조.

 

5. Model Serving 통합 (운영 패턴)

 

5.1 에이전트 로드 패턴 (alias + TTL 캐시 + fallback)

import os, time, mlflow

mlflow.set_tracking_uri("databricks")     # 고정값 — Databricks 호스팅 MLflow tracking 서버
mlflow.set_registry_uri("databricks-uc")  # 고정값 — UC 등록·로드 (서빙에서도 두 URI 필수)

PROMPT_NAME  = "<catalog>.<schema>.css_cmarket_system_prompt"  # 로드할 프롬프트 (dev 카탈로그)
PROMPT_ALIAS = os.getenv("PROMPT_ALIAS", "dev")                # 환경별 config 로 주입
PROMPT_TTL   = float(os.getenv("PROMPT_TTL_SECONDS", "300"))   # 캐시 TTL(초)

# 로드 실패 시 쓸 내장 기본값 (예시 — 운영 코드의 SYSTEM_PROMPT 역할)
FALLBACK_SYSTEM = "당신은 A항공 화물(Cargo) 어시스턴트입니다. 한국어로 답변합니다."

_cache, _cache_t, _ver = None, 0.0, None  # 캐시된 템플릿 / 마지막 로드 시각 / 활성 버전

def system_prompt():
    """캐시된 system 프롬프트 반환 (TTL 경과 시 registry 에서 최신 재로드)."""
    global _cache, _cache_t, _ver
    now = time.monotonic()
    if _cache is None or (now - _cache_t) >= PROMPT_TTL:         # TTL 경과 → 최신 재로드
        try:
            p = mlflow.genai.load_prompt(f"prompts:/{PROMPT_NAME}@{PROMPT_ALIAS}")
            _cache, _ver, _cache_t = p.template, p.version, now  # 성공: 템플릿·버전·시각 갱신
        except Exception:
            # 첫 로드 실패 → 내장 기본값으로 대체
            if _cache is None:
                _cache, _cache_t = FALLBACK_SYSTEM, now
    return _cache

 

5.2 로깅·배포 (권한 부여·env override)

from mlflow.models.resources import DatabricksServingEndpoint

# resources 에는 LLM 엔드포인트만 넣습니다. 프롬프트 접근 권한은 아래 env override 로 해결합니다.
logged = mlflow.pyfunc.log_model(
    name="agent", python_model="agent.py",
    pip_requirements=["mlflow[databricks]>=3.1.0", "databricks-sdk", "openai"],
    resources=[DatabricksServingEndpoint(endpoint_name="databricks-gemini-3-flash")])

서빙 런타임에서 프롬프트를 로드하려면, 권한을 가진 Service Principal(SP) 자격증명을 env override 로 주입합니다. 아래 ①②③ 을 적용합니다.

① 스키마 권한 — 서빙 SP 에 §4.2 등록과 동일한 스키마 권한을 부여합니다. load_prompt 도 레지스트리 갱신을 동반하므로 USE SCHEMA·CREATE FUNCTION·EXECUTE·MANAGE 가 모두 필요합니다.

GRANT USE CATALOG ON CATALOG <catalog> TO `<sp-application-id>`;
GRANT USE SCHEMA, CREATE FUNCTION, EXECUTE, MANAGE ON SCHEMA <catalog>.<schema> TO `<sp-application-id>`;

② LLM 엔드포인트 호출 권한 — LLM 이 커스텀 서빙 엔드포인트일 때만 그 SP 에 CAN_QUERY 를 부여합니다. Foundation Model API 엔드포인트(databricks-*, 예 databricks-gemini-3-flash)는 별도 부여 없이 SP 가 호출할 수 있어 이 단계가 필요 없습니다. 커스텀 엔드포인트는 UI, 또는 CLI(엔드포인트 ID 를 받음 — 이름 아님)로 부여합니다:

EP_ID=$(databricks serving-endpoints get <custom-endpoint-name> | jq -r .id)
databricks serving-endpoints update-permissions "$EP_ID" \
  --json '{"access_control_list":[{"service_principal_name":"<sp-application-id>","permission_level":"CAN_QUERY"}]}'

③ 배포 시 그 SP 자격증명을 env override 로 주입 — 자동 인증 토큰은 로깅 시 선언한 resources(LLM 엔드포인트)로만 제한되므로, 프롬프트 접근 권한을 가진 SP 자격증명을 환경변수로 직접 넣습니다:

from databricks import agents
agents.deploy(MODEL, version, endpoint_name="<agent-endpoint>", scale_to_zero=True,
    environment_vars={
        "MLFLOW_EXPERIMENT_ID":     "<exp-id>",
        "DATABRICKS_HOST":          "https://<workspace-host>",
        "DATABRICKS_CLIENT_ID":     "<sp-application-id>",      # ①(+커스텀 LLM이면 ②)를 grant 한 SP
        "DATABRICKS_CLIENT_SECRET": "{{secrets/<scope>/<key>}}",
    })

env override 를 쓰면, 자동 토큰이 대신 처리하던 LLM 엔드포인트 자동 권한(passthrough)이 꺼집니다. 따라서 주입하는 SP 가 ①프롬프트 스키마 권한을 가져야 하며, LLM 이 커스텀 엔드포인트CAN_QUERY 도 함께 필요합니다(FMAPI 면 불요).

 

6. 평가 기반 승격 흐름

Databricks 권장 운영 흐름은 여러 버전 생성 → 평가 → best 선택 → alias 승격 입니다.

  • a. 후보 프롬프트 여러 버전 등록 (v1, v2, …)
  • b. 동일 evaluation dataset 으로 비교 — mlflow.genai.evaluate(data=..., predict_fn=..., scorers=[...])
  • c. 점수가 좋은 버전 선택
  • d. set_prompt_alias(PROMPT, alias="dev", version=<best>)승격
import mlflow
from mlflow.genai.scorers import Correctness, RelevanceToQuery   # 내장 scorer 예시

# eval_dataset (직접 구성): [{"inputs": {"question": "..."},
#                            "expectations": {"expected_response": "..."}}, ...]
# call_llm(prompt_text) -> str : LLM 호출 래퍼 (직접 구현)
METRIC = "correctness/mean"   # 실제 키는 사용한 scorer 에 따라 다름 — res.metrics 로 확인

results = {}
for v in [1, 2, 3]:
    pr = mlflow.genai.load_prompt(f"prompts:/{PROMPT}/{v}")
    res = mlflow.genai.evaluate(
        data=eval_dataset,
        predict_fn=lambda q, _p=pr: call_llm(_p.format(question=q)),
        scorers=[Correctness(), RelevanceToQuery()],
    )
    results[v] = res.metrics
best = max(results, key=lambda v: results[v][METRIC])
mlflow.genai.set_prompt_alias(PROMPT, alias="dev", version=best)

eval_dataset 구성: 각 row 는 inputs(프롬프트 변수)와 expectations(정답·기대 응답) 컬럼을 가집니다. 점수 metric 키 이름(correctness/mean 등)은 사용한 scorer 에 따라 달라지므로, 선택 기준으로 쓰기 전에 res.metrics 를 한 번 출력해 실제 키를 확인하세요. 승격은 alias 재지정 한 번이며, 운영 에이전트는 TTL 주기 내 자동으로 새 버전을 사용합니다.

 

7. 참고 자료

 

📚 시리즈 — Databricks LLMOps

댓글