개인정보처리방침© 2026 DEV BAK - 기술블로그. All rights reserved.
DEV BAK - 기술블로그
AI

멀티모달 RAG 파이프라인 구축: 이미지·표를 LLM이 이해하게 만드는 법

RAG를 처음 도입했을 때 저도 비슷한 경험을 했습니다. PDF 몇 백 개를 파싱해서 벡터 DB에 넣고 검색해보니, 텍스트로 잘 설명된 내용은 꽤 잘 뽑아왔는데, 문서 중간에 끼어있던 차트나 제품 사진, 회로 다이어그램은 완전히 무시되더군요. 그 시각 정보가 사실상 문서의 핵심인 경우가 많은데도요. 기존 텍스트 RAG의 구조적 한계였습니다.

멀티모달 RAG는 바로 그 지점에서 출발합니다. 이미지, 표, 오디오, 비디오까지 텍스트와 동일한 파이프라인에서 검색하고 생성에 활용하는 아키텍처입니다. 이 글에서는 멀티모달 RAG의 작동 원리와 실제 파이프라인 구성법, 그리고 운영 시 반드시 고려해야 할 트레이드오프를 다룹니다. 이 글은 기본 텍스트 RAG 파이프라인을 한 번이라도 구성해본 독자를 기준으로 합니다. 벡터 DB, 임베딩 모델, 청킹이 아직 낯설다면 RAG 기초 개념을 먼저 살펴보고 오시는 것을 권장합니다.

의료 영상, 이커머스, 기술 문서 등 이미 여러 분야의 프로덕션 서비스에서 쓰이는 기술이지만, 지금이 진입하기 좋은 타이밍입니다. Google Gemini Embedding 2처럼 텍스트·이미지·오디오를 단일 벡터 공간에 매핑하는 네이티브 멀티모달 임베딩 모델이 막 안정화되면서, 1년 전과 비교해 구현 난이도가 눈에 띄게 낮아졌거든요.


핵심 개념

멀티모달 RAG의 세 단계 구조

기존 RAG가 "텍스트 → 임베딩 → 벡터 검색 → 생성"의 단선적 흐름이라면, 멀티모달 RAG는 각 단계에서 모달리티별 처리를 병렬로 수행합니다.

css
[원본 문서]
    ├── 텍스트 청크 → Transformer 임베딩
    ├── 이미지/차트 → Vision Encoder (CLIP, ColPali 등)
    └── 표 데이터 → 텍스트 변환 후 Transformer 임베딩
 
          ↓ (통합 벡터 공간에 저장)
 
[질의(Query)] → 교차 모달 검색 → [텍스트 + 이미지 + 표 컨텍스트]
                                          ↓
                                  VLM (GPT-4o, Gemini 등)
                                          ↓
                                    [최종 응답]

1단계 — 멀티모달 인덱싱: 모달리티마다 전용 인코더로 임베딩한 뒤, 하나의 통합 벡터 공간에 저장합니다. 텍스트는 BGE-M3(한국어 포함 다국어 지원이 강한 트랜스포머 기반 임베딩 모델) 같은 모델을 씁니다. 이미지는 CLIP이나 ColPali를 활용하는데, 둘은 접근 방식이 꽤 다릅니다. CLIP이 이미지-텍스트 쌍을 정렬하는 방식이라면, ColPali는 OCR 없이 문서 페이지 이미지 자체를 임베딩하는 Late Interaction 방식으로 레이아웃·서식 정보까지 보존합니다. 여기서 임베딩 품질이 이후 검색 성능 전체를 좌우합니다.

2단계 — 교차 모달 검색(Cross-modal Retrieval): 텍스트 질의가 들어와도 연관된 이미지나 표까지 함께 끌어오는 것이 핵심입니다. 벡터 유사도 검색만으로는 한계가 있어서, BM25 키워드 검색과 결합한 하이브리드 검색에 텐서 리랭킹(Tensor Reranker)을 얹어 정확도를 올리는 방식이 현재 프로덕션 표준에 가깝습니다.

3단계 — 멀티모달 생성: 검색된 텍스트, 이미지, 표를 모두 컨텍스트로 주입해 GPT-4o나 Gemini 같은 VLM이 응답을 만들어냅니다. 텍스트만 넣을 때보다 할루시네이션이 경험적으로 유의미하게 줄어드는데, 실제로 써보면 납득이 갑니다. 시각 근거가 있으니 모델이 함부로 지어내기 어려운 거죠.


기존 텍스트 RAG와 무엇이 다른가

솔직히 처음엔 "그냥 이미지에서 텍스트 뽑아서 넣으면 되지 않나?" 싶었습니다. OCR로 이미지 텍스트 추출하면 기존 RAG로도 커버되지 않나 하고요. 그런데 차트의 색상 분포, 의료 영상의 공간적 패턴, 제품 사진의 질감 같은 정보는 텍스트로 변환하는 순간 손실됩니다. 이게 누적되면 검색 품질에 직접 영향을 미칩니다.

VisRAG: 문서를 텍스트로 파싱하지 않고 페이지 이미지 그대로 검색·추론하는 방식. OCR 변환 과정에서 발생하는 레이아웃 정보 손실 없이 원본 문서 구조를 유지할 수 있습니다.


2025~2026년 주목할 변화들

멀티모달 RAG 생태계가 빠르게 바뀌고 있어서, 한 달 전 아키텍처가 지금은 구식이 되는 경우도 있습니다.

트렌드 내용
네이티브 멀티모달 임베딩 Google Gemini Embedding 2가 텍스트·이미지·비디오·오디오를 단일 벡터 공간에 매핑. 모달리티별 인코더를 따로 두지 않아도 됩니다
Agentic RAG 검색-생성 단일 루프에서 Retriever·Validator·Summarizer Agent가 협력하는 멀티스텝 추론으로 진화 중
실시간 RAG 정적 인덱스에서 뉴스·센서·주가 같은 동적 실시간 데이터 연결 구조로 확장
RAG-as-a-Service 멀티모달 RAG 기능을 API로 제공하는 엔터프라이즈용 관리형 서비스 급증

실전 적용

그러면 이 파이프라인을 실제로 어떻게 구성하는지 코드로 살펴보겠습니다.

예시 1: LlamaIndex로 PDF 문서 멀티모달 파이프라인 구성

기술 문서 어시스턴트 시나리오를 생각해볼게요. 회로도, 다이어그램, 코드 스니펫, 텍스트 설명이 뒤섞인 엔지니어링 PDF를 처리하는 경우입니다. 저도 처음에 storage_context 없이 인덱스 생성을 시도했다가 NameError를 만났는데, 텍스트 스토어와 이미지 스토어를 각각 만든 뒤 반드시 StorageContext로 묶어줘야 합니다. 이 단계를 빠뜨리면 복붙해도 바로 에러가 납니다.

python
from llama_index.core import SimpleDirectoryReader, StorageContext
from llama_index.multi_modal_llms.openai import OpenAIMultiModal
from llama_index.core.indices.multi_modal import MultiModalVectorStoreIndex
from llama_index.vector_stores.qdrant import QdrantVectorStore
from llama_index.embeddings.clip import ClipEmbedding
import qdrant_client
 
# 1. 멀티모달 문서 로드 (텍스트 + 이미지 함께 추출)
documents = SimpleDirectoryReader(
    input_dir="./docs",
    required_exts=[".pdf"],
    recursive=True
).load_data()
 
# 2. 벡터 스토어 설정 (텍스트/이미지 인덱스 분리)
client = qdrant_client.QdrantClient(host="localhost", port=6333)
 
text_store = QdrantVectorStore(
    client=client, collection_name="text_collection"
)
image_store = QdrantVectorStore(
    client=client, collection_name="image_collection"
)
 
# 3. StorageContext로 텍스트/이미지 스토어 통합 (필수 단계)
storage_context = StorageContext.from_defaults(
    vector_store=text_store,
    image_store=image_store,
)
 
# 4. CLIP 임베딩으로 이미지-텍스트 정렬
clip_embedding = ClipEmbedding()
 
# 5. 멀티모달 인덱스 빌드
index = MultiModalVectorStoreIndex.from_documents(
    documents,
    image_embed_model=clip_embedding,
    storage_context=storage_context,
)
 
# 6. VLM 설정 및 쿼리 엔진 구성
mm_llm = OpenAIMultiModal(
    model="gpt-4o",
    max_new_tokens=1500
)
 
query_engine = index.as_query_engine(
    multi_modal_llm=mm_llm,
    similarity_top_k=5,       # 상위 5개 텍스트 청크
    image_similarity_top_k=3, # 상위 3개 이미지
)
 
response = query_engine.query(
    "이 회로에서 전압 조정 모듈의 동작 원리를 설명해줘"
)
print(response)
코드 포인트 역할
MultiModalVectorStoreIndex 텍스트·이미지 인덱스를 통합 관리
StorageContext.from_defaults 텍스트·이미지 벡터 스토어를 하나로 묶는 필수 단계
ClipEmbedding 이미지와 텍스트를 같은 벡터 공간에 매핑
image_similarity_top_k 검색 시 이미지 후보 수 조절 (비용-품질 트레이드오프)
OpenAIMultiModal 이미지+텍스트 컨텍스트를 함께 처리하는 VLM

예시 2: 하이브리드 검색으로 교차 모달 정확도 높이기

벡터 검색만으로는 키워드 일치 케이스에서 놓치는 경우가 생깁니다. BM25와 결합한 하이브리드 검색 + 텐서 리랭킹을 적용하면 실질적 차이가 납니다. 프로덕션에서 가장 자주 만지게 되는 파라미터는 similarity_top_k와 top_n인데, 이 둘의 균형이 검색 품질과 레이턴시를 동시에 결정합니다.

python
from llama_index.multi_modal_llms.openai import OpenAIMultiModal
from llama_index.retrievers.bm25 import BM25Retriever
from llama_index.core.retrievers import QueryFusionRetriever
from llama_index.postprocessor.colbert_rerank import ColbertRerank
from llama_index.core.query_engine import RetrieverQueryEngine
 
# 독립 실행 시 mm_llm 정의 필요
mm_llm = OpenAIMultiModal(
    model="gpt-4o",
    max_new_tokens=1500
)
 
# 벡터 검색 리트리버
vector_retriever = index.as_retriever(similarity_top_k=10)
 
# BM25 키워드 검색 리트리버
bm25_retriever = BM25Retriever.from_defaults(
    index=index,
    similarity_top_k=10
)
 
# 두 리트리버를 Reciprocal Rank Fusion으로 결합
hybrid_retriever = QueryFusionRetriever(
    retrievers=[vector_retriever, bm25_retriever],
    similarity_top_k=5,
    num_queries=1,
    mode="reciprocal_rerank",
)
 
# ColBERT 기반 텐서 리랭킹으로 최종 순위 조정
reranker = ColbertRerank(top_n=3)
 
query_engine = RetrieverQueryEngine(
    retriever=hybrid_retriever,
    node_postprocessors=[reranker],
    llm=mm_llm,
)

Reciprocal Rank Fusion (RRF): 여러 검색 결과의 순위를 점수로 변환해 결합하는 앙상블 기법. 구현이 단순하면서도 성능이 꽤 안정적으로 나와서 현업에서 자주 씁니다.


예시 3: 문서 파싱 단계에서 이미지·표 분리 추출

파이프라인 품질은 인제스천 단계에서 결정됩니다. Unstructured.io로 PDF에서 텍스트, 이미지, 표를 각각 분리해 처리해볼 수 있습니다.

python
from unstructured.partition.pdf import partition_pdf
 
def parse_multimodal_document(pdf_path: str) -> dict:
    """PDF에서 텍스트, 이미지, 표를 분리 추출"""
 
    elements = partition_pdf(
        filename=pdf_path,
        extract_images_in_pdf=True,
        extract_image_block_types=["Image", "Table"],
        infer_table_structure=True,
        strategy="hi_res",         # 고해상도 파싱 (느리지만 정확)
    )
 
    result = {"texts": [], "images": [], "tables": []}
 
    for element in elements:
        element_type = type(element).__name__
 
        if element_type == "Table":
            result["tables"].append({
                "content": element.text,
                "metadata": element.metadata.to_dict()
            })
        elif element_type == "Image":
            if hasattr(element.metadata, "image_base64"):
                result["images"].append({
                    "base64": element.metadata.image_base64,
                    "metadata": element.metadata.to_dict()
                })
        else:
            result["texts"].append({
                "content": element.text,
                "metadata": element.metadata.to_dict()
            })
 
    return result
파싱 전략 속도 정확도 권장 상황
fast 빠름 낮음 텍스트 중심 문서, 프로토타이핑
auto 중간 중간 일반적인 PDF
hi_res 느림 높음 이미지·표 많은 기술 문서, 프로덕션

장단점 분석

장점

항목 내용
정보 손실 감소 차트 색상 분포, 이미지 공간 관계 등 텍스트 변환 시 소실되는 시각 정보를 그대로 활용
할루시네이션 억제 멀티소스 그라운딩으로 텍스트만 쓸 때보다 생성 신뢰도가 경험적으로 유의미하게 향상
풍부한 응답 품질 텍스트 + 이미지 + 표를 함께 반환해 사용자 경험 개선
기존 생태계 호환 LangChain·LlamaIndex 에코시스템에서 확장 가능. 기존 RAG 자산 재활용 가능

단점 및 주의사항

항목 내용 대응 방안
인프라 복잡성 모달리티별 인코더, 청킹 전략, 인덱스가 달라 운영 부담 급증 Gemini Embedding 2 같은 네이티브 멀티모달 임베딩으로 단순화
저장·검색 비용 백만 페이지 규모에서 인덱스가 TB 수준으로 증가 벡터 DB 샤딩, 이미지 해상도 최적화, 계층적 스토리지 설계
레이턴시 모달리티 처리 단계가 직렬 연결되면 응답 속도 저하 비동기 인제스천 큐, 캐싱, 모달리티별 병렬 처리
교차 모달 정렬 품질 텍스트 질의로 이미지 검색 시 임베딩 품질 차이가 성능에 직결 고품질 CLIP/ColPali 임베딩 선택, 도메인 파인튜닝 고려
평가 지표 부재 멀티모달 RAG 품질 측정 표준 벤치마크가 아직 미성숙 RAGAS 프레임워크 활용 + 모달리티별 개별 평가 지표 조합

텐서 리랭킹(Tensor Reranker): ColBERT 스타일로 질의와 문서의 토큰 수준 상호작용을 계산해 순위를 재조정하는 기법. 일반 코사인 유사도보다 정확하지만 연산 비용이 높아 상위 후보에만 적용하는 것이 일반적입니다.

실무에서 가장 흔한 실수

  1. 이미지 전처리를 생략하는 것 — 해상도가 낮거나 노이즈가 많은 이미지를 그대로 임베딩하면 검색 품질이 급격히 떨어집니다. 인제스천 단계에서 리사이징, 정규화, 품질 필터링을 미리 적용해두는 것이 좋습니다.

  2. 텍스트 청크와 이미지의 위치 연결을 끊어버리는 것 — "3번 그림 참조"라는 텍스트와 실제 3번 그림의 메타데이터 연결이 파싱 과정에서 끊기면, 검색 시 맥락이 없는 이미지가 뽑히게 됩니다. 파싱 단계에서 페이지 번호, 섹션 정보 등 위치 메타데이터를 보존하는 것을 권장합니다.

  3. 단일 모달리티로 전체 파이프라인을 검증하는 것 — 텍스트 검색이 잘 된다고 이미지 검색도 잘 될 거라 가정하면 나중에 큰 낭패를 봅니다. 모달리티별로 별도 평가 세트를 만들어 각각 검증하는 과정이 필요합니다.


마치며

멀티모달 RAG는 "텍스트만 이해하는 AI"에서 "문서 전체를 이해하는 AI"로 넘어가는 핵심 아키텍처입니다.

복잡해 보이지만 시작점은 생각보다 가깝습니다. 지금 바로 시작해볼 수 있는 3단계입니다.

  1. 환경 구성 — 아래 명령어로 이 글의 예시 1, 2, 3을 모두 실행할 수 있는 환경이 갖춰집니다. 예시 2의 BM25Retriever와 ColbertRerank는 별도 패키지이니 함께 설치해야 합니다.
bash
pip install llama-index-multi-modal-llms-openai \
            llama-index-vector-stores-qdrant \
            llama-index-embeddings-clip \
            llama-index-retrievers-bm25 \
            llama-index-postprocessor-colbert-rerank \
            unstructured[pdf]
 
docker run -p 6333:6333 qdrant/qdrant
  1. 가장 단순한 케이스로 파이프라인 검증 — 이미지가 10~20장 정도 포함된 PDF 한두 개로 시작해보세요. strategy="hi_res"로 파싱하고, CLIP 임베딩으로 인덱싱한 뒤, 이미지를 설명하는 질의를 날려보면 멀티모달 검색이 어떻게 동작하는지 눈으로 확인할 수 있습니다. 첫 실행에서 이미지 검색 결과가 이상해 보여도 당황할 필요 없습니다. 인제스천 단계 로그를 먼저 확인해보시면 원인이 금방 나옵니다.

  2. 하이브리드 검색과 리랭킹은 두 번째 이터레이션에서 — 기본 파이프라인이 동작하는 것을 확인한 뒤 BM25 결합과 ColBERT 리랭킹을 단계적으로 얹어가면서 검색 품질 변화를 비교해보시면 각 컴포넌트의 실제 기여도를 바로 체감할 수 있습니다.


참고 자료

  • Building a Multimodal RAG with Text, Images, Tables | Towards Data Science
  • mRAG: Elucidating the Design Space of Multi-modal RAG | arXiv 2505.24073
  • Multimodal RAG Explained: From Text to Images and Beyond | USAII
  • Gemini Embedding 2 — How Multimodal Embeddings Change RAG | jangwook.net
  • From RAG to Context: A 2025 Year-End Review | RAGFlow
  • What is Multimodal RAG? | IBM
  • Multimodal RAG: A Hands-On Guide | DataCamp
  • Multimodal RAG Development: 12 Best Practices | Augment Code
  • Bridging Modalities: Multimodal RAG for Advanced Information Retrieval | InfoQ
  • RAG-Anything: All-in-One RAG Framework | GitHub (HKUDS)
  • Real-World Applications of Multimodal Search and RAG | Milvus
  • Best Open-Source RAG Frameworks 2026 | Firecrawl
  • RAG in 2026: How Retrieval-Augmented Generation Works for Enterprise AI | Techment
#멀티모달RAG#LlamaIndex#VectorDB#CLIP#ColPali#하이브리드검색#LLM#임베딩#Qdrant#Unstructured
공유하기

목차

핵심 개념멀티모달 RAG의 세 단계 구조기존 텍스트 RAG와 무엇이 다른가2025~2026년 주목할 변화들실전 적용예시 1: LlamaIndex로 PDF 문서 멀티모달 파이프라인 구성예시 2: 하이브리드 검색으로 교차 모달 정확도 높이기예시 3: 문서 파싱 단계에서 이미지·표 분리 추출장단점 분석장점단점 및 주의사항실무에서 가장 흔한 실수마치며참고 자료

추천 포스트

OpenTelemetry로 LLM 트레이싱 구축하기: RAG·멀티에이전트 흐름을 gen_ai 표준으로 추적하는 법
AI

OpenTelemetry로 LLM 트레이싱 구축하기: RAG·멀티에이전트 흐름을 gen_ai 표준으로 추적하는 법

GPT-4를 붙여놓은 서비스가 갑자기 엉뚱한 답을 내놓기 시작했다. 로그를 뒤져봐도 에러는 없다. HTTP 응답코드도 200이다. 근데 사용자는 화가 났다. 이 상황, 한 번쯤 겪어보셨을 것 같다. 전통적인 APM으로는 "LLM 호출에 1.2초 걸렸음"까지만 알 수 있고, 정작 중요한 ...

2026년 05월 30일읽는 데 25분
Pydantic AI: Python AI 에이전트에서 타입 안전한 LLM 도구 호출 구현
AI

Pydantic AI: Python AI 에이전트에서 타입 안전한 LLM 도구 호출 구현

RunContext · output_type · 의존성 주입으로 런타임 오류를 작성 시점에 잡아내기 LLM 기반 기능을 Python 코드에 얹다 보면 어느 순간 묘한 불안감이 생깁니다. "이 도구 함수, LLM이 엉뚱한 타입으로 호출하면 어떻게 되지?" 저도 처음에 FastAPI ...

2026년 05월 30일읽는 데 24분
LLM 평가 프레임워크 직접 구축 vs 기성 도구: 2026 팀별 선택 기준
AI

LLM 평가 프레임워크 직접 구축 vs 기성 도구: 2026 팀별 선택 기준

RAG·챗봇·에이전트를 프로덕션에 올리는 팀이라면 지금 이 선택이 기다리고 있습니다 AI 기능을 제품에 붙이고 나서 "이게 동작하는 건 맞는데, 얼마나 잘 동작하는지는 어떻게 알지?"라는 질문을 해본 적 있다면, 딱 맞는 시점에 닿은 겁니다. 저도 처음엔 "프롬프트 몇 개 수동으로...

2026년 05월 30일읽는 데 24분
AI 에이전트 장기 메모리 비교: Mem0 vs Letta vs Zep — 세 가지 철학과 실전 선택 기준
AI

AI 에이전트 장기 메모리 비교: Mem0 vs Letta vs Zep — 세 가지 철학과 실전 선택 기준

LLM 기반 앱을 한 번이라도 만들어봤다면 반드시 이 벽에 부딪힌다. "지난 대화를 어떻게 기억하게 할까?" 컨텍스트 윈도우에 전체 대화를 욱여넣으면 되겠지 싶지만, 현실은 그렇지 않다. 토큰 비용이 폭발하고, 대화가 길어질수록 LLM의 집중력은 흐트러지며, 세션이 끊기면 모든 것이 사...

2026년 05월 30일읽는 데 29분
LangGraph Supervisor Pattern: 멀티 에이전트 시스템에서 제어를 잃지 않으려면
AI

LangGraph Supervisor Pattern: 멀티 에이전트 시스템에서 제어를 잃지 않으려면

멀티 에이전트 시스템을 처음 설계할 때 가장 많이 저지르는 실수가 있습니다. "에이전트끼리 알아서 협력하겠지"라는 막연한 기대 아래 각 에이전트를 느슨하게 연결해두는 것이죠. 저도 처음엔 그렇게 생각했는데, 결과는 언제나 같았습니다. 제어 흐름이 어디 있는지 알 수 없고, 어디서 실패했...

2026년 05월 30일읽는 데 22분
AI 에이전트 88%가 프로덕션에 실패하는 이유: 5계층 하네스 아키텍처가 그 답이다
AI

AI 에이전트 88%가 프로덕션에 실패하는 이유: 5계층 하네스 아키텍처가 그 답이다

GPT-4가 처음 나왔을 때, 저도 그랬고 주변 개발자들도 다들 비슷한 착각을 했습니다. "모델만 좋으면 되는 거 아닌가?" 프롬프트 몇 줄 붙여서 프로토타입 만들고, 그럴싸하게 돌아가는 것 같으니 프로덕션 배포. 그리고 얼마 지나지 않아 에이전트가 엉뚱한 파일을 삭제하거나, 컨텍스트를...

2026년 05월 29일읽는 데 28분