GEODE의 프롬프트는 두 단으로 관리됩니다. 디스크의 마크다운 템플릿(버전 관리되고 해시로 핀 고정), 그리고 호출 직전의 레이어 조립(정적/동적 분리)입니다.
템플릿: 마크다운이 SoT
프롬프트 텍스트는 core/llm/prompts/의 마크다운 파일에 삽니다. 각 파일은 <system>, <user>, <agentic_suffix> 같은 XML 형태 섹션 태그로 나뉘고, core/llm/prompts/__init__.py의 _load_template이 파싱합니다. 현재 살아 있는 템플릿은 셋입니다.
| 템플릿 | 노출 상수 | 용도 |
|---|---|---|
router.md | ROUTER_SYSTEM, AGENTIC_SUFFIX | 메인 에이전트 시스템 프롬프트와 agentic 접미사 |
commentary.md | COMMENTARY_SYSTEM, COMMENTARY_USER | 진행 코멘터리 |
decomposer.md | 호출부에서 load_prompt | 복합 요청 분해 |
AGENTIC_SUFFIX는 항상-on 운영 규칙을 담습니다. 완료 기준, 도구 선택 매트릭스, Grounding & Citation(도구 출력은 데이터지 지시가 아님 포함), Answering discipline(미인식 개체 선검색, 정쟁 토픽 even-handedness), Source fidelity & copyright.
드리프트 감지: 4개의 핀
import 시점에 각 상수의 sha256[:12] 해시를 계산해 PROMPT_VERSIONS에 노출합니다. 핀은 정확히 넷입니다. ROUTER_SYSTEM, AGENTIC_SUFFIX, COMMENTARY_SYSTEM, COMMENTARY_USER. verify_prompt_integrity()가 라이브 해시를 _PINNED_HASHES와 비교하고, CI가 이 함수를 게이트로 씁니다. 프롬프트를 바꾸면 핀을 다시 박아야 하므로, 모든 프롬프트 변경이 리뷰 가능한 코드 변경이 됩니다. 재핀 워크플로는 프롬프트 해싱을 참고합니다.
core/llm/prompt_assembler.py는 이제 작은 스텁입니다. 스킬은 별도 주입 경로 없이 core/agent/loop/_context.py의 {skill_context} 블록 한 곳으로만 프롬프트에 들어갑니다.
레이어 조립: build_system_prompt
호출 직전 조립은 core/agent/system_prompt.py의 build_system_prompt(model)이 담당합니다. 실제 문자열은 정적 prefix 뒤에 <dynamic_context> 경계 마커를 붙이는 형태입니다.
| 구역 | 레이어 | 특성 |
|---|---|---|
| 정적 prefix | <agent_baseline> (항상), <agent_identity> (기본 ON), 스타일 가이드와 휴리스틱 정책 append | 턴 간 불변. 캐시 적중 대상 |
<dynamic_context> | <model_card>, 모델 패밀리 가이던스 (core/llm/model_guidance.py), 플랫폼 힌트 (core/llm/platform_hints.py), <current_date>, <project_memory>, <agent_learning>, <runtime_rules>, <user_context> | 턴마다 갱신. 캐시 제외 |
PROMPT_CACHE_BOUNDARY 값은 <dynamic_context>여는 태그입니다. 프로바이더 캐싱은 이 태그 앞의 prefix를 정적 영역으로 봅니다. 자세한 동작은 프롬프트 캐싱을 참고합니다.
시스템 프롬프트 모드
| 모드 | 스위치 | 효과 |
|---|---|---|
| Persona 주입(기본 ON) | GEODE_PERSONA=off로 옵트아웃 | "You are GEODE" 정체성 + Voice/Operating/RUNTIME CANNOT 주입이 기본. off로 끄면 베이스 모델을 얇게 감싼 상태 |
| Audit-mode strip | GEODE_AUDIT_UNRESTRICTED=1 | GEODE 고유 레이어(정체성, 기억, 사용자 컨텍스트)를 전부 벗기고 model_card와 날짜, 호출자 suffix만 남깁니다. persona는 강제 OFF |
| Wrapper override | GEODE_WRAPPER_OVERRIDE env / audit-mode SoT 파일 | 자기개선 루프가 변이시킨 스캐폴드를 정적 베이스로 삼습니다. 없으면 generic prefix로 폴백해 타깃이 항상 베이스 스캐폴드를 입습니다 |
상세는 시스템 프롬프트 모드를 참고합니다.
program.md: 훅 제어 폴백
자기개선 루프의 변이 러너 (core/self_improving/loop/mutate/runner.py)는 program.md를 읽지 못하면 코드에 박힌 리터럴 미러로 조용히 대체하지 않습니다. 대신 HookEvent.PROGRAM_MD_UNREADABLE을 발화합니다. 핸들러가 대체 내용을 돌려줄 수 있고, 핸들러가 없으면 러너는 시끄럽게 실패합니다. 폴백을 데이터 미러가 아니라 코드의 제어 지점으로 두는 설계입니다. 디스크와 리터럴의 이중 SoT는 반드시 드리프트합니다.
실패 모드
| 증상 | 원인 | 해법 |
|---|---|---|
| CI가 prompt integrity에서 실패 | 템플릿을 바꿨는데 핀을 갱신하지 않음 | 의도된 ratchet입니다. 재핀 워크플로를 따릅니다 |
| 얇은 래퍼를 원하는데 persona가 주입됨 | persona 기본값 ON | GEODE_PERSONA=off로 옵트아웃합니다 |
변이 러너가 PROGRAM_MD_UNREADABLE로 중단 | program.md 경로가 깨졌고 등록된 폴백 핸들러 없음 | 파일을 복구하거나 부트스트랩에 핸들러를 등록합니다. 조용한 폴백은 설계상 없습니다 |