Blog
chatbotragai-agentfine-tuningenterprisellmai
엔터프라이즈 AI 챗봇 구축 가이드 - RAG + Agent + Fine-Tuning 통합
RAG, AI Agent, Fine-Tuning을 결합한 엔터프라이즈 AI 챗봇 구축 방법을 정리합니다. 아키텍처 설계, 대화 관리, 도구 연동, 평가, 운영 모니터링까지 실전 가이드를 제공합니다.
Data Dynamics2026년 4월 16일8 min read
엔터프라이즈 AI 챗봇은 단순한 Q&A를 넘어, 사내 문서 검색, 업무 자동화, 시스템 연동까지 수행하는 종합 AI 어시스턴트입니다. 이 글에서는 RAG + Agent + Fine-Tuning을 결합한 프로덕션 수준의 챗봇 구축 방법을 다룹니다.
1. 엔터프라이즈 챗봇의 요구사항
| 요구사항 | 설명 | 기술 |
|---|---|---|
| 사내 문서 검색 | 위키, Confluence, 기술 문서 검색 | RAG |
| 시스템 연동 | Jira, Slack, DB, 모니터링 연동 | Agent + Tool Use |
| 도메인 특화 응답 | 사내 기술 스택에 맞는 정확한 응답 | Fine-Tuning |
| 대화 맥락 유지 | 멀티턴 대화에서 문맥 유지 | 메모리 관리 |
| 접근 제어 | 사용자 권한에 따른 정보 접근 | ACL + 메타데이터 필터 |
| 안전성 | 할루시네이션 방지, 유해 콘텐츠 차단 | 가드레일 |
2. 아키텍처
통합 아키텍처
[엔터프라이즈 AI 챗봇 아키텍처]
사용자 (Slack / Web / Teams)
↓
┌────────────────────────────────────────────┐
│ API Gateway (인증, 속도 제한) │
└───────────────┬────────────────────────────┘
↓
┌────────────────────────────────────────────┐
│ 대화 관리 서비스 │
│ ├─ 세션 관리 (Redis) │
│ ├─ 의도 분류 → 라우팅 │
│ └─ 대화 이력 관리 │
└───────────────┬────────────────────────────┘
↓
┌────────────────────────────────────────────┐
│ AI 엔진 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ RAG │ │ Agent │ │ Fine- │ │
│ │ 검색 │ │ 도구 │ │ Tuned │ │
│ │ 파이프 │ │ 실행 │ │ 모델 │ │
│ │ 라인 │ │ │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ↓ ↓ │
│ [벡터 DB] [Jira, Slack, │
│ DB, 모니터링] │
└───────────────┬────────────────────────────┘
↓
┌────────────────────────────────────────────┐
│ 가드레일 (입력 검증 + 출력 필터링) │
└───────────────┬────────────────────────────┘
↓
응답 반환
의도 분류 → 라우팅
def route_query(user_message: str, context: dict) -> str:
"""사용자 질의를 적절한 파이프라인으로 라우팅"""
classification = classify_intent(user_message)
if classification == "document_search":
return rag_pipeline(user_message, context)
elif classification == "system_action":
return agent_pipeline(user_message, context)
elif classification == "general_chat":
return chat_pipeline(user_message, context)
elif classification == "data_query":
return text_to_sql_pipeline(user_message, context)
else:
return "죄송합니다. 질문을 이해하지 못했습니다. 다시 질문해주세요."3. RAG 파이프라인 (문서 검색)
사내 문서 인덱싱
# 다양한 소스에서 문서 수집 및 인덱싱
sources = {
"confluence": ConfluenceLoader(url="https://wiki.company.com"),
"github": GitHubLoader(repos=["company/docs", "company/runbook"]),
"notion": NotionLoader(token="..."),
"slack_history": SlackLoader(channels=["#engineering", "#incidents"]),
}
all_docs = []
for source_name, loader in sources.items():
docs = loader.load()
for doc in docs:
doc.metadata["source"] = source_name
doc.metadata["access_level"] = get_access_level(doc)
all_docs.extend(docs)
# 청킹 + 임베딩 + 벡터 DB 저장
chunks = text_splitter.split_documents(all_docs)
vectorstore = Chroma.from_documents(chunks, embeddings)접근 제어 통합 검색
def secure_rag_search(query: str, user: dict) -> str:
"""사용자 권한에 따른 RAG 검색"""
# 사용자 권한으로 필터 구성
access_filter = {
"access_level": {"$in": user["allowed_levels"]},
"department": {"$in": user["departments"]}
}
# 필터링된 검색
docs = vectorstore.similarity_search(
query, k=5, filter=access_filter
)
# LLM으로 응답 생성
context = format_docs_with_sources(docs)
response = rag_chain.invoke({"context": context, "question": query})
return response4. Agent 파이프라인 (업무 자동화)
from langchain_core.tools import tool
@tool
def search_jira(query: str) -> str:
"""Jira에서 이슈를 검색합니다."""
issues = jira_client.search_issues(query, maxResults=5)
return format_issues(issues)
@tool
def create_jira_ticket(title: str, description: str, priority: str) -> str:
"""Jira 티켓을 생성합니다. 사용자 확인 후 실행됩니다."""
issue = jira_client.create_issue(
project="ENG", summary=title, description=description, priority=priority
)
return f"티켓 생성 완료: {issue.key}"
@tool
def query_grafana(metric: str, time_range: str) -> str:
"""Grafana에서 메트릭을 조회합니다."""
data = grafana_client.query(metric, time_range)
return format_metrics(data)
@tool
def send_slack(channel: str, message: str) -> str:
"""Slack 채널에 메시지를 전송합니다."""
slack_client.chat_postMessage(channel=channel, text=message)
return f"메시지 전송 완료: {channel}"
# Agent 구성
agent = create_react_agent(
llm=fine_tuned_llm, # Fine-Tuned 모델 사용
tools=[search_jira, create_jira_ticket, query_grafana, send_slack, search_docs],
prompt="당신은 Data Dynamics의 AI 어시스턴트입니다..."
)5. Fine-Tuned 모델 통합
[Fine-Tuning 효과]
기반 모델 응답: "Spark에서 OOM이 발생하면 메모리를 늘리세요."
Fine-Tuned 모델 응답: "Spark executor OOM 해결 방법:
1. spark.executor.memory를 8g → 16g로 조정 (Airflow DAG: etl_daily.py 수정)
2. 사내 표준 설정: conf/spark-defaults.conf 참고
3. 데이터 스큐 의심 시: #data-team 채널의 '스큐 해결 가이드' 참고
4. 긴급 시: @oncall-data 멘션으로 당직자 호출"
→ 사내 맥락, 도구, 프로세스를 반영한 응답
6. 대화 관리
멀티턴 대화
class ConversationManager:
def __init__(self, max_history: int = 20):
self.sessions = {} # Redis 기반 세션 관리
self.max_history = max_history
def get_context(self, session_id: str) -> list:
"""대화 이력 조회 (요약 포함)"""
history = self.sessions.get(session_id, [])
if len(history) > self.max_history:
# 오래된 대화 요약
old = history[:-10]
summary = llm.invoke(f"다음 대화를 3줄로 요약: {old}")
history = [{"role": "system", "content": f"이전 대화 요약: {summary}"}] + history[-10:]
self.sessions[session_id] = history
return history
def add_message(self, session_id: str, role: str, content: str):
if session_id not in self.sessions:
self.sessions[session_id] = []
self.sessions[session_id].append({"role": role, "content": content})7. 운영 및 모니터링
핵심 지표
| 지표 | 설명 | 목표 |
|---|---|---|
| 응답 정확도 | 정확한 답변 비율 | > 85% |
| 1차 해결률 | 추가 질문 없이 해결 | > 70% |
| 응답 시간 | 평균 응답 지연 | < 5초 |
| 사용자 만족도 | 피드백 긍정 비율 | > 80% |
| 할루시네이션율 | 부정확한 응답 비율 | < 5% |
| DAU | 일 활성 사용자 수 | 증가 추세 |
피드백 루프
[지속적 개선 사이클]
1. 사용자 피드백 수집 (좋아요/싫어요 + 코멘트)
↓
2. 부정 피드백 분석
├─ 검색 실패 → 문서 추가, 청킹 조정
├─ 잘못된 응답 → 프롬프트 개선, Fine-Tuning 데이터 추가
└─ 기능 부재 → 새 도구/API 연동
↓
3. 개선 적용 (RAG/프롬프트/모델 업데이트)
↓
4. A/B 테스트로 효과 검증
↓
5. 반복
참고: 엔터프라이즈 챗봇은 "한 번 구축하고 끝"이 아닙니다. 사내 문서가 업데이트되고, 시스템이 변경되며, 사용자의 기대 수준이 높아지므로, 지속적인 개선 체계가 핵심입니다.
References
- LangChain Documentation — https://python.langchain.com/docs/
- LangGraph Documentation — https://langchain-ai.github.io/langgraph/
- Anthropic. "Building Effective Agents" — https://www.anthropic.com/research/building-effective-agents
— Data Dynamics 엔지니어링 팀