지식위키

FL_10 PromptCore — 결정적 공개안전 운영 프로파일

한 줄 요약

PromptCore는 모델에 주입할 “전이 가능한 운영 절차(procedure)“를 frozen dataclass 단일 정본으로 보유하고, 그것을 결정론적으로 core/promptcore.md와 동등한 마크다운으로 렌더하는 FableLayer의 1층 부품이다. 왜 배우나 — 프롬프트로 모델의 능력을 바꿀 수는 없지만 행동 규율(검증·완결·조사·안전)은 전이된다는 경계를 코드로 강제하는 법을 익히기 위해서다.

그림

flowchart TD
    subgraph SRC["정본 소스 (promptcore.py)"]
        S["_SECTIONS<br/>9개 Section (frozen)"]
        P["_PREAMBLE / _FOOTER<br/>머리말·꼬리말"]
        N["_OUTPUT_STRUCTURE_NOTE<br/>§6 뒤 주의블록"]
        I["_SECTION_INTROS<br/>섹션 도입문장 8개"]
    end
    DC["default_core(version)<br/>PromptCore 9 sections"]
    RM["render_markdown(core)<br/>결정론적 렌더"]
    MD["core/promptcore.md<br/>(정본 마크다운)"]
    CHK{"--check<br/>드리프트 검사"}

    S --> DC
    DC --> RM
    P --> RM
    N --> RM
    I --> RM
    RM --> MD
    RM --> CHK
    MD --> CHK
    CHK -->|"동등"| OK["exit 0"]
    CHK -->|"불일치"| FAIL["exit 1 (fail-closed)"]
flowchart LR
    B["base PromptCore"] --> M["merge(base, overlay)"]
    O["overlay PromptCore"] --> M
    M --> G{"COMPRESSION_RE<br/>매치 검사"}
    G -->|"_VETTED_RULES 면제"| PASS["dedupe 후 병합<br/>version = overlay.version"]
    G -->|"신규 압축 강제 rule"| ERR["ValueError (FL3 위반)"]

쉽게 풀기

요리책 비유로 본다. 식당 본사가 “조리 표준 매뉴얼” 한 권을 들고 있다고 하자. 이 매뉴얼은 셰프의 타고난 미각(능력)을 옮겨주지 못한다. 하지만 “재료는 직접 맛보고 내라”, “한 접시라도 미완이면 코스 전체를 미완으로 본다”, “되돌릴 수 없는 행위(손님에게 서빙)는 확인 뒤에만” 같은 절차는 어느 주방에 줘도 그대로 작동한다. PromptCore가 담는 것이 바로 이 “절차” 부분이다.

단계로 풀면 이렇다.

  1. 매뉴얼의 내용(9개 섹션)을 사람이 수정할 수 없는 형태(frozen dataclass)로 코드 안에 굳혀 둔다.
  2. default_core()를 부르면 그 9개 섹션이 담긴 정본 한 부가 나온다.
  3. render_markdown()은 이 정본을 항상 똑같은 글자로 마크다운으로 찍어낸다 — 같은 입력이면 언제 돌려도 한 글자도 안 달라진다(결정론적).
  4. 본사 매뉴얼 파일(core/promptcore.md)과 코드가 찍어낸 결과가 어긋나면 --check가 exit 1로 막는다. 코드와 문서가 따로 노는 것(드리프트)을 차단한다.
  5. 누군가 매뉴얼에 “토큰 아끼려고 답을 강제로 줄여라” 같은 품질 깎는 규칙을 끼워 넣으려 하면, merge가 그 문장을 잡아내 에러로 거부한다(FL3 압축 금지).

핵심 메시지 하나: PromptCore는 약한 모델에 절차를 얹어 강한 모델인 척 시키지 않는다. 깊은 추론이 필요한 자리에는 “이건 더 강한 모델로 올려라(escalate)“는 정직한 신호를 만든다.

핵심 정리

구성요소종류역할
Sectionfrozen dataclassid / title / inspired_by / rules 보유
PromptCorefrozen dataclassversion + sections(9개 Section)
default_core(version="v2")함수9 섹션 정본 PromptCore 반환
render_markdown(core)함수결정론적 마크다운 렌더, [inspired-by:] 태그 방출
merge(base, overlay)함수id별 rule dedupe 병합, FL3 압축 게이트
main(--check / --render)CLI드리프트 검사 / 정본 출력
핵심 규칙무엇을 강제하나
결정론같은 입력은 항상 같은 마크다운 → 드리프트 --check로 fail-closed
FL3 압축금지COMPRESSION_RE 매치 시 merge에서 ValueError
_VETTED_RULES 면제이미 출하된 정본 rule(§6 “상태 스냅샷으로 압축”)은 오탐 방지 위해 재심 안 함
capability/procedure 경계능력은 전이 안 됨, 절차만 전이 → escalate 신호로 경계 보존

