/** * [v2.2.311] KV 캐시 친화 프롬프트 분리 — 정적/동적 경계 계약. * * 정적 프롬프트(getStaticSystemPrompt)에 분 단위 시각 같은 가변 요소가 섞이는 순간 * llama.cpp prompt cache 는 요청 첫 토큰부터 무효화된다 (실측: 13k 토큰 전체 재프리필 * ≈ 90초/턴). 이 테스트는 그 회귀를 막는 가드레일이다. */ import { getStaticSystemPrompt, getDateTimeContextBlock, getSystemPrompt } from '../src/utils'; import { buildAstraModeSystemPrompt } from '../src/agent/handlePrompt/buildAstraModeSystemPrompt'; describe('정적/동적 시스템 프롬프트 분리', () => { test('정적 프롬프트에는 날짜/시각이 없다 (캐시 무효화 가드)', () => { const s = getStaticSystemPrompt(); expect(s).not.toContain('[CURRENT DATE/TIME]'); // 오늘 날짜 문자열이 어떤 형태로도 들어있으면 안 된다. const iso = new Date().toISOString().split('T')[0]; expect(s).not.toContain(iso); }); test('정적 프롬프트는 연속 호출에서 동일하다 (프리픽스 불변)', () => { expect(getStaticSystemPrompt()).toBe(getStaticSystemPrompt()); }); test('날짜 블록은 오늘 날짜를 포함한다', () => { const block = getDateTimeContextBlock(); expect(block).toContain('[CURRENT DATE/TIME]'); expect(block).toContain(new Date().toISOString().split('T')[0]); }); test('레거시 getSystemPrompt 는 정적 본문 + 날짜 블록을 모두 포함 (호환)', () => { const s = getSystemPrompt(); expect(s).toContain('[CURRENT DATE/TIME]'); expect(s).toContain('[출력 위생 규칙 — 반드시 준수]'); }); test('빌더에 systemPrompt="" 를 넘겨도 [CONTEXT] 골격은 유지된다 (tail 생성 경로)', () => { const tail = buildAstraModeSystemPrompt({ prompt: '질문', systemPrompt: '', modeBridgeCtx: '', priorConclusionCtx: '', designerCtx: '', projectArchitectureCtx: '', secondBrainTraceCtx: '', memoryCtx: 'RAG 본문', knowledgeContextForPrompt: '', contextBlock: '열린 파일 내용', negativeCtx: '', isCasualConversation: false, localPathContext: '', knowledgeMix: null, dynamicBlocks: new Map([['behavior-constraints', '[GROUNDING] 근거 강도: 보통']]), }); expect(tail).toContain('[CONTEXT]'); expect(tail).toContain('RAG 본문'); expect(tail).toContain('열린 파일 내용'); expect(tail).toContain('[GROUNDING]'); expect(tail.startsWith('You are Astra')).toBe(false); // 정적 본문이 섞이면 안 됨 }); });