CTRL+F AI Gateway 아키텍처

최종 검증일: 2026-01-07


1. 전체 시스템 아키텍처

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                    CTRL+F 시스템 구성도                                    │
└─────────────────────────────────────────────────────────────────────────────────────────┘

                                    ┌─────────────┐
                                    │   사용자     │
                                    │  (Browser)  │
                                    └──────┬──────┘
                                           │
                                           ▼
                              ┌────────────────────────┐
                              │     ctrlf-front        │
                              │       (React)          │
                              │                        │
                              │  - 채팅 UI             │
                              │  - 관리자 대시보드     │
                              │  - 교육 영상 뷰어     │
                              └───────────┬────────────┘
                                          │
                                          │ REST API / SSE / WebSocket
                                          ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                  ctrlf-back (Spring Boot)                               │
│                                                                                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  │
│  │ Auth        │  │ User        │  │ Education   │  │ Policy      │  │ Video       │  │
│  │ Service     │  │ Service     │  │ Service     │  │ Service     │  │ Service     │  │
│  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘  │
│                                                                                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐                    │
│  │ FAQ         │  │ Quiz        │  │ Telemetry   │  │ Storage     │                    │
│  │ Service     │  │ Service     │  │ Service     │  │ Service     │                    │
│  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘                    │
└────────────────────────────────────────┬────────────────────────────────────────────────┘
                                         │
                                         │ Internal API (X-Internal-Token)
                                         ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              ctrlf-ai (FastAPI) - AI Gateway                            │
