Skip to content
okdoitttttPublic

About

terminal-based RAG system for document Q&A with citation support

Resources

Stars

32 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

134 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Terminal RAG (Web Interface) πŸš€

터미널 감성을 담은 Full-Stack RAG (Retrieval-Augmented Generation) μ›Ή μ• ν”Œλ¦¬μΌ€μ΄μ…˜μž…λ‹ˆλ‹€. Python(FastAPI) λ°±μ—”λ“œμ™€ Next.js ν”„λ‘ νŠΈμ—”λ“œλ‘œ κ΅¬μ„±λ˜μ–΄ 있으며, 둜컬 ν™˜κ²½μ—μ„œ μ•ˆμ „ν•˜κ³  κ°•λ ₯ν•œ λ¬Έμ„œ 검색 및 질문 λ‹΅λ³€ κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€.

μ‚¬μš©μž 인증, μ‚¬μš©μžλ³„ λ¬Έμ„œ 격리, ν•˜μ΄λΈŒλ¦¬λ“œ 검색 + Cross-Encoder μž¬μ •λ ¬, λ¬Έμ„œ μš”μ•½ λͺ¨λ“œ, 슀트리밍 λ‹΅λ³€, 그리고 검색/생성 ν’ˆμ§ˆμ„ μ •λŸ‰μ μœΌλ‘œ μΈ‘μ •ν•˜λŠ” 평가 νŒŒμ΄ν”„λΌμΈκΉŒμ§€ κ°–μΆ˜ ν’€μŠ€νƒ μ‹œμŠ€ν…œμž…λ‹ˆλ‹€.

