Ver3.5 LangChain으로 구축하는 AI 지식관리 자동화 플랫폼 실전 가이드

LangChain으로 구축하는 AI 지식관리 자동화 플랫폼 실전 가이드
🤖 RAG · 벡터DB · 에이전트까지, 지식을 자동으로 수집·정리·검색하는 시스템을 직접 만들어보자!
솔직히 말하면, 2023년부터 AI 개발자 커뮤니티에서 LangChain 얘기 안 나오면 간첩 취급받을 정도였음 ㅋㅋㅋ
근데 막상 "LangChain이 뭐야?"라고 물어보면 제대로 설명 못 하는 사람이 꽤 많아.
그래서 오늘은 LangChain으로 AI 지식관리 자동화 플랫폼을 실제로 어떻게 구축하는지,
개념부터 코드까지 싹 다 정리해볼게.
LangChain은 대형 언어 모델(LLM)을 활용한 애플리케이션을 쉽게 개발할 수 있도록 도와주는 오픈소스 프레임워크야.
2022년 10월 Harrison Chase가 처음 공개했고, 2023년에 폭발적으로 성장해서
GitHub 스타 수가 수개월 만에 7만 개 이상을 돌파했어.
핵심은 LLM(GPT-4, Claude, Gemini 등)을 단순히 API 호출하는 게 아니라,
체인(Chain) 형태로 여러 작업을 연결하고 자동화할 수 있다는 거야.
쉽게 비유하면 이런 거야 👇
LangChain은 '문서 읽기 → 요약 → 저장 → 검색 → 답변 생성'
이런 복잡한 워크플로우를 자동으로 연결해주는 거야!"
진짜 AI 지식관리 시스템이 완성되는 거지 ㅎㅎ"
| 구분 | 단순 LLM API | LangChain 기반 |
|---|---|---|
| 처리 방식 | 단일 요청-응답 | 다단계 체인 자동화 |
| 메모리 | 없음 (매번 초기화) | 대화 히스토리 유지 가능 |
| 외부 데이터 | 프롬프트에 직접 삽입 | 벡터DB 연동 자동 검색 |
| 도구 사용 | 불가 | 에이전트로 자율 도구 선택 |
| 확장성 | 낮음 | 모듈식 고확장성 |
AI 지식관리 자동화 플랫폼이라고 하면 거창하게 들리는데,
사실 핵심 개념은 되게 직관적이야.
"내가 가진 모든 문서·데이터를 AI가 자동으로 읽고, 정리하고, 필요할 때 찾아주는 시스템"
예를 들어볼게 👇
🏢 기업 내부 문서 관리
수천 개의 사내 문서, 회의록, 기술 문서가 있는데
직원이 "작년 Q3 마케팅 전략이 뭐였지?"라고 물으면
AI가 자동으로 관련 문서를 찾아서 요약해서 답해주는 시스템
📚 개인 지식 베이스
내가 읽은 책, 저장한 아티클, 메모들을 AI가 자동으로 인덱싱해서
"저번에 읽은 딥러닝 논문에서 attention 메커니즘 설명 어떻게 했더라?"
이런 질문에 바로 답해주는 개인 AI 비서
🛠️ 개발팀 기술 문서 봇
API 문서, README, 코드 주석을 자동으로 수집해서
"이 함수 어떻게 쓰는 거야?"라고 물으면 관련 코드 예시와 함께 답해주는 봇
이 모든 게 LangChain + 벡터DB + LLM 조합으로 구현 가능해.
이제 실제로 어떻게 만드는지 단계별로 파헤쳐보자! 🔥
LangChain 기반 지식관리 플랫폼의 핵심은 RAG(Retrieval-Augmented Generation)야.
이게 뭔지 모르면 아무것도 못 만드니까 꼭 이해하고 넘어가야 해.
RAG = 검색(Retrieval) + 생성(Generation)
LLM은 학습 데이터 기반으로만 답하기 때문에
최신 정보나 내부 문서에 대해서는 모름.
RAG는 이 문제를 해결하기 위해
질문과 관련된 문서를 먼저 검색한 뒤, 그 내용을 컨텍스트로 넣어서 LLM이 답하게 하는 방식이야.
Fine-tuning은 모델 자체를 재학습시키는 거라 비용이 엄청 많이 들고 데이터 업데이트가 어려워.
RAG는 외부 DB만 업데이트하면 되니까 훨씬 유연하고 실용적이야.
실무에서는 대부분 RAG를 먼저 시도해봐.
자 이제 진짜 코드 짜는 얘기 해보자.
먼저 환경 세팅부터 ㄱㄱ 🚀
# 기본 LangChain 패키지
pip install langchain langchain-community langchain-openai
# 벡터 DB (로컬용 Chroma)
pip install chromadb
# 문서 로더들
pip install pypdf docx2txt beautifulsoup4 requests
# 임베딩 모델 (OpenAI 또는 로컬)
pip install openai tiktoken
# 환경변수 관리
pip install python-dotenv
# 선택: Pinecone (클라우드 벡터DB)
pip install pinecone-client
# .env 파일
OPENAI_API_KEY=sk-your-api-key-here
PINECONE_API_KEY=your-pinecone-key # 선택사항
PINECONE_ENV=us-east-1 # 선택사항
| 라이브러리 | 역할 | 비고 |
|---|---|---|
| langchain | 핵심 프레임워크, 체인·에이전트 구성 | 필수 |
| langchain-openai | OpenAI LLM·임베딩 연동 | 필수 (GPT 사용 시) |
| chromadb | 로컬 벡터 데이터베이스 | 무료, 로컬 개발용 |
| pypdf | PDF 문서 파싱 | 문서 수집 시 필요 |
| pinecone-client | 클라우드 벡터 DB | 프로덕션 환경 권장 |
LangChain은 업데이트가 매우 빠른 라이브러리야.
2024년 기준으로
langchain과 langchain-community가 분리됐어.구버전 코드 그대로 쓰면 import 에러 엄청 남 ㅠㅠ
반드시 공식 문서 최신 버전 확인하고 써!
지식관리 플랫폼의 첫 번째 단계는 데이터 수집이야.
LangChain은 다양한 Document Loader를 제공해서
PDF, 웹페이지, Word, CSV, Notion 등 거의 모든 형식을 지원해.
from langchain_community.document_loaders import PyPDFLoader
from langchain_community.document_loaders import DirectoryLoader
# 단일 PDF 로드
loader = PyPDFLoader("knowledge/document.pdf")
documents = loader.load()
print(f"총 {len(documents)}페이지 로드됨")
print(documents[0].page_content[:200]) # 첫 페이지 미리보기
# 폴더 전체 PDF 자동 로드 (핵심!)
dir_loader = DirectoryLoader(
"knowledge/",
glob="**/*.pdf",
loader_cls=PyPDFLoader
)
all_docs = dir_loader.load()
print(f"총 {len(all_docs)}개 문서 로드 완료")
from langchain_community.document_loaders import WebBaseLoader
import bs4
# 단일 URL 로드
loader = WebBaseLoader(
web_paths=["https://example.com/article"],
bs_kwargs=dict(
parse_only=bs4.SoupStrainer(
class_=("post-content", "article-body") # 필요한 부분만 추출
)
)
)
docs = loader.load()
# 여러 URL 한 번에 처리
urls = [
"https://example.com/page1",
"https://example.com/page2",
"https://example.com/page3"
]
multi_loader = WebBaseLoader(web_paths=urls)
all_web_docs = multi_loader.load()
from langchain_community.document_loaders import (
CSVLoader, # CSV 파일
UnstructuredWordDocumentLoader, # Word 문서
NotionDirectoryLoader, # Notion 내보내기
SlackDirectoryLoader, # Slack 메시지
GitLoader, # Git 저장소 코드
YoutubeLoader, # YouTube 자막
ArxivLoader, # 논문 (arXiv)
)
# 예시: YouTube 영상 자막 로드
yt_loader = YoutubeLoader.from_youtube_url(
"https://www.youtube.com/watch?v=example",
add_video_info=True
)
yt_docs = yt_loader.load()
DirectoryLoader를 cron job이나 스케줄러와 연결하면특정 폴더에 파일을 넣기만 해도 자동으로 지식 베이스에 추가되는 시스템을 만들 수 있어.
진짜 "자동화"의 핵심 포인트야 👍
문서를 로드했으면 이제 청킹(Chunking)을 해야 해.
청킹이란 긴 문서를 LLM이 처리할 수 있는 작은 단위로 나누는 작업이야.
이게 생각보다 엄청 중요해. 청킹을 잘못하면 검색 품질이 확 떨어짐 😱
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 기본 설정
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # 청크 최대 크기 (문자 수)
chunk_overlap=200, # 청크 간 겹치는 부분 (문맥 유지용)
length_function=len,
separators=["\n\n", "\n", ".", "!", "?", " ", ""]
)
# 문서 분할
chunks = text_splitter.split_documents(all_docs)
print(f"총 {len(chunks)}개 청크 생성")
print(f"첫 번째 청크: {chunks[0].page_content[:100]}...")
# 청크 크기 분포 확인
chunk_sizes = [len(c.page_content) for c in chunks]
print(f"평균 청크 크기: {sum(chunk_sizes)/len(chunk_sizes):.0f}자")
| 전략 | 특징 | 적합한 경우 |
|---|---|---|
| RecursiveCharacter | 계층적 구분자로 자연스럽게 분할 | 일반 텍스트, 대부분의 경우 |
| CharacterTextSplitter | 단순 문자 기준 분할 | 구조가 단순한 텍스트 |
| TokenTextSplitter | 토큰 수 기준 분할 | 토큰 비용 정밀 제어 필요 시 |
| MarkdownHeaderSplitter | 마크다운 헤더 기준 분할 | 기술 문서, README |
| SemanticChunker | 의미 단위로 분할 (임베딩 활용) | 고품질 검색이 필요한 경우 |
너무 작으면 → 문맥이 끊겨서 검색 품질 저하
너무 크면 → LLM 컨텍스트 창 초과, 비용 증가
일반적으로 chunk_size: 500~1500, overlap: 100~300이 무난해.
도메인에 따라 실험해보면서 최적값 찾는 게 중요!
청킹된 텍스트를 벡터(숫자 배열)로 변환해서 DB에 저장하는 단계야.
이게 바로 "의미 기반 검색"을 가능하게 하는 핵심 기술이야.
"사과"와 "과일"이 비슷한 벡터값을 가지게 되는 거지 🍎
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from dotenv import load_dotenv
import os
load_dotenv()
# 임베딩 모델 초기화
embeddings = OpenAIEmbeddings(
model="text-embedding-3-small", # 비용 효율적
# model="text-embedding-3-large" # 더 높은 품질
)
# Chroma DB에 저장 (로컬 파일로 영구 저장)
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db", # 저장 경로
collection_name="knowledge_base"
)
print(f"벡터 DB 구축 완료! 총 {vectorstore._collection.count()}개 벡터 저장")
# 기존 DB 불러오기 (재시작 시)
vectorstore = Chroma(
persist_directory="./chroma_db",
embedding_function=embeddings,
collection_name="knowledge_base"
)
from langchain_community.vectorstores import Pinecone
from pinecone import Pinecone as PineconeClient, ServerlessSpec
# Pinecone 초기화
pc = PineconeClient(api_key=os.getenv("PINECONE_API_KEY"))
# 인덱스 생성 (최초 1회)
index_name = "knowledge-base"
if index_name not in pc.list_indexes().names():
pc.create_index(
name=index_name,
dimension=1536, # text-embedding-3-small 차원
metric="cosine",
spec=ServerlessSpec(cloud="aws", region="us-east-1")
)
# LangChain과 연동
vectorstore = Pinecone.from_documents(
documents=chunks,
embedding=embeddings,
index_name=index_name
)
OpenAI 임베딩이 비용 부담되면 HuggingFace 임베딩을 써봐.
from langchain_community.embeddings import HuggingFaceEmbeddingsembeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-m3")완전 무료에 한국어도 잘 지원해! 속도는 좀 느리지만 로컬에서 돌릴 수 있어.
드디어 LangChain의 꽃, 체인(Chain) 구성이야!
벡터 DB에서 관련 문서를 검색하고, LLM에게 답변을 생성하게 하는 파이프라인을 만들어보자.
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
# LLM 초기화
llm = ChatOpenAI(
model="gpt-4o-mini", # 비용 효율적
temperature=0, # 일관된 답변을 위해 0으로 설정
max_tokens=1000
)
# 커스텀 프롬프트 템플릿
prompt_template = """
당신은 지식 관리 AI 어시스턴트입니다.
아래 제공된 컨텍스트를 기반으로 질문에 답변하세요.
컨텍스트에 없는 내용은 "해당 정보를 찾을 수 없습니다"라고 답하세요.
컨텍스트:
{context}
질문: {question}
답변:"""
PROMPT = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
# Retriever 설정
retriever = vectorstore.as_retriever(
search_type="similarity", # 유사도 검색
search_kwargs={"k": 5} # 상위 5개 청크 검색
)
# RAG 체인 생성
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff", # 검색된 문서를 모두 컨텍스트에 넣음
retriever=retriever,
chain_type_kwargs={"prompt": PROMPT},
return_source_documents=True # 출처 문서도 반환
)
# 질문하기!
result = qa_chain.invoke({"query": "머신러닝의 핵심 알고리즘은 무엇인가요?"})
print("답변:", result["result"])
print("\n출처 문서:")
for doc in result["source_documents"]:
print(f" - {doc.metadata.get('source', '알 수 없음')}")
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# 프롬프트 템플릿
prompt = ChatPromptTemplate.from_template("""
다음 컨텍스트를 기반으로 질문에 답변하세요:
컨텍스트: {context}
질문: {question}
""")
# 문서 포맷팅 함수
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
# LCEL 체인 구성 (파이프 연산자로 연결!)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
# 스트리밍 응답
for chunk in rag_chain.stream("LangChain의 주요 컴포넌트는?"):
print(chunk, end="", flush=True)
LangChain 0.1.0부터 도입된 새로운 체인 구성 방식이야.
| 파이프 연산자로 컴포넌트를 연결하는 게 특징.스트리밍, 비동기, 배치 처리를 기본으로 지원하고
코드가 훨씬 직관적이고 읽기 쉬워져. 신규 프로젝트엔 LCEL 쓰는 걸 강력 추천!
단순 Q&A를 넘어서 대화 맥락을 기억하는 챗봇을 만들려면
메모리 관리가 필수야.
"아까 말한 그 내용에 대해 더 설명해줘" 이런 게 가능해지는 거지!
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferWindowMemory
# 최근 N개 대화만 기억하는 메모리 (토큰 절약)
memory = ConversationBufferWindowMemory(
k=5, # 최근 5개 대화 유지
memory_key="chat_history",
return_messages=True,
output_key="answer"
)
# 대화형 RAG 체인
conv_chain = ConversationalRetrievalChain.from_llm(
llm=llm,
retriever=retriever,
memory=memory,
return_source_documents=True,
verbose=True # 디버깅용 로그 출력
)
# 대화 시뮬레이션
questions = [
"LangChain이 무엇인가요?",
"그것의 주요 컴포넌트를 설명해주세요", # '그것'이 LangChain을 가리킴
"그 중에서 가장 중요한 게 뭔가요?" # 이전 맥락 참조
]
for q in questions:
result = conv_chain.invoke({"question": q})
print(f"Q: {q}")
print(f"A: {result['answer']}\n")
| 메모리 타입 | 특징 | 토큰 사용량 |
|---|---|---|
| ConversationBufferMemory | 전체 대화 기록 유지 | 높음 (대화 길수록 증가) |
| ConversationBufferWindowMemory | 최근 N개 대화만 유지 | 중간 (고정) |
| ConversationSummaryMemory | 대화를 요약해서 저장 | 낮음 (요약 비용 발생) |
| ConversationTokenBufferMemory | 토큰 수 기준으로 유지 | 정밀 제어 가능 |
여기서부터가 진짜 고급 기능이야.
에이전트(Agent)는 LLM이 스스로 어떤 도구를 사용할지 결정하는 시스템이야.
단순히 검색만 하는 게 아니라, 계산도 하고, 웹 검색도 하고, 코드도 실행하는 거지!
from langchain.tools import tool
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# 커스텀 도구 정의
@tool
def search_knowledge_base(query: str) -> str:
"""지식 베이스에서 관련 정보를 검색합니다."""
docs = retriever.get_relevant_documents(query)
return "\n\n".join([d.page_content for d in docs[:3]])
@tool
def save_new_knowledge(content: str, title: str) -> str:
"""새로운 지식을 지식 베이스에 저장합니다."""
from langchain.schema import Document
new_doc = Document(
page_content=content,
metadata={"title": title, "source": "user_input"}
)
new_chunks = text_splitter.split_documents([new_doc])
vectorstore.add_documents(new_chunks)
return f"'{title}' 지식이 성공적으로 저장되었습니다. ({len(new_chunks)}개 청크)"
@tool
def summarize_topic(topic: str) -> str:
"""특정 주제에 대한 지식 베이스 내용을 요약합니다."""
docs = retriever.get_relevant_documents(topic)
context = "\n".join([d.page_content for d in docs])
summary_prompt = f"다음 내용을 3줄로 요약해주세요:\n{context}"
return llm.invoke(summary_prompt).content
# 도구 목록
tools = [search_knowledge_base, save_new_knowledge, summarize_topic]
# 에이전트 프롬프트
agent_prompt = ChatPromptTemplate.from_messages([
("system", """당신은 지식 관리 AI 어시스턴트입니다.
사용 가능한 도구를 활용해 사용자의 요청을 처리하세요."""),
MessagesPlaceholder(variable_name="chat_history"),
("human", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
# 에이전트 생성
agent = create_openai_tools_agent(llm, tools, agent_prompt)
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True,
max_iterations=5
)
# 에이전트 실행
result = agent_executor.invoke({
"input": "머신러닝 관련 내용을 검색하고 요약해줘",
"chat_history": []
})
print(result["output"])
체인(Chain): 미리 정해진 순서대로 실행 (결정론적)
에이전트(Agent): LLM이 상황에 따라 어떤 도구를 쓸지 스스로 결정 (자율적)
지식관리 플랫폼에서는 두 가지를 조합해서 쓰는 게 베스트야.
단순 검색은 체인으로, 복잡한 멀티스텝 작업은 에이전트로!
이제 모든 컴포넌트를 합쳐서 완전 자동화 파이프라인을 만들어보자.
새 문서가 추가되면 자동으로 처리하고, 질문에 답하는 완성형 시스템이야!
import os
import time
from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyPDFLoader, TextLoader
from dotenv import load_dotenv
load_dotenv()
class KnowledgeManagementPlatform:
"""AI 지식관리 자동화 플랫폼 메인 클래스"""
def __init__(self, watch_dir: str = "./knowledge_docs"):
self.watch_dir = watch_dir
self.embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
self.llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, chunk_overlap=200
)
# 벡터 DB 초기화
self.vectorstore = Chroma(
persist_directory="./chroma_db",
embedding_function=self.embeddings,
collection_name="auto_knowledge"
)
# 감시 폴더 생성
Path(watch_dir).mkdir(exist_ok=True)
print(f"✅ 플랫폼 초기화 완료. '{watch_dir}' 폴더 감시 중...")
def process_document(self, file_path: str):
"""문서를 자동으로 처리해서 벡터 DB에 저장"""
try:
ext = Path(file_path).suffix.lower()
if ext == ".pdf":
loader = PyPDFLoader(file_path)
elif ext in [".txt", ".md"]:
loader = TextLoader(file_path, encoding="utf-8")
else:
print(f"⚠️ 지원하지 않는 형식: {ext}")
return
docs = loader.load()
chunks = self.text_splitter.split_documents(docs)
self.vectorstore.add_documents(chunks)
print(f"✅ '{Path(file_path).name}' 처리 완료 ({len(chunks)}개 청크)")
except Exception as e:
print(f"❌ 처리 실패 {file_path}: {e}")
def ask(self, question: str) -> dict:
"""질문에 답변 생성"""
retriever = self.vectorstore.as_retriever(
search_kwargs={"k": 5}
)
docs = retriever.get_relevant_documents(question)
if not docs:
return {"answer": "관련 정보를 찾을 수 없습니다.", "sources": []}
context = "\n\n".join([d.page_content for d in docs])
prompt = f"""컨텍스트:\n{context}\n\n질문: {question}\n\n답변:"""
answer = self.llm.invoke(prompt).content
sources = list(set([d.metadata.get("source", "알 수 없음") for d in docs]))
return {"answer": answer, "sources": sources}
def get_stats(self) -> dict:
"""플랫폼 통계 반환"""
count = self.vectorstore._collection.count()
return {
"total_chunks": count,
"watch_directory": self.watch_dir,
"db_path": "./chroma_db"
}
# 파일 변경 감지 핸들러
class DocumentHandler(FileSystemEventHandler):
def __init__(self, platform: KnowledgeManagementPlatform):
self.platform = platform
def on_created(self, event):
if not event.is_directory:
print(f"📄 새 파일 감지: {event.src_path}")
time.sleep(1) # 파일 쓰기 완료 대기
self.platform.process_document(event.src_path)
# 메인 실행
if __name__ == "__main__":
platform = KnowledgeManagementPlatform()
# 파일 감시 시작
observer = Observer()
observer.schedule(DocumentHandler(platform), platform.watch_dir, recursive=True)
observer.start()
print("🚀 AI 지식관리 플랫폼 가동 중!")
print("knowledge_docs 폴더에 파일을 넣으면 자동으로 처리됩니다.\n")
# 대화형 인터페이스
while True:
user_input = input("질문 (종료: q): ").strip()
if user_input.lower() == "q":
break
if user_input:
result = platform.ask(user_input)
print(f"\n💬 답변: {result['answer']}")
print(f"📚 출처: {', '.join(result['sources'])}\n")
observer.stop()
observer.join()
기본 구현은 됐는데, 실제 서비스에 쓰려면 성능 최적화가 필수야.
실무에서 자주 마주치는 문제들과 해결책을 정리해봤어 👇
# 1. MMR (Maximal Marginal Relevance) - 다양성 확보
retriever = vectorstore.as_retriever(
search_type="mmr",
search_kwargs={
"k": 6,
"fetch_k": 20, # 후보 20개 중에서
"lambda_mult": 0.5 # 유사도 vs 다양성 균형 (0~1)
}
)
# 2. 유사도 점수 임계값 설정
retriever = vectorstore.as_retriever(
search_type="similarity_score_threshold",
search_kwargs={"score_threshold": 0.7} # 70% 이상 유사한 것만
)
# 3. 하이브리드 검색 (키워드 + 의미 검색)
from langchain_community.retrievers import BM25Retriever
from langchain.retrievers import EnsembleRetriever
bm25_retriever = BM25Retriever.from_documents(chunks)
bm25_retriever.k = 5
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, vectorstore.as_retriever(search_kwargs={"k": 5})],
weights=[0.4, 0.6] # BM25 40%, 벡터 검색 60%
)
# 캐싱으로 동일 질문 재처리 방지
from langchain.globals import set_llm_cache
from langchain_community.cache import SQLiteCache
set_llm_cache(SQLiteCache(database_path=".langchain.db"))
# 배치 처리로 임베딩 비용 절감
# 한 번에 여러 문서 임베딩 (API 호출 횟수 감소)
batch_size = 100
for i in range(0, len(chunks), batch_size):
batch = chunks[i:i+batch_size]
vectorstore.add_documents(batch)
print(f"배치 {i//batch_size + 1} 처리 완료")
# 문서 로드 시 메타데이터 추가
from langchain.schema import Document
doc_with_meta = Document(
page_content="내용...",
metadata={
"source": "technical_docs",
"category": "AI",
"date": "2024-01-15",
"author": "홍길동"
}
)
# 특정 카테고리만 검색
retriever = vectorstore.as_retriever(
search_kwargs={
"k": 5,
"filter": {"category": "AI"} # AI 카테고리 문서만
}
)
LangChain 팀이 만든 LangSmith를 연동하면
모든 체인 실행 과정을 시각적으로 추적할 수 있어.
어느 단계에서 느린지, 토큰을 얼마나 쓰는지 한눈에 보여.
LANGCHAIN_TRACING_V2=true 환경변수만 설정하면 바로 활성화돼!
만든 플랫폼을 실제 서비스로 배포하려면 API 서버가 필요해.
FastAPI + LangChain 조합이 현재 가장 많이 쓰이는 패턴이야.
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain.chains import RetrievalQA
import uvicorn
app = FastAPI(title="AI 지식관리 플랫폼 API")
# 전역 플랫폼 인스턴스
platform = KnowledgeManagementPlatform()
class QuestionRequest(BaseModel):
question: str
max_results: int = 5
class UploadRequest(BaseModel):
content: str
title: str
category: str = "general"
@app.post("/ask")
async def ask_question(request: QuestionRequest):
"""지식 베이스에 질문하기"""
try:
result = platform.ask(request.question)
return {
"status": "success",
"answer": result["answer"],
"sources": result["sources"]
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/upload")
async def upload_knowledge(request: UploadRequest):
"""새 지식 추가하기"""
from langchain.schema import Document
doc = Document(
page_content=request.content,
metadata={"title": request.title, "category": request.category}
)
chunks = platform.text_splitter.split_documents([doc])
platform.vectorstore.add_documents(chunks)
return {"status": "success", "chunks_added": len(chunks)}
@app.get("/stats")
async def get_stats():
"""플랫폼 통계"""
return platform.get_stats()
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
재능넷의 AI/개발 카테고리를 보면
LangChain 기반 챗봇, 문서 분석 시스템, 지식관리 플랫폼 개발 의뢰가 꽤 있어.
이 기술 스택 익혀두면 프리랜서로도 활용 가능하니까 참고해봐 😊
이 기술 스택을 제대로 익히면 진짜 다양한 곳에 써먹을 수 있어.
특히 기업 내부 문서봇은 요즘 수요가 폭발적으로 늘고 있어서
프리랜서나 스타트업 아이템으로도 충분히 경쟁력 있어 💪
LangChain 개발하다 보면 이런 에러들 꼭 만나게 됨 ㅋㅋㅋ
미리 알아두면 삽질 시간 확 줄일 수 있어!
원인: OpenAI API 요청 한도 초과
해결: 배치 처리 시 time.sleep(1) 추가, 또는 tenacity 라이브러리로 재시도 로직 구현
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))
def embed_with_retry(texts):
return embeddings.embed_documents(texts)
원인: 검색된 문서가 너무 많아서 LLM 컨텍스트 창 초과
해결: k 값 줄이기, 청크 크기 줄이기, 또는 map_reduce 체인 타입 사용
# chain_type을 "stuff" 대신 "map_reduce"로 변경
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="map_reduce", # 각 문서를 개별 처리 후 합침
retriever=retriever
)
원인: 청킹 전략 또는 임베딩 모델 문제
해결 체크리스트
✅ chunk_size와 overlap 값 조정 (도메인에 맞게)
✅ 더 좋은 임베딩 모델 사용 (text-embedding-3-large)
✅ 메타데이터 필터링 추가
✅ 하이브리드 검색(BM25 + 벡터) 적용
✅ 쿼리 확장(Query Expansion) 기법 적용
영어 기준으로 설계된 청킹 전략이 한국어에서는 잘 안 맞을 수 있어.
한국어는 형태소 단위로 처리하는 게 더 효과적이야.
konlpy나 kiwipiepy 같은 한국어 형태소 분석기와 조합하면검색 품질이 눈에 띄게 올라가!
오늘 다룬 내용을 한 번에 정리해보면 👇
🔹 LangGraph: 복잡한 멀티에이전트 워크플로우 구현 (LangChain 팀 신작)
🔹 LangSmith: 프로덕션 모니터링 & 평가 시스템
🔹 Llama Index: LangChain의 대안, 특히 RAG에 특화
🔹 로컬 LLM: Ollama + LangChain으로 완전 오프라인 시스템
🔹 멀티모달 RAG: 이미지·음성까지 처리하는 고급 시스템
LangChain은 계속 빠르게 발전하고 있어서 공식 문서를 주기적으로 체크하는 게 중요해.
그리고 재능넷 같은 플랫폼에서 실제 프로젝트 경험을 쌓으면서
이론과 실무를 함께 익히는 게 가장 빠른 성장 방법이야 🚀
처음엔 복잡해 보여도 하나씩 만들다 보면 어느새 완성된 AI 지식관리 플랫폼이 눈앞에 있을 거야.
코딩은 결국 만들어보는 게 답이니까! 화이팅 💪
댓글 작성
이 글에 대한 여러분의 생각을 들려주세요
로그인이 필요합니다
댓글을 작성하려면 먼저 로그인해주세요.
댓글 0
지식인의 숲 - 지적 재산권 보호 고지
지적 재산권 보호 고지
- 저작권 및 소유권: 본 컨텐츠는 재능넷의 독점 AI 기술로 생성되었으며, 대한민국 저작권법 및 국제 저작권 협약에 의해 보호됩니다.
- AI 생성 컨텐츠의 법적 지위: 본 AI 생성 컨텐츠는 재능넷의 지적 창작물로 인정되며, 관련 법규에 따라 저작권 보호를 받습니다.
- 사용 제한: 재능넷의 명시적 서면 동의 없이 본 컨텐츠를 복제, 수정, 배포, 또는 상업적으로 활용하는 행위는 엄격히 금지됩니다.
- 데이터 수집 금지: 본 컨텐츠에 대한 무단 스크래핑, 크롤링, 및 자동화된 데이터 수집은 법적 제재의 대상이 됩니다.
- AI 학습 제한: 재능넷의 AI 생성 컨텐츠를 타 AI 모델 학습에 무단 사용하는 행위는 금지되며, 이는 지적 재산권 침해로 간주됩니다.
아직 댓글이 없습니다.