│                                        :8000                                            │
│                                                                                         │
│  ┌──────────────────────────────────────────────────────────────────────────────────┐  │
│  │                              API Layer (FastAPI Routers)                          │  │
│  │                                                                                   │  │
│  │  /ai/chat/*  /ai/quiz/*  /ai/faq/*  /ai/gap/*  /internal/ai/*  /ws/*            │  │
│  └──────────────────────────────────────────────────────────────────────────────────┘  │
│                                         │                                               │
│  ┌──────────────────────────────────────┴───────────────────────────────────────────┐  │
│  │                              Service Layer                                        │  │
│  │                                                                                   │  │
│  │  ChatService  QuizService  FaqService  GapService  VideoRenderer  SourceSetOrch │  │
│  └──────────────────────────────────────────────────────────────────────────────────┘  │
│                                         │                                               │
│  ┌──────────────────────────────────────┴───────────────────────────────────────────┐  │
│  │                              Client Layer                                         │  │
│  │                                                                                   │  │
│  │  LLMClient  MilvusClient  RAGFlowClient  BackendClient  TTSProvider  Storage    │  │
│  └──────────────────────────────────────────────────────────────────────────────────┘  │
└────────────────────────────────────────┬────────────────────────────────────────────────┘
                                         │
              ┌──────────────────────────┼──────────────────────────┐
              │                          │                          │
              ▼                          ▼                          ▼
┌─────────────────────────┐  ┌─────────────────────────┐  ┌─────────────────────────┐
│   LLM Server (vLLM)     │  │   ctrlf-ragflow         │  │   Milvus               │
│                         │  │                         │  │                         │
│  EXAONE-3.5-7.8B        │  │  - Document Parsing     │  │  - Vector Storage       │
│  (OpenAI API 호환)      │  │  - Chunking             │  │  - Similarity Search    │
│                         │  │  - RAG Search           │  │                         │
│  :1237                  │  │  :8080                  │  │  :19540                 │
└─────────────────────────┘  └─────────────────────────┘  └─────────────────────────┘

              ┌──────────────────────────┬──────────────────────────┐
              │                          │                          │
              ▼                          ▼                          ▼
┌─────────────────────────┐  ┌─────────────────────────┐  ┌─────────────────────────┐
│   Elasticsearch         │  │   Kibana                │  │   AWS S3 / MinIO       │
│                         │  │                         │  │                         │
│  - Log Storage          │  │  - Log Dashboard        │  │  - Video Storage        │
│  - Search               │  │  - Visualization        │  │  - Document Storage     │
│                         │  │                         │  │                         │
│  :9200                  │  │  :5601                  │  │                         │
└─────────────────────────┘  └─────────────────────────┘  └─────────────────────────┘


2. AI Gateway 내부 아키텍처

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              ctrlf-ai (FastAPI) 내부 구조                                │
└─────────────────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                      app/                                               │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                         │
│  ┌─────────────────────────────────────────────────────────────────────────────────┐   │
│  │                              api/v1/ (API Layer)                                 │   │
│  │                                                                                  │   │
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │   │
│  │  │ chat.py  │ │chat_     │ │ quiz_    │ │ faq.py   │ │ gap_     │ │ health.py│ │   │
│  │  │          │ │stream.py │ │generate  │ │          │ │suggest   │ │          │ │   │
│  │  │ /ai/chat │ │/ai/chat/ │ │/ai/quiz/ │ │/ai/faq/  │ │/ai/gap/  │ │ /health  │ │   │
│  │  │ /messages│ │stream    │ │generate  │ │generate  │ │policy-   │ │ /ready   │ │   │
│  │  └──────────┘ └──────────┘ └──────────┘ └──────────┘ │edu/sugg  │ └──────────┘ │   │
│  │                                                      └──────────┘              │   │
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐              │   │
│  │  │source_   │ │rag_      │ │render_   │ │ws_render │ │feedback  │              │   │
│  │  │sets.py   │ │documents │ │jobs.py   │ │_progress │ │.py       │              │   │
│  │  │          │ │.py       │ │          │ │.py       │ │          │              │   │
│  │  │/internal │ │/internal │ │/internal │ │/ws/      │ │/internal │              │   │
│  │  │/ai/      │ │/ai/rag-  │ │/ai/      │ │videos/   │ │/ai/      │              │   │
│  │  │source-   │ │documents │ │render-   │ │{id}/     │ │feedback  │              │   │
│  │  │sets/     │ │/ingest   │ │jobs      │ │render-   │ │          │              │   │
│  │  │{id}/     │ │          │ │          │ │progress  │ │          │              │   │
│  │  │start     │ │          │ │          │ │(WS)      │ │          │              │   │
│  │  └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘              │   │
│  └─────────────────────────────────────────────────────────────────────────────────┘   │
│                                          │                                              │
│                                          ▼                                              │
│  ┌─────────────────────────────────────────────────────────────────────────────────┐   │
│  │                            services/ (Business Logic)                            │   │
│  │                                                                                  │   │
│  │  ┌────────────────────┐  ┌────────────────────┐  ┌────────────────────┐         │   │
│  │  │   chat_service.py  │  │ chat_stream_       │  │ quiz_generate_     │         │   │
│  │  │                    │  │ service.py         │  │ service.py         │         │   │
│  │  │  - PII Masking     │  │                    │  │                    │         │   │
│  │  │  - Intent Class.   │  │  - NDJSON Stream   │  │  - Quiz Gen        │         │   │
│  │  │  - RAG Search      │  │  - Chunked Response│  │  - QC Validation   │         │   │
│  │  │  - LLM Generate    │  │                    │  │                    │         │   │
│  │  │  - Response Build  │  │                    │  │                    │         │   │
│  │  └────────────────────┘  └────────────────────┘  └────────────────────┘         │   │
│  │                                                                                  │   │
│  │  ┌────────────────────┐  ┌────────────────────┐  ┌────────────────────┐         │   │
│  │  │   faq_service.py   │  │ gap_suggestion_    │  │ source_set_        │         │   │
│  │  │                    │  │ service.py         │  │ orchestrator.py    │         │   │
│  │  │  - FAQ Draft Gen   │  │                    │  │                    │         │   │
│  │  │  - Batch Generate  │  │  - Gap Analysis    │  │  - Doc Processing  │         │   │
│  │  │  - RAG Search      │  │  - Suggestion Gen  │  │  - RAGFlow Ingest  │         │   │
│  │  └────────────────────┘  └────────────────────┘  └────────────────────┘         │   │
│  │                                                                                  │   │
│  │  ┌────────────────────┐  ┌────────────────────┐  ┌────────────────────┐         │   │
│  │  │ video_renderer_    │  │ render_job_        │  │ script_generation_ │         │   │
│  │  │ mvp.py             │  │ runner.py          │  │ service.py         │         │   │
│  │  │                    │  │                    │  │                    │         │   │
│  │  │  - TTS Generation  │  │  - Job Management  │  │  - Script Gen      │         │   │
│  │  │  - Video Assembly  │  │  - Progress Track  │  │  - Scene Planning  │         │   │
│  │  │  - S3 Upload       │  │  - Callback        │  │                    │         │   │
│  │  └────────────────────┘  └────────────────────┘  └────────────────────┘         │   │
│  │                                                                                  │   │
│  │  ┌────────────────────┐  ┌────────────────────┐                                 │   │
│  │  │   intent_service   │  │   pii_service.py   │                                 │   │
│  │  │                    │  │                    │                                 │   │
│  │  │  - Rule Router     │  │  - INPUT Masking   │                                 │   │
│  │  │  - LLM Router      │  │  - OUTPUT Masking  │                                 │   │
│  │  │  - Intent Classify │  │  - LOG Masking     │                                 │   │
│  │  └────────────────────┘  └────────────────────┘                                 │   │
│  └─────────────────────────────────────────────────────────────────────────────────┘   │
│                                          │                                              │
│                                          ▼                                              │
│  ┌─────────────────────────────────────────────────────────────────────────────────┐   │
│  │                           clients/ (External Connections)                        │   │
│  │                                                                                  │   │
│  │  ┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐               │   │
│  │  │  llm_client.py   │  │ milvus_client.py │  │ ragflow_client.py│               │   │
│  │  │                  │  │                  │  │                  │               │   │
│  │  │  - Chat Complet. │  │  - Vector Search │  │  - RAG Search    │               │   │
│  │  │  - Streaming     │  │  - Similarity    │  │  - Doc Ingest    │               │   │
│  │  │  - OpenAI API    │  │                  │  │  - Chunk Fetch   │               │   │
│  │  └──────────────────┘  └──────────────────┘  └──────────────────┘               │   │
│  │                                                                                  │   │
│  │  ┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐               │   │
│  │  │ backend_client   │  │ tts_provider.py  │  │ storage_provider │               │   │
│  │  │                  │  │                  │  │                  │               │   │
│  │  │  - User Data     │  │  - gTTS          │  │  - Local         │               │   │
│  │  │  - Education     │  │  - AWS Polly     │  │  - S3            │               │   │
│  │  │  - Callback      │  │  - GCP TTS       │  │  - Presigned URL │               │   │
│  │  └──────────────────┘  └──────────────────┘  └──────────────────┘               │   │
│  │                                                                                  │   │
│  │  ┌──────────────────┐                                                           │   │
│  │  │  http_client.py  │                                                           │   │
│  │  │                  │                                                           │   │
│  │  │  - httpx Async   │                                                           │   │
│  │  │  - Connection    │                                                           │   │
│  │  │    Pooling       │                                                           │   │
│  │  └──────────────────┘                                                           │   │
│  └─────────────────────────────────────────────────────────────────────────────────┘   │
│                                          │                                              │
│                                          ▼                                              │
│  ┌─────────────────────────────────────────────────────────────────────────────────┐   │
│  │                              core/ (Infrastructure)                              │   │
│  │                                                                                  │   │
│  │  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐            │   │
│  │  │ config.py    │ │ logging.py   │ │ exceptions   │ │ metrics.py   │            │   │
│  │  │              │ │              │ │              │ │              │            │   │
│  │  │ - Settings   │ │ - JSON Log   │ │ - Custom     │ │ - Latency    │            │   │
│  │  │ - Env Vars   │ │ - ELK Format │ │   Errors     │ │ - Counters   │            │   │
│  │  │ - Validation │ │ - Context    │ │ - Upstream   │ │              │            │   │
│  │  └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘            │   │
│  └─────────────────────────────────────────────────────────────────────────────────┘   │
│                                          │                                              │
│                                          ▼                                              │
│  ┌─────────────────────────────────────────────────────────────────────────────────┐   │
│  │                           telemetry/ (Observability)                             │   │
│  │                                                                                  │   │
│  │  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐            │   │
│  │  │ context.py   │ │ publisher.py │ │ emitters.py  │ │ models.py    │            │   │
│  │  │              │ │              │ │              │ │              │            │   │
│  │  │ - Request    │ │ - Async Queue│ │ - Event Emit │ │ - Telemetry  │            │   │
│  │  │   Context    │ │ - Batch Send │ │ - Fire&Forget│ │   Events     │            │   │
│  │  │ - Trace ID   │ │ - Retry      │ │              │ │              │            │   │
│  │  └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘            │   │
│  └─────────────────────────────────────────────────────────────────────────────────┘   │
│                                                                                         │
└─────────────────────────────────────────────────────────────────────────────────────────┘


3. 채팅 요청 처리 파이프라인 (RAG + LLM)

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              채팅 요청 처리 파이프라인                                    │
└─────────────────────────────────────────────────────────────────────────────────────────┘

사용자 질문
    │
    ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                  1. PII Masking (INPUT)                                 │
│                                                                                         │
│   "홍길동 사원의 연차 규정이 뭐야?" → "OOO 사원의 연차 규정이 뭐야?"                    │
│                                                                                         │
│   - 이름, 전화번호, 이메일 등 개인정보 마스킹                                            │
│   - PII_ENABLED=true 시 활성화                                                          │
└─────────────────────────────────────────────────────────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              2. Intent Classification                                   │
│                                                                                         │
│   ┌─────────────────┐         ┌─────────────────┐         ┌─────────────────┐          │
│   │  Rule Router    │ ──YES──▶│  High Confidence│ ──YES──▶│  Skip LLM       │          │
│   │  (Pattern Match)│         │  (>= 0.85)      │         │  Router         │          │
│   └─────────────────┘         └────────┬────────┘         └─────────────────┘          │
│                                        │ NO                                             │
│                                        ▼                                                │
│                               ┌─────────────────┐                                       │
│                               │   LLM Router    │                                       │
│                               │   (EXAONE)      │                                       │
│                               └─────────────────┘                                       │
│                                                                                         │
│   Output: IntentResult { intent_type, route_type, domain, confidence }                 │
│                                                                                         │
│   Intent Types: POLICY_QA, EDUCATION_QA, SMALL_TALK, BACKEND_QUERY, ...               │
│   Route Types:  RAG_INTERNAL, LLM_ONLY, BACKEND_API, MIXED_BACKEND_RAG                 │
└─────────────────────────────────────────────────────────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              3. RAG Search (route=RAG_INTERNAL)                         │
│                                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                              Retrieval Backend Selection                         │  │
│   │                                                                                  │  │
│   │   RETRIEVAL_BACKEND=ragflow  ──▶  RAGFlow API Search                           │  │
│   │   RETRIEVAL_BACKEND=milvus   ──▶  Milvus Vector Search                         │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                              RAGFlow Search Flow                                 │  │
│   │                                                                                  │  │
│   │   Query ──▶ RAGFlow API ──▶ Document Chunks ──▶ Relevance Scoring              │  │
│   │                                                                                  │  │
│   │   dataset_id 매핑: POLICY → 사내규정, EDUCATION → 직무교육/법정의무교육         │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                              Milvus Search Flow                                  │  │
│   │                                                                                  │  │
│   │   Query ──▶ Embedding (OpenAI/BGE-M3) ──▶ Vector Search ──▶ Top-K Results      │  │
│   │                                                                                  │  │
│   │   Collection: ragflow_chunks_openai (3072 dim) / ragflow_chunks_sroberta (768) │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                         │
│   Output: sources[] = [{ content, doc_id, score, metadata }, ...]                      │
└─────────────────────────────────────────────────────────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              4. Quality Gate (Low Relevance Check)                      │
│                                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                              L2 Distance Gate                                    │  │
│   │                                                                                  │  │
│   │   min_score > RAG_MAX_L2_DISTANCE (1.5)  ──▶  Low Relevance                     │  │
│   │   max_score < RAG_MIN_MAX_SCORE (0.55)   ──▶  Low Relevance                     │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                              Anchor Keyword Gate                                 │  │
│   │                                                                                  │  │
│   │   질문의 핵심 키워드가 검색 결과에 없으면 ──▶  Soft Demote                      │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                         │
│   Low Relevance 판정 시:                                                               │
│   - RAG_QUALITY_HARD_DROP_ENABLED=true  ──▶  sources=[] (RAG 답변 금지)              │
│   - RAG_QUALITY_HARD_DROP_ENABLED=false ──▶  경고 로그만                             │
└─────────────────────────────────────────────────────────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              5. LLM Prompt Building                                     │
│                                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                              System Prompt                                       │  │
│   │                                                                                  │  │
│   │   "당신은 CTRL+F 기업 내부 AI 어시스턴트입니다..."                              │  │
│   │   "아래 검색 결과를 기반으로 답변하세요..."                                      │  │
│   │   "검색 결과에 없는 내용은 추측하지 마세요..."                                   │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                              RAG Context Injection                               │  │
│   │                                                                                  │  │
│   │   [검색 결과 1]                                                                 │  │
│   │   문서: 사내규정_연차휴가.pdf                                                    │  │
│   │   내용: 연차휴가는 1년 만근 시 15일이 부여되며...                               │  │
│   │                                                                                  │  │
│   │   [검색 결과 2]                                                                 │  │
│   │   ...                                                                           │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                              Conversation History                                │  │
│   │                                                                                  │  │
│   │   User: 이전 질문...                                                            │  │
│   │   Assistant: 이전 답변...                                                       │  │
│   │   (최대 4턴, 2000 토큰 제한)                                                    │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              6. LLM Response Generation                                 │
│                                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                              LLM Server (vLLM)                                   │  │
│   │                                                                                  │  │
│   │   Model: LGAI-EXAONE/EXAONE-3.5-7.8B-Instruct                                   │  │
│   │   API: OpenAI-compatible /v1/chat/completions                                   │  │
│   │                                                                                  │  │
│   │   Alternative: OpenAI gpt-4o-mini (관리자 선택 시)                              │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                         │
│   Timeout: TIMEOUT_LLM_SIMPLE_SEC (30s) / TIMEOUT_LLM_COMPLEX_SEC (60s)                │
│                                                                                         │
│   Fallback: LLM 실패 시 에러 응답 반환 (error_type: UPSTREAM_TIMEOUT/UPSTREAM_ERROR)  │
└─────────────────────────────────────────────────────────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              7. Answer Guard (Post-Processing)                          │
│                                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                              Citation Validation                                 │  │
│   │                                                                                  │  │
│   │   CITATION_VALIDATION_STRICT=true 시:                                           │  │
│   │   - RAG sources에 없는 조항 인용 ──▶ 차단                                       │  │
│   │   - 출처 없는 답변 ──▶ 경고                                                     │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                              Statistical Claim Validation                        │  │
│   │                                                                                  │  │
│   │   STATISTICAL_CLAIM_VALIDATION=true 시:                                         │  │
│   │   - "TOP 5", "가장 많이", "N%" 등 통계 주장 ──▶ sources 필수                   │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              8. PII Masking (OUTPUT)                                    │
│                                                                                         │
│   LLM 응답에서 개인정보 마스킹 (혹시 모를 유출 방지)                                    │
└─────────────────────────────────────────────────────────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              9. Telemetry & Logging                                     │
│                                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │  TelemetryPublisher.enqueue(event)                                              │  │
│   │                                                                                  │  │
│   │  - CHAT_TURN event                                                              │  │
│   │  - latency (rag_latency_ms, llm_latency_ms)                                    │  │
│   │  - trace_id, user_id, conversation_id                                          │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                         │
│   Fire-and-forget: 비동기 배치 전송 (채팅 응답에 영향 없음)                            │
└─────────────────────────────────────────────────────────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              10. Response Build                                         │
│                                                                                         │
│   {                                                                                     │
│     "answer": "연차휴가는 1년 만근 시 15일이 부여됩니다...",                           │
│     "sources": [                                                                        │
│       { "doc_id": "...", "content": "...", "score": 0.92 }                             │
│     ],                                                                                  │
│     "meta": {                                                                           │
│       "intent_type": "POLICY_QA",                                                       │
│       "route_type": "RAG_INTERNAL",                                                     │
│       "rag_latency_ms": 120,                                                           │
│       "llm_latency_ms": 850                                                            │
│     }                                                                                   │
│   }                                                                                     │
└─────────────────────────────────────────────────────────────────────────────────────────┘


4. 문서 인덱싱 파이프라인 (SourceSet Orchestration)

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              문서 인덱싱 파이프라인                                       │
└─────────────────────────────────────────────────────────────────────────────────────────┘

    Backend (Spring)
         │
         │ POST /internal/ai/source-sets/{sourceSetId}/start
         │ X-Internal-Token: ctrlf-b2a-xxx
         │
         ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              AI Gateway - SourceSet Orchestrator                        │
│                                                                                         │
│   1. 요청 수신 ──▶ 202 Accepted 즉시 반환 (비동기 처리)                                │
│                                                                                         │
│   2. 백그라운드 처리 시작                                                               │
│      │                                                                                  │
│      ▼                                                                                  │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                              For each document in sourceSet                      │  │
│   │                                                                                  │  │
│   │   ┌─────────────┐      ┌─────────────┐      ┌─────────────┐                     │  │
│   │   │ Download    │ ──▶  │ RAGFlow     │ ──▶  │ Poll Status │                     │  │
│   │   │ File (S3)   │      │ Ingest      │      │ Until DONE  │                     │  │
│   │   └─────────────┘      └─────────────┘      └─────────────┘                     │  │
│   │                                                                                  │  │
│   │   Timeout: RAGFLOW_POLL_TIMEOUT_SEC (40분)                                      │  │
│   │   Retry: RAGFLOW_MAX_RETRY_COUNT (2회)                                          │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                         │
│   3. 완료 콜백                                                                          │
│      │                                                                                  │
│      ▼                                                                                  │
└─────────────────────────────────────────────────────────────────────────────────────────┘
         │
         │ POST /internal/source-sets/{sourceSetId}/complete
         │ { "status": "COMPLETED", "processed": 10, "failed": 0 }
         │
         ▼
    Backend (Spring)


5. 영상 렌더링 파이프라인

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              영상 렌더링 파이프라인                                       │
└─────────────────────────────────────────────────────────────────────────────────────────┘

    Backend (Spring)
         │
         │ POST /internal/ai/render-jobs
         │ { "jobId": "job-001", "videoId": "video-001" }
         │
         ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              AI Gateway - Video Renderer                                │
│                                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                              1. Render Spec Fetch                                │  │
│   │                                                                                  │  │
│   │   Backend API 호출 ──▶ Script + Scene 정보 조회 ──▶ Snapshot 저장              │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                          │                                              │
│                                          ▼                                              │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                              2. TTS Generation                                   │  │
│   │                                                                                  │  │
│   │   For each scene:                                                               │  │
│   │   ┌─────────────┐      ┌─────────────┐      ┌─────────────┐                     │  │
│   │   │ Text Split  │ ──▶  │ TTS Request │ ──▶  │ Audio Merge │                     │  │
│   │   │ (Sentences) │      │ (gTTS/Polly)│      │ (per Scene) │                     │  │
│   │   └─────────────┘      └─────────────┘      └─────────────┘                     │  │
│   │                                                                                  │  │
│   │   TTS Provider: gTTS (기본) / AWS Polly / GCP TTS                               │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                          │                                              │
│                                          ▼                                              │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                              3. Video Assembly                                   │  │
│   │                                                                                  │  │
│   │   ┌─────────────────────────────────────────────────────────────────────────┐   │  │
│   │   │  VIDEO_VISUAL_STYLE = "basic"                                           │   │  │
│   │   │  - 단색 배경 + 텍스트 오버레이                                          │   │  │
│   │   │  - 자막 (caption) 타임라인 동기화                                       │   │  │
│   │   └─────────────────────────────────────────────────────────────────────────┘   │  │
│   │                                                                                  │  │
│   │   ┌─────────────────────────────────────────────────────────────────────────┐   │  │
│   │   │  VIDEO_VISUAL_STYLE = "animated"                                        │   │  │
│   │   │  - Scene 이미지 + Ken Burns 효과                                        │   │  │
│   │   │  - Fade 전환 (VIDEO_FADE_DURATION: 0.5s)                               │   │  │
│   │   └─────────────────────────────────────────────────────────────────────────┘   │  │
│   │                                                                                  │  │
│   │   Output: 1920x1080, 30fps MP4                                                  │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                          │                                              │
│                                          ▼                                              │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                              4. Storage Upload                                   │  │
│   │                                                                                  │  │
│   │   STORAGE_PROVIDER = "local"           ──▶  Local Filesystem                    │  │
│   │   STORAGE_PROVIDER = "s3"              ──▶  AWS S3 Direct                       │  │
│   │   STORAGE_PROVIDER = "backend_presigned" ──▶  Backend Presigned URL             │  │
│   │                                                                                  │  │
│   │   Retry: STORAGE_UPLOAD_RETRY_MAX (3회)                                         │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                          │                                              │
│                                          ▼                                              │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                              5. WebSocket Progress                               │  │
│   │                                                                                  │  │
│   │   /ws/videos/{video_id}/render-progress                                         │  │
│   │                                                                                  │  │
│   │   { "status": "PROCESSING", "step": "GENERATE_TTS", "progress": 25 }           │  │
│   │   { "status": "PROCESSING", "step": "ASSEMBLE_VIDEO", "progress": 75 }         │  │
│   │   { "status": "COMPLETED", "progress": 100, "video_url": "..." }               │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                         │
└─────────────────────────────────────────────────────────────────────────────────────────┘
         │
         │ POST /internal/videos/{videoId}/render-complete
         │ { "status": "COMPLETED", "videoUrl": "..." }
         │
         ▼
    Backend (Spring)


6. 서비스 간 통신 흐름

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              서비스 간 통신 흐름                                         │
└─────────────────────────────────────────────────────────────────────────────────────────┘

┌─────────────┐     REST/SSE      ┌─────────────┐    Internal API    ┌─────────────┐
│  ctrlf-     │ ◀─────────────▶   │  ctrlf-     │ ◀────────────────▶ │  ctrlf-ai   │
│  front      │                   │  back       │  X-Internal-Token  │  (FastAPI)  │
│  (React)    │                   │  (Spring)   │                    │             │
└─────────────┘                   └──────┬──────┘                    └──────┬──────┘
                                         │                                  │
                                         │                                  │
                    ┌────────────────────┼──────────────────────────────────┼────────────┐
                    │                    │                                  │            │
                    ▼                    ▼                                  ▼            │
            ┌─────────────┐      ┌─────────────┐                    ┌─────────────┐     │
            │  Database   │      │  AWS S3     │                    │  LLM Server │     │
            │  (MySQL)    │      │  (Storage)  │                    │  (vLLM)     │     │
            └─────────────┘      └─────────────┘                    └─────────────┘     │
                                                                           │            │
                                                                           │ OpenAI API │
                                                                           │ Compatible │
                    ┌──────────────────────────────────────────────────────┘            │
                    │                                                                    │
                    ▼                                                                    │
            ┌─────────────┐      ┌─────────────┐      ┌─────────────┐                   │
            │  ctrlf-     │      │  Milvus     │      │Elasticsearch│                   │
            │  ragflow    │      │  (Vector)   │      │  (Logs)     │                   │
            └─────────────┘      └─────────────┘      └─────────────┘                   │
                    │                    │                    │                          │
                    └────────────────────┴────────────────────┴──────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                              인증 토큰 체계                                              │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                         │
│   Backend → AI:                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │  Header: X-Internal-Token: ${BACKEND_INTERNAL_TOKEN}                            │  │
│   │  용도: /internal/ai/* 엔드포인트 인증                                           │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                         │
│   AI → Backend:                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │  Header: X-Internal-Token: ${BACKEND_SERVICE_TOKEN}                             │  │
│   │  용도: 콜백 및 데이터 조회 API 인증                                             │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                         │
│   AI → RAGFlow:                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │  Header: Authorization: Bearer ${RAGFLOW_API_KEY}                               │  │
│   │  용도: RAGFlow API 인증                                                         │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                         │
│   RAGFlow → AI (Callback):                                                              │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │  Header: X-Internal-Token: ${AI_CALLBACK_TOKEN}                                 │  │
│   │  용도: RAGFlow ingest 완료 콜백 인증                                            │  │
│   └─────────────────────────────────────────────────────────────────────────────────┘  │
│                                                                                         │
└─────────────────────────────────────────────────────────────────────────────────────────┘