from __future__ import annotations from langchain_core.messages import HumanMessage, SystemMessage from app.services.llm import ModelTier, get_chat_model COMPACTION_PROMPT = """당신은 대화 요약 전문가입니다. 아래 대화를 구조화된 요약으로 압축하세요. 반드시 다음 섹션을 포함하세요: ## 원래 질문 ## 핵심 데이터 ## 분석 진행 상황 ## 수치 데이터 (정확한 값 유지) ## 오류/이슈 ## 다음 단계""" class ContextCompactor: """대화 컨텍스트가 너무 커지면 LLM으로 요약하여 압축합니다.""" @staticmethod def _estimate_tokens(text: str) -> int: """텍스트의 토큰 수를 추정합니다 (~4자당 1토큰).""" return len(text) // 4 def should_compact( self, messages: list[dict], threshold: int = 50000 ) -> bool: """메시지의 토큰 추정치가 임계값을 초과하는지 확인합니다. Args: messages: 대화 메시지 목록. threshold: 토큰 임계값 (기본 50,000). Returns: 압축이 필요하면 True. """ total_text = "".join( str(m.get("content", "")) for m in messages ) return self._estimate_tokens(total_text) > threshold async def compact( self, messages: list[dict], system_prompt: str ) -> list[dict]: """대화를 요약하여 압축된 메시지 목록을 반환합니다. Args: messages: 기존 대화 메시지 목록. system_prompt: 원래 시스템 프롬프트. Returns: 압축된 메시지 목록 (시스템 메시지 + 요약 메시지). """ # 대화 내용을 텍스트로 변환 conversation_lines: list[str] = [] for msg in messages: role = msg.get("role", "unknown") content = str(msg.get("content", "")) conversation_lines.append(f"[{role}]: {content}") conversation_text = "\n".join(conversation_lines) llm = get_chat_model(tier=ModelTier.FAST) response = await llm.ainvoke( [ SystemMessage(content=COMPACTION_PROMPT), HumanMessage(content=conversation_text), ] ) summary_content = ( response.content if isinstance(response.content, str) else str(response.content) ) return [ {"role": "system", "content": system_prompt}, { "role": "user", "content": f"[이전 대화 요약]\n{summary_content}", }, ]