반응형
SMALL
Retrieval + Multi‑Agent 결합: 사내 FAQ 챗봇에 수치 계산 에이전트 붙이기
‘LangChain 시리즈’ 다섯 번째 글입니다. 앞선 1–4편에서 학습한 LangChain (core·tools·LCEL), LangGraph, 멀티 에이전트 패턴을 모두 활용해 엔터프라이즈 FAQ 챗봇을 만들겠습니다. 목표는 문서 검색(RAG), 수치 계산, 대화 기억을 결합해 “콘텐츠 + 계산 결과 + 맥락 유지”를 한 번에 제공하는 프로덕션급 시스템을 구축하는 것입니다.
1. 프로젝트 개요
| 항목 | 설명 |
|---|---|
| 도메인 | 사내 정책·제도·복리후생 FAQ + 급여 계산 |
| 핵심 기능 | ▲ 문서 검색 FAQ 답변 ▲ 실수령/세율 계산 ▲ 대화 맥락 기억 ▲ 대시보드 관측 |
| 기술 스택 | Python 3.11, LangChain 0.3.1, LangGraph 0.0.x, DuckDB 벡터스토어, FastAPI + LangServe, Redis Cache, Prometheus + Grafana |
| 패턴 | Supervisor (FAQ Agent + Calc Agent) + Short‑Term Memory |
| 요구 수준 | SLA p95 응답 < 3 s, 월 운영 비용 ≤ 50 USD, PII Zero‑Leak |
| 유저 스토리 | “연 봉 6,000에서 4대 보험 공제 후 실수령?” → RAG 규정 검색 → Calc Agent 계산 → Supervisor 종합 답변 (출처 + 숫자) |
확장 목표: 이후 글에서 OrgChart 라우팅, 다국어 번역 Agent, 실시간 Slack HITL을 추가할 예정입니다.
2. 시스템 아키텍처 (확장판)
┌─────────────── API LAYER ────────────────┐
│ LangServe │
│ /chat (REST/WS) /health /metrics │
└────────────────────────────────────────────┘
│ ▲
▼ │ Prometheus Exporter
┌────────────────────────────────────────────┐
│ LangGraph Workflow (Supervisor) │
│ ↳ Checkpoint: sqlite:///faq.db │
└──────────────────┬─────────────────────────┘
tool:ask_faq │ tool:ask_calc
│
┌──────────────┐ │ ┌───────────────┐
│ FAQ Agent │ │ │ Calc Agent │
│ (RAG) │ │ │ (Tool: tax) │
└──────────────┘ │ └───────────────┘
▲ │ ▲
│ │ │
DuckDB Vectors │ Redis Cache (net_salary)
▲ │
│ Embeddings (OpenAI) & Metadata
Raw PDF/Text ◀── Data Pipeline (GitHub Actions)
2‑1. 확장 고려 사항
- 다중 모델 – FAQ Agent는 GPT‑3.5, Calc Agent는 GPT‑4o 사용.
- 캐시 계층 – Redis TTL 300 s로 계산 중복 호출 비용 ↓.
- PII Gateway – 답변 전
re.sub()로 주민번호·계좌 등 패턴 제거.
3. 데이터 파이프라인 & 인덱싱
3‑1. ETL 워크플로
- 수집 (GitHub Actions Cron) : SharePoint → Azure Blob → repo
/data/raw. - 변환 : PDFToText → markdown,
extract_yaml_frontmatter. - 분할 & 메타데이터 부여 :
RecursiveCharacterTextSplitter. - 임베딩 & 벡터 저장 : DuckDB
faq.duckdb테이블faq_vectors. - CI Test : 새 문서 5 건 랜덤 QA 테스트 → LangSmith Eval score ≥ 0.8 시 머지.
# .github/workflows/faq_ingest.yaml
jobs:
ingest:
schedule: [cron: '0 3 * * *']
steps:
- uses: actions/checkout@v4
- run: pip install -r requirements.txt
- run: python scripts/ingest_faq.py
- run: python scripts/test_eval.py # LangSmith Eval
- run: git add data && git commit -m "Update FAQ" && git push
3‑2. 벡터 인덱스 만들기 (상세)
chunker = RecursiveCharacterTextSplitter(
chunk_size=400,
chunk_overlap=80,
separators=["\n\n", "\n", ". "],
)
chunks = chunker.split_documents(docs)
store = DuckDB.from_documents(
chunks,
OpenAIEmbeddings(model="text-embedding-3-small"),
db_file="faq.duckdb",
table="faq_vectors",
metadata_fields=["title", "category", "updated_at"],
)
4. 에이전트·체인 설계 (세부)
4‑1. FAQ Agent (RAG + Summary Memory)
retriever = DuckDB("faq.duckdb", table="faq_vectors").as_retriever(k=5)
faq_prompt = ChatPromptTemplate(
"""당신은 HR 전문가입니다. 문서 {context}를 참고해 최대 180 자 이내로 답하세요.
출처는 title (yyyy‑mm) 형식으로 표기.
질문: {question}
답변:"""
)
faq_chain = {
"context": retriever,
"question": RunnablePassthrough(),
} | faq_prompt | ChatOpenAI(model="gpt-3.5-turbo") | StrOutputParser()
faq_memory = ConversationSummaryMemory(llm=ChatOpenAI(), max_token_limit=600)
faq_agent = create_react_agent(llm=ChatOpenAI(), tools=[], chain=faq_chain, memory=faq_memory)
4‑2. Calc Agent & 금액 포매팅 툴 개선
@tool(spec={
"title": "net_salary",
"description": "연봉과 세율을 입력받아 월 실수령액과 연 세후액을 계산합니다.",
"parameters": {
"type": "object",
"properties": {
"gross": {"type": "integer", "description": "연봉(원)", "minimum": 10000000},
"tax_year": {"type": "integer", "description": "세율이 적용될 과세연도"}
},
"required": ["gross"]
}
})
def net_salary(gross:int, tax_year:int=2025) -> str:
monthly, yearly = taxcalc.net(gross, year=tax_year)
return f"세후 연 {yearly:,}원 / 월 {monthly:,}원"
calc_agent = create_react_agent(
ChatOpenAI(model="gpt-4o-mini"),
tools=[net_salary],
system_message="Payroll calculator. 모든 금액은 원 단위로 콤마 구분.",
)
4‑3. Supervisor 에이전트 프롬프트 개선
sys_msg = """당신은 HR 헬프데스크 AI입니다.
- 1단계: ask_faq 로 규정·제도 답변 요약
- 2단계: 필요한 경우 ask_calc 로 계산
- 최종 출력 형식: [답변]\n[계산 결과](있으면)\n[출처: title (yyyy‑mm)]"""
5. LangGraph 워크플로 & 체크포인트 전략
workflow = StateGraph(supervisor, memory="sqlite:///faq.db", checkpoint="OnError")
workflow.on_error(lambda err, state: send_slack_alert(err))
- Checkpoint OnError: 오류 발생 시에만 DB 쓰기 → TPS ↑ 15 %.
- Slack Alert: 3 회 연속 오류 시 채널 #ai‑faq‑alert 알림.
6. LangServe 배포 & Docker Compose
6‑1. main.py
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(CORSMiddleware, allow_origins=["https://intra.company.com"], allow_methods=["*"]) # CORS
6‑2. docker-compose.yml
services:
faq-bot:
build: .
command: gunicorn -k uvicorn.workers.UvicornWorker main:app -b 0.0.0.0:8000
volumes:
- ./faq.duckdb:/app/faq.duckdb
environment:
OPENAI_API_KEY: ${OPENAI_API_KEY}
depends_on:
- redis
redis:
image: redis:7-alpine
prometheus:
image: prom/prometheus
grafana:
image: grafana/grafana
- Healthcheck:
/health엔드포인트로 컨테이너 재시작 정책.
7. 모니터링·평가·알림
| 레이어 | 도구 | 지표 |
|---|---|---|
| LLM 콜 | LangSmith Trace | 토큰·지연·비용 per Node |
| API | Prometheus | p95 latency, error_rate |
| DB | DuckDB PRAGMA | check point flush latency |
| Ops | GitHub Actions | ingest 테스트 Eval score |
7‑1. LangSmith Eval 스크립트
from langsmith import EvaluationResult, Client
client = Client()
score = client.evaluate_run(run_id, runset=">=0.85")
if not score.passed:
raise SystemExit("Eval failed")
- Blocking: CI 머지 전 자동 검증, 품질 하락 커밋 차단.
8. 성능·보안 최적화 총정리
8‑1. 성능
- Batch Embedding 96 → TPS +30 %.
- Async stream → 프론트 체감 지연 ‑45 %.
- Checkpoint OnError → DB I/O ‑60 %.
8‑2. 보안
| 위험 | 완화 |
|---|---|
| PII 유출 | 마스킹 레이어 + HITL 승인 단계 |
| Tool 과다 호출 | Rate limit + max_iterations 8 |
| API Key 노출 | Docker Secrets, .env gitignore |
반응형
LIST
'인공지능 (AI) > LangChain&LangGraph' 카테고리의 다른 글
| 6 – 챗봇 배포와 최적화 (2) | 2025.06.18 |
|---|---|
| 4 – LangGraph로 멀티 에이전트 구성하기 (1) | 2025.06.18 |
| 3 – LangChain 핵심 모듈 심화 (1) | 2025.06.18 |
| 2 – LangGraph 소개 (0) | 2025.06.18 |
| 1 – LangChain 개요와 아키텍처 (0) | 2025.06.18 |