예제 9: 대화형 지식베이스
# week6_11_interactive_kb.py
from langchain_ollama import OllamaEmbeddings, OllamaLLM
from langchain_chroma import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
class InteractiveKnowledgeBase:
"""대화형 지식베이스"""
def __init__(self):
self.embeddings = OllamaEmbeddings(model="mxbai-embed-large")
self.llm = OllamaLLM(model="qwen3:1.7b", temperature=0.5)
self.vectorstore = None
self.retriever = None
self.rag_chain = None
def initialize(self, vectorstore_path="./company_kb"):
"""초기화"""
# 벡터 저장소 로드
self.vectorstore = Chroma(
collection_name="company_knowledge",
embedding_function=self.embeddings,
persist_directory=vectorstore_path
)
# Retriever 설정
self.retriever = self.vectorstore.as_retriever(
search_kwargs={"k": 3}
)
# RAG 체인 구성
template = """당신은 회사의 AI 어시스턴트입니다.
다음 문서를 참고하여 직원의 질문에 답변하세요.
참고 문서:
{context}
직원 질문: {question}
답변 (친절하고 정확하게):"""
prompt = ChatPromptTemplate.from_template(template)
def format_docs(docs):
return "\n\n".join(
f"[{doc.metadata.get('title', '문서')}]\n{doc.page_content}"
for doc in docs
)
self.rag_chain = (
{"context": self.retriever | format_docs,
"question": RunnablePassthrough()}
| prompt
| self.llm
| StrOutputParser()
)
def ask(self, question):
"""질문하기"""
if not self.rag_chain:
return "지식베이스가 초기화되지 않았습니다."
# 관련 문서 찾기
docs = self.retriever.invoke(question)
# 답변 생성
answer = self.rag_chain.invoke(question)
return {
"answer": answer,
"sources": [
{
"title": doc.metadata.get("title", "문서"),
"category": doc.metadata.get("category", "미분류")
}
for doc in docs
]
}
def chat(self):
"""대화형 인터페이스"""
print("=== 회사 지식베이스 챗봇 ===")
print("궁금한 것을 질문하세요. (종료: 'quit')\n")
while True:
question = input("직원: ")
if question.lower() in ['quit', 'exit', '종료']:
print("챗봇을 종료합니다.")
break
result = self.ask(question)
print(f"\nAI: {result['answer']}")
print(f"\n참고 문서: {', '.join([s['title'] for s in result['sources']])}")
print("-" * 70 + "\n")
# 사용 예시 (비대화형 테스트)
kb = InteractiveKnowledgeBase()
kb.initialize()
test_questions = [
"휴가 사용 방법을 알려주세요",
"재택근무는 어떻게 하나요?",
"회의실을 예약하고 싶어요"
]
print("=== 자동 테스트 모드 ===\n")
for question in test_questions:
print(f"질문: {question}")
result = kb.ask(question)
print(f"답변: {result['answer']}")
print(f"출처: {[s['title'] for s in result['sources']]}\n")
결과 :
질문: 휴가 사용 방법을 알려주세요
답변: 휴가 사용 방법:
1. 신청 방법:
사용 조건:
연차 휴가 범위:
사용 제한:
친절한 안내:
휴가 신청 시 부서장과 미리 확인하고, 사용 전 반드시 메신저 체크인을 완료해 주세요! 😊
출처: ['재택근무 규정', '연차 휴가 정책', '회의실 예약 규정']
질문: 재택근무는 어떻게 하나요?
답변: 재택근무 관련 규정은 현재 제공된 문서에 명시되지 않았습니다. 그러나 일반적인 회사 운영 방침에 따르면 다음과 같은 절차를 따릅니다:
예약 및 신청
근무 시간 및 장소
추가 사항
문의 사항이면
감사합니다! 🌟
출처: ['회의실 예약 규정', '개발 환경 설정', '연차 휴가 정책']
질문: 회의실을 예약하고 싶어요
답변: 회의실 예약 절차 및 주의사항
예약 방법:
회의실 종류 선택:
예약 확인:
필요 사항:
노쇼 정책:
참고:
계속적으로 회의실 예약을 원하시면, 부서장과 상담 후 시스템을 통해 예약해 주세요! 😊
출처: ['재택근무 규정', '연차 휴가 정책', '회의실 예약 규정']
예제 10: 문서 업데이트 시스템
# week6_12_update_system.py
from langchain_ollama import OllamaEmbeddings
from langchain_chroma import Chroma
from langchain_core.documents import Document
from datetime import datetime
class VersionedKnowledgeBase:
"""버전 관리 지식베이스"""
def __init__(self, persist_directory="./versioned_kb"):
self.embeddings = OllamaEmbeddings(model="mxbai-embed-large")
self.persist_directory = persist_directory
self.vectorstore = None
def initialize(self):
"""초기화"""
try:
self.vectorstore = Chroma(
collection_name="versioned_docs",
embedding_function=self.embeddings,
persist_directory=self.persist_directory
)
except:
self.vectorstore = None
def add_document(self, content, metadata):
"""문서 추가 (버전 관리)"""
# 버전 정보 추가
metadata["version"] = metadata.get("version", 1)
metadata["updated_at"] = datetime.now().isoformat()
doc = Document(page_content=content, metadata=metadata)
if self.vectorstore is None:
# 새로 생성
self.vectorstore = Chroma.from_documents(
documents=[doc],
embedding=self.embeddings,
collection_name="versioned_docs",
persist_directory=self.persist_directory
)
else:
# 기존에 추가
self.vectorstore.add_documents([doc])
return metadata["version"]
def update_document(self, doc_id, new_content, old_version):
"""문서 업데이트"""
# 새 버전 생성
new_version = old_version + 1
metadata = {
"doc_id": doc_id,
"version": new_version,
"updated_at": datetime.now().isoformat()
}
return self.add_document(new_content, metadata)
def get_latest_version(self, doc_id):
"""최신 버전 조회"""
# 모든 버전 검색
results = self.vectorstore.similarity_search(
"",
k=100, # 충분히 큰 수
filter={"doc_id": doc_id}
)
if not results:
return None
# 버전 번호로 정렬
latest = max(results, key=lambda x: x.metadata.get("version", 0))
return latest
# 테스트
print("=== 버전 관리 시스템 ===\n")
kb = VersionedKnowledgeBase()
kb.initialize()
# 문서 추가
print("1. 초기 문서 추가")
v1 = kb.add_document(
"연차는 입사 1년 후 15일 제공됩니다.",
{"doc_id": "vacation_policy", "version": 1}
)
print(f" 버전 {v1} 생성 완료\n")
# 문서 업데이트
print("2. 문서 업데이트")
v2 = kb.update_document(
"vacation_policy",
"연차는 입사 1년 후 15일 제공됩니다. 3년 이상 근속 시 추가 제공됩니다.",
v1
)
print(f" 버전 {v2} 생성 완료\n")
# 최신 버전 조회
print("3. 최신 버전 조회")
latest = kb.get_latest_version("vacation_policy")
if latest:
print(f" 버전: {latest.metadata['version']}")
print(f" 내용: {latest.page_content}")
print(f" 수정일: {latest.metadata['updated_at']}")
결과 :

