LLM & Generative AI/RAG in Practice

[LangChain RAG 구축 시리즈 Ep.28] 🧵 멀티 테이블 + 대화형 흐름을 위한 Memory 설계 전략

ygtoken 2025. 4. 5. 22:44
728x90

 

지금까지 우리는 사용자 질문을 기반으로
적절한 Iceberg 테이블을 선택하고, 해당 문서에서 정보를 검색해
GPT가 정답을 생성하는 단발성 RAG 시스템을 구축해왔습니다.

하지만 실제 업무에서 사용자가 묻는 방식은 이렇습니다:

  • “상품 목록 알려줘”
  • (이후) “그 중에서 가격이 가장 높은 건 뭐야?”
  • (이후) “그 상품의 고객 리뷰는 있어?”

이처럼 대화 흐름이 이어지는 구조에서는
이전 질문과 응답이 다음 질문에 영향을 주어야 합니다.

이번 글에서는 이러한 흐름을 구현하기 위해
ConversationBufferMemory를 기반으로 대화 상태를 유지하는 구조를 설계합니다.


🎯 목표

  • LangChain의 Memory 기능을 이해하고 적용
  • 질문-응답의 히스토리를 유지한 대화형 RAG 구현
  • 다양한 테이블에서 이어지는 질의를 자연스럽게 처리

🧠 Step 1. 대화형 QA 체인 생성 함수

# src/conversational_chain.py

from langchain.chains import ConversationalRetrievalChain
from langchain.chat_models import ChatOpenAI
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.memory import ConversationBufferMemory

def create_conversational_chain(db_path: str, collection_name: str, k: int = 2):
    """
    대화형 QA 체인을 구성합니다.
    문맥을 유지하기 위해 Memory 객체를 함께 사용합니다.
    """

    # ✅ 문서 임베딩 모델 설정
    embedding = OpenAIEmbeddings()

    # ✅ ChromaDB 컬렉션 로딩
    vectordb = Chroma(
        persist_directory=db_path,
        collection_name=collection_name,
        embedding_function=embedding
    )

    # ✅ GPT 모델 구성 (대화 기반 응답 생성)
    llm = ChatOpenAI(
        model_name="gpt-3.5-turbo",
        temperature=0.2
    )

    # ✅ 메모리 구성: 이전 질문과 답변을 자동으로 기억
    memory = ConversationBufferMemory(
        memory_key="chat_history",       # 체인에서 사용하는 메모리 키 이름
        return_messages=True             # 이전 대화 기록을 message 형태로 반환
    )

    # ✅ 대화형 QA 체인 생성
    chain = ConversationalRetrievalChain.from_llm(
        llm=llm,
        retriever=vectordb.as_retriever(search_kwargs={"k": k}),
        memory=memory,
        return_source_documents=True     # GPT가 참조한 문서를 함께 반환
    )

    return chain

⚙️ Step 2. FastAPI에서 메모리 적용

# src/rag_server_conversational.py

from fastapi import FastAPI, Depends
from pydantic import BaseModel
from src.auth import verify_api_key
from src.conversational_chain import create_conversational_chain
import uvicorn

app = FastAPI()

# ✅ 요청 구조: 질문 외에 세션 ID 추가 가능 (여기서는 단순 처리)
class QueryRequest(BaseModel):
    question: str

# ✅ 체인 생성 (여기선 단일 컬렉션 사용 예시)
qa_chain = create_conversational_chain(
    db_path="chroma_db",
    collection_name="iceberg_products",
    k=2
)

@app.post("/rag/conversation")
async def rag_conversation(request: QueryRequest, _: str = Depends(verify_api_key)):
    """
    대화형 RAG API.
    이전 질문과 답변을 기억한 상태로 새로운 질문을 처리합니다.
    """

    # ✅ 사용자 질문 추출
    question = request.question

    # ✅ QA 체인 실행 (메모리 포함)
    result = qa_chain(question)

    # ✅ GPT 응답 및 참조 문서 반환
    return {
        "question": question,
        "answer": result["result"],
        "sources": [doc.page_content for doc in result["source_documents"]]
    }

if __name__ == "__main__":
    uvicorn.run("src.rag_server_conversational:app", host="0.0.0.0", port=8000, reload=True)

🧪 테스트 예시 (httpie 사용)

http POST http://localhost:8000/rag/conversation \
  Authorization:"my-secret-key" \
  question="products 테이블의 상품 목록 알려줘"
http POST http://localhost:8000/rag/conversation \
  Authorization:"my-secret-key" \
  question="그 중에서 가격이 높은 상품은?"

✅ GPT는 이전 질문에서 나온 내용과 연결하여 응답 생성


📎 요약 및 핵심 정리

  • LangChain의 ConversationBufferMemory를 활용하여
    질문-응답 흐름을 메모리에 유지하고, 연속적인 문맥 처리가 가능하게 만들었습니다.
  • FastAPI API를 통해 RAG 시스템을 대화형 인터페이스로 확장할 수 있습니다.
  • 이 구조는 SlackBot, 웹챗, 어시스턴트 등 다양한 UI와도 연동 가능합니다.
728x90