✨ μ£Όμš” κΈ°λŠ₯

  • μ›Ή 기반 μΈν„°νŽ˜μ΄μŠ€: 터미널 μŠ€νƒ€μΌμ˜ λͺ¨λ˜ν•˜κ³  직관적인 Web UI
  • μ‚¬μš©μž 인증 & 격리:
    • 이메일/λΉ„λ°€λ²ˆν˜Έ 기반 νšŒμ›κ°€μž…Β·λ‘œκ·ΈμΈ (NextAuth v5 + JWT)
    • μ‚¬μš©μžλ³„(user_id) λ¬Έμ„œΒ·μΈλ±μŠ€ 격리둜 λ©€ν‹° ν…Œλ„ŒνŠΈ 지원
  • λ¬Έμ„œ 관리:
    • Drag & Drop 파일 μ—…λ‘œλ“œ (.txt, .md, .pdf, .docx, .xlsx, .pptx, .csv, .hwpx 지원)
      • 지원 ν™•μž₯μžλŠ” νŒŒμ„œ λ ˆμ§€μŠ€νŠΈλ¦¬(src/rag/ingestion/parsers)λ₯Ό 단일 μ†ŒμŠ€λ‘œ κ΄€λ¦¬ν•˜λ©°, GET /config/supported-extensions둜 μ‘°νšŒλ©λ‹ˆλ‹€. μƒˆ νŒŒμ„œ μΆ”κ°€ μ‹œ UI ν—ˆμš© ν˜•μ‹λ„ μžλ™ 반영.
    • μžλ™ νŒŒμ‹± β†’ μ²­ν‚Ή β†’ 벑터 인덱싱
    • λ¬Έμ„œλ³„ λͺ©λ‘ 관리, 검색, κ°œλ³„/일괄 μ‚­μ œ (인덱슀 동기 정리)
    • 증뢄 인덱싱: 파일 ν•΄μ‹œ μΆ”μ μœΌλ‘œ λ³€κ²½λœ 파일만 μž¬μΈλ±μ‹± (CLI)
  • κ³ κΈ‰ 검색 & μ±„νŒ…:
    • 전체 λ¬Έμ„œ μ±„νŒ…: μ—…λ‘œλ“œλœ λͺ¨λ“  λ¬Έμ„œλ₯Ό λŒ€μƒμœΌλ‘œ 질문
    • λ¬Έμ„œ λ‚΄ μ±„νŒ… (Document Scope): νŠΉμ • λ¬Έμ„œ μ•ˆμ—μ„œλ§Œ 질문-λ‹΅λ³€
    • λ¬Έμ„œ μš”μ•½ λͺ¨λ“œ (doc_mode): "μš”μ•½ν•΄μ€˜"와 같은 μš”μ•½ μ˜λ„λ₯Ό κ°μ§€ν•˜λ©΄ top-k 검색 λŒ€μ‹  ν•΄λ‹Ή λ¬Έμ„œμ˜ 청크λ₯Ό μˆœμ„œλŒ€λ‘œ(μƒν•œ summary_max_chunks, κΈ°λ³Έ 50) LLM에 νˆ¬μž…
    • 슀트리밍 λ‹΅λ³€: SSE(Server-Sent Events) 기반 토큰 λ‹¨μœ„ μ‹€μ‹œκ°„ 좜λ ₯
    • νžˆμŠ€ν† λ¦¬ μ €μž₯: λŒ€ν™” λ‚΄μš© μžλ™ μ €μž₯ 및 쑰회
  • RAG μ—”μ§„:
    • Hybrid Search: BM25(ν‚€μ›Œλ“œ) + Vector(의미) 검색을 RRF λ˜λŠ” κ°€μ€‘ν•©μœΌλ‘œ κ²°ν•©
    • Reranking: Cross-Encoder(bge-reranker-v2-m3) 기반 μ •λ°€ μž¬μ •λ ¬ (κΈ°λ³Έ ON)
    • Query Expansion: LLM으둜 μ§ˆλ¬Έμ„ μ—¬λŸ¬ 검색 λ³€ν˜•μœΌλ‘œ μž¬μž‘μ„± (ꡐ차 μ–Έμ–΄ ν•œβ†”μ˜ 포함)
    • ν•œκ΅­μ–΄ ν˜•νƒœμ†Œ 뢄석: Kiwi 기반 ν† ν¬λ‚˜μ΄μ§•μœΌλ‘œ ν•œκ΅­μ–΄ ν‚€μ›Œλ“œ 검색 ν’ˆμ§ˆ ν–₯상
    • λ‹€μ–‘ν•œ μ²­ν‚Ή μ „λž΅: 일반 ν…μŠ€νŠΈ / Markdown ꡬ쑰 보쑴 / Semantic(λ¬Έμž₯ μž„λ² λ”© μœ μ‚¬λ„)
  • μœ μ—°ν•œ λͺ¨λΈ 지원:
    • Cloud: Google Gemini (κΈ°λ³Έ gemini-2.5-flash)
    • Local: Ollama (Llama 3, Mistral λ“±) 연동 κ°€λŠ₯
    • Embedding: Google Gemini Embedding 2 (gemini-embedding-2, κΈ°λ³Έ) / 둜컬 SentenceTransformers μ „ν™˜ κ°€λŠ₯ (embedding.provider)
    • Vector Store: Qdrant(κΈ°λ³Έ) / FAISS 선택
  • CLI 도ꡬ: μΈλ±μ‹±Β·κ²€μƒ‰Β·μ§ˆμ˜λ₯Ό ν„°λ―Έλ„μ—μ„œ 직접 μˆ˜ν–‰ (python -m cli.main)
  • 평가 νŒŒμ΄ν”„λΌμΈ: 검색 λ©”νŠΈλ¦­(Recall@k, MRR λ“±) + RAGAS(LLM-as-judge) μ •λŸ‰ 평가 및 리포트 생성

πŸ—οΈ μ‹œμŠ€ν…œ μ•„ν‚€ν…μ²˜