[!note]- 9개 섹션 전체 목록 (render_markdown이 §1~§9 번호 부여)

  1. 운영 정체성 (Operating Identity) — proactive·grounded, 완결 책임, 불확실성 정직 표시, 의도 보존
  2. 검증 grounding 규율 — 실행 후 관찰, 증거 형태 명시, HARD vs SOFT 구분
  3. 완결성 evidence gate — 부분 단위 분해, 근거 없는 done 거부, 집계는 fail-closed
  4. 체계적 조사 규율 — 재현 먼저, 가설 경쟁, 인과 사슬
  5. early-stop 방지 — continuation 우선, 잔여 단위 점검, 정직한 escalate
  6. 출력구조 규약 — 구조화 우선, 상태 스냅샷, 참조에 의한 첨부 (+ 압축 주의블록)
  7. 모델 가성비 운영 — 기본은 비용효율 모델, 깊은 추론은 escalate, 라우팅 근거 기록
  8. drift 방지 & passive 운영 — passive 상시 적용, drift 점검, 제약 보존
  9. 안전·승인 경계 — 비가역 행위는 승인 뒤, 비밀값 비노출, leaked 전문 비포함

[!note]- 렌더 구조 (정본 마크다운이 조립되는 순서) _PREAMBLE(제목 + 인용블록 + §0 전제) → 각 섹션마다 ## {n}. {title} + [inspired-by:...] + 도입문장(_SECTION_INTROS, §9는 없음) + rules 목록 → §6(output-structure) 뒤에만 _OUTPUT_STRUCTURE_NOTE 삽입 → 마지막에 _FOOTER(적용 방법). 전체는 "\n\n".join(parts) + "\n"로 결합된다. §0은 “capability는 전이 안 되고 procedure만 전이된다”는 이 레이어 전체의 전제를 못 박는다.

실제 예시

정본 데이터와 결정론적 렌더의 실제 형태.

# fablelayer/promptcore.py
COMPRESSION_RE = r"(압축|compress|간결하게 줄|truncate output|글자\s*\s*제한)"
_COMPRESSION_PATTERN = re.compile(COMPRESSION_RE)

@dataclass(frozen=True)
class Section:
    id: str
    title: str
    inspired_by: tuple[str, ...]
    rules: tuple[str, ...]

@dataclass(frozen=True)
class PromptCore:
    version: str
    sections: tuple[Section, ...]

def default_core(version: str = "v2") -> PromptCore:
    """9 섹션을 가진 정본 PromptCore 를 반환한다."""
    return PromptCore(version=version, sections=_SECTIONS)

FL3 압축 게이트 — 출하된 정본 rule은 면제하고, 신규 압축 강제 rule만 차단한다.

# fablelayer/promptcore.py
def _assert_no_compression(rules: tuple[str, ...], origin: str) -> None:
    """FL3: 압축 강제 규칙이 보이면 fail-closed 로 ValueError.
    출하 정본(_VETTED_RULES)에 속한 rule 은 이미 게이트 통과분이라 면제한다."""
    for rule in rules:
        if rule in _VETTED_RULES:
            continue
        if _COMPRESSION_PATTERN.search(rule):
            raise ValueError(
                f"FL3 위반: {origin} 에 압축/분량깎기 규칙 금지 "
                f"(COMPRESSION_RE 매치): {rule!r}"
            )

merge의 id별 dedupe 병합 — base 먼저, overlay 추가, version은 overlay를 따른다.

# fablelayer/promptcore.py
combined_rules = _dedupe_rules(base_section.rules + overlay_section.rules)
combined_inspired = _dedupe_rules(
    base_section.inspired_by + overlay_section.inspired_by
)
# ...
return PromptCore(version=overlay.version, sections=tuple(merged))

드리프트 검사 CLI — 코드 렌더와 정본 파일이 한 글자라도 다르면 exit 1.

# 정본과 render_markdown(default_core()) 동등성 검사 (드리프트 시 exit 1)
python -m fablelayer.promptcore --check
# 정본 마크다운 출력
python -m fablelayer.promptcore --render

렌더 결과가 정본 파일과 다르면 _check_drift()가 첫 불일치 라인을 짚는다.

# fablelayer/promptcore.py  (_check_drift 내부)
if r != c:
    sys.stderr.write(
        f"  line {i + 1}:\n    rendered: {r!r}\n    canonical: {c!r}\n"
    )
    break

요약 & 셀프체크

  • PromptCore는 9개 frozen Section을 단일 정본으로 들고, render_markdown이 그것을 결정론적으로 core/promptcore.md와 동등하게 찍어내며 --check로 드리프트를 fail-closed로 막는다.
  • 담는 것은 procedure(검증·완결·조사·early-stop 방지·출력구조·안전)뿐이고, capability가 필요한 자리에는 escalate 경계 신호를 남긴다 — 약한 모델을 강한 모델로 분장시키지 않는다.
  • merge는 id별 rule을 dedupe로 합치되, COMPRESSION_RE에 걸리는 신규 압축 강제 규칙은 ValueError로 거부하고(FL3), 이미 출하된 _VETTED_RULES는 오탐 방지를 위해 면제한다.

셀프체크

  1. render_markdown이 “결정론적”이어야 하는 이유는 무엇이며, 그 성질이 깨지면 --check는 어떻게 반응하는가?
  2. merge에서 §6의 “상태 스냅샷으로 압축”이라는 정본 rule이 COMPRESSION_RE에 걸리는데도 ValueError가 나지 않는 까닭은?
  3. PromptCore가 “약한 모델에 procedure를 얹어 강한 모델 행세를 시키지 않는다”는 원칙은 어느 섹션(§)과 §0 전제에서 강제되는가?

연결

FL_개요 · _분석축_루브릭