LangChain을 이용하여 채팅 앱을 개발하는데, 유저들의 쓸데없는 입력으로 인하여 토큰 낭비가 심하다. invoke 전, 입력된 요청에 대해 검증 - 레이어 등을 적용하고 싶다. 이런 구조를 통하여 사용자의 의미없는 입력을 바로 LLM을 호출하여 토큰을 낭비하지 않고 정확한 요청에 대해서만 invoke를 실행하여 토큰 낭비를 방지하는 장치를 마련하고 싶다. 아이디어를 구체화하고 가능한 코드를 작성해서 보여주세요.
좋은 방향입니다. 단순히 “LLM 앞단에 필터 하나 둔다” 수준이 아니라, 경량 검증 레이어 → 규칙 기반 필터 → 필요 시 소형 모델 → 최종 LLM invoke 구조로 설계해야 효과가 큽니다. 핵심은 비싼 모델을 최대한 늦게 호출하는 것입니다.
아래처럼 단계적으로 설계해보세요.
[User Input]
↓
[1. Fast Validation Layer (0-cost)]
↓
[2. Rule-based Filter (cheap)]
↓
[3. Lightweight Classifier (optional, small LLM or local model)]
↓
[4. Intent Router]
↓
[5. Main LLM invoke (LangChain)]
👉 가장 먼저 “쓸데없는 입력”을 컷
import re
def fast_validate(text: str) -> tuple[bool, str]:
text = text.strip()
if not text:
return False, "입력이 비어 있습니다."
if len(text) < 3:
return False, "입력이 너무 짧습니다."
if len(text) > 1000:
return False, "입력이 너무 깁니다."
# 반복 문자 패턴
if re.fullmatch(r'(.)\1{5,}', text):
return False, "반복 입력 감지"
# 의미 없는 키보드 입력
if re.fullmatch(r'[asdfqwerzxcv]+', text):
return False, "무의미한 입력 감지"
return True, text
👉 LLM 없이 의도 분류
def rule_filter(text: str) -> tuple[bool, str]:
meaningless_keywords = [
"ㅋㅋ", "ㅎㅎ", "lol", "test", "1234"
]
for k in meaningless_keywords:
if k in text.lower():
return False, "의미 없는 입력으로 판단됨"
return True, text
👉 여기서 작은 모델을 사용하면 효과가 큼
from langchain.chat_models import ChatOpenAI
small_llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
def classify_intent(text: str) -> str:
prompt = f"""
다음 입력을 분류하세요:
{text}
카테고리:
- meaningful
- nonsense
- spam
- greeting
결과만 출력:
"""
result = small_llm.invoke(prompt)
return result.content.strip()
def route_by_intent(intent: str, text: str):
if intent in ["nonsense", "spam"]:
return "차단됨: 의미 없는 요청"
if intent == "greeting":
return "안녕하세요! 무엇을 도와드릴까요?"
return None # meaningful → 다음 단계
from langchain.chat_models import ChatOpenAI
main_llm = ChatOpenAI(model="gpt-4o", temperature=0.7)
def invoke_main_llm(text: str):
return main_llm.invoke(text).content
def process_input(user_input: str):
# 1. Fast validation
valid, result = fast_validate(user_input)
if not valid:
return result
# 2. Rule filter
valid, result = rule_filter(result)
if not valid:
return result
# 3. Intent classification
intent = classify_intent(result)
# 4. Routing
routed = route_by_intent(intent, result)
if routed:
return routed
# 5. Main LLM
return invoke_main_llm(result)
같은 질문 반복 방지
from functools import lru_cache
@lru_cache(maxsize=1000)
def cached_llm(text: str):
return invoke_main_llm(text)
👉 “이미 답한 질문인지” 판단
from sklearn.metrics.pairwise import cosine_similarity
# 이전 질문 embedding 저장
👉 유저별 요청 제한
user_request_count = {}
def rate_limit(user_id):
user_request_count.setdefault(user_id, 0)
user_request_count[user_id] += 1
if user_request_count[user_id] > 20:
return False
return True
👉 입력 길이 자동 제한
def truncate_input(text: str, max_tokens=300):
return text[:max_tokens]
원하면 다음 단계로
👉 “LangChain Runnable / LCEL 기반으로 파이프라인 구성”
👉 “FastAPI + Redis 캐시 + rate limit 포함한 실서비스 구조”
까지 실제 서비스 수준으로 확장해 줄게요.