로컬 LLM 채팅에 메시지 기록과 컨텍스트 구현하기: Python 활용
이전 파트의 LiteLLM과 Ollama 기반 콘솔 채팅은 요청을 독립적으로 처리합니다. 모델이 이전 메시지를 볼 수 없어 대화가 끊어지는데, 과거 응답에 대한 참조가 무시됩니다. 해결책은 Python 리스트에 메시지 기록을 저장하고 각 새 요청과 함께 전달하는 것입니다.
이를 통해 일련의 호출이 일관된 대화로 바뀝니다. 모델은 전체 컨텍스트를 받습니다: 시스템 지시, 과거 사용자/어시스턴트 쌍, 현재 입력.
컨텍스트를 위한 메시지 구조
메시지 형식은 OpenAI 호환 API에 표준입니다:
system: 지속적인 지시, 별도로 추가user: 사용자의 질문assistant: 모델의 응답
기록에는 사용자/어시스턴트 쌍만 포함됩니다. 전송 시:
[
{"role": "system", "content": SYSTEM_PROMPT},
*conversation_history,
{"role": "user", "content": user_message}
]
순서가 중요합니다: 시스템은 동작을 설정하고, 기록은 컨텍스트를 제공하며, 새 사용자는 현재 질문입니다.
업데이트된 main.py 코드
기록과 컨텍스트 트리밍이 포함된 전체 스크립트:
# -*- coding: utf-8 -*-
import time
from typing import Optional
from litellm import completion
MODEL = "ollama_chat/qwen2.5:3b"
API_BASE = "http://localhost:11434"
SYSTEM_PROMPT = "You are a helpful assistant. Respond concisely and to the point."
MAX_HISTORY_MESSAGES = 6
def trim_history(history: list, limit: int) -> list:
if len(history) <= limit:
return history
return history[-limit:]
def send_request_to_llm(user_message: str, history: list) -> Optional[str]:
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
*history,
{"role": "user", "content": user_message},
]
try:
start_time = time.time()
response = completion(
model=MODEL,
messages=messages,
api_base=API_BASE,
request_timeout=120,
)
duration = time.time() - start_time
print(f"\nGeneration time: {duration:.2f} sec")
return response.choices[0].message.content
except Exception as e:
print(f"\nRequest error: {e}")
return None
def main() -> None:
print("Local AI assistant with memory started.")
print("Enter a question or 'exit' to quit.\n")
conversation_history = []
while True:
user_input = input("You: ").strip()
if user_input.lower() in ("exit", "quit"):
print("Goodbye!")
break
if not user_input:
print("Please enter a question.")
continue
print("\nModel thinking...")
answer = send_request_to_llm(user_input, conversation_history)
if answer is not None:
print(f"\nAI: {answer}\n")
conversation_history.append({"role": "user", "content": user_input})
conversation_history.append({"role": "assistant", "content": answer})
conversation_history = trim_history(conversation_history, MAX_HISTORY_MESSAGES)
else:
print("\nFailed to get a response. Check if Ollama is running.\n")
if __name__ == "__main__":
main()
주요 논리 변경 사항
- conversation_history: main()의 리스트가 메시지 쌍 저장
- send_request_to_llm: 기록을 받아
*로 확장 - 쌍 저장: 응답 후 사용자와 어시스턴트 추가
- trim_history: MAX_HISTORY_MESSAGES(6개 항목 — 3개 대화)로 트리밍
| 구성 요소 | 업데이트 전 | 업데이트 후 |
|-----------|---------------|--------------|
| messages | system + user | system + history + user |
| 메모리 | 없음 | RAM의 리스트 |
| 크기 | 무제한 | 최대 6개 메시지 |
트리밍은 컨텍스트 오버플로를 방지합니다: 오래된 메시지는 토큰을 증가시키고 추론을 느리게 합니다.
컨텍스트 테스트
종속적 질문이 있는 대화:
- 사용자: Python 웹 개발 프레임워크 세 가지 이름을 알려주세요
- AI: Django, Flask, FastAPI
- 사용자: 두 번째에 대해 더 자세히 알려주세요
- AI: Flask는 가벼운 마이크로프레임워크입니다...
모델이 이전 응답을 보고 "두 번째"를 올바르게 참조합니다.
현재 구현의 한계
- RAM의 기록: 재시작 시 손실
- 고정 제한: 6개 메시지(구성 가능)
- 지속성 없음: 프로덕션에는 Redis/DB 필요
텔레그램 봇이나 웹 앱의 경우 논리는 동일합니다: 사용자 세션의 기록.
중요한 점
- 메시지 형식: 시스템은 별도, 기록은 사용자/어시스턴트만
- 기록 트리밍: [-limit:]로 최근 쌍 보존
- 책임 분리: LLM 요청은 send_request_to_llm, 상태는 main()
- 타임아웃: 긴 컨텍스트를 위한 request_timeout=120
- 로깅: 성능 디버깅을 위한 생성 시간
— Editorial Team
아직 댓글이 없습니다.