LLM 컨텍스트 윈도우 관리: 긴 문서를 처리하기 위한 기술
요약
LLM의 컨텍스트 윈도우 제한은 단일 요청에서 처리할 수 있는 최대 토큰 수를 의미하며, 아무리 큰 모델이라도 이 한계가 존재합니다. 본 글에서는 Claude 3.5 Sonnet이나 GPT-4 Turbo 등 주요 LLM의 컨텍스트 크기를 비교하고, 이를 효율적으로 관리하는 두 가지 핵심 기술을 소개합니다. 첫째는 의미 단위로 문서를 분할하는 '시맨틱 청킹'이며, 둘째는 필요한 정보만 검색하여 사용하는 'RAG(검색 증강 생성)' 기법입니다.
핵심 포인트
- LLM의 컨텍스트 윈도우는 모델별로 제한적이며, 이를 초과하면 오류가 발생하거나 비용 낭비가 생길 수 있습니다.
- 시맨틱 청킹은 단순히 글자 수를 자르는 것이 아니라 의미 단위(문단)를 고려하여 문서를 분할하는 기술입니다.
- RAG(Retrieval-Augmented Generation)는 모든 정보를 프롬프트에 넣지 않고, 사용자 쿼리와 가장 관련 있는 정보만 검색하여 사용하는 효율적인 방법입니다.
- 제공된 Python 코드는 토큰 추정 및 시맨틱 청킹 로직을 구현하여 실제 애플리케이션 개발에 활용할 수 있도록 합니다.
모든 LLM에는 컨텍스트 윈도우 (Context Window) 제한이 있습니다. 이는 단일 요청에서 전달할 수 있는 최대 토큰 (Token) 수입니다. Claude 3.5 Sonnet은 200K 토큰을 제공하지만, 이 역시 유한합니다. 여기서는 프로덕션 AI 애플리케이션을 위해 컨텍스트를 효율적으로 관리하는 방법을 소개합니다.
컨텍스트 윈도우 제한 이해하기
| 모델 | 컨텍스트 윈도우 | 대략적인 페이지 수 |
|---|---|---|
| Claude 3.5 Sonnet | 200K tokens | ~500 pages |
| GPT-4 Turbo | 128K tokens | ~300 pages |
| Claude 3 Opus | 200K tokens | ~500 pages |
제한을 초과하면 에러가 발생합니다. 제한에 근접하면 가치를 더하지 못하는 토큰에 비용을 낭비하게 됩니다.
토큰 추정 (Token Estimation)
import re
def estimate_tokens(text: str) -> int:
"""
대략적인 토큰 추정.
영어 텍스트의 경우 토큰당 약 4자.
"""
return len(text) // 4
def estimatetokensprecise(text: str) -> int:
"""
단어 수를 사용한 더 정밀한 추정.
평균적인 영어 단어는 약 1.3 토큰.
"""
words = len(re.findall(r'\w+', text))
return int(words * 1.3)
기술 1: 시맨틱 청킹 (Semantic Chunking)
글자 수가 아닌 의미 단위로 문서를 분할합니다:
import re
def semanticchunk(text: str, maxtokens: int = 4000, overlap: int = 200) -> list[str]:
"""
텍스트를 시맨틱 청크(문단)로 분할합니다.
"""
# 이중 줄바꿈(문단)으로 분할
paragraphs = re.split(r'\n\n+', text)
chunks = []
current_chunk = []
current_tokens = 0
for para in paragraphs:
para_tokens = estimate_tokens(para)
if current_tokens + para_tokens > max_tokens:
# 현재 청크 저장
if current_chunk:
chunks.append('\n\n'.join(current_chunk))
# 오버랩(Overlap)을 포함하여 새 청크 시작
overlap_paras = []
overlap_tokens = 0
for p in reversed(current_chunk):
t = estimate_tokens(p)
if overlap_tokens + t <= overlap:
overlap_paras.insert(0, p)
overlap_tokens += t
else:
break
current_chunk = overlap_paras + [para]
current_tokens = overlap_tokens + para_tokens
else:
current_chunk.append(para)
current_tokens += para_tokens
if current_chunk:
chunks.append('\n\n'.join(current_chunk))
return chunks
기술 2: RAG — 검색 증강 생성 (Retrieval-Augmented Generation)
모든 내용을 프롬프트 (Prompt)에 넣지 마세요.
관련된 내용만 검색하세요: `python
class SimpleRAG:
def init(self, documents: list[str], chunk_size: int = 1000):
self.chunks = self.create_chunks(documents, chunk_size)
self.embeddings = self.create_embeddings(self.chunks)
def retrieve(self, query: str, top_k: int = 3) -> list[str]:
"""쿼리에 가장 관련 있는 청크(Chunk)를 찾습니다."""
query_embedding = self._embed(query)
scores = [
self.cosine_similarity(query_embedding, e)
for e in self.embeddings
]
top_indices = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)[:top_k]
return [self.chunks[i] for i in top_indices]
def create_chunks(self, documents: list[str], chunk_size: int) -> list[str]:
chunks = []
for doc in documents:
chunks.extend(semantic_chunk(doc, max_tokens=chunk_size))
return chunks
def _embed(self, text: str) -> list[float]:
# 프로덕션 환경에서는 OpenAI 또는 cohere.ai 임베딩을 사용하세요
pass
`
기술 3: 대화 요약 (Conversation Summary)
문맥 (Context)을 보존하기 위해 오래된 메시지를 요약합니다:
`python
class SummarizingConversation:
def init(self, max_tokens: int = 16000):
self.max_tokens = max_tokens
self.messages = []
self.summary = ""
def add_message(self, role: str, content: str):
self.messages.append({"role": role, "content": content})
self.maybe_summarize()
def maybe_summarize(self):
total_tokens = sum(estimate_tokens(m["content"]) for m in self.messages)
if total_tokens > self.max_tokens:
# 오래된 메시지를 요약합니다
older_messages = self.messages[:-5] # 마지막 5개는 유지
recent = self.messages[-5:]
summary_prompt = f"""
다음 대화의 핵심 정보를 보존하면서 간결하게 요약하세요:
{
chr(10).join(f'{m["role"]}: {m["content"]}' for m in older_messages)
}
"""
# 요약을 위해 LLM 호출 (의사 코드)
self.summary = call_llm_summarize(summary_prompt)
self.messages = [{"role": "system", "content": f"이전 문맥: {self.summary}"}] + recent
def get_messages(self) -> list[dict]:
return self.messages
`
기술 4: 시스템 프롬프트 최적화 (System Prompt Optimization)
시스템 프롬프트를 간결하게 유지하세요:
`python
❌ 장황한 시스템 프롬프트 (토큰 낭비)
verbose_system = """
당신은 도움이 되는 AI입니다.
"""
assistant. 당신은 정중하고 전문적이며 도움이 되도록 설계되었습니다. 정확한 정보를 제공해야 하며, 모르는 것이 있을 때는 정직해야 합니다. 당신은 ... [200개 단어 더 있음] """ ✅ 간결한 시스템 프롬프트 (효과적) lean_system = """ 역할: 도움이 되는 AI 어시스턴트 목표: 정확하고 간결한 답변 제공 불확실할 때: "모릅니다"라고 말하기 """ `
기법 5: 토큰 추적을 포함한 스트리밍 (Streaming with Token Tracking)
`python
class StreamingTokenTracker:
def init(self, model: str = "claude-3-5-sonnet-20241022"):
self.model = model
self.totalinputtokens = 0
self.totaloutputtokens = 0
async def stream_chat(self, messages: list[dict]) -> str:
"""토큰 사용량을 추적하면서 응답을 스트리밍합니다."""
response = await fetch(' https://api.ofox.ai/v1/chat/completions ', {
method: 'POST',
headers: {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
},
body: json.dumps({
'model': self.model,
'messages': messages,
'stream': True
})
})
reader = response.body.getReader()
decoder = TextDecoder()
full_response = []
while True:
chunk = await reader.read()
if chunk.done:
break
data = decoder.decode(chunk.value)
for line in data.split('\n'):
if line.startswith('data: '):
delta = json.loads(line[6:]).get('choices', [{}])[0].get('delta', {})
if content := delta.get('content'):
self.totaloutputtokens += estimate_tokens(content)
yield content
# 입력 토큰 추적
self.totalinputtokens = sum(
estimate_tokens(m['content']) for m in messages
)
def getcost(self, inputcostper1k=0.003, outputcostper_1k=0.015):
inputcost = (self.totalinputtokens / 1000) * inputcostper1k
outputcost = (self.totaloutputtokens / 1000) * outputcostper_1k
return inputcost + outputcost
`
실용적인 경험 법칙 (Practical Rule of Thumb)
프롬프트를 컨텍스트 윈도우 (Context Window)의 50% 미만으로 유지하세요. 이렇게 하면 다음과 같은 여유 공간을 확보할 수 있습니다:
- 사용자 입력의 변동성
- 모델의 추론 (Reasoning)
- 예상치 못한 응답 길이
시작하기
ofox.ai를 통해 토큰 효율적인 AI 애플리케이션을 구축해 보세요. — 이들의 OpenAI 호환 API를 사용하면 경쟁력 있는 가격으로 넉넉한 컨텍스트 윈도우를 가진 Claude에 접근할 수 있습니다.
👉 ofox.ai로 시작하기 이 기사에는 제휴 링크가 포함되어 있습니다. 태그: llm, artificial-intelligence, programming, developer, performance Canonical URL: https://dev.to/zny10289
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기