graph TD
    Client["Browser (Next.js)"] <-->|REST / SSE| API["API Server (FastAPI)"]
    Client <-->|Auth / Session| Auth["NextAuth v5 + Prisma"]
    Auth --> DB[("SQLite (User / Document)")]

    subgraph Backend
        API --> Engine["RAG Engine"]
        Engine -->|Hybrid Search| Retriever["BM25 + Vector + RRF"]
        Retriever --> Reranker["Cross-Encoder Reranker"]
        Engine -->|Store/Search| VectorDB[("Qdrant / FAISS")]
        Engine -->|Generate| LLM["LLM (Gemini / Ollama)"]
    end

    subgraph Ingestion
        Upload["File Upload"] --> Parser["Document Parser (txt/md/pdf/docx/xlsx/pptx/csv/hwpx)"]
        Parser --> Chunker["Chunker (text / markdown / semantic)"]
        Chunker --> Embedder["Embedding (Gemini / Local)"]
        Embedder --> VectorDB
    end

    subgraph Tooling
        CLI["CLI (Typer)"] --> Engine
        Evals["Evaluation (Metrics + RAGAS)"] --> Engine
    end
Loading

πŸ› οΈ 기술 μŠ€νƒ

Frontend

  • Framework: Next.js 16 (App Router)
  • Language: TypeScript 5.x / React 19
  • Styling: TailwindCSS 4
  • State Management: Zustand (Global Store)
  • Auth: NextAuth v5 (Credentials Provider) + @auth/prisma-adapter
  • ORM / DB: Prisma 5 + SQLite
  • UI Components: Lucide React, react-markdown, next-themes

Backend

  • Framework: FastAPI (Python 3.12+)
  • ASGI Server: Uvicorn
  • Package Manager: uv (Rust based)
  • CLI: Typer + Rich
  • Logging: structlog (JSON ꡬ쑰화 λ‘œκΉ…)

AI & Infrastructure

  • Vector DB: Qdrant (Docker, κΈ°λ³Έ) / FAISS (μ˜΅μ…˜)
  • Embedding: Google Gemini Embedding 2 (gemini-embedding-2, 3072-dim, κΈ°λ³Έ) / 둜컬 SentenceTransformers 선택 κ°€λŠ₯ (embedding.provider μ„€μ •μœΌλ‘œ μ „ν™˜)
  • LLM: Google Gemini API / Ollama
  • Reranker: BAAI/bge-reranker-v2-m3 (Cross-Encoder)
  • Keyword Search: rank-bm25
  • Morphology: KiwiPiePy (ν•œκ΅­μ–΄ ν˜•νƒœμ†Œ 뢄석)
  • Evaluation: RAGAS (faithfulness / answer_relevancy / context_precision / context_recall)

μ •ν™•ν•œ νŒ¨ν‚€μ§€/버전은 pyproject.toml, ui/package.json을 항상 μš°μ„  μ°Έμ‘°ν•˜μ„Έμš”.


⚑ Quick Start (Docker Compose)

λͺ¨λ“  ꡬ성 μš”μ†Œ(Qdrant, API, UI)λ₯Ό Docker Compose둜 ν•œ λ²ˆμ— μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€. compose.yaml은 λ‹€μŒ 3개 μ„œλΉ„μŠ€λ₯Ό λ„μ›λ‹ˆλ‹€.

μ„œλΉ„μŠ€ μ„€λͺ… 포트
qdrant 벑터 λ°μ΄ν„°λ² μ΄μŠ€ 6333 (REST), 6334 (gRPC)
api FastAPI λ°±μ—”λ“œ (RAG μ—”μ§„) 8000
ui Next.js ν”„λ‘ νŠΈμ—”λ“œ 3000
# 1. λ°±μ—”λ“œ ν™˜κ²½ λ³€μˆ˜ μ„€μ • (Google Gemini API Key λ“±)
cp .env.example .env
vi .env
#   GOOGLE_API_KEY=...        # Gemini μ‚¬μš© μ‹œ
#   API_KEY=...               # API ↔ UI κ°„ 인증 ν‚€ (production ν•„μˆ˜)
#   OLLAMA_BASE_URL=...       # Ollama μ‚¬μš© μ‹œ (μ˜΅μ…˜)