| 개념 | 설명 | 사용 시점 |
|---|---|---|
| Embedding | 텍스트 → 벡터 변환 | 모든 RAG 시스템에서 필수 |
| Chroma DB | 벡터 저장소 | 문서 저장 및 검색 |
| Similarity Search | 유사도 기반 검색 | 기본 검색 방식 |
| MMR | 다양성 고려 검색 | 다양한 결과가 필요할 때 |
| Metadata Filter | 조건부 검색 | 특정 카테고리만 검색할 때 |
| RAG Chain | 검색 + 생성 통합 | QA 시스템 구현 시 활용 |
성능 최적화 팁
1. 적절한 청크 크기
python
# 문서 길이에 따라 조절
short_splitter = RecursiveCharacterTextSplitter(chunk_size=200) # 짧은 문서
long_splitter = RecursiveCharacterTextSplitter(chunk_size=1000) # 긴 문서
python
# k값 조정: 너무 많으면 노이즈, 너무 적으면 정보 부족
retriever = vectorstore.as_retriever(search_kwargs={"k": 3}) # 보통 3-5개
python
# 카테고리, 날짜, 부서 등으로 사전 필터링
results = vectorstore.similarity_search(
query,
filter={"department": "개발", "year": 2024}
)
FAQ
Q: Embedding 모델을 바꿀 수 있나요?
A: 네, 다른 Ollama 모델이나 OpenAI Embeddings로 변경 가능합니다. 단, 벡터 차원이 달라지면 재구축이 필요합니다.
Q: Chroma DB 대신 다른 벡터 DB를 사용할 수 있나요?
A: 네, FAISS, Pinecone, Weaviate 등 다양한 옵션이 있습니다.
Q: 검색 정확도를 높이려면?
A:
Q: 대용량 문서는 어떻게 처리하나요?
A: 배치로 나눠서 추가하고, persist_directory로 영속화하세요.