services: # ============================================ # 경량 버전 (외부 API 사용 + 로컬 MongoDB) # ============================================ # 1) 임베딩 단계 (preprocess 생략, qa_raw.jsonl 직접 사용) embed: image: "${RAG_BATCH_IMAGE:-rag-batch-slim:latest}" network_mode: host build: context: . dockerfile: docker/batch-slim.Dockerfile command: ["python", "/app/scripts/ingest_qa.py"] working_dir: /app depends_on: - qdrant # 모델 게이트웨이 호스트명 → IP 매핑 (DNS 미등록 대응) extra_hosts: - "llm-ai.ex.co.kr:${GATEWAY_IP:-172.16.163.96}" - "embedding-ai.ex.co.kr:${GATEWAY_IP:-172.16.163.96}" - "reranker-ai.ex.co.kr:${GATEWAY_IP:-172.16.163.96}" environment: # 프록시 비활성화 (명시적) HTTP_PROXY: "" HTTPS_PROXY: "" http_proxy: "" https_proxy: "" # Python 출력 버퍼링 비활성화 (실시간 로그) PYTHONUNBUFFERED: "1" # 임베딩(게이트웨이) 설정 TEI_EMBED_URL: "${TEI_EMBED_URL:-https://embedding-ai.ex.co.kr}" EMBED_MODEL_NAME: "${EMBED_MODEL_NAME:-Qwen/Qwen3-Embedding-8B}" MODEL_API_KEY: "${MODEL_API_KEY:-}" MODEL_VERIFY_SSL: "${MODEL_VERIFY_SSL:-false}" API_TIMEOUT: "${API_TIMEOUT:-60}" EMBED_BATCH_SIZE: "${EMBED_BATCH_SIZE:-100}" # Qdrant 저장 배치 (4096차원 → 32MB 요청 한도 초과 방지, 100 권장) VECTOR_BATCH_SIZE: "${VECTOR_BATCH_SIZE:-100}" # 벡터 DB 설정 VECTOR_STORE: "${VECTOR_STORE:-qdrant}" QDRANT_HOST: "${QDRANT_HOST:-127.0.0.1}" QDRANT_PORT: "${QDRANT_PORT:-6333}" QDRANT_COLLECTION: "${QDRANT_COLLECTION:-qa_vectors}" HYBRID_SEARCH_ENABLED: "${HYBRID_SEARCH_ENABLED:-false}" SPARSE_SEARCH_ENABLED: "${SPARSE_SEARCH_ENABLED:-true}" SPARSE_TOP_K: "${SPARSE_TOP_K:-30}" HYBRID_MERGE_TOP_K: "${HYBRID_MERGE_TOP_K:-40}" # 프록시 우회 (내부 IP 직접 연결) NO_PROXY: "${NO_PROXY:-localhost,127.0.0.1,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8,.ex.co.kr}" no_proxy: "${no_proxy:-localhost,127.0.0.1,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8,.ex.co.kr}" volumes: - ./data:/app/data - ./scripts:/app/scripts restart: "no" # 2) 인덱싱 단계 index: image: "${RAG_BATCH_IMAGE:-rag-batch-slim:latest}" network_mode: host command: ["python", "/app/scripts/build_index_qa.py"] working_dir: /app depends_on: embed: condition: service_completed_successfully environment: # 프록시 비활성화 (명시적) HTTP_PROXY: "" HTTPS_PROXY: "" http_proxy: "" https_proxy: "" # Python 출력 버퍼링 비활성화 (실시간 로그) PYTHONUNBUFFERED: "1" # 벡터 DB 설정 VECTOR_STORE: "${VECTOR_STORE:-faiss}" # 프록시 우회 NO_PROXY: "${NO_PROXY:-localhost,127.0.0.1,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8}" no_proxy: "${no_proxy:-localhost,127.0.0.1,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8}" volumes: - ./data:/app/data - ./scripts:/app/scripts restart: "no" # 3) API 서비스 api: image: "${RAG_API_IMAGE:-rag-api-slim:latest}" network_mode: host # embed/index/admin과 통일: 127.0.0.1로 qdrant/mongo 접근 build: context: . dockerfile: docker/service-slim.Dockerfile depends_on: - index - qdrant # 모델 게이트웨이 호스트명 → IP 매핑 (DNS 미등록 대응) extra_hosts: - "llm-ai.ex.co.kr:${GATEWAY_IP:-172.16.163.96}" - "embedding-ai.ex.co.kr:${GATEWAY_IP:-172.16.163.96}" - "reranker-ai.ex.co.kr:${GATEWAY_IP:-172.16.163.96}" environment: # 프록시 비활성화 (명시적) HTTP_PROXY: "" HTTPS_PROXY: "" http_proxy: "" https_proxy: "" # Python 출력 버퍼링 비활성화 (실시간 로그) PYTHONUNBUFFERED: "1" # 모델 게이트웨이 공통 인증/TLS MODEL_API_KEY: "${MODEL_API_KEY:-}" MODEL_VERIFY_SSL: "${MODEL_VERIFY_SSL:-false}" # LLM API (게이트웨이) LLM_BASE_URL: "${LLM_BASE_URL:-https://llm-ai.ex.co.kr}" LLM_MODEL_NAME: "${LLM_MODEL_NAME:-Qwen/Qwen3.6-27B-FP8}" LLM_API_KEY: "${LLM_API_KEY:-}" # 임베딩 / 리랭커 (게이트웨이) TEI_EMBED_URL: "${TEI_EMBED_URL:-https://embedding-ai.ex.co.kr}" EMBED_MODEL_NAME: "${EMBED_MODEL_NAME:-Qwen/Qwen3-Embedding-8B}" TEI_RERANK_URL: "${TEI_RERANK_URL:-https://reranker-ai.ex.co.kr}" TEI_RERANK_MODEL: "${TEI_RERANK_MODEL:-Qwen/Qwen3-Reranker-8B}" EMBED_API_STYLE: "${EMBED_API_STYLE:-openai}" RERANK_API_STYLE: "${RERANK_API_STYLE:-gateway}" API_TIMEOUT: "${API_TIMEOUT:-60}" # Agent tool loop → chatbotApi (WAS 동일 서버) AGENT_MODE: "${AGENT_MODE:-legacy}" AGENT_MAX_ROUNDS: "${AGENT_MAX_ROUNDS:-6}" CHATBOT_API_BASE_URL: "${CHATBOT_API_BASE_URL:-http://127.0.0.1:8086/api}" INTERNAL_TOOL_API_KEY: "${INTERNAL_TOOL_API_KEY:-}" CHATBOT_TOOL_TIMEOUT: "${CHATBOT_TOOL_TIMEOUT:-30}" # MongoDB (로컬 호스트 - 데이터베이스별 인증) MONGO_HOST: "${MONGO_HOST:-127.0.0.1}" MONGO_PORT: "${MONGO_PORT:-27017}" MONGO_USER: "${MONGO_USER:-exlink}" MONGO_PASSWORD: "${MONGO_PASSWORD:-!wkcproqkf1}" MONGO_DATABASE: "${MONGO_DATABASE:-chat_history}" MONGO_COLLECTION: "${MONGO_COLLECTION:-rag_conversations}" # 벡터 DB VECTOR_STORE: "${VECTOR_STORE:-qdrant}" QDRANT_HOST: "${QDRANT_HOST:-127.0.0.1}" QDRANT_PORT: "${QDRANT_PORT:-6333}" QDRANT_COLLECTION: "${QDRANT_COLLECTION:-qa_vectors}" HYBRID_SEARCH_ENABLED: "${HYBRID_SEARCH_ENABLED:-false}" SPARSE_SEARCH_ENABLED: "${SPARSE_SEARCH_ENABLED:-true}" SPARSE_TOP_K: "${SPARSE_TOP_K:-30}" HYBRID_MERGE_TOP_K: "${HYBRID_MERGE_TOP_K:-40}" # 성능 튜닝 FAISS_TOP_K: "${FAISS_TOP_K:-30}" FAISS_THRESHOLD: "${FAISS_THRESHOLD:-0.55}" FAISS_THRESHOLD_REWRITE: "${FAISS_THRESHOLD_REWRITE:-0.50}" RERANK_CANDIDATES: "${RERANK_CANDIDATES:-20}" RERANK_BATCH_SIZE: "${RERANK_BATCH_SIZE:-16}" LOW_CONFIDENCE_THRESHOLD: "${LOW_CONFIDENCE_THRESHOLD:-0.65}" HIGH_CONFIDENCE_THRESHOLD: "${HIGH_CONFIDENCE_THRESHOLD:-0.75}" TOP_N_FOR_LLM: "${TOP_N_FOR_LLM:-5}" LLM_MAX_TOKENS: "${LLM_MAX_TOKENS:-2048}" QUERY_REWRITE_ENABLED: "${QUERY_REWRITE_ENABLED:-true}" CHAT_HISTORY_LIMIT: "${CHAT_HISTORY_LIMIT:-10}" CHAT_HISTORY_HOURS: "${CHAT_HISTORY_HOURS:-24}" CHAT_HISTORY_ALWAYS_INCLUDE: "${CHAT_HISTORY_ALWAYS_INCLUDE:-true}" # 프록시 우회 (내부 IP 직접 연결 + 게이트웨이 도메인) NO_PROXY: "${NO_PROXY:-localhost,127.0.0.1,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8,.ex.co.kr}" no_proxy: "${no_proxy:-localhost,127.0.0.1,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8,.ex.co.kr}" volumes: - ./data:/app/data - ./scripts:/app/scripts restart: unless-stopped # Qdrant 벡터 DB (기본 벡터 스토어) # ※ profiles 제거 → 별도 플래그 없이 항상 기동 qdrant: image: qdrant/qdrant:v1.18.2 # 폐쇄망에 docker load 한 이미지 태그와 반드시 일치시킬 것 ports: - "6333:6333" # REST / gRPC - "6334:6334" # gRPC (선택) volumes: - ./qdrant_data:/qdrant/storage # 벡터 영속 저장 (컨테이너 재시작에도 유지) restart: unless-stopped # 4) 큐레이션 어드민 (벡터DB 검색/조회/삭제/추가 웹 UI) # 실시간 api와 같은 이미지/모듈을 재사용하되 별도 프로세스로 분리 admin: image: "${RAG_API_IMAGE:-rag-api-slim:latest}" network_mode: host command: - python - -m - uvicorn - scripts.admin_service:app - --host - 0.0.0.0 - --port - "${ADMIN_PORT:-28013}" working_dir: /app depends_on: - qdrant # 모델 게이트웨이 호스트명 → IP 매핑 (어드민도 임베딩 호출) extra_hosts: - "embedding-ai.ex.co.kr:${GATEWAY_IP:-172.16.163.96}" - "reranker-ai.ex.co.kr:${GATEWAY_IP:-172.16.163.96}" - "llm-ai.ex.co.kr:${GATEWAY_IP:-172.16.163.96}" environment: HTTP_PROXY: "" HTTPS_PROXY: "" http_proxy: "" https_proxy: "" PYTHONUNBUFFERED: "1" # 임베딩(게이트웨이) — 어드민 검색/추가 시 사용 TEI_EMBED_URL: "${TEI_EMBED_URL:-https://embedding-ai.ex.co.kr}" EMBED_MODEL_NAME: "${EMBED_MODEL_NAME:-Qwen/Qwen3-Embedding-8B}" MODEL_API_KEY: "${MODEL_API_KEY:-}" MODEL_VERIFY_SSL: "${MODEL_VERIFY_SSL:-false}" API_TIMEOUT: "${API_TIMEOUT:-60}" VECTOR_STORE: "${VECTOR_STORE:-qdrant}" QDRANT_HOST: "${QDRANT_HOST:-127.0.0.1}" QDRANT_PORT: "${QDRANT_PORT:-6333}" QDRANT_COLLECTION: "${QDRANT_COLLECTION:-qa_vectors}" HYBRID_SEARCH_ENABLED: "${HYBRID_SEARCH_ENABLED:-false}" SPARSE_SEARCH_ENABLED: "${SPARSE_SEARCH_ENABLED:-true}" SPARSE_TOP_K: "${SPARSE_TOP_K:-30}" HYBRID_MERGE_TOP_K: "${HYBRID_MERGE_TOP_K:-40}" ADMIN_CORS_ORIGINS: "${ADMIN_CORS_ORIGINS:-*}" # 별도 프론트 origin 제한 시 콤마 구분으로 지정 NO_PROXY: "${NO_PROXY:-localhost,127.0.0.1,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8,.ex.co.kr}" no_proxy: "${no_proxy:-localhost,127.0.0.1,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8,.ex.co.kr}" volumes: - ./data:/app/data - ./scripts:/app/scripts restart: unless-stopped