# 2. ν”„λ‘ νŠΈμ—”λ“œ ν™˜κ²½ λ³€μˆ˜ μ„€μ • (NextAuth)
cp ui/.env.example ui/.env       # λ„μ»€μš©
#   AUTH_SECRET=$(openssl rand -base64 32)  둜 μ±„μš°κ³ 
#   API_KEY λŠ” μœ„ .env 의 κ°’κ³Ό λ™μΌν•˜κ²Œ λ§žμΆ˜λ‹€.

# 3. μ„œλΉ„μŠ€ μ‹€ν–‰
docker compose up -d --build

μ‹€ν–‰ ν›„ λΈŒλΌμš°μ €μ—μ„œ http://localhost:3000으둜 μ ‘μ†ν•˜μ„Έμš”. 졜초 접속 μ‹œ νšŒμ›κ°€μž…(/register) β†’ 둜그인(/login) ν›„ μ‚¬μš©ν•©λ‹ˆλ‹€.

ℹ️ 운영(production) 배포 μ‹œ compose.yaml의 APP_ENV=production 주석을 ν•΄μ œν•˜μ„Έμš”. API_KEYκ°€ λ―Έμ„€μ • μƒνƒœλ‘œ κΈ°λ™λ˜λ©΄ API 미듀웨어가 RuntimeError둜 fail-fast ν•©λ‹ˆλ‹€.


πŸš€ μˆ˜λ™ μ‹œμž‘ν•˜κΈ° (Manual Setup)

0. 사전 μ€€λΉ„ (Prerequisites)

  • Docker: Qdrant 싀행을 μœ„ν•΄ ν•„μš”
  • Node.js: 18.0.0 이상
  • Python: 3.12 이상
  • uv: Python νŒ¨ν‚€μ§€ λ§€λ‹ˆμ € (pip install uv)

1. ν”„λ‘œμ νŠΈ 클둠 & ν™˜κ²½ λ³€μˆ˜ μ„€μ •

git clone https://github.com/okdoittttt/rag.git
cd rag

# λ°±μ—”λ“œ ν™˜κ²½λ³€μˆ˜ μ„€μ •
echo "GOOGLE_API_KEY=your_gemini_api_key" > .env
# Qdrant μ„€μ • (둜컬 μ‹€ν–‰)
echo "QDRANT_HOST=localhost" >> .env
echo "QDRANT_PORT=6333" >> .env

2. 인프라 μ‹€ν–‰ (Vector DB)

Dockerλ₯Ό μ‚¬μš©ν•˜μ—¬ Qdrant 벑터 λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό μ‹€ν–‰ν•©λ‹ˆλ‹€.

# Docker μ»¨ν…Œμ΄λ„ˆ μ‹€ν–‰
docker run -d --name qdrant \
    -p 6333:6333 \
    -v $(pwd)/data/qdrant_storage:/qdrant/storage \
    qdrant/qdrant

3. λ°±μ—”λ“œ μ‹€ν–‰ (API Server)

FastAPI μ„œλ²„λ₯Ό μ‹€ν–‰ν•©λ‹ˆλ‹€.

# μ˜μ‘΄μ„± μ„€μΉ˜ 및 κ°€μƒν™˜κ²½ 동기화
uv sync

# μ„œλ²„ μ‹€ν–‰ (포트 8000)
PYTHONPATH=src uv run uvicorn api.main:app --host 127.0.0.1 --port 8000 --reload

4. ν”„λ‘ νŠΈμ—”λ“œ μ‹€ν–‰ (Web Client)

μƒˆ 터미널을 μ—΄κ³  Next.js ν΄λΌμ΄μ–ΈνŠΈλ₯Ό μ‹€ν–‰ν•©λ‹ˆλ‹€.

cd ui

