프롬프트 조합과 KV-Cache 최적화
프롬프트는 조립되는 것이다
섹션 제목: “프롬프트는 조립되는 것이다”에이전트 시스템에서 프롬프트는 단일 문자열이 아닙니다. 여러 소스에서 동적으로 조립됩니다. 잘 설계된 프롬프트 조합 시스템은 다음 특성을 가집니다.
- 모듈성: 각 섹션이 독립적으로 교체 가능
- 우선순위: 컨텍스트 한계에 가까워질 때 무엇을 유지할지 결정
- 캐시 친화성: KV-Cache 히트율을 최대화하는 순서 배치
우선순위 기반 프롬프트 조합
섹션 제목: “우선순위 기반 프롬프트 조합”OpenDev 연구에서 제안된 방식은 프롬프트 섹션에 **우선순위 번호(10~95)**를 부여하여 조합 순서와 컨텍스트 압박 시 생략 순서를 결정합니다.
interface PromptSection { priority: number; // 10(필수) ~ 95(선택) content: string; cacheable: boolean; // KV-Cache 대상 여부 provider?: 'openai' | 'anthropic' | 'google'; // 특정 제공자 전용}
function assemblePrompt( sections: PromptSection[], maxTokens: number): string { // 우선순위 오름차순 정렬 (낮을수록 중요) const sorted = [...sections].sort((a, b) => a.priority - b.priority);
let result = ''; let tokenCount = 0;
for (const section of sorted) { const sectionTokens = estimateTokens(section.content); if (tokenCount + sectionTokens > maxTokens) { // 우선순위 높은(숫자 큰) 섹션부터 생략 continue; } result += section.content + '\n\n'; tokenCount += sectionTokens; }
return result;}섹션 우선순위 예시
섹션 제목: “섹션 우선순위 예시”| 우선순위 | 섹션 종류 | 설명 |
|---|---|---|
| 10 | 시스템 역할 정의 | 절대 생략 불가 |
| 20 | 안전 가이드라인 | 필수 제약 사항 |
| 30 | 현재 태스크 설명 | 핵심 지시 |
| 50 | 관련 도구 목록 | 태스크 관련 도구만 |
| 70 | 최근 대화 히스토리 | 압박 시 요약으로 대체 |
| 85 | 참고 예시 | 공간 있을 때만 포함 |
| 95 | 상세 배경 지식 | 선택적 보강 |
KV-Cache의 원리
섹션 제목: “KV-Cache의 원리”**KV-Cache(Key-Value Cache)**는 트랜스포머 어텐션 메커니즘에서 이미 계산한 키-값 쌍을 재사용하는 기술입니다. 같은 프롬프트 prefix가 반복될 때 재계산을 건너뜁니다.
Manus의 실측 데이터에 따르면:
캐시 미스 토큰 비용: $3.00 / 1M tokens캐시 히트 토큰 비용: $0.30 / 1M tokens──────────────────────────────────────비용 절감: 10배 (90% 감소)캐시 최적화 세 가지 원칙
섹션 제목: “캐시 최적화 세 가지 원칙”1. 안정적인 내용을 앞에 배치
섹션 제목: “1. 안정적인 내용을 앞에 배치”// 나쁜 예: 자주 변하는 내용이 앞에 있으면 캐시 무효화const prompt = `현재 시각: ${new Date().toISOString()} // 매번 변함 → 캐시 미스당신은 코드 리뷰 전문가입니다.[안전 가이드라인 500 토큰...][코드베이스 컨텍스트 2000 토큰...]코드: ${userCode}`;
// 좋은 예: 안정적인 내용을 앞에, 동적 내용을 뒤에const prompt = `당신은 코드 리뷰 전문가입니다. // 캐시 가능 ✓[안전 가이드라인 500 토큰...] // 캐시 가능 ✓[코드베이스 컨텍스트 2000 토큰...] // 캐시 가능 ✓현재 시각: ${new Date().toISOString()} // 동적코드: ${userCode} // 동적`;2. Append-only 컨텍스트
섹션 제목: “2. Append-only 컨텍스트”대화 히스토리를 수정하지 않고 항상 끝에 추가합니다. 중간 메시지를 편집하거나 삭제하면 그 이후의 모든 캐시가 무효화됩니다.
class AppendOnlyContext { private messages: Message[] = [];
// 메시지 추가만 허용, 수정/삭제 없음 append(message: Message): void { this.messages = [...this.messages, message]; }
// 압축이 필요할 때: 오래된 메시지를 요약으로 대체 compactOldMessages(keepRecent: number): void { if (this.messages.length <= keepRecent) return;
const toSummarize = this.messages.slice(0, -keepRecent); const summary = summarize(toSummarize);
// 요약 이후 메시지는 그대로 유지 → 캐시 보존 this.messages = [ { role: 'system', content: `[이전 대화 요약]: ${summary}` }, ...this.messages.slice(-keepRecent), ]; }}3. 캐시 브레이크포인트 명시
섹션 제목: “3. 캐시 브레이크포인트 명시”Anthropic API는 cache_control 파라미터로 캐시 구간을 명시적으로 지정합니다.
const response = await anthropic.messages.create({ model: 'claude-opus-4-5', messages: [ { role: 'user', content: [ { type: 'text', text: systemPrompt, cache_control: { type: 'ephemeral' }, // 캐시 브레이크포인트 }, { type: 'text', text: userMessage, // 이 이후는 캐시 미적용 }, ], }, ],});도구 마스킹을 통한 캐시 보존
섹션 제목: “도구 마스킹을 통한 캐시 보존”도구 목록이 바뀌면 시스템 프롬프트 캐시가 깨집니다. Manus가 사용하는 해결책은 **도구 제거 대신 로짓 마스킹(logit masking)**입니다.
모든 도구를 프롬프트에 유지하되, 디코딩 단계에서 특정 도구 호출에 해당하는 토큰의 로짓을 -∞으로 설정하여 선택 불가능하게 만듭니다. 이렇게 하면 시스템 프롬프트는 변경 없이 유지되어 캐시가 보존됩니다.
프롬프트 조합은 우선순위를 부여한 모듈 단위로 설계하고, KV-Cache를 최대화하려면 안정적인 내용을 앞에, 동적 내용을 뒤에 배치해야 합니다. Append-only 컨텍스트 관리와 명시적 캐시 브레이크포인트를 조합하면 동일한 기능을 최대 10배 낮은 비용으로 운영할 수 있습니다.