85 lines
2.6 KiB
Python
85 lines
2.6 KiB
Python
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}",
|
|
},
|
|
]
|