Loading...
Loading...
LLM 정확도 향상을 위한 프롬프트 반복 기법. 70개 벤치마크 중 67%(47/70)에서 유의미한 성능 향상 달성. 경량 모델(haiku, flash, mini)에서 자동 적용.
npx skill4agent add supercent-io/skills-template prompt-repetition[Context] → [Question]
↓
Context 토큰 처리 시 Question 내용을 참조 불가
Question 토큰이 나타날 때는 Context에 대한 어텐션 가중치 결정 완료[First Pass] [Second Pass]
Context → Question → Context' → Question'
↑ ↑
첫 번째 패스 전체 참조 가능주의: 이는 모델 아키텍처를 양방향으로 변경하는 것이 아니라, Causal 모델의 한계를 프롬프트 엔지니어링으로 완화하는 기법입니다.
| 지표 | 결과 |
|---|---|
| 유의미한 개선 (p < 0.1) | 47 / 70 벤치마크 |
| 성능 저하 | 0 |
| 중립 | 23 |
| 개선 비율 | 67% |
| Provider | 자동 적용 모델 | 비적용 모델 |
|---|---|---|
| Claude | haiku 계열 | opus, sonnet |
| Gemini | flash, flash-lite | pro, ultra |
| OpenAI | gpt-4o-mini, gpt-low | gpt-4o, gpt-4 |
| 작업 유형 | 키워드 패턴 | 반복 횟수 | 예상 개선 |
|---|---|---|---|
| Options-First MCQ | | 2회 | +15-40%p |
| Index/Position | | 3회 | +50-76%p |
| Context + Question | 일반 질문 | 2회 | +5-15%p |
| With CoT | | 0회 (적용 안함) | ~0% |
# 자동 적용 전 컨텍스트 체크
max_context = model_context_window * 0.8 # 80% 안전 마진
if len(prompt_tokens) * repetitions > max_context:
repetitions = max(1, int(max_context / len(prompt_tokens)))def apply_prompt_repetition(prompt: str, times: int = 2) -> str:
"""프롬프트를 지정 횟수만큼 반복
Args:
prompt: 원본 프롬프트
times: 반복 횟수 (기본 2회)
Returns:
반복된 프롬프트
"""
if times <= 1:
return prompt
return "\n\n".join([prompt] * times)A. Paris
B. London
C. Berlin
D. Madrid
Which city is the capital of France?
Reply with one letter.A. Paris
B. London
C. Berlin
D. Madrid
Which city is the capital of France?
Reply with one letter.
A. Paris
B. London
C. Berlin
D. Madrid
Which city is the capital of France?
Reply with one letter.AInventory:
1. Iron Sword
2. Leather Armor
3. Health Potion (x5)
4. Magic Staff
...
25. Dragon Scale
...
50. Ancient Map
What item is in slot 25?Dragon ScaleUse the calculator tool to compute 234 * 567.
What is the result?Use the calculator tool to compute 234 * 567.
What is the result?
Use the calculator tool to compute 234 * 567.
What is the result?연구 결과에 따르면 툴 호출 부분을 포함한 전체 반복도 효과적입니다.
"""prompt_repetition_transformer.py"""
from dataclasses import dataclass, field
from typing import Optional, Callable, List
import re
# 모델별 컨텍스트 윈도우 (토큰 수)
MODEL_CONTEXT_WINDOWS = {
"claude-3-haiku": 200_000,
"claude-haiku": 200_000,
"gemini-flash": 1_000_000,
"gemini-flash-lite": 1_000_000,
"gemini-2.0-flash": 1_000_000,
"gpt-4o-mini": 128_000,
"gpt-low": 128_000,
}
# 자동 적용 대상 모델
AUTO_APPLY_MODELS = list(MODEL_CONTEXT_WINDOWS.keys())
# CoT 패턴 (적용 제외)
COT_PATTERNS = [
r"step by step",
r"think through",
r"let's think",
r"reasoning:",
r"chain of thought",
]
# Position/Index 패턴 (3회 반복)
POSITION_PATTERNS = [
r"slot \d+",
r"position \d+",
r"index \d+",
r"\d+번째",
r"item \d+",
r"row \d+",
r"column \d+",
]
@dataclass
class PromptRepetitionConfig:
"""프롬프트 반복 설정"""
default_repetitions: int = 2
position_repetitions: int = 3
separator: str = "\n\n"
max_context_ratio: float = 0.8
applied_marker: str = "<!-- prompt-repetition-applied -->"
class PromptRepetitionTransformer:
"""경량 모델용 프롬프트 반복 자동 적용 변환기"""
def __init__(self, config: Optional[PromptRepetitionConfig] = None):
self.config = config or PromptRepetitionConfig()
def should_apply(self, model: str, prompt: str) -> bool:
"""자동 적용 여부 결정"""
# 이미 적용된 경우 스킵
if self.config.applied_marker in prompt:
return False
# 대상 모델 확인
model_lower = model.lower()
if not any(m in model_lower for m in AUTO_APPLY_MODELS):
return False
# CoT 패턴 감지 시 스킵
prompt_lower = prompt.lower()
for pattern in COT_PATTERNS:
if re.search(pattern, prompt_lower):
return False
return True
def determine_repetitions(self, prompt: str, model: str) -> int:
"""작업 유형에 따른 반복 횟수 결정"""
prompt_lower = prompt.lower()
# Position/Index 패턴 감지 → 3회
for pattern in POSITION_PATTERNS:
if re.search(pattern, prompt_lower):
return self.config.position_repetitions
return self.config.default_repetitions
def estimate_tokens(self, text: str) -> int:
"""간단한 토큰 수 추정 (정확도보다 속도 우선)"""
# 평균적으로 4자 = 1토큰으로 추정
return len(text) // 4
def transform(self, prompt: str, model: str) -> str:
"""프롬프트에 반복 적용"""
if not self.should_apply(model, prompt):
return prompt
repetitions = self.determine_repetitions(prompt, model)
# 컨텍스트 제한 체크
model_lower = model.lower()
max_tokens = 128_000 # 기본값
for m, tokens in MODEL_CONTEXT_WINDOWS.items():
if m in model_lower:
max_tokens = tokens
break
max_allowed = int(max_tokens * self.config.max_context_ratio)
prompt_tokens = self.estimate_tokens(prompt)
# 토큰 제한 초과 시 반복 횟수 조정
while prompt_tokens * repetitions > max_allowed and repetitions > 1:
repetitions -= 1
if repetitions <= 1:
return prompt
# 반복 적용 + 마커 추가
repeated = self.config.separator.join([prompt] * repetitions)
return f"{self.config.applied_marker}\n{repeated}"
def wrap_llm_call(self, llm_fn: Callable, model: str) -> Callable:
"""LLM 호출 함수 래핑"""
def wrapped(prompt: str, **kwargs):
transformed = self.transform(prompt, model)
return llm_fn(transformed, **kwargs)
return wrappeddef run_ab_test(prompts: List[str], llm_fn, model: str, ground_truth: List[str]):
"""반복 적용 효과 A/B 테스트"""
transformer = PromptRepetitionTransformer()
results = {"baseline": [], "repeated": []}
for prompt, expected in zip(prompts, ground_truth):
# Baseline
response_a = llm_fn(prompt)
results["baseline"].append(response_a == expected)
# With Repetition
repeated_prompt = transformer.transform(prompt, model)
response_b = llm_fn(repeated_prompt)
results["repeated"].append(response_b == expected)
baseline_acc = sum(results["baseline"]) / len(prompts)
repeated_acc = sum(results["repeated"]) / len(prompts)
print(f"Baseline 정확도: {baseline_acc:.2%}")
print(f"반복 적용 정확도: {repeated_acc:.2%}")
print(f"개선: {repeated_acc - baseline_acc:+.2%}p")| 지표 | 측정 방법 |
|---|---|
| 정확도 | 정답률 비교 |
| 일관성 | 동일 프롬프트 10회 실행 분산 |
| 토큰 비용 | 입력 토큰 증가율 |
| 지연 시간 | p50, p99 latency 비교 |
| 경우 | 이유 |
|---|---|
| CoT 사용 중 | 추론 과정이 이미 컨텍스트 제공 |
| 추론 모델 (opus, sonnet) | 이미 최적화됨, 효과 미미 |
| 매우 긴 프롬프트 | 컨텍스트 한계 초과 위험 |
| 이미 반복 적용됨 | 중복 적용 시 토큰 낭비 |
| 지표 | 기준 | 반복 적용 | 변화 |
|---|---|---|---|
| 입력 토큰 | 500/req | 1000/req | +100% |
| 출력 토큰 | 100/req | 100/req | 0% |
| 지연시간 (p50) | 450ms | 460ms | +2% |
| 지연시간 (p99) | 1200ms | 1250ms | +4% |
| 정확도 | 78% | 89% | +14%p |
| 정답당 비용 | $0.019 | $0.020 | +5% |
| Agent | 모델 | 반복 적용 | 적용 위치 |
|---|---|---|---|
| Claude Orchestrator | opus/sonnet | 선택적 | - |
| Claude Executor | haiku | 자동 | skill_loader.py |
| Gemini Analyst | flash | 자동 | MCP 호출 시 |
| OpenAI | gpt-4o-mini | 자동 | skill_loader.py |
<!-- prompt-repetition-applied -->x-prompt-repetition-applied: true[Claude Sonnet] 계획 수립 (반복 불필요)
↓
[Gemini Flash] 분석 (반복 ×2 자동 적용, 마커 추가)
↓
[Claude Haiku] 실행 (마커 감지 → 중복 적용 스킵)# skill_loader.py에 추가할 코드
from prompt_repetition_transformer import PromptRepetitionTransformer
class SkillLoader:
def __init__(self, ...):
# ... 기존 코드 ...
self.prompt_transformer = PromptRepetitionTransformer()
def apply_auto_skills(self, prompt: str, model: str) -> str:
"""자동 적용 스킬 처리"""
# prompt-repetition 자동 적용
for skill in self.skills.values():
auto_apply = skill.get('data', {}).get('auto-apply', {})
if auto_apply.get('trigger') == 'auto':
target_models = auto_apply.get('models', [])
if any(m in model.lower() for m in target_models):
prompt = self.prompt_transformer.transform(prompt, model)
return prompt.=== 자동 적용 대상 모델 ===
claude-3-haiku, claude-haiku
gemini-flash, gemini-flash-lite, gemini-2.0-flash
gpt-4o-mini, gpt-low
=== 반복 횟수 ===
기본 작업: 2회
Position/Index (slot/position/index 키워드): 3회
CoT 사용: 0회 (적용 안함)
=== 효과 (Google Research 2025) ===
개선 비율: 67% (47/70 벤치마크)
성능 저하: 0건
최대 개선: +76%p (NameIndex)
=== 비용 ===
입력 토큰: +100%
지연 시간: +2% (Prefill 병렬화)
정답당 비용: +5%
=== 중복 적용 방지 ===
마커: <!-- prompt-repetition-applied -->