RAGFlow
RAGFlow ingests heterogeneous documents — PDFs with scanned tables, slides, code books, audio, e-mails — and turns them into citable, searchable knowledge that LLMs can reason over. It ships its own deep-learning OCR/layout pipeline, a Agent Canvas Canvas & Execution — Canvas & Execution" class="xref">Graph-RAG construction stack, an editable agent-canvas DSL, and a polished web UI; not just a thin wrapper around an embedding model.
Python · TypeScript · Go · C++ DeepDoc OCR + Layout GraphRAG · RAPTOR · Agent Canvas Apache 2.0
🎯 Project Philosophy
When you first look at this codebase, the most striking thing is how much RAGFlow refuses to outsource. Most "RAG products" are 800 lines of Python that calls an embedding API and a chat API. RAGFlow ships its own Chinese-aware tokenizer (in C++ with a DART double-array trie), its own layout-detection and table-structure neural networks (PaddlePaddle/ONNX, in deepdoc/vision/), its own Graph-RAG construction stack with Leiden community detection, and its own agent-canvas DSL with iteration, loops, switches, and a code sandbox. Reading the source feels less like reading a "wrapper" and more like reading a small operating system for retrieval.
Several design principles emerge from the code patterns:
- Pluggable everywhere it matters. Doc-stores (ES, Infinity, OpenSearch, OceanBase), object-stores (MinIO, S3, Azure, GCS, OSS, OpenDAL, encrypted), LLM providers, parsers, chunkers, agent components — every layer is a registry/factory. RAGFlow is built for the enterprise that already has half a stack.
- Workers, not threads. The Flask API never blocks on parsing. Heavy work is enqueued in Redis and consumed by
rag/svr/task_executor.py. The API stays thin and stateless; ingest scales by adding workers. - Domain-first naming.
ParserType.PAPER,ParserType.MANUAL,Pipeline,Canvas. Source code reads like documentation — terms map directly to UI concepts, not CS abstractions. - Agents and pipelines unified.
Pipeline(Graph)inrag/flow/pipeline.pyextendsagent.canvas.Graph. The same DAG engine runs both user-built agents and system ingest pipelines — a significant bet on a single execution model. - Self-contained doc understanding. The biggest differentiator is also the steepest dependency cost. RAGFlow is willing to ship its own ML pipeline because layout-aware chunking is what makes retrieval useful on real-world enterprise PDFs.
- Python first, Go where it matters. The Python stack owns ML, orchestration, and prompts. The parallel Go service in
internal/exists for high-throughput HTTP and to cgo-bind the C++ tokenizer. Both stacks share the schema; neither is canonical.
The implicit value system you can read in commits and naming: correctness of retrieval > raw latency, deployment optionality > opinionated stack, end-to-end ownership > vendor dependency. The team has clearly chosen to take on more code in exchange for fewer integration risks at runtime.
🏗️ Design Decisions
| Decision | Choice Made | Alternatives | Why This Approach |
|---|---|---|---|
| Doc parsing strategy | Ship in-house OCR + layout NN (deepdoc/vision/) | Outsource to Azure Form Recognizer, AWS Textract, Unstructured.io | Layout-aware chunking is the moat; vendor APIs lag on Chinese docs and don't expose bbox confidence; on-prem deploy must run airgapped |
| Backend framework | Flask + Peewee ORM + worker queue | FastAPI + SQLAlchemy + Celery; Django + DRF | Flask blueprints map cleanly to the per-domain API surface (kb_app, dialog_app, document_app); Peewee is lighter than SQLAlchemy and the schema is simple enough; Redis queue avoids Celery's broker complexity |
| Heavy work isolation | Out-of-process worker (rag/svr/task_executor.py) over Redis | Threadpool inside Flask; gevent; Celery | Parsing is CPU-/GPU-bound and unpredictable; pushing it out of the API process keeps request latency stable and lets ingest scale horizontally without touching the API tier |
| Workflow DSL | JSON DAG canvas (agent.canvas.Graph) reused for ingest pipelines | Python-only flows (LangChain), YAML pipelines (Haystack) | Visual graph editing in web/; one execution engine for both agent runs and parse pipelines (Pipeline(Graph)); JSON survives DB round-trip and version migration |
| Vector / doc store | Pluggable backends via rag/utils/storage_factory.py | Tie hard to a single store (e.g. only ES, only Pinecone) | Different enterprises already run ES, Infinity, OpenSearch, or OceanBase; lock-in to one would be a deal-breaker; the abstraction cost is one interface per store |
| Tokenizer | Custom DART-trie tokenizer in C++ (internal/cpp/) with cgo+ctypes bindings | jieba (Python pure), HanLP, OpenSearch built-in | The DART double-array trie is O(1) per char with cache-friendly memory layout; needed for Chinese segmentation at indexing throughput; cgo overhead is amortised over batches |
| Knowledge graph | Both Microsoft GraphRAG and LightRAG ports (rag/graphrag/general/ + light/) | Pick one; build a custom graph extractor | The two represent different cost/quality points — General is heavyweight (community reports), Light is faster; users pick per-KB rather than the team picking for them |
| Frontend stack | React + Vite + Tailwind + shadcn/ui + Zustand | Next.js full-stack; UmiJS (was used previously); Vue/Svelte | The agent-canvas page needs heavy client-side interaction (drag-drop graph, real-time SSE, complex state); Vite gives fast HMR; shadcn/ui provides accessible primitives without a runtime cost |
| Parallel Go service | Mirror DAO/handler/service layers in internal/ | Python-only; rewrite to Go entirely; gRPC sidecar | Go gives ~10x throughput for thin HTTP paths and cleaner cgo into the C++ tokenizer; full rewrite would lose ML ecosystem; sharing MySQL keeps both stacks honest |
System Architecture
How the modules compose. Edges are labelled with the contract at each boundary — what flows between modules and why the dependency exists.
flowchart TD
subgraph Frontend["Frontend Tier"]
WEB["web/ React SPA"]
end
subgraph API_Tier["Python API Tier"]
API["api/ Flask blueprints"]
APIDB["api/db Peewee models + services"]
AGENT["agent/ canvas + components"]
end
subgraph Workers["Worker Tier"]
EXEC["rag/svr/task_executor.py"]
PIPE["rag/flow/Pipeline DAG"]
end
subgraph Core["Core RAG Engine"]
APP["rag/app per ParserType chunkers"]
DEEP["deepdoc parsers + vision"]
NLP["rag/nlp tokenizer + search"]
LLM["rag/llm provider adapters"]
GRAPH["rag/graphrag GraphRAG + Light"]
RAPTOR["rag/raptor recursive index"]
end
subgraph Stores["Storage Backends"]
DOCSTORE["ES / Infinity / OpenSearch / OB"]
OBJSTORE["MinIO / S3 / Azure / GCS / OSS"]
REDIS["Redis queue + locks + progress"]
MYSQL["MySQL Tenant / KB / Doc / Task"]
end
subgraph GoTier["Parallel Go Tier"]
GOSVR["internal/ Gin server"]
CPP["internal/cpp tokenizer"]
end
subgraph Interop["Interop"]
SDK["sdk/ Python wrapper"]
MCP["mcp/ MCP server"]
COMMON["common/ shared utils"]
MEMORY["memory/ chat memory"]
end
WEB -->|REST + SSE| API
WEB -->|REST| GOSVR
API --> APIDB
API --> AGENT
APIDB -->|ORM| MYSQL
API -->|enqueue| REDIS
REDIS -->|consume| EXEC
EXEC --> APP
EXEC --> PIPE
EXEC --> RAPTOR
EXEC --> GRAPH
PIPE -->|extends Graph| AGENT
APP --> DEEP
APP --> NLP
APP --> LLM
GRAPH --> LLM
GRAPH --> NLP
EXEC -->|index chunks| DOCSTORE
EXEC -->|store files| OBJSTORE
AGENT -->|retrieval| NLP
AGENT -->|chat/embed| LLM
API -->|search| NLP
NLP -->|query| DOCSTORE
GOSVR --> MYSQL
GOSVR --> CPP
GOSVR -->|same schema| DOCSTORE
SDK -->|HTTP| API
MCP -->|via SDK| SDK
MEMORY -->|persists in| DOCSTORE
COMMON -.->|imported by| API
COMMON -.->|imported by| EXEC
Modules
Core Engine
rag/
Per-document-type chunkers (rag/app/), the new DAG ingest pipeline (rag/flow/), Chinese-aware tokenizer (rag/nlp/), provider-agnostic LLM adapters (rag/llm/), Graph-RAG construction (rag/graphrag/), and pluggable storage connectors (rag/utils/).
deepdoc/
Per-format extractors (PDF, DOCX, XLSX, PPT, HTML, EPUB, JSON, MD), alternative engines (mineru, docling, paddleocr, tcadp), plus PaddlePaddle/ONNX models for layout detection and table structure recognition.
Orchestration & API
api/
Entry point ragflow_server.py; per-domain blueprints under api/apps/; database models in api/db/db_models.py; service layer in api/db/services/ handles document, dialog, KB, task, canvas, LLM operations.
agent/
The Graph execution engine (canvas.py) plus 20+ component types: LLM, agent-with-tools, categorize, switch, iteration, loop, message, invoke, data-operations. Tools include web search, code sandbox, SQL, retrieval invocation.
internal/
Mirrors the Python API for high-throughput deployments. Layered handler → service → dao → entity. Includes internal/cpp/ with the C++ rag-analyzer (DART trie + PCRE2 + opencc + re2) bound via a C-API and cgo.
Frontend
Shared / Interop
common/
Shared settings.py, constants.py (LLMType, ParserType, TaskType enums), crypto/token/string utilities, SSRF guard, MCP tool-call connector, doc-store / data-source interfaces.
memory/
Per-tenant persistent memory layer for multi-session chats; vector + summary storage.
mcp/
Lets external LLM clients (Claude Desktop, etc.) invoke RAGFlow KB-search and chat through the standard MCP interface.
sdk/
Programmatic client used by the MCP server, examples, and external integrations. Modules cover chat, KB, document, agent.
tools/
Misc CLI utilities for migration, data import, model conversion.
admin/
Tenant management, system maintenance, user administration helpers.
Navigation Guide
| Level | What You'll Find | When to Use |
|---|---|---|
| 0 — Project Hub (this page) | Project philosophy, design decisions, module map, end-to-end data flows | Getting started, choosing where to dive in |
1 — Module Overview (e.g., rag_doc/index.html) | Module purpose, sub-module cards, narrated walkthrough, algorithm spotlight, failure analysis | Understanding a module's role and design choices before reading code |
2 — Sub-Module Deep Dive (e.g., rag_doc/graphrag/index.html) | Annotated code walkthroughs, design rationale, edge cases, configuration knobs, real code examples | Implementing a feature, debugging, or extending a sub-module |
Focus Pages (e.g., rag_doc/graphrag/leiden-community-detection/) | Algorithm trace, alternatives comparison, complexity analysis, failure recovery | Deeply understanding a specific mechanism (why Leiden vs Louvain, how DART trie works, etc.) |
🔗 Key Data Flows
Three flows you should understand before reading individual modules. Note the annotations explaining why each hand-off occurs and where latency lives.
Flow A — Ingest a PDF into a knowledge base
sequenceDiagram
participant U as User (web/)
participant API as api/apps/document_app
participant DB as MySQL (Document, Task)
participant OBJ as MinIO/S3
participant Q as Redis Queue
participant W as rag/svr/task_executor
participant DD as deepdoc parsers + vision
participant CHK as rag/app chunker
participant LLM as rag/llm/embedding
participant DS as ES / Infinity
U->>API: POST /v1/document/upload (multipart)
API->>OBJ: write raw file
Note over API,OBJ: Heavy bytes never touch the API process beyond a stream
API->>DB: insert Document + Task rows
API->>Q: LPUSH task_id
API-->>U: 200 with task_id (returns fast)
Note over U,API: API stays unblocked; ingest is async
Q-->>W: BRPOP task_id
W->>OBJ: read file
W->>DD: parse layout + OCR if scanned
Note over W,DD: deepdoc gives bbox-aware text + tables
W->>CHK: chunk per ParserType (naive/paper/manual/...)
CHK->>LLM: embed each chunk
LLM-->>CHK: vectors
CHK->>DS: bulk index chunks + vectors
W->>DB: update Task progress (also pushes Redis log)
Note over W,DB: progress visible to web/ via SSE polling
Flow B — Chat query with retrieval-augmented generation
sequenceDiagram
participant U as User (web/ chat)
participant API as api/apps/restful_apis
participant DLG as DialogService
participant SRC as rag/nlp/search
participant DS as ES / Infinity
participant RR as rag/llm/rerank_model
participant CHT as rag/llm/chat_model
U->>API: POST /v1/conversation/completion (SSE)
API->>DLG: build LLMBundle + retrieve
DLG->>SRC: hybrid query (BM25 + dense)
SRC->>DS: search content_ltks + vector
DS-->>SRC: top-K candidates
SRC->>RR: rerank top-K
RR-->>SRC: scored top-N
SRC-->>DLG: chunks + citations
Note over DLG: Compose prompt with citations
DLG->>CHT: stream completion
CHT-->>DLG: token stream
DLG-->>API: SSE token frames
API-->>U: text/event-stream
Note over API,U: Citations link back to original chunks for trust
Flow C — Run an agent canvas (or ingest pipeline)
sequenceDiagram
participant U as User (web/ canvas)
participant API as api/apps canvas
participant G as agent.canvas.Graph
participant N1 as Node Begin
participant N2 as Node LLM or AgentWithTools
participant N3 as Node Iteration or Switch
participant T as agent/tools or rag/llm
U->>API: run canvas (DSL JSON)
API->>G: instantiate Graph(dsl)
G->>N1: execute begin
N1-->>G: initial variables
G->>N2: schedule next ready node
N2->>T: invoke LLM / tool / retrieval
T-->>N2: result
N2-->>G: write outputs to var scope
Note over G,N2: scheduler picks edges by topological order + branch conditions
G->>N3: evaluate switch/iteration
N3-->>G: select next path or sub-graph
Note over N3: iteration runs sub-graph per item with isolated scope
G-->>API: SSE per-component progress
API-->>U: live trace
How the Python and Go Tiers Connect
RAGFlow keeps a parallel Go service. Reading the directory layout, both stacks share the same MySQL schema and the same doc-store; neither is canonical. The Go side handles high-throughput HTTP paths and binds the C++ tokenizer through cgo for performance.
sequenceDiagram
participant Web as web/ SPA
participant Py as Python api/ (Flask)
participant Go as Go internal/ (Gin)
participant DB as MySQL (shared)
participant CPP as C++ rag_analyzer
participant DS as Doc-store (shared)
Web->>Py: heavy ops (upload, agent run, chat, GraphRAG build)
Web->>Go: thin reads (search, list, fetch)
Py->>DB: write tasks, docs, chats
Go->>DB: read same schema
Go->>CPP: cgo tokenize_query
CPP-->>Go: terms + weights
Go->>DS: search hybrid
Py->>DS: index + search
Note over Py,Go: They never call each other directly — coupling is via DB + doc-store
Key insight: The Python tier owns state-changing work (ingest, agent runs, chat sessions). The Go tier specialises in read-heavy paths (search, list). The boundary is shared MySQL + doc-store, not RPC. This means schema migrations must be coordinated, but neither tier becomes a bottleneck for the other.
Directory Map
Project source tree
ragflow/
├── api/ ← Flask REST API server (entry: api/ragflow_server.py)
│ ├── apps/ ← Blueprints: document, llm, restful_apis, sdk, services, auth
│ ├── db/ ← Peewee models + services + joint_services
│ └── common/, utils/, validation.py
├── rag/ ← Core RAG engine
│ ├── app/ ← Per-ParserType chunkers (naive, paper, manual, qa, table, ...)
│ ├── flow/ ← New DAG ingest pipeline (Pipeline extends agent.canvas.Graph)
│ ├── nlp/ ← rag_tokenizer + search/query rewriting + term-weight
│ ├── llm/ ← Provider adapters (chat / embedding / rerank / cv / ocr / tts)
│ ├── graphrag/ ← Microsoft GraphRAG-General + LightRAG variants + Leiden
│ ├── utils/ ← Pluggable doc-store + object-store backends + Redis
│ ├── svr/ ← task_executor.py, cache_file_svr, sync_data_source
│ ├── prompts/ ← System prompt templates
│ ├── res/ ← Resource files (dictionaries, etc.)
│ └── raptor.py ← RAPTOR recursive abstraction index
├── deepdoc/ ← Deep document parsing
│ ├── parser/ ← pdf, docx, xlsx, ppt, html, md, json, epub, figure, resume, ...
│ └── vision/ ← OCR + layout + table-structure recognizers (PaddlePaddle/ONNX)
├── agent/ ← Agent canvas DSL
│ ├── canvas.py ← Graph execution engine
│ ├── component/ ← 20+ node types
│ ├── tools/ ← External integrations (web search, code, SQL, retrieval)
│ ├── sandbox/ ← Code-exec sandbox
│ ├── templates/ ← Pre-built canvas DSL JSONs
│ └── plugin/ ← Third-party plugin loader
├── internal/ ← Parallel Go service
│ ├── server/, router/, handler/, service/, dao/, entity/
│ ├── engine/, storage/, cache/, tokenizer/, common/, utility/
│ └── cpp/ ← C++ rag_analyzer (DART trie + opencc + pcre2 + re2)
├── web/ ← React SPA
│ └── src/{pages, components, services, hooks, layouts, locales, theme, routes, utils, lib}
├── common/ ← Cross-cutting Python utilities
├── memory/ ← Long-term chat memory subsystem
├── mcp/ ← Model Context Protocol server
├── sdk/ ← Python SDK wrapping REST
├── tools/ ← DevOps / data-loading scripts
├── admin/ ← Admin tooling
├── docker/ ← Compose files + dockerfiles
├── conf/ ← Runtime config + model lists
├── helm/ ← Kubernetes Helm chart
├── docs/ ← User-facing markdown documentation
├── test/ ← Tests (pytest + playwright)
├── pyproject.toml ← uv-managed Python deps
├── Dockerfile ← Production image
└── README.md
Build & Tech Stack
Languages
Python 3.10+ (backend), TypeScript/React (web), Go 1.22+ (internal), C++17 (tokenizer)
Backend Framework
Flask + Peewee ORM + Gunicorn workers; Quart support for async paths
Frontend Stack
React 18 + Vite + Tailwind + shadcn/ui + Zustand + React Query + React Router
Storage
MySQL (state); ES / Infinity / OpenSearch / OceanBase (chunks + vectors); MinIO / S3 / Azure / GCS / OSS / OpenDAL (objects); Redis (queue + locks + progress)
ML / Inference
PaddlePaddle + ONNX runtime (layout, OCR, TSR); embedding/rerank/chat via vendor adapters (OpenAI, Bedrock, Tongyi, Ollama, Xinference, ...)
Build & Dev
uv sync --python 3.12 --all-extras; Docker Compose; pre-commit; ruff; pytest; npm + Vite