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

RAG-Anything
HKUDSMIT活跃维护一站式多模态 RAG 框架,把文本、图像等不同模态的检索与生成统一到同一套流程里,省去自己拼接多个组件的麻烦。
"RAG-Anything: All-in-One RAG Framework"
pip install raganything
pip install 'raganything[all]'README 未说明显存或 GPU 要求;MinerU 解析器在首次使用时自动下载模型
基于 LightRAG 的一体化多模态 RAG 框架,统一处理文档中的文本、图片、表格和公式
一站式多模态 RAG 框架,把文本、图像等不同模态的检索与生成统一到同一套流程里,省去自己拼接多个组件的麻烦。
核心能力
- 端到端多模态流水线,从文档解析到多模态问答
- 内置图片、表格、数学公式的专用内容处理器
- 自动抽取实体并构建跨模态知识图谱
- 支持 MinerU 解析与直接注入预解析内容两种模式
- 支持文本、图片、表格、公式的多模态混合查询
容易踩的坑
- 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 checkThe upgrade selects MinerU 3.x. Removing
docvortexresolves a leftoverpypdfium2dependency conflict. A fresh virtual environment also avoids these leftovers. Keepmineru-magikain an upgraded environment: its installed files overlap withmagika, so uninstalling it can delete the module MinerU 3.x needs. If that already happened, reinstall the installedmagikaversion, for examplepip install --force-reinstall --no-deps magika==1.0.3for 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-0Security: standalone Markdown-to-PDF conversion
The WeasyPrint backend of
raganything.enhanced_markdownnow loads onlydata: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_urlto allow local resources. Pandoc has its own resource loading and is unchanged. Sincemethod="auto"chooses Pandoc when installed, explicitly selectmethod="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
clsand MinerUvlm_url; the URL is hashed in cache metadata (#432, #446). - Configuration: read environment defaults when each
RAGAnythingConfigis 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
- Ingestion: re-inserting an already-processed multimodal document returns to
- 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()andprocess_document_complete()sent.html,.htmand.xhtmlfiles 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 asfile://<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()andprocess_documents_with_rag_batch()never picked up HTML.process_folder_complete()did so only when HTML extensions were added tofile_extensionsorSUPPORTED_FILE_EXTENSIONS.- HTML is now accepted only by parsers with a native
parse_htmlentry point. Among the built-in parsers, that is Docling. MinerU, PaddleOCR and custom parsers withoutparse_htmlraise aValueErrorbefore any conversion. The error tells you how to select Docling:RAGAnythingConfig(parser="docling"), thePARSER=doclingenvironment variable, or--parser doclingon the CLI (#418, #420). Parser.convert_office_to_pdf()(and therefore the MinerU and PaddleOCRparse_office_doc()) andMineruParser.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
.docor.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 ofraganything[all]) and setRAGAnythingConfig(parser="docling")orPARSER=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()andprocess_documents_with_rag_batch()now find.html,.htmand.xhtmlfiles.parse_document()andprocess_document_complete()send these files toDoclingParser.parse_html. In 1.4.2, batches skipped HTML, andprocess_document_complete()with Docling failed withUnsupported office format: .html(#405, @er-s-an).process_folder_complete()still filters onSUPPORTED_FILE_EXTENSIONS, which does not list HTML. Passfile_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()andaquery_vlm_enhanced()withstream=Trueused to ignore the flag whenever retrieved images were sent to the vision model, and returned a complete string. They now callvision_model_func(..., stream=True)and return its async iterator unchanged. Non-streaming calls do not passstreamand 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 - HTML is now accepted only by parsers with a native
- 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
.mdcontainingor 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 thepage_idxof the blocks it came from, pluspage_idx_endwhen 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_idxrather than guessed when its position can't be established. That happens for: a customchunking_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 atsplit_by_characterare 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 differentsplit_by_charactersettings; a document still queued when inserted throughprocess_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_chunksrecord. 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 itsfull_doc_idnames. 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,
ainsertonly queued the document and returned. RAG-Anything then overwrote its status, so LightRAG never picked it up: the text was never indexed whiledoc_statussaidprocessed. This affectedprocess_folder_complete()(files run concurrently) and any concurrentprocess_document_complete()/insert_content_list()callers. In the PR's reproduction, three of four staggered concurrent inserts lost all their text.process_document_complete()andinsert_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 reportedprocessed. The document staysfailedindoc_status, and LightRAG retries it on its next pipeline run (for example, when the file is processed again). `process_folder_c
- 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(envENABLE_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 (
) 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.modulescorruption fromtest_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=mediumor larger withWHISPER_LANGUAGEset — measured on the same Mandarin clip,basegarbles proper nouns,mediumrecovers 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) throughprocess_folder_complete - edge cases: silent video (no audio track), no-speech audio,
force_multimodal_reprocesson 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
- 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.jsonis normalized into the existing content-list contract — titles, paragraphs, lists, equations, images, tables, charts, code and algorithms, withpage_idx/bbox/heading levels/captions preserved. Layout furniture (page_header,page_footer,page_number, …) is filtered by default; passinclude_layout_blocks=Trueto keep it. Legacy content lists work unchanged, with an automatic fallback chain. - Direct
.txt/.mdparsing (#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 carrytext_level; encoding falls back UTF-8 → gbk → latin-1 → cp1252. force_multimodal_reprocessflag (#318, closes #154): opt-in parameter onprocess_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-recursivefor 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_listbookkeeping: 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 crashesconvert_text_to_pdf(#316). - Retry delays are capped at
max_delayafter jitter (#323). insert_content_list([])now raisesValueErrorinstead of creating an empty document record (#326).- MinerU/pdftext
PageCharscrashes are self-diagnosing (#321) and prevented for fresh installs by requiringmineru[core]>=3.4.1(#337). - LM Studio example tolerates servers without a working
/modelsendpoint (#349).
Migration notes (one-time effects on upgrade)
.txt/.mdre-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.- 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.
insert_content_list([])now raisesValueErrorinstead of silently succeeding.- 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. - 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
- Native MinerU content list v2 (#347, closes #224): MinerU 3.x
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
🚀 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, thenRAGAnythingConfig(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()andinsert_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 raiseRuntimeErrorand the document staysfailedindoc_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_functhen receivesstream=True, so a custom function must accept**kwargsand pass them to a client that can stream, as the example above does; one without**kwargsraisesTypeError.
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.jpgorC:\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_idxof the blocks it came from (pluspage_idx_endwhen it spans pages). Positions come from replaying LightRAG's default chunker, never from searching for chunk text:split_by_characterpieces 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 withoutpage_idxrather than guessed when that is not possible: a customchunking_funcor 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 differentsplit_by_charactersettings, or, throughprocess_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 itsfull_doc_idnames (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 thetext_chunksrecord: 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 MinerUmodalprocessors_example.py: Direct multimodal content processingoffice_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_METHODis deprecated, please usePARSE_METHOD
Note: API keys are only required for full RAG processing with LLM integration. The parsing test files (
office_document_test.pyandimage_format_test.py) only test parser functionality and do not require API keys.
Troubleshooting and multimodal checklist
- See docs/multimodal_rag_failure_modes.md for a short checklist of common pipeline issues (OCR, tables, retrieval bias, debugging tips). Related: #207, #213.
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_urlfields 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_listprocessing - 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.jsonconfiguration 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 setenable_audio_processing=True(envENABLE_AUDIO_PROCESSING); local transcription via faster-whisper, model picked byWHISPER_MODEL(defaultbase; for CJK audio useWHISPER_MODEL=mediumor larger and setWHISPER_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 setenable_video_processing=True(envENABLE_VIDEO_PROCESSING); dual-channel: SceneDetect + keyframe VLM description + audio-track transcription. Media files can be passed directly toprocess_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 () that point to local image files (an image extension and image file contents) become image blocks and flow through the s