티스토리 뷰
728x90
반응형
3·4부에서 만든 로딩/전처리/리트리버·Generation 파트를 실전 서비스로 엮습니다. 이 글은 간단한 문서 Q&A 봇 → 사내 지식베이스 챗봇 → 웹/앱 통합 → FastAPI 백엔드 → Slack/Discord 봇 배포 순서로 진행하며, 즉시 실행 가능한 코드를 제공합니다.
2025.08.16 - [RAG] - RAG Bot 만들기: Generation 파트 — Retrieval·LLM 결합, RetrievalQA, 프롬프트 전략, 대화 메모리
RAG Bot 만들기: Generation 파트 — Retrieval·LLM 결합, RetrievalQA, 프롬프트 전략, 대화 메모리
2025.08.16 - [RAG] - 문서 인덱싱과 검색 — 벡터 스토어 구축 · Semantic Search 구현 문서 인덱싱과 검색 — 벡터 스토어 구축 · Semantic Search 구현2025.08.16 - [RAG] - RAG Bot 만들기: Retrieval 파트 —Vector DB, Vec
4ourfuture.tistory.com
0. 준비물 & 설치
# Core
pip install -U langchain langchain-openai langchain-community langchain-text-splitters
# Vector store (Chroma 권장: 로컬/서버 둘 다 간단)
pip install -U chromadb langchain-chroma
# Loaders (필요한 것만 선택 설치)
pip install pypdf beautifulsoup4 lxml atlassian-python-api \
langchain-google-community google-auth google-auth-oauthlib google-api-python-client
# API 서버
pip install fastapi uvicorn[standard]
# Bot (선택)
pip install slack_bolt discord.py
환경 변수
- OPENAI_API_KEY (필수)
- (선택) Google/Confluence 자격증명
1. 간단한 문서 기반 Q&A 봇 만들기
1-1. 데이터 적재·전처리·청킹
# 1) 샘플: 로컬 폴더에서 문서 로딩
from langchain_community.document_loaders import PyPDFLoader, WebBaseLoader, CSVLoader
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
import re
raw_docs = []
raw_docs += PyPDFLoader("docs/policy.pdf").load()
raw_docs += WebBaseLoader(["https://example.com/faq"]).load()
raw_docs += CSVLoader("data/faq.csv").load()
# 2) 가벼운 클리닝
ZWS = "\u200b"
def clean_text(t: str) -> str:
t = t.replace(ZWS, "")
t = re.sub(r"[\x00-\x08\x0B\x0C\x0E-\x1F]", "", t)
t = re.sub(r"\s+", " ", t).strip()
return t
base_docs = [Document(page_content=clean_text(d.page_content), metadata=d.metadata) for d in raw_docs]
# 3) 청킹
splitter = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=120)
docs = splitter.split_documents(base_docs)
print(f"원문 {len(raw_docs)} → 청크 {len(docs)}")
1-2. 벡터 스토어(Chroma) 구축
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
emb = OpenAIEmbeddings(model="text-embedding-3-small")
vs = Chroma(collection_name="kb", persist_directory="indexes/chroma_kb", embedding_function=emb)
vs.add_documents(docs) # 초기 적재
vs.persist()
retriever = vs.as_retriever(search_kwargs={"k": 4})
1-3. RAG 체인으로 답변 생성
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
PROMPT = ChatPromptTemplate.from_messages([
("system", "너는 정확하고 간결한 한국어 도우미다. 컨텍스트에 없으면 모른다고 답해라."),
("human", "컨텍스트:\n{context}\n\n질문: {question}")
])
def fmt(docs):
return "\n\n".join(f"[source: {d.metadata.get('source','?')}]\n{d.page_content}" for d in docs)
qa = (
{"context": retriever | fmt, "question": RunnablePassthrough()}
| PROMPT | llm | StrOutputParser()
)
print(qa.invoke("노쇼 수수료는?"))
2. 사내 지식베이스 챗봇 구현하기
2-1. 사내 소스 로더(Confluence/Google Drive)
from langchain_community.document_loaders import ConfluenceLoader, UnstructuredFileIOLoader
from langchain_google_community import GoogleDriveLoader
conf_docs = ConfluenceLoader(
url="https://<org>.atlassian.net/wiki",
username="you@company.com",
api_key="ATLASSIAN_API_TOKEN",
space_key="SPACE",
limit=100,
).load()
# Google Drive 폴더 전체 수집 (하위 포함)
gdocs = GoogleDriveLoader(
folder_id="<folder-id>", recursive=True,
file_loader_cls=UnstructuredFileIOLoader,
file_loader_kwargs={"mode": "elements"},
).load()
2-2. 메타데이터·필터·권한
# 문서에 접근 레벨/팀/언어 등 태그 추가
for d in conf_docs + gdocs:
d.metadata.setdefault("access", "employee")
d.metadata.setdefault("team", d.metadata.get("space", "general"))
d.metadata.setdefault("locale", "ko")
# 컬렉션에 병합 업서트
vs.add_documents(conf_docs + gdocs)
vs.persist()
# 예: 팀 기반 필터링 리트리버
team_ret = vs.as_retriever(search_kwargs={"k": 4, "filter": {"team": "support"}})
print(qa.invoke({"question": "영수증 발급 정책?", "context": team_ret})) # (개념 예시)
권한 모델: API단에서 사용자 토큰→팀/권한을 해석해 필터 조건으로 강제하세요(Zero-trust). Pinecone/Weaviate/pgvector 등도 메타 필터를 제공합니다.
2-3. 평가/관측(LangSmith)
운영 전후로 run_on_dataset 평가와 태깅을 넣어 리콜/정확도·지연·비용을 추적하세요.
3. 웹/앱 서비스와의 통합 (최소 예제)
3-1. 간단 웹 위젯 (HTML)
<!doctype html>
<html>
<body>
<input id="q" placeholder="질문을 입력하세요" />
<button onclick="ask()">Ask</button>
<pre id="out"></pre>
<script>
async function ask(){
const r = await fetch("/ask", {method:"POST", headers:{"Content-Type":"application/json"}, body: JSON.stringify({question: document.getElementById('q').value})});
const j = await r.json();
document.getElementById('out').textContent = j.answer;
}
</script>
</body>
</html>
3-2. CORS/보안
- 도메인 화이트리스트로 CORS 제한, Auth 헤더(JWT/쿠키)로 사용자 식별.
- API 게이트웨이/역프록시에서 속도 제한·IP 허용 목록 적용.
4. FastAPI 백엔드 연동
4-1. 단일턴 /ask + 멀티턴 /chat
from fastapi import FastAPI
from pydantic import BaseModel
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
app = FastAPI()
_session = {}
def get_hist(session_id: str):
if session_id not in _session:
_session[session_id] = ChatMessageHistory()
return _session[session_id]
class AskIn(BaseModel):
question: str
@app.post("/ask")
def ask(in_: AskIn):
return {"answer": qa.invoke(in_.question)}
# 멀티턴 (세션 유지)
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
chat_prompt = ChatPromptTemplate.from_messages([
("system", "컨텍스트 기반으로만 답해라. 모르면 모른다고 답해라."),
MessagesPlaceholder("chat_history"),
("human", "컨텍스트:\n{context}\n\n질문: {question}")
])
rag_chat = ({"context": retriever | fmt, "question": RunnablePassthrough()} | chat_prompt | llm | StrOutputParser())
rag_with_hist = RunnableWithMessageHistory(rag_chat, get_hist, input_messages_key="question", history_messages_key="chat_history")
class ChatIn(BaseModel):
session_id: str
question: str
@app.post("/chat")
def chat(in_: ChatIn):
cfg = {"configurable": {"session_id": in_.session_id}}
ans = rag_with_hist.invoke({"question": in_.question}, config=cfg)
return {"answer": ans}
4-2. 실행
uvicorn app:app --reload --port 8000
5. Slack/Discord Bot 배포
5-1. Slack (Bolt for Python, Events API)
# slack_app.py
import os
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
SLACK_BOT_TOKEN = os.getenv("SLACK_BOT_TOKEN")
SLACK_APP_TOKEN = os.getenv("SLACK_APP_TOKEN") # xapp- with Socket Mode
app = App(token=SLACK_BOT_TOKEN)
@app.event("app_mention")
@app.message(re.compile(r"^/ask\s+(.+)", re.I))
def handle_events(message, say, context):
text = message.get("text", "")
q = text.split(" ", 1)[-1] if "/ask" in text else text
answer = qa.invoke(q)
say(answer[:3000])
if __name__ == "__main__":
SocketModeHandler(app, SLACK_APP_TOKEN).start()
- 슬래시 커맨드(/ask)를 만들어도 좋습니다(응답 URL로 답변).
- 사내 권한·채널 제한, 민감어 필터링을 추가하세요.
5-2. Discord (discord.py)
# discord_bot.py
import os, asyncio
import discord
TOKEN = os.getenv("DISCORD_TOKEN")
intents = discord.Intents.default(); intents.message_content = True
client = discord.Client(intents=intents)
@client.event
async def on_message(message: discord.Message):
if message.author.bot: return
if message.content.startswith("!ask "):
q = message.content[5:]
ans = qa.invoke(q)
await message.channel.send(ans[:1900])
asyncio.run(client.start(TOKEN))
6. 마무리
- Q&A 봇: 로컬·웹·CSV/PDF를 통합해 빠르게 구축
- 사내 챗봇: Confluence/Google Drive 등과 연동, 메타 필터로 권한 제어
- 서비스 통합: FastAPI로 엔드포인트 제공, Slack/Discord 봇으로 사내 유통
- 확장: Pinecone/Weaviate/pgvector/Redis Vector 교체, 하이브리드/리랭킹/메모리/평가 추가
728x90
반응형
'RAG' 카테고리의 다른 글
| RAG Bot 만들기: Generation 파트 — Retrieval·LLM 결합, RetrievalQA, 프롬프트 전략, 대화 메모리 (5) | 2025.08.16 |
|---|---|
| 문서 인덱싱과 검색 — 벡터 스토어 구축 · Semantic Search 구현 (3) | 2025.08.16 |
| RAG Bot 만들기: Retrieval 파트 —Vector DB, Vectorization (5) | 2025.08.16 |
| [LangChain] 문서 로딩과 전처리 가이드 (4) | 2025.08.16 |
| LangSmith로 LangChain 체인/에이전트 관측과 평가하기 (3) | 2025.08.16 |
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 클래스형 뷰
- Python
- chat gpt 가격 예상
- 골든크로스
- 1165회 로또
- 퀀트투자
- 자동매매
- chat gpt 모델별 예산
- chat gpt api 비용 계산
- 케라스
- chat gpt 모델 별 가격
- 로또 1164회 당첨
- chat gpt 4o 예산
- 토치비전
- chat gpt 한국어 가격
- chat gpt 모델 api 가격 예측
- Numpy
- 차트분석
- 로또 ai
- 티스토리챌린지
- 기술적분석
- 인공지능 로또 예측
- 오블완
- 주린이탈출
- 주식공부
- 장고 orm sql문 비교
- 주식투자
- 1164회 로또
- 재테크
- 자동매매로직
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
글 보관함
250x250