# ν™˜κ²½ λ³€μˆ˜ μ„€μ • (.env.local 은 .env 보닀 μš°μ„  λ‘œλ“œλ¨)
cp .env.example .env.local
#   AUTH_SECRET : openssl rand -base64 32 둜 생성
#   API_KEY     : 루트 .env 의 κ°’κ³Ό λ™μΌν•˜κ²Œ
#   API_URL     : 둜컬 직접 μ‹€ν–‰ μ‹œ http://127.0.0.1:8000
#   UPLOAD_DIR  : λ°±μ—”λ“œ ν—ˆμš© κ²½λ‘œμ™€ 일치 (μ ˆλŒ€κ²½λ‘œ ꢌμž₯, 예: <repo>/data/uploads)

# μ˜μ‘΄μ„± μ„€μΉ˜
npm install

# DB(SQLite) μŠ€ν‚€λ§ˆ 적용 (졜초 1회)
npx prisma migrate deploy   # λ˜λŠ” 개발 ν™˜κ²½: npx prisma migrate dev

# 개발 μ„œλ²„ μ‹€ν–‰ (포트 3000)
npm run dev

5. 접속

λΈŒλΌμš°μ €μ—μ„œ http://localhost:3000 으둜 μ ‘μ†ν•˜μ—¬ νšŒμ›κ°€μž… ν›„ μ‚¬μš©ν•©λ‹ˆλ‹€.


πŸ–₯️ CLI μ‚¬μš©λ²•

μ›Ή UI 없이 ν„°λ―Έλ„μ—μ„œ 직접 μΈλ±μ‹±Β·κ²€μƒ‰Β·μ§ˆμ˜λ₯Ό μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€. Typer 기반 CLI(cli/main.py)λŠ” cli.main λͺ¨λ“ˆλ‘œ μ‹€ν–‰ν•©λ‹ˆλ‹€.

# λ¬Έμ„œ 인덱싱 (디렉터리/파일 경둜)
PYTHONPATH=src uv run python -m cli.main index ./docs
PYTHONPATH=src uv run python -m cli.main index ./docs --reset   # κΈ°μ‘΄ 인덱슀 μ΄ˆκΈ°ν™” ν›„ μž¬μΈλ±μ‹±

# 검색 (LLM 호좜 없이 κ΄€λ ¨ 청크만 확인 β€” λ””λ²„κΉ…μš©)
PYTHONPATH=src uv run python -m cli.main search "RAG μ²­ν‚Ή μ „λž΅" --top-k 5 --alpha 0.5

# 질의-응닡 (검색 + LLM 생성)
PYTHONPATH=src uv run python -m cli.main ask "이 λ¬Έμ„œμ˜ 핡심 λ‚΄μš©μ„ μ•Œλ €μ€˜" \
    --rerank --expand --show-context --provider gemini

ask μ£Όμš” μ˜΅μ…˜: --top-k, --rerank(μž¬μ •λ ¬), --expand(쿼리 ν™•μž₯), --show-context(μ°Έμ‘° 청크 ν‘œμ‹œ), --provider(gemini/ollama), --verbose. searchλŠ” --alpha(0.0=BM25 ~ 1.0=Vector, κΈ°λ³Έ 0.5)둜 ν•˜μ΄λΈŒλ¦¬λ“œ κ°€μ€‘μΉ˜λ₯Ό μ‘°μ ˆν•˜κ³ , indexλŠ” --reset으둜 κΈ°μ‘΄ 인덱슀λ₯Ό μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.


🌐 μ£Όμš” API μ—”λ“œν¬μΈνŠΈ

Method Path μ„€λͺ…
POST /ask 검색 + LLM λ‹΅λ³€ 생성 (μ°Έμ‘° 청크 포함)
POST /ask/stream SSE 슀트리밍 λ‹΅λ³€ (μ°Έμ‘° 선전솑 ν›„ 토큰 슀트리밍)
POST /search LLM 없이 ν•˜μ΄λΈŒλ¦¬λ“œ 검색 결과만 λ°˜ν™˜
POST /index ν…μŠ€νŠΈ/파일 μ²­ν‚Ή 및 인덱싱 (동일 source μž¬μΈλ±μ‹± μ‹œ κΈ°μ‘΄ 청크 정리)
POST /index/stream SSE둜 인덱싱 μ§„ν–‰ 상황(νŒŒμ‹±β†’μ²­ν‚Ήβ†’μž„λ² λ”© μ§„ν–‰λ₯ β†’μ™„λ£Œ) 슀트리밍
DELETE /index/by-source filename + user_id λ‹¨μœ„ 인덱슀 μ‚­μ œ
GET /config/supported-extensions μ—…λ‘œλ“œ ν—ˆμš© ν™•μž₯자 λͺ©λ‘ 쑰회 (νŒŒμ„œ λ ˆμ§€μŠ€νŠΈλ¦¬ 기반)
GET/POST /config/system-prompt μ‹œμŠ€ν…œ ν”„λ‘¬ν”„νŠΈ 쑰회/κ°±μ‹ 

/ask μš”μ²­μ€ doc_mode, source_filter, summarize_override, expand, rerank, provider, user_id λ“±μœΌλ‘œ λ™μž‘μ„ μ„Έλ°€ν•˜κ²Œ μ œμ–΄ν•  수 μžˆμŠ΅λ‹ˆλ‹€. (μŠ€ν‚€λ§ˆ: src/api/schemas.py)

인증: APIλŠ” X-API-Key 헀더 기반 인증을 μ‚¬μš©ν•©λ‹ˆλ‹€. production ν™˜κ²½μ—μ„œλŠ” API_KEYκ°€ ν•„μˆ˜μž…λ‹ˆλ‹€.


πŸ§ͺ ν…ŒμŠ€νŠΈ 및 평가

λ°±μ—”λ“œ ν…ŒμŠ€νŠΈ

# λ‹¨μœ„/톡합 ν…ŒμŠ€νŠΈ μ‹€ν–‰
uv run pytest

평가 νŒŒμ΄ν”„λΌμΈ (Evaluation)

골든셋(JSONL)을 기반으둜 검색·생성 ν’ˆμ§ˆμ„ μ •λŸ‰ μΈ‘μ •ν•˜κ³  Markdown 리포트λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€. μžμ„Έν•œ λ‚΄μš©μ€ docs/evaluation.mdλ₯Ό μ°Έκ³ ν•˜μ„Έμš”.

# λΉ λ₯Έ νšŒκ·€ (검색 λ©”νŠΈλ¦­λ§Œ: Recall@k, Precision@k, MRR, source_recall)
python -m evals.ragas_runner --golden evals/golden_set.jsonl --out evals/history/quick.csv

# Reranker A/B 비ꡐ
python -m evals.ragas_runner --out evals/history/baseline.csv
python -m evals.ragas_runner --rerank --out evals/history/with_rerank.csv
python -m evals.report --current evals/history/with_rerank.csv --baseline evals/history/baseline.csv

# λ‹΅λ³€ 생성 + RAGAS(LLM-as-judge) 전체 평가
python -m evals.ragas_runner --with-answer --with-ragas --out evals/history/full.csv

μΈ‘μ • μ§€ν‘œ: recall@{1,3,5,10}, precision@5, mrr, source_recall, RAGAS의 faithfulness / answer_relevancy / context_precision / context_recall, 그리고 검색/생성 latency.

API λ¬Έμ„œ

μ„œλ²„κ°€ μ‹€ν–‰ 쀑일 λ•Œ http://localhost:8000/docs μ—μ„œ Swagger UIλ₯Ό 톡해 APIλ₯Ό 직접 ν…ŒμŠ€νŠΈν•  수 μžˆμŠ΅λ‹ˆλ‹€.

About

terminal-based RAG system for document Q&A with citation support

Resources

Stars

32 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages