RAG-Anything

HKUDSMIT活跃维护

一站式多模态 RAG 框架,把文本、图像等不同模态的检索与生成统一到同一套流程里,省去自己拼接多个组件的麻烦。

"RAG-Anything: All-in-One RAG Framework"

v1.4.4发布于 2026-10-28 07:03 UTC24,481star +29 / 7 天2,865fork同步于 2026-11-10 23:24 UTC

怎么上手

由 README 提炼,执行前请确认
pip install raganything
pip install 'raganything[all]'
硬件要求按 README 说明

README 未说明显存或 GPU 要求;MinerU 解析器在首次使用时自动下载模型

支持 Apple Silicon
下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

基于 LightRAG 的一体化多模态 RAG 框架,统一处理文档中的文本、图片、表格和公式

一站式多模态 RAG 框架,把文本、图像等不同模态的检索与生成统一到同一套流程里,省去自己拼接多个组件的麻烦。

核心能力

  • 端到端多模态流水线,从文档解析到多模态问答
  • 内置图片、表格、数学公式的专用内容处理器
  • 自动抽取实体并构建跨模态知识图谱
  • 支持 MinerU 解析与直接注入预解析内容两种模式
  • 支持文本、图片、表格、公式的多模态混合查询

容易踩的坑

默认 MinerU 解析器仅支持 Python 3.10~3.13 且需 MinerU 3.x,Python 3.14 需改用 Docling 解析器;处理 Office 文档需另行安装 LibreOffice;精简 Linux/Docker 镜像需补装 libgl1 和 libglib2.0-0 才能运行 MinerU

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v1.4.4
  1. v1.4.4最新正式版2026-10-28 · 2026-10-28 07:03 UTC

    MinerU compatibility

    RAG-Anything now requires MinerU >=3.4.1,<4 on Python 3.10–3.13. MinerU 4 replaced the CLI used by this integration. The parser now rejects incompatible versions at initialization and includes the actual command output and repair advice in errors (#449).

    On Python 3.14, install RAG-Anything with Docling and select parser="docling", or use Python 3.13 for MinerU. MinerU 3.x cannot be installed on Python 3.14, so this release excludes that dependency there. The configured parser is checked at initialization, including operations that insert pre-parsed content.

    For existing Python 3.10–3.13 environments that picked up MinerU 4:

    pip install -U raganything
    pip uninstall -y docvortex mineru-llama-cpp
    mineru --version
    pip check
    

    The upgrade selects MinerU 3.x. Removing docvortex resolves a leftover pypdfium2 dependency conflict. A fresh virtual environment also avoids these leftovers. Keep mineru-magika in an upgraded environment: its installed files overlap with magika, so uninstalling it can delete the module MinerU 3.x needs. If that already happened, reinstall the installed magika version, for example pip install --force-reinstall --no-deps magika==1.0.3 for the validated environment.

    Minimal Debian/Ubuntu images need the OpenCV system libraries:

    apt-get update && apt-get install -y --no-install-recommends libgl1 libglib2.0-0
    

    Security: standalone Markdown-to-PDF conversion

    The WeasyPrint backend of raganything.enhanced_markdown now loads only data: resources and local images, CSS and fonts within the Markdown file's directory and its subdirectories. Resolved paths and extensions are checked, including symlinks. Remote resources, files outside that directory and other attachment types are refused (#446).

    This changes the standalone PDF conversion utility; document ingestion uses the direct Markdown parser. For string conversion, supply base_url to allow local resources. Pandoc has its own resource loading and is unchanged. Since method="auto" chooses Pandoc when installed, explicitly select method="weasyprint" for these restrictions.

    If you converted untrusted Markdown with earlier WeasyPrint configurations, check generated PDFs before sharing them: referenced local files may have been embedded or attached.

    Fixes

    • Ingestion: re-inserting an already-processed multimodal document returns to PROCESSED; README-style equation items retain their LaTeX formula and description; captions and footnotes reach model prompts as readable text (#446).
    • VLM queries: retrieved image paths preserve spaces, uppercase extensions and full filenames while retaining the existing image-path validation (#445).
    • Tables and equations: preserve every table cell's column; handle Word function names, limits, tracked deletions and hidden radical degrees correctly (#423, #422, #421, #443).
    • Text parsing: preserve Japanese kana joins without deleting ordinary word spaces; skip null caption items; give regex-fallback entities useful names (#441, #444, #416).
    • Batch processing: start per-file timeout accounting when the worker starts, report timeouts as failures, and reparse a file if its output directory was removed (#434, #431).
    • Office conversion: isolate generated PDFs by source path so same-stem documents cannot overwrite one another (#437).
    • Parse cache: include result-affecting Docling options, PaddleOCR cls and MinerU vlm_url; the URL is hashed in cache metadata (#432, #446).
    • Configuration: read environment defaults when each RAGAnythingConfig is created (#428).
    • Media and reliability: close MoviePy readers after failed audio extraction, discard empty Whisper segments, and ignore outcomes from older circuit-breaker recovery cycles (#427, #438, #425).
    • Markdown-to-PDF: honor configured CSS files, preserve relative images, and import successfully when optional WeasyPrint system libraries are missi
  2. v1.4.32026-10-21 · 2026-10-21 05:57 UTC
    查看更新说明

    Security

    HTML is no longer converted to PDF with LibreOffice (#418)

    Since 1.2.4, parse_document() and process_document_complete() sent .html, .htm and .xhtml files to the Office path when the parser was MinerU (and PaddleOCR, from its addition in 1.2.10). That path converts them to PDF with LibreOffice. The conversion fetches the remote URLs the page references, such as images and stylesheets. It also embeds local images the page references, such as file:// <img> sources, into the PDF that is then parsed. Ingesting an untrusted HTML file therefore made outbound requests and put referenced local images into the parsed PDF. BatchParser, the batch CLI, process_documents_batch() and process_documents_with_rag_batch() never picked up HTML. process_folder_complete() did so only when HTML extensions were added to file_extensions or SUPPORTED_FILE_EXTENSIONS.

    • HTML is now accepted only by parsers with a native parse_html entry point. Among the built-in parsers, that is Docling. MinerU, PaddleOCR and custom parsers without parse_html raise a ValueError before any conversion. The error tells you how to select Docling: RAGAnythingConfig(parser="docling"), the PARSER=docling environment variable, or --parser docling on the CLI (#418, #420).
    • Parser.convert_office_to_pdf() (and therefore the MinerU and PaddleOCR parse_office_doc()) and MineruParser.parse_document() (which used to try HTML as a PDF) reject HTML as well. PaddleOCRParser.parse_document() already refused HTML as an unsupported format and now raises the same Docling error.
    • Docling's HTML backend does not fetch the page's images. Their caption or alt text is indexed as text (#417).
    • HTML is recognised by file extension. HTML content saved under an Office extension, such as .doc or .xls, still goes to LibreOffice, which makes the same remote requests and embeds the same local images.

    Upgrade note: to keep parsing HTML, install Docling (pip install docling; it is not part of raganything[all]) and set RAGAnythingConfig(parser="docling") or PARSER=docling. If you ingested untrusted HTML with MinerU or PaddleOCR, LibreOffice requested the URLs that HTML referenced, and the local images it referenced were embedded in the converted PDF and parsed with it.

    What's new

    HTML documents with Docling (#405, #417, #418)

    With parser="docling", HTML files now go through RAG-Anything end to end. BatchParser, the batch CLI, process_documents_batch() and process_documents_with_rag_batch() now find .html, .htm and .xhtml files. parse_document() and process_document_complete() send these files to DoclingParser.parse_html. In 1.4.2, batches skipped HTML, and process_document_complete() with Docling failed with Unsupported office format: .html (#405, @er-s-an).

    • process_folder_complete() still filters on SUPPORTED_FILE_EXTENSIONS, which does not list HTML. Pass file_extensions=[".html", ".htm", ".xhtml"].
    • Pictures without image data, which includes every <img> from Docling's HTML backend, are now skipped. Previously they were indexed as the literal text [Image processing failed: ]. Docling emits the caption, or the alt text when there is no caption, as a separate text item (#417).

    Streaming answers from VLM-enhanced image queries (#400)

    With a vision model configured, aquery() and aquery_vlm_enhanced() with stream=True used to ignore the flag whenever retrieved images were sent to the vision model, and returned a complete string. They now call vision_model_func(..., stream=True) and return its async iterator unchanged. Non-streaming calls do not pass stream and behave as before. Queries that retrieve no images keep falling back to LightRAG, which streams (@IlyaKhalafi).

    Incremental RAG batches finish incomplete ingestion (#393)

    process_documents_with_rag_batch(..., incremental=True) only ingested the files it had just parsed. If a file parsed successfully but

  3. v1.4.22026-10-06 · 2026-10-06 11:55 UTC
    查看更新说明

    Security

    Only real image files are sent to the vision model (#387)

    In 1.4.1, a Markdown image reference to any existing local file became an image block, and its bytes were base64-encoded and sent to the vision-model provider. Ingesting an untrusted .md containing ![x](/etc/hosts) or a reference to a key file could therefore send that file to the model API.

    • A Markdown reference now becomes an image block only if the file has an image extension and starts with a PNG, JPEG, GIF, BMP, TIFF/BigTIFF or WebP signature. Anything else stays literal text.
    • Both image encoders also check the bytes they are about to send. This covers parse-cache entries written by 1.4.1, content lists, files swapped after parsing, and Image Path: lines in retrieved text during VLM-enhanced queries.
    • Referenced images are still sent wherever they are on disk, so only ingest Markdown whose image references you trust.

    Upgrade note: if you ingested untrusted Markdown with 1.4.1, check which files it referenced. Any non-image files among them were already sent to your vision-model provider.

    What's new

    Page numbers on text chunks (#361)

    Text chunks now record their source pages, as multimodal chunks already did. Text blocks are still joined into one string before LightRAG splits them. Once LightRAG has processed the document, each of its stored text chunks (text_chunks) is annotated with the page_idx of the blocks it came from, plus page_idx_end when it spans pages. Closes #330. Originally contributed by @kokhlo.

    • Positions are computed by replaying LightRAG's default chunker, never by searching for chunk text.
    • A chunk is left without page_idx rather than guessed when its position can't be established. That happens for: a custom chunking_func; a chunk cut into token windows by a tokenizer that doesn't decode losslessly position by position (LightRAG's default tiktoken does; pieces split at split_by_character are placed with any tokenizer); a re-insert or failed retry of a doc_id LightRAG already knows; a queued document chunked by another insert's run with different split_by_character settings; a document still queued when inserted through process_document_complete_lightrag_api (which does not wait); or a chunk whose text recurs on other pages.
    • The fields are extra keys on the text_chunks record. The default JSON KV storage and other schemaless backends keep them; fixed-schema backends such as PostgreSQL may drop them. LightRAG stores one record per distinct chunk text, so a chunk shared by several documents carries the pages of the document its full_doc_id names. On backends that merge updates, such as MongoDB, a chunk re-stored by a document that was not annotated can keep the previous document's pages.

    Queued text is no longer lost; failed text inserts now raise (#386)

    When LightRAG's pipeline was busy with another insert, ainsert only queued the document and returned. RAG-Anything then overwrote its status, so LightRAG never picked it up: the text was never indexed while doc_status said processed. This affected process_folder_complete() (files run concurrently) and any concurrent process_document_complete() / insert_content_list() callers. In the PR's reproduction, three of four staggered concurrent inserts lost all their text.

    process_document_complete() and insert_content_list() now wait after the text insert until LightRAG has finished the document's text. If a pipeline run ended without reaching it, they run the pipeline themselves.

    Behaviour changes:

    • If LightRAG fails a document's text and no pipeline run is left to retry it, both methods now raise RuntimeError: LightRAG failed to process the text of <doc_id>: <error>. Previously the failure was overwritten and the document was reported processed. The document stays failed in doc_status, and LightRAG retries it on its next pipeline run (for example, when the file is processed again). `process_folder_c
  4. v1.4.12026-09-02 · 2026-09-02 09:32 UTC
    查看更新说明

    What's new

    Audio & video modalities (#292)

    Audio and video are now first-class optional modalities:

    • AudioModalProcessor — local ASR via faster-whisper with timestamped segments; long recordings are windowed into ordered chunks (token-budget bounded), no content dropped.
    • VideoModalProcessor — dual-channel: SceneDetect scene boundaries + OpenCV keyframes + VLM visual description + audio-track transcription, merged by timestamp; long videos spill into multiple ordered chunks.
    • Opt-in via enable_audio_processing / enable_video_processing (env ENABLE_AUDIO_PROCESSING / ENABLE_VIDEO_PROCESSING), default off. Install extras: pip install raganything[audio] / raganything[video] (video also needs ffmpeg on PATH). With deps missing or flags off, media items fall back to the generic processor.

    Media files as direct inputs (#354)

    process_document_complete("meeting.mp3") and folder batches now work: audio/video files short-circuit the parser dispatch into a single media content item (no document parser involved) and flow through the normal multimodal path. Items carry a streaming SHA-256 of the file, so replacing a recording at the same path re-ingests it as new content.

    Markdown image extraction (#352)

    Markdown image references (![alt](path)) that resolve to readable local files become image blocks shaped exactly like MinerU's and flow through the same multimodal pipeline as images extracted from PDFs — lossless originals, no PDF re-encode. URLs and missing files stay literal text. Fenced code blocks are never interpreted (this also stops # inside fences from becoming headings). The old PDF round trip never actually carried images (ReportLab rendered the syntax as literal text), so this implements the original intent for the first time.

    Fixes

    • Test-session sys.modules corruption from test_modalprocessors_config.py (#353) — stub teardown now restores real modules; unrelated tests no longer fail depending on execution order.

    Docs

    • CJK audio guidance: use WHISPER_MODEL=medium or larger with WHISPER_LANGUAGE set — measured on the same Mandarin clip, base garbles proper nouns, medium recovers them (#355).

    Validation

    Beyond the unit suites (396 tests, macOS/py3.10 + Linux/py3.11), this release was exercised end-to-end on a clean Linux host with real dependencies and live LLM/VLM/embeddings:

    • audio formats wav/mp3/flac (two-speaker English meeting) and m4a/ogg-opus (Chinese narration), via both file-path and content-list entry
    • video containers mp4/webm/mkv (4-slide lecture with narration): on-screen slide text and spoken facts both recovered
    • 9.3-minute narration transcribed and windowed into multiple ordered chunks
    • one document combining text + image + audio + video, answering a single query with facts from three modalities
    • mixed folder batch (.md + .mp3 + .mp4) through process_folder_complete
    • edge cases: silent video (no audio track), no-speech audio, force_multimodal_reprocess on media with no duplicate chunks
    • real MinerU 3.4.5 parse of an image-embedded PDF (v2 content list) cross-checked against markdown image blocks
    • whisper model ladder on Mandarin: base -> small -> medium quality progression verified; pipeline identical across runs

    Full history: https://github.com/HKUDS/RAG-Anything/compare/v1.4.0...v1.4.1

  5. v1.4.02026-09-02 · 2026-09-02 08:01 UTC
    查看更新说明

    What's new

    Features

    • Native MinerU content list v2 (#347, closes #224): MinerU 3.x content_list_v2.json is normalized into the existing content-list contract — titles, paragraphs, lists, equations, images, tables, charts, code and algorithms, with page_idx/bbox/heading levels/captions preserved. Layout furniture (page_header, page_footer, page_number, …) is filtered by default; pass include_layout_blocks=True to keep it. Legacy content lists work unchanged, with an automatic fallback chain.
    • Direct .txt/.md parsing (#333, closes #331): plain text and markdown no longer take a ReportLab→PDF→OCR round trip (~3× faster measured; the whole family of PDF-rendering fidelity bugs #24/#226/#316 becomes structurally impossible on this path). Markdown ATX headings carry text_level; encoding falls back UTF-8 → gbk → latin-1 → cp1252.
    • force_multimodal_reprocess flag (#318, closes #154): opt-in parameter on process_document_complete() / insert_content_list() to re-run image/table/equation processing for an already-processed document — e.g. after switching graph/vector storage backends. Deterministic chunk ids are deduplicated on re-runs.
    • Vision prompts handle diagrams (#319, closes #313): the default prompts now transcribe text labels verbatim and enumerate nodes/edges/topology for schematics instead of describing "colors and lighting".
    • --no-recursive for the batch CLI (#327).

    Fixes

    • Documents inserted through process_document_complete_lightrag_api() were silently skipped by LightRAG's dedup and indexed zero chunks (#334, fixes #328).
    • Multimodal chunks_list bookkeeping: merges are idempotent and a missing doc_status record is surfaced (#335, refs #332).
    • Parse cache now fingerprints file content (SHA-256), so in-place replacements with preserved mtime can no longer serve stale parses (#325).
    • Multimodal query cache keys include full media identity and all query options, eliminating cross-file collisions (#324).
    • Batch inputs with the same filename no longer share an output directory (#346) — batch outputs now live in <stem>_<hash>/.
    • LibreOffice conversion timeout is configurable via LIBREOFFICE_CONVERT_TIMEOUT (default 600 s) and timeouts are reported as timeouts, not as a missing installation (#329).
    • LA (grayscale+alpha) images are composited onto white correctly before MinerU (#317).
    • Markdown with bare angle brackets (score <threshold) no longer crashes convert_text_to_pdf (#316).
    • Retry delays are capped at max_delay after jitter (#323).
    • insert_content_list([]) now raises ValueError instead of creating an empty document record (#326).
    • MinerU/pdftext PageChars crashes are self-diagnosing (#321) and prevented for fresh installs by requiring mineru[core]>=3.4.1 (#337).
    • LM Studio example tolerates servers without a working /models endpoint (#349).

    Migration notes (one-time effects on upgrade)

    1. .txt/.md re-ingest as new documents. The content list for text files changes shape, so content-derived doc ids change. Previously ingested text documents will be treated as new on re-processing; remove the old entries if duplicates matter to you.
    2. Parse caches invalidate once. #325 (content fingerprint) and #347 (new cache-relevant options) change the parse-cache key; every document re-parses once after upgrading.
    3. insert_content_list([]) now raises ValueError instead of silently succeeding.
    4. Batch output directories move from <output_dir>/<stem>/ to <output_dir>/<stem>_<pathhash>/. Incremental manifests keep skipping already-processed files; delete the manifest if you want a directory-layout rebuild.
    5. MinerU lower bound: mineru[core]>=3.4.1 (pairs with pdftext ≥0.7; older MinerU crashes on Office documents).

    Full history: https://github.com/HKUDS/RAG-Anything/compare/v1.3.1...v1.4.0

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    🚀 RAG-Anything: All-in-One RAG Framework


    🎉 News

    • [2026.06]🎯📢 🎉 LightRAG enables multimodal RAG through native integration of RAG-Anything.
    • [2025.10]🎯📢 🚀 We have released the technical report of RAG-Anything. Access it now to explore our latest research findings.
    • [2025.08]🎯📢 🔍 RAG-Anything now features VLM-Enhanced Query mode! When documents include images, the system seamlessly integrates them into VLM for advanced multimodal analysis, combining visual and textual context for deeper insights.
    • [2025.07]🎯📢 RAG-Anything now features a context configuration module, enabling intelligent integration of relevant contextual information to enhance multimodal content processing.
    • [2025.07]🎯📢 🚀 RAG-Anything now supports multimodal query capabilities, enabling enhanced RAG with seamless processing of text, images, tables, and equations.
    • [2025.07]🎯📢 🎉 RAG-Anything has reached 1k🌟 stars on GitHub! Thank you for your incredible support and valuable contributions to the project.

    🌟 System Overview

    Next-Generation Multimodal Intelligence

    Modern documents increasingly contain diverse multimodal content—text, images, tables, equations, charts, and multimedia—that traditional text-focused RAG systems cannot effectively process. RAG-Anything addresses this challenge as a comprehensive All-in-One Multimodal Document Processing RAG system built on LightRAG.

    As a unified solution, RAG-Anything eliminates the need for multiple specialized tools. It provides seamless processing and querying across all content modalities within a single integrated framework. Unlike conventional RAG approaches that struggle with non-textual elements, our all-in-one system delivers comprehensive multimodal retrieval capabilities.

    Users can query documents containing interleaved text, visual diagrams, structured tables, and mathematical formulations through one cohesive interface. This consolidated approach makes RAG-Anything particularly valuable for academic research, technical documentation, financial reports, and enterprise knowledge management where rich, mixed-content documents demand a unified processing framework.

    🎯 Key Features

    • 🔄 End-to-End Multimodal Pipeline - Complete workflow from document ingestion and parsing to intelligent multimodal query answering
    • 📄 Universal Document Support - Seamless processing of PDFs, Office documents, images, and diverse file formats
    • 🧠 Specialized Content Analysis - Dedicated processors for images, tables, mathematical equations, and heterogeneous content types
    • 🔗 Multimodal Knowledge Graph - Automatic entity extraction and cross-modal relationship discovery for enhanced understanding
    • ⚡ Adaptive Processing Modes - Flexible MinerU-based parsing or direct multimodal content injection workflows
    • 📋 Direct Content List Insertion - Bypass document parsing by directly inserting pre-parsed content lists from external sources
    • 🎯 Hybrid Intelligent Retrieval - Advanced search capabilities spanning textual and multimodal content with contextual understanding

    🏗️ Algorithm & Architecture

    Core Algorithm

    RAG-Anything implements an effective multi-stage multimodal pipeline that fundamentally extends traditional RAG architectures to seamlessly handle diverse content modalities through intelligent orchestration and cross-modal understanding.

    1. Document Parsing Stage

    The system provides high-fidelity document extraction through adaptive content decomposition. It intelligently segments heterogeneous elements while preserving contextual relationships. Universal format compatibility is achieved via specialized optimized parsers.

    Key Components:

    • ⚙️ MinerU Integration: Leverages MinerU for high-fidelity document structure extraction and semantic preservation across complex layouts.

    • 🧩 Adaptive Content Decomposition: Automatically segments documents into coherent text blocks, visual elements, structured tables, mathematical equations, and specialized content types while preserving contextual relationships.

    • 📁 Universal Format Support: Provides comprehensive handling of PDFs, Office documents (DOC/DOCX/PPT/PPTX/XLS/XLSX), images, and emerging formats through specialized parsers with format-specific optimization.

    2. Multi-Modal Content Understanding & Processing

    The system automatically categorizes and routes content through optimized channels. It uses concurrent pipelines for parallel text and multimodal processing. Document hierarchy and relationships are preserved during transformation.

    Key Components:

    • 🎯 Autonomous Content Categorization and Routing: Automatically identify, categorize, and route different content types through optimized execution channels.

    • ⚡ Concurrent Multi-Pipeline Architecture: Implements concurrent execution of textual and multimodal content through dedicated processing pipelines. This approach maximizes throughput efficiency while preserving content integrity.

    • 🏗️ Document Hierarchy Extraction: Extracts and preserves original document hierarchy and inter-element relationships during content transformation.

    3. Multimodal Analysis Engine

    The system deploys modality-aware processing units for heterogeneous data modalities:

    Specialized Analyzers:

    • 🔍 Visual Content Analyzer:

      • Integrate vision model for image analysis.
      • Generates context-aware descriptive captions based on visual semantics.
      • Extracts spatial relationships and hierarchical structures between visual elements.
    • 📊 Structured Data Interpreter:

      • Performs systematic interpretation of tabular and structured data formats.
      • Implements statistical pattern recognition algorithms for data trend analysis.
      • Identifies semantic relationships and dependencies across multiple tabular datasets.
    • 📐 Mathematical Expression Parser:

      • Parses complex mathematical expressions and formulas with high accuracy.
      • Provides native LaTeX format support for seamless integration with academic workflows.
      • Establishes conceptual mappings between mathematical equations and domain-specific knowledge bases.
    • 🔧 Extensible Modality Handler:

      • Provides configurable processing framework for custom and emerging content types.
      • Enables dynamic integration of new modality processors through plugin architecture.
      • Supports runtime configuration of processing pipelines for specialized use cases.

    4. Multimodal Knowledge Graph Index

    The multi-modal knowledge graph construction module transforms document content into structured semantic representations. It extracts multimodal entities, establishes cross-modal relationships, and preserves hierarchical organization. The system applies weighted relevance scoring for optimized knowledge retrieval.

    Core Functions:

    • 🔍 Multi-Modal Entity Extraction: Transforms significant multimodal elements into structured knowledge graph entities. The process includes semantic annotations and metadata preservation.

    • 🔗 Cross-Modal Relationship Mapping: Establishes semantic connections and dependencies between textual entities and multimodal components. This is achieved through automated relationship inference algorithms.

    • 🏗️ Hierarchical Structure Preservation: Maintains original document organization through "belongs_to" relationship chains. These chains preserve logical content hierarchy and sectional dependencies.

    • ⚖️ Weighted Relationship Scoring: Assigns quantitative relevance scores to relationship types. Scoring is based on semantic proximity and contextual significance within the document structure.

    5. Modality-Aware Retrieval

    The hybrid retrieval system combines vector similarity search with graph traversal algorithms for comprehensive content retrieval. It implements modality-aware ranking mechanisms and maintains relational coherence between retrieved elements to ensure contextually integrated information delivery.

    Retrieval Mechanisms:

    • 🔀 Vector-Graph Fusion: Integrates vector similarity search with graph traversal algorithms. This approach leverages both semantic embeddings and structural relationships for comprehensive content retrieval.

    • 📊 Modality-Aware Ranking: Implements adaptive scoring mechanisms that weight retrieval results based on content type relevance. The system adjusts rankings according to query-specific modality preferences.

    • 🔗 Relational Coherence Maintenance: Maintains semantic and structural relationships between retrieved elements. This ensures coherent information delivery and contextual integrity.


    🚀 Quick Start

    Initialize Your AI Journey

    Installation

    Python version: RAG-Anything supports Python 3.10 and later, but its default MinerU parser needs MinerU 3.x, which supports Python 3.10 to 3.13 only. On Python 3.14, RAG-Anything is installed without MinerU: use the Docling parser there (pip install docling, then RAGAnythingConfig(parser="docling")), or create the environment with Python 3.13 to use MinerU.

    Option 1: Install from PyPI (Recommended)
    # Basic installation
    pip install raganything
    
    # With optional dependencies for extended format support:
    pip install 'raganything[all]'              # All optional features (Docling not included)
    pip install 'raganything[image]'            # Image format conversion (BMP, TIFF, GIF, WebP)
    pip install 'raganything[text]'             # Text file processing (TXT, MD)
    pip install 'raganything[image,text]'       # Multiple features
    
    Option 2: Install from Source
    # Install uv (if not already installed)
    curl -LsSf https://astral.sh/uv/install.sh | sh
    
    # Clone and setup the project with uv
    git clone https://github.com/HKUDS/RAG-Anything.git
    cd RAG-Anything
    
    # Install the package and dependencies in a virtual environment
    uv sync
    
    # If you encounter network timeouts (especially for opencv packages):
    # UV_HTTP_TIMEOUT=120 uv sync
    
    # Run commands directly with uv (recommended approach)
    uv run python examples/raganything_example.py --help
    
    # Install with optional dependencies
    uv sync --extra image --extra text  # Specific extras
    uv sync --all-extras                 # All optional features
    
    Optional Dependencies
    • [image] - Enables processing of BMP, TIFF, GIF, WebP image formats (requires Pillow)
    • [text] - Enables processing of TXT and MD files (requires ReportLab)
    • [all] - Includes all Python optional dependencies

    ⚠️ Office Document Processing Requirements:

    • Office documents (.doc, .docx, .ppt, .pptx, .xls, .xlsx) require LibreOffice installation
    • Download from LibreOffice official website
    • Windows: Download installer from official website
    • macOS: brew install --cask libreoffice
    • Ubuntu/Debian: sudo apt-get install libreoffice
    • CentOS/RHEL: sudo yum install libreoffice

    Check MinerU installation:

    On Python 3.10 to 3.13, RAG-Anything installs MinerU 3.x as a dependency (mineru[core]>=3.4.1,<4). MinerU 3.x does not support Python 3.14, so on Python 3.14 MinerU is not installed and the MinerU parser is unavailable (see the Python version note above). MinerU 4.x is not supported yet: it replaced the command-line interface that RAG-Anything drives. With MinerU 4.x installed, check_parser_installation() returns False and a RAGAnything instance using the MinerU parser fails to initialize with an error that asks for a 3.x release. If mineru --version reports 4.x, install a 3.x release in the same environment (Python 3.10 to 3.13):

    pip install -U "mineru[core]>=3.4.1,<4"
    # Optional: remove two packages only MinerU 4.x uses; docvortex otherwise makes
    # `pip check` report a pypdfium2 conflict. Keep mineru-magika: it provides the
    # `magika` module MinerU 3.x imports. A fresh virtual environment also works.
    pip uninstall -y docvortex mineru-llama-cpp
    

    On minimal Linux or Docker images (for example python:3.12-slim), MinerU 3.x also needs system libraries for OpenCV. Without them, mineru --version fails with libxcb.so.1 or libGL.so.1: cannot open shared object file, and the installation check reports that error. On Debian or Ubuntu:

    apt-get update && apt-get install -y --no-install-recommends libgl1 libglib2.0-0
    
    # Verify installation (should report a 3.x version)
    mineru --version
    
    # Check if properly configured
    python -c "from raganything import RAGAnything; rag = RAGAnything(); print('✅ MinerU installed properly' if rag.check_parser_installation() else '❌ MinerU installation issue')"
    

    Models are downloaded automatically on first use. For manual download, refer to MinerU Model Source Configuration (MinerU 3.x docs; MinerU's current docs describe MinerU 4.x, which RAG-Anything does not support yet).

    Usage Examples

    1. End-to-End Document Processing
    import asyncio
    import base64
    from functools import partial
    from raganything import RAGAnything, RAGAnythingConfig
    from raganything.image_signature import image_mime_type
    from lightrag.llm.openai import openai_complete_if_cache, openai_embed
    from lightrag.utils import EmbeddingFunc
    
    async def main():
        # Set up API configuration
        api_key = "your-api-key"
        base_url = "your-base-url"  # Optional
    
        # Create RAGAnything configuration
        config = RAGAnythingConfig(
            working_dir="./rag_storage",
            parser="mineru",  # Parser selection: mineru, docling, or paddleocr
            parse_method="auto",  # Parse method: auto, ocr, or txt
            enable_image_processing=True,
            enable_table_processing=True,
            enable_equation_processing=True,
        )
    
        # Define LLM model function
        def llm_model_func(prompt, system_prompt=None, history_messages=[], **kwargs):
            return openai_complete_if_cache(
                "gpt-4o-mini",
                prompt,
                system_prompt=system_prompt,
                history_messages=history_messages,
                api_key=api_key,
                base_url=base_url,
                **kwargs,
            )
    
        # Define vision model function for image processing
        def vision_model_func(
            prompt, system_prompt=None, history_messages=[], image_data=None, messages=None, **kwargs
        ):
            # If messages format is provided (for multimodal VLM enhanced query), use it directly
            if messages:
                return openai_complete_if_cache(
                    "gpt-4o",
                    "",
                    system_prompt=None,
                    history_messages=[],
                    messages=messages,
                    api_key=api_key,
                    base_url=base_url,
                    **kwargs,
                )
            # Traditional single image format
            elif image_data:
                # Label the image with its real media type (PNG, GIF, WebP, ...),
                # identified from its first bytes (24 base64 characters)
                mime_type = image_mime_type(base64.b64decode(image_data[:24])) or "image/jpeg"
                return openai_complete_if_cache(
                    "gpt-4o",
                    "",
                    system_prompt=None,
                    history_messages=[],
                    messages=[
                        {"role": "system", "content": system_prompt}
                        if system_prompt
                        else None,
                        {
                            "role": "user",
                            "content": [
                                {"type": "text", "text": prompt},
                                {
                                    "type": "image_url",
                                    "image_url": {
                                        "url": f"data:{mime_type};base64,{image_data}"
                                    },
                                },
                            ],
                        }
                        if image_data
                        else {"role": "user", "content": prompt},
                    ],
                    api_key=api_key,
                    base_url=base_url,
                    **kwargs,
                )
            # Pure text format
            else:
                return llm_model_func(prompt, system_prompt, history_messages, **kwargs)
    
        # Define embedding function
        embedding_func = EmbeddingFunc(
            embedding_dim=3072,
            max_token_size=8192,
            func=partial(
                openai_embed.func,
                model="text-embedding-3-large",
                api_key=api_key,
                base_url=base_url,
            ),
        )
    
        # Initialize RAGAnything
        rag = RAGAnything(
            config=config,
            llm_model_func=llm_model_func,
            vision_model_func=vision_model_func,
            embedding_func=embedding_func,
        )
    
        # Process a document
        await rag.process_document_complete(
            file_path="path/to/your/document.pdf",
            output_dir="./output",
            parse_method="auto"
        )
    
        # Query the processed content
        # Pure text query - for basic knowledge base search
        text_result = await rag.aquery(
            "What are the main findings shown in the figures and tables?",
            mode="hybrid"
        )
        print("Text query result:", text_result)
    
        # Multimodal query with specific multimodal content
        multimodal_result = await rag.aquery_with_multimodal(
        "Explain this formula and its relevance to the document content",
        multimodal_content=[{
            "type": "equation",
            "latex": "P(d|q) = \\frac{P(q|d) \\cdot P(d)}{P(q)}",
            "equation_caption": "Document relevance probability"
        }],
        mode="hybrid"
    )
        print("Multimodal query result:", multimodal_result)
    
    if __name__ == "__main__":
        asyncio.run(main())
    

    Text indexing: process_document_complete() and insert_content_list() return only after LightRAG has indexed the document's text; if another insert is running LightRAG's pipeline, they wait for that run to process it. If LightRAG fails the text (for example, the LLM is unavailable) and no run is left to retry it, they raise RuntimeError and the document stays failed in doc_status; LightRAG retries it on its next pipeline run, such as when the file is processed again. See multimodal ingestion concurrency.

    2. Direct Multimodal Content Processing
    import asyncio
    import base64
    from functools import partial
    from lightrag import LightRAG
    from lightrag.llm.openai import openai_complete_if_cache, openai_embed
    from lightrag.utils import EmbeddingFunc
    from raganything.modalprocessors import ImageModalProcessor, TableModalProcessor
    from raganything.image_signature import image_mime_type
    
    def image_data_url(image_data):
        # Label the image with its real media type (PNG, GIF, WebP, ...),
        # identified from its first bytes (24 base64 characters)
        mime_type = image_mime_type(base64.b64decode(image_data[:24])) or "image/jpeg"
        return f"data:{mime_type};base64,{image_data}"
    
    
    async def process_multimodal_content():
        # Set up API configuration
        api_key = "your-api-key"
        base_url = "your-base-url"  # Optional
    
        # Initialize LightRAG
        rag = LightRAG(
            working_dir="./rag_storage",
            llm_model_func=lambda prompt, system_prompt=None, history_messages=[], **kwargs: openai_complete_if_cache(
                "gpt-4o-mini",
                prompt,
                system_prompt=system_prompt,
                history_messages=history_messages,
                api_key=api_key,
                base_url=base_url,
                **kwargs,
            ),
            embedding_func=EmbeddingFunc(
                embedding_dim=3072,
                max_token_size=8192,
                func=partial(
                    openai_embed.func,
                    model="text-embedding-3-large",
                    api_key=api_key,
                    base_url=base_url,
                ),
            )
        )
        await rag.initialize_storages()
    
        # Process an image
        image_processor = ImageModalProcessor(
            lightrag=rag,
            modal_caption_func=lambda prompt, system_prompt=None, history_messages=[], image_data=None, **kwargs: openai_complete_if_cache(
                "gpt-4o",
                "",
                system_prompt=None,
                history_messages=[],
                messages=[
                    {"role": "system", "content": system_prompt} if system_prompt else None,
                    {"role": "user", "content": [
                        {"type": "text", "text": prompt},
                        {"type": "image_url", "image_url": {"url": image_data_url(image_data)}}
                    ]} if image_data else {"role": "user", "content": prompt}
                ],
                api_key=api_key,
                base_url=base_url,
                **kwargs,
            ) if image_data else openai_complete_if_cache(
                "gpt-4o-mini",
                prompt,
                system_prompt=system_prompt,
                history_messages=history_messages,
                api_key=api_key,
                base_url=base_url,
                **kwargs,
            )
        )
    
        image_content = {
            "img_path": "path/to/image.jpg",
            "image_caption": ["Figure 1: Experimental results"],
            "image_footnote": ["Data collected in 2024"]
        }
    
        description, entity_info, _ = await image_processor.process_multimodal_content(
            modal_content=image_content,
            content_type="image",
            file_path="research_paper.pdf",
            entity_name="Experimental Results Figure"
        )
    
        # Process a table
        table_processor = TableModalProcessor(
            lightrag=rag,
            modal_caption_func=lambda prompt, system_prompt=None, history_messages=[], **kwargs: openai_complete_if_cache(
                "gpt-4o-mini",
                prompt,
                system_prompt=system_prompt,
                history_messages=history_messages,
                api_key=api_key,
                base_url=base_url,
                **kwargs,
            )
        )
    
        table_content = {
            "table_body": """
            | Method | Accuracy | F1-Score |
            |--------|----------|----------|
            | RAGAnything | 95.2% | 0.94 |
            | Baseline | 87.3% | 0.85 |
            """,
            "table_caption": ["Performance Comparison"],
            "table_footnote": ["Results on test dataset"]
        }
    
        description, entity_info, _ = await table_processor.process_multimodal_content(
            modal_content=table_content,
            content_type="table",
            file_path="research_paper.pdf",
            entity_name="Performance Results Table"
        )
    
    if __name__ == "__main__":
        asyncio.run(process_multimodal_content())
    
    3. Batch Processing
    # Process multiple documents
    await rag.process_folder_complete(
        folder_path="./documents",
        output_dir="./output",
        file_extensions=[".pdf", ".docx", ".pptx"],
        recursive=True,
        max_workers=4
    )
    
    4. Custom Modal Processors
    from raganything.modalprocessors import GenericModalProcessor
    
    class CustomModalProcessor(GenericModalProcessor):
        async def process_multimodal_content(self, modal_content, content_type, file_path, entity_name):
            # Your custom processing logic
            enhanced_description = await self.analyze_custom_content(modal_content)
            entity_info = self.create_custom_entity(enhanced_description, entity_name)
            return await self._create_entity_and_chunk(enhanced_description, entity_info, file_path)
    
    5. Query Options

    RAG-Anything provides three types of query methods:

    Pure Text Queries - Direct knowledge base search using LightRAG:

    # Different query modes for text queries
    text_result_hybrid = await rag.aquery("Your question", mode="hybrid")
    text_result_local = await rag.aquery("Your question", mode="local")
    text_result_global = await rag.aquery("Your question", mode="global")
    text_result_naive = await rag.aquery("Your question", mode="naive")
    
    # Synchronous version
    sync_text_result = rag.query("Your question", mode="hybrid")
    

    VLM Enhanced Queries - Automatically analyze images in retrieved context using VLM:

    # VLM enhanced query (automatically enabled when vision_model_func is provided)
    vlm_result = await rag.aquery(
        "Analyze the charts and figures in the document",
        mode="hybrid"
        # vlm_enhanced=True is automatically set when vision_model_func is available
    )
    
    # Manually control VLM enhancement
    vlm_enabled = await rag.aquery(
        "What do the images show in this document?",
        mode="hybrid",
        vlm_enhanced=True  # Force enable VLM enhancement
    )
    
    vlm_disabled = await rag.aquery(
        "What do the images show in this document?",
        mode="hybrid",
        vlm_enhanced=False  # Force disable VLM enhancement
    )
    
    # When documents contain images, VLM can see and analyze them directly
    # The system will automatically:
    # 1. Retrieve relevant context containing image paths
    # 2. Load and encode images as base64
    # 3. Send both text context and images to VLM for comprehensive analysis
    

    Streaming: with stream=True, a VLM enhanced query that sends retrieved images returns the vision model's async iterator instead of a string (without images it falls back to LightRAG's streaming answer). vision_model_func then receives stream=True, so a custom function must accept **kwargs and pass them to a client that can stream, as the example above does; one without **kwargs raises TypeError.

    Multimodal Queries - Enhanced queries with specific multimodal content analysis:

    # Query with table data
    table_result = await rag.aquery_with_multimodal(
        "Compare these performance metrics with the document content",
        multimodal_content=[{
            "type": "table",
            "table_data": """Method,Accuracy,Speed
                            RAGAnything,95.2%,120ms
                            Traditional,87.3%,180ms""",
            "table_caption": "Performance comparison"
        }],
        mode="hybrid"
    )
    
    # Query with equation content
    equation_result = await rag.aquery_with_multimodal(
        "Explain this formula and its relevance to the document content",
        multimodal_content=[{
            "type": "equation",
            "latex": "P(d|q) = \\frac{P(q|d) \\cdot P(d)}{P(q)}",
            "equation_caption": "Document relevance probability"
        }],
        mode="hybrid"
    )
    
    6. Loading Existing LightRAG Instance
    import asyncio
    import base64
    from functools import partial
    from raganything import RAGAnything, RAGAnythingConfig
    from raganything.image_signature import image_mime_type
    from lightrag import LightRAG
    from lightrag.llm.openai import openai_complete_if_cache, openai_embed
    from lightrag.kg.shared_storage import initialize_pipeline_status
    from lightrag.utils import EmbeddingFunc
    import os
    
    async def load_existing_lightrag():
        # Set up API configuration
        api_key = "your-api-key"
        base_url = "your-base-url"  # Optional
    
        # First, create or load existing LightRAG instance
        lightrag_working_dir = "./existing_lightrag_storage"
    
        # Check if previous LightRAG instance exists
        if os.path.exists(lightrag_working_dir) and os.listdir(lightrag_working_dir):
            print("✅ Found existing LightRAG instance, loading...")
        else:
            print("❌ No existing LightRAG instance found, will create new one")
    
        # Create/load LightRAG instance with your configuration
        lightrag_instance = LightRAG(
            working_dir=lightrag_working_dir,
            llm_model_func=lambda prompt, system_prompt=None, history_messages=[], **kwargs: openai_complete_if_cache(
                "gpt-4o-mini",
                prompt,
                system_prompt=system_prompt,
                history_messages=history_messages,
                api_key=api_key,
                base_url=base_url,
                **kwargs,
            ),
            embedding_func=EmbeddingFunc(
                embedding_dim=3072,
                max_token_size=8192,
                func=partial(
                    openai_embed.func,
                    model="text-embedding-3-large",
                    api_key=api_key,
                    base_url=base_url,
                ),
            )
        )
    
        # Initialize storage (this will load existing data if available)
        await lightrag_instance.initialize_storages()
        await initialize_pipeline_status()
    
        # Define vision model function for image processing
        def vision_model_func(
            prompt, system_prompt=None, history_messages=[], image_data=None, messages=None, **kwargs
        ):
            # If messages format is provided (for multimodal VLM enhanced query), use it directly
            if messages:
                return openai_complete_if_cache(
                    "gpt-4o",
                    "",
                    system_prompt=None,
                    history_messages=[],
                    messages=messages,
                    api_key=api_key,
                    base_url=base_url,
                    **kwargs,
                )
            # Traditional single image format
            elif image_data:
                # Label the image with its real media type (PNG, GIF, WebP, ...),
                # identified from its first bytes (24 base64 characters)
                mime_type = image_mime_type(base64.b64decode(image_data[:24])) or "image/jpeg"
                return openai_complete_if_cache(
                    "gpt-4o",
                    "",
                    system_prompt=None,
                    history_messages=[],
                    messages=[
                        {"role": "system", "content": system_prompt}
                        if system_prompt
                        else None,
                        {
                            "role": "user",
                            "content": [
                                {"type": "text", "text": prompt},
                                {
                                    "type": "image_url",
                                    "image_url": {
                                        "url": f"data:{mime_type};base64,{image_data}"
                                    },
                                },
                            ],
                        }
                        if image_data
                        else {"role": "user", "content": prompt},
                    ],
                    api_key=api_key,
                    base_url=base_url,
                    **kwargs,
                )
            # Pure text format
            else:
                return lightrag_instance.llm_model_func(prompt, system_prompt, history_messages, **kwargs)
    
        # Now use existing LightRAG instance to initialize RAGAnything
        rag = RAGAnything(
            lightrag=lightrag_instance,  # Pass existing LightRAG instance
            vision_model_func=vision_model_func,
            # Note: working_dir, llm_model_func, embedding_func, etc. are inherited from lightrag_instance
        )
    
        # Query existing knowledge base
        result = await rag.aquery(
            "What data has been processed in this LightRAG instance?",
            mode="hybrid"
        )
        print("Query result:", result)
    
        # Add new multimodal document to existing LightRAG instance
        await rag.process_document_complete(
            file_path="path/to/new/multimodal_document.pdf",
            output_dir="./output"
        )
    
    if __name__ == "__main__":
        asyncio.run(load_existing_lightrag())
    
    7. Direct Content List Insertion

    For scenarios where you already have a pre-parsed content list (e.g., from external parsers or previous processing), you can directly insert it into RAGAnything without document parsing:

    import asyncio
    import base64
    from functools import partial
    from raganything import RAGAnything, RAGAnythingConfig
    from raganything.image_signature import image_mime_type
    from lightrag.llm.openai import openai_complete_if_cache, openai_embed
    from lightrag.utils import EmbeddingFunc
    
    async def insert_content_list_example():
        # Set up API configuration
        api_key = "your-api-key"
        base_url = "your-base-url"  # Optional
    
        # Create RAGAnything configuration
        config = RAGAnythingConfig(
            working_dir="./rag_storage",
            enable_image_processing=True,
            enable_table_processing=True,
            enable_equation_processing=True,
        )
    
        # Define model functions
        def llm_model_func(prompt, system_prompt=None, history_messages=[], **kwargs):
            return openai_complete_if_cache(
                "gpt-4o-mini",
                prompt,
                system_prompt=system_prompt,
                history_messages=history_messages,
                api_key=api_key,
                base_url=base_url,
                **kwargs,
            )
    
        def vision_model_func(prompt, system_prompt=None, history_messages=[], image_data=None, messages=None, **kwargs):
            # If messages format is provided (for multimodal VLM enhanced query), use it directly
            if messages:
                return openai_complete_if_cache(
                    "gpt-4o",
                    "",
                    system_prompt=None,
                    history_messages=[],
                    messages=messages,
                    api_key=api_key,
                    base_url=base_url,
                    **kwargs,
                )
            # Traditional single image format
            elif image_data:
                # Label the image with its real media type (PNG, GIF, WebP, ...),
                # identified from its first bytes (24 base64 characters)
                mime_type = image_mime_type(base64.b64decode(image_data[:24])) or "image/jpeg"
                return openai_complete_if_cache(
                    "gpt-4o",
                    "",
                    system_prompt=None,
                    history_messages=[],
                    messages=[
                        {"role": "system", "content": system_prompt} if system_prompt else None,
                        {
                            "role": "user",
                            "content": [
                                {"type": "text", "text": prompt},
                                {"type": "image_url", "image_url": {"url": f"data:{mime_type};base64,{image_data}"}}
                            ],
                        } if image_data else {"role": "user", "content": prompt},
                    ],
                    api_key=api_key,
                    base_url=base_url,
                    **kwargs,
                )
            # Pure text format
            else:
                return llm_model_func(prompt, system_prompt, history_messages, **kwargs)
    
        embedding_func = EmbeddingFunc(
            embedding_dim=3072,
            max_token_size=8192,
            func=partial(
                openai_embed.func,
                model="text-embedding-3-large",
                api_key=api_key,
                base_url=base_url,
            ),
        )
    
        # Initialize RAGAnything
        rag = RAGAnything(
            config=config,
            llm_model_func=llm_model_func,
            vision_model_func=vision_model_func,
            embedding_func=embedding_func,
        )
    
        # Example: Pre-parsed content list from external source
        content_list = [
            {
                "type": "text",
                "text": "This is the introduction section of our research paper.",
                "page_idx": 0  # Page number where this content appears
            },
            {
                "type": "image",
                "img_path": "/absolute/path/to/figure1.jpg",  # IMPORTANT: Use absolute path
                "image_caption": ["Figure 1: System Architecture"],
                "image_footnote": ["Source: Authors' original design"],
                "page_idx": 1  # Page number where this image appears
            },
            {
                "type": "table",
                "table_body": "| Method | Accuracy | F1-Score |\n|--------|----------|----------|\n| Ours | 95.2% | 0.94 |\n| Baseline | 87.3% | 0.85 |",
                "table_caption": ["Table 1: Performance Comparison"],
                "table_footnote": ["Results on test dataset"],
                "page_idx": 2  # Page number where this table appears
            },
            {
                "type": "equation",
                "latex": "P(d|q) = \\frac{P(q|d) \\cdot P(d)}{P(q)}",
                "text": "Document relevance probability formula",
                "page_idx": 3  # Page number where this equation appears
            },
            {
                "type": "text",
                "text": "In conclusion, our method demonstrates superior performance across all metrics.",
                "page_idx": 4  # Page number where this content appears
            }
        ]
    
        # Insert the content list directly
        await rag.insert_content_list(
            content_list=content_list,
            file_path="research_paper.pdf",  # Reference file name for citation
            split_by_character=None,         # Optional text splitting
            split_by_character_only=False,   # Optional text splitting mode
            doc_id=None,                     # Optional custom document ID (will be auto-generated if not provided)
            display_stats=True               # Show content statistics
        )
    
        # Query the inserted content
        result = await rag.aquery(
            "What are the key findings and performance metrics mentioned in the research?",
            mode="hybrid"
        )
        print("Query result:", result)
    
        # You can also insert multiple content lists with different document IDs
        another_content_list = [
            {
                "type": "text",
                "text": "This is content from another document.",
                "page_idx": 0  # Page number where this content appears
            },
            {
                "type": "table",
                "table_body": "| Feature | Value |\n|---------|-------|\n| Speed | Fast |\n| Accuracy | High |",
                "table_caption": ["Feature Comparison"],
                "page_idx": 1  # Page number where this table appears
            }
        ]
    
        await rag.insert_content_list(
            content_list=another_content_list,
            file_path="another_document.pdf",
            doc_id="custom-doc-id-123"  # Custom document ID
        )
    
    if __name__ == "__main__":
        asyncio.run(insert_content_list_example())
    

    Content List Format:

    The content_list should follow the standard format with each item being a dictionary containing:

    • Text content: {"type": "text", "text": "content text", "page_idx": 0}
    • Image content: {"type": "image", "img_path": "/absolute/path/to/image.jpg", "image_caption": ["caption"], "image_footnote": ["note"], "page_idx": 1}
    • Table content: {"type": "table", "table_body": "markdown table", "table_caption": ["caption"], "table_footnote": ["note"], "page_idx": 2}
    • Equation content: {"type": "equation", "latex": "LaTeX formula", "text": "description", "page_idx": 3}
    • Generic content: {"type": "custom_type", "content": "any content", "page_idx": 4}
    • Audio content: {"type": "audio", "audio_path": "/absolute/path/to/talk.wav", "audio_caption": ["caption"], "page_idx": 5} — transcribed locally with faster-whisper (timestamped segments); long recordings are windowed into ordered chunks
    • Video content: {"type": "video", "video_path": "/absolute/path/to/demo.mp4", "video_caption": ["caption"], "page_idx": 6} — SceneDetect scene boundaries + keyframe VLM description + audio-track transcription, merged by timestamp

    Important Notes:

    • img_path: Must be an absolute path to the image file (e.g., /home/user/images/chart.jpg or C:\Users\user\images\chart.jpg)
    • page_idx: Represents the page number where the content appears in the original document (0-based indexing)
    • Text chunk pages: text blocks are joined before LightRAG splits them; once LightRAG has processed the document, each of its text chunks is annotated with the page_idx of the blocks it came from (plus page_idx_end when it spans pages). Positions come from replaying LightRAG's default chunker, never from searching for chunk text: split_by_character pieces are placed with any tokenizer, token windows only with a tokenizer that decodes losslessly position by position (LightRAG's default tiktoken does). A chunk is left without page_idx rather than guessed when that is not possible: a custom chunking_func or other tokenizer, a document already known to LightRAG (a re-insert or failed retry), one queued behind another insert and chunked by that insert's pipeline run with different split_by_character settings, or, through process_document_complete_lightrag_api (which does not wait for LightRAG), one still queued, or a chunk whose text recurs on other pages. LightRAG stores one record per distinct chunk text, so a chunk shared by several documents carries the pages of the document its full_doc_id names (on backends that merge updates, such as MongoDB, a chunk re-stored by a document that was not annotated can keep the previous document's pages). The fields are extra keys on the text_chunks record: kept by the default JSON KV storage and other schemaless backends, but fixed-schema backends such as PostgreSQL may drop them
    • Content ordering: Items are processed in the order they appear in the list

    This method is particularly useful when:

    • You have content from external parsers (non-MinerU/Docling)
    • You want to process programmatically generated content
    • You need to insert content from multiple sources into a single knowledge base
    • You have cached parsing results that you want to reuse
    8. Re-processing Multimodal Content After a Storage Backend Change

    By default, process_document_complete and insert_content_list skip multimodal (image/table/equation) processing for a document that is already marked as fully processed, to avoid redundant LLM calls. If you switch to a new graph/vector storage backend (e.g. migrating from the default file-based storage to Neo4j), the new backend will not yet contain the multimodal entities/relations that were written to the old one — see #154.

    Pass force_multimodal_reprocess=True to explicitly re-run multimodal processing and re-populate the current storage backend for an already-processed document:

    # Re-run multimodal processing so the current storage backend has the
    # image/table/equation entities and relations (default is False, i.e. no change
    # to existing behavior for documents that have not switched backends)
    await rag.process_document_complete(
        file_path="path/to/your/document.pdf",
        doc_id="doc-already-processed-id",
        force_multimodal_reprocess=True,
    )
    
    # Same flag is available for direct content list insertion
    await rag.insert_content_list(
        content_list=content_list,
        doc_id="doc-already-processed-id",
        force_multimodal_reprocess=True,
    )
    

    This flag only affects multimodal content; it is opt-in and defaults to False, so existing behavior is unchanged unless you explicitly set it.


    🛠️ Examples

    Practical Implementation Demos

    The examples/ directory contains comprehensive usage examples:

    • raganything_example.py: End-to-end document processing with MinerU
    • modalprocessors_example.py: Direct multimodal content processing
    • office_document_test.py: Office document parsing test with MinerU (no API key required)
    • image_format_test.py: Image format parsing test with MinerU (no API key required)
    • text_format_test.py: Text format parsing test with MinerU (no API key required)

    Run examples:

    # End-to-end processing with parser selection
    python examples/raganything_example.py path/to/document.pdf --api-key YOUR_API_KEY --parser mineru
    
    # Direct modal processing
    python examples/modalprocessors_example.py --api-key YOUR_API_KEY
    
    # Office document parsing test (MinerU only)
    python examples/office_document_test.py --file path/to/document.docx
    
    # Image format parsing test (MinerU only)
    python examples/image_format_test.py --file path/to/image.bmp
    
    # Text format parsing test (MinerU only)
    python examples/text_format_test.py --file path/to/document.md
    
    # Check LibreOffice installation
    python examples/office_document_test.py --check-libreoffice --file dummy
    
    # Check PIL/Pillow installation
    python examples/image_format_test.py --check-pillow --file dummy
    
    # Check ReportLab installation
    python examples/text_format_test.py --check-reportlab --file dummy
    

    🔧 Configuration

    System Optimization Parameters

    Environment Variables

    Create a .env file (refer to .env.example):

    OPENAI_API_KEY=your_openai_api_key
    OPENAI_BASE_URL=your_base_url  # Optional
    OUTPUT_DIR=./output             # Default output directory for parsed documents
    PARSER=mineru                   # Parser selection: mineru, docling, or paddleocr
    PARSE_METHOD=auto              # Parse method: auto, ocr, or txt
    

    Note: For backward compatibility, legacy environment variable names are still supported:

    • MINERU_PARSE_METHOD is deprecated, please use PARSE_METHOD

    Note: API keys are only required for full RAG processing with LLM integration. The parsing test files (office_document_test.py and image_format_test.py) only test parser functionality and do not require API keys.

    Troubleshooting and multimodal checklist

    Public media URLs (CDN / object storage)

    When ingestion runs on a server but your UI or another service needs HTTPS (or S3-style) links to figures, set:

    # Base URL for assets (no trailing slash required)
    RAGANYTHING_PUBLIC_ASSET_BASE_URL=https://my-bucket.s3.us-east-1.amazonaws.com/prefix
    # Filesystem root that should be stripped from absolute paths under that tree
    RAGANYTHING_PUBLIC_ASSET_STRIP_PREFIX=/var/rag/output
    

    After parsing, each non-empty img_path, table_img_path, or equation_img_path may gain a sibling field *_public_url while the original path stays on disk for local processing. See #272.

    Scope today: this mapping runs in the MinerU parser path only. Other parsers (e.g. Docling) keep working but will not produce *_public_url fields until the helper is wired into their content_list post-processing as well. If only one of the two env vars is set, RAG-Anything logs a warning and skips URL attachment.

    Parser Configuration

    The parser is chosen when RAGAnything is created: set RAGAnythingConfig(parser=...) to "mineru" (default), "docling", or "paddleocr", or use the PARSER environment variable (command-line tools: --parser). It cannot be switched per process_document_complete() call.

    RAGAnything now supports multiple parsers, each with specific advantages:

    MinerU Parser
    • Supports PDF, images, Office documents, and more formats
    • Powerful OCR and table extraction capabilities
    • GPU acceleration support
    Docling Parser
    • Optimized for Office documents and HTML files
    • The only built-in parser that accepts HTML (.html, .htm, .xhtml); MinerU and PaddleOCR reject it (see Document Formats)
    • Better document structure preservation
    • Native support for multiple Office formats

    Install Docling (not included in raganything[all]):

    pip install docling
    
    PaddleOCR Parser
    • OCR-focused parser for images and PDFs
    • Produces text blocks compatible with existing content_list processing
    • Supports optional Office/TXT/MD parsing by converting to PDF first

    Install PaddleOCR parser extras:

    pip install -e ".[paddleocr]"
    # or
    uv sync --extra paddleocr
    

    Note: PaddleOCR also requires paddlepaddle (CPU/GPU package varies by platform). Install it with the official guide: https://www.paddlepaddle.org.cn/install/quick

    MinerU Configuration

    # MinerU 3.x uses command-line parameters instead of config files (MinerU 4.x is not supported)
    # Check available options:
    mineru --help
    
    # Common configurations:
    mineru -p input.pdf -o output_dir -m auto    # Automatic parsing mode
    mineru -p input.pdf -o output_dir -m ocr     # OCR-focused parsing
    mineru -p input.pdf -o output_dir -b pipeline --device cuda  # GPU acceleration
    

    You can also configure parsing through RAGAnything parameters:

    # The parser comes from RAGAnythingConfig (or PARSER), not from process_document_complete()
    config = RAGAnythingConfig(parser="mineru")  # "mineru", "docling", or "paddleocr"
    rag = RAGAnything(config=config, llm_model_func=llm_model_func, embedding_func=embedding_func)
    
    # Basic parsing configuration
    await rag.process_document_complete(
        file_path="document.pdf",
        output_dir="./output/",
        parse_method="auto"           # or "ocr", "txt"
    )
    
    # Advanced parsing configuration with special parameters
    await rag.process_document_complete(
        file_path="document.pdf",
        output_dir="./output/",
        parse_method="auto",          # Parsing method: "auto", "ocr", "txt"
    
        # MinerU special parameters - all supported kwargs:
        lang="ch",                   # Document language for OCR optimization (e.g., "ch", "en", "ja")
        device="cuda:0",             # Inference device: "cpu", "cuda", "cuda:0", "npu", "mps"
        start_page=0,                # Starting page number (0-based, for PDF)
        end_page=10,                 # Ending page number (0-based, for PDF)
        formula=True,                # Enable formula parsing
        table=True,                  # Enable table parsing
        backend="pipeline",          # Parsing backend: pipeline|hybrid-auto-engine|hybrid-http-client|vlm-auto-engine|vlm-http-client.
        source="huggingface",        # Model source: "huggingface", "modelscope", "local"
        # vlm_url="http://127.0.0.1:3000" # Service address when using backend=vlm-http-client
        include_layout_blocks=False,  # Keep MinerU v2 headers, footers, and page numbers only when needed
    
        # Standard RAGAnything parameters
        display_stats=True,          # Display content statistics
        split_by_character=None,     # Optional character to split text by
        doc_id=None                  # Optional document ID
    )
    

    Note: MinerU 2.0 no longer uses the magic-pdf.json configuration file. All settings are now passed as command-line parameters or function arguments. RAG-Anything supports multiple document parsers, including MinerU, Docling, and PaddleOCR.

    MinerU 3.x Content List v2

    RAG-Anything natively reads MinerU 3.x *_content_list_v2.json and content_list_v2.json outputs when they are available, ahead of the legacy flat content list. Document-scoped output directories are preferred over shared roots; within one bundle, v2 is preferred over legacy and exact stem filenames are preferred over generic filenames. The v2 page-grouped representation is converted to the existing flat content_list contract while preserving page indexes, bounding boxes, anchors, heading levels, visual metadata, captions, and footnotes. Algorithm blocks use the compatible type="code", sub_type="algorithm" representation.

    Charts with an img_path use the image processor for ingestion and multimodal queries, including their captions, footnotes and extracted text. Charts without an image keep generic text processing. The original parsed content and document IDs stay unchanged. To update previously indexed charts, re-ingest with force_multimodal_reprocess=True.

    Page headers, footers, page numbers, aside text, and page footnotes are excluded by default so repeated layout artifacts do not enter semantic retrieval or graph construction. Set include_layout_blocks=True only when those blocks are meaningful for your use case:

    await rag.process_document_complete(
        file_path="document.pdf",
        include_layout_blocks=True,
    )
    

    MinerU marks v2 as an evolving output schema. RAG-Anything skips unknown future block types with a warning, but falls back to the legacy content list when a v2 file is malformed, structurally invalid, or yields no supported semantic blocks and a legacy file is present.

    Processing Requirements

    Different content types require specific optional dependencies:

    • Office Documents (.doc, .docx, .ppt, .pptx, .xls, .xlsx): Install LibreOffice
    • Extended Image Formats (.bmp, .tiff, .gif, .webp): Install with pip install raganything[image]
    • Audio (.mp3, .wav, .flac, .m4a, .ogg): Install with pip install raganything[audio] and set enable_audio_processing=True (env ENABLE_AUDIO_PROCESSING); local transcription via faster-whisper, model picked by WHISPER_MODEL (default base; for CJK audio use WHISPER_MODEL=medium or larger and set WHISPER_LANGUAGE, e.g. zh — smaller models garble non-Latin proper nouns)
    • Video (.mp4, .mov, .webm, .avi, .mkv): Install with pip install raganything[video] (also needs ffmpeg on PATH) and set enable_video_processing=True (env ENABLE_VIDEO_PROCESSING); dual-channel: SceneDetect + keyframe VLM description + audio-track transcription. Media files can be passed directly to process_document_complete() / folder batches, or inserted as content-list items
    • Text Files (.txt, .md): Install with pip install raganything[text]. Parsed directly (no PDF/OCR round trip); markdown image references (![alt](https://github.com/HKUDS/RAG-Anything/blob/HEAD/path)) that point to local image files (an image extension and image file contents) become image blocks and flow through the s