An open-source retrieval-augmented generation platform for ingesting knowledge from multiple sources, indexing multimodal content, and serving secure, citation-ready retrieval to applications and AI agents.
RAG Platform separates ingestion, indexing, retrieval, lifecycle management, and agent integration into independently deployable services. It supports dense and lexical retrieval, tenant and knowledge-base isolation, role-based filtering, asynchronous document ingestion, and an MCP client for model-facing tool use.
Important
The project is under active development and its packages are currently versioned 0.1.0. Public APIs may evolve before the first stable release.
- Pluggable ingestion — source connectors feed a shared download, clean, chunk, and vectorize pipeline. Yuque and local Markdown connectors are included.
- Hybrid retrieval — combines vector similarity, BM25, and title relevance with reciprocal rank fusion.
- Agent-ready API — provides versioned, tenant-isolated document ingestion, search, update, deletion, and ingestion-job endpoints.
- Stable citations — returns document IDs, chunk IDs, source URLs, scores, metadata, and real character locations when available.
- Recoverable writes — persists ingestion jobs and request snapshots before processing, with idempotency checks and stale-job recovery.
- Access control — supports knowledge-base and document-level RBAC, public collections, user-token introspection, and service-token protection.
- MCP integration — exposes remote retrieval and health checks as MCP tools without coupling the client to the platform internals.
- Evaluation tooling — includes offline hit rate, recall, precision, MRR, and NDCG evaluation with baseline comparison.
- Deployment flexibility — each runtime component has its own container image and can be deployed independently.
flowchart LR
Sources["Knowledge sources<br>Yuque · local Markdown · custom connectors"]
Pipeline["rag-pipeline<br>sync · clean · vectorize"]
API["rag-api<br>versioned ingestion and retrieval API"]
Search["rag-search<br>hybrid retrieval and RBAC"]
MCP["rag-mcp<br>MCP stdio client"]
Apps["Applications and AI agents"]
PostgreSQL[(PostgreSQL<br>metadata and jobs)]
Qdrant[(Qdrant<br>vectors and payloads)]
Redis[(Redis<br>search cache)]
Sources --> Pipeline
Pipeline --> PostgreSQL
Pipeline --> Qdrant
Apps --> API
API --> PostgreSQL
API --> Qdrant
Apps --> Search
MCP --> Search
Apps --> MCP
Search --> PostgreSQL
Search --> Qdrant
Search --> Redis
The ingestion and query paths intentionally share embedding and Qdrant payload contracts through rag-core. They may run on different hosts, but they must use the same embedding model, vector dimension, collection, and payload schema.
Running the offline pipeline end-to-end:
# Stage 1 — Download & ingest
$ uv run rag-pipeline sync --source yuque --output-dir output/20260904
2026-09-04 08:12:43,844 INFO PDF OCR extracted 57 chars from output/20260904/a8f3b2c1.pdf
2026-09-04 08:12:45,115 INFO PDF OCR extracted 529 chars from output/20260904/a8f3b2c1.pdf
[yuque] 2 scope: 已下载 3818, 跳过 0, 删除 0, 图片 386, 附件 674, 失败 0
# Stage 2 — Clean & chunk
2026-09-04 08:31:55,832 INFO [7744567] cleaned → 20260904/
2026-09-04 08:31:55,838 INFO [7744564] cleaned → 20260904/
2026-09-04 08:31:55,841 INFO [7744561] cleaned → 20260904/
2026-09-04 08:31:55,845 INFO [7744558] cleaned → 20260904/
# Stage 3 — Vectorize & index
2026-09-04 08:34:04,193 INFO HTTP Request: PUT .../collections/knowledge_documents/points?wait=true "HTTP/1.1 200 OK"
2026-09-04 08:34:05,799 INFO HTTP Request: PUT .../collections/knowledge_documents/points?wait=true "HTTP/1.1 200 OK"
2026-09-04 08:34:06,417 INFO HTTP Request: PUT .../collections/knowledge_documents/points?wait=true "HTTP/1.1 200 OK"
2026-09-04 08:34:07,415 INFO HTTP Request: PUT .../collections/knowledge_documents/points?wait=true "HTTP/1.1 200 OK"All three stages — download, clean, and vectorize — run against output/20260904/ with Qdrant returning 200 OK for every indexed point.
This repository is a uv workspace containing five packages:
| Path | Purpose | Deployable |
|---|---|---|
packages/rag-core |
Shared configuration, database access, embeddings, vector storage, cleaning, chunking, payload contracts, and RBAC data access | No |
packages/rag-pipeline |
Source connectors and the offline sync, clean, and vectorize pipeline | Yes |
packages/rag-search |
FastAPI hybrid-search service with Redis caching and RBAC filtering | Yes |
packages/rag-api |
Versioned API for isolated ingestion, retrieval, updates, deletion, and job recovery | Yes |
packages/rag-mcp |
Standalone MCP client for rag-search |
Yes |
Additional directories:
eval— retrieval datasets, runners, metrics, reports, and regression comparison.deploy— SQL migrations, Docker Compose, systemd units, cron examples, and operational scripts.config/connectors— connector configuration examples.
- Python 3.10 or newer
- uv
- PostgreSQL 14 or newer
- Qdrant
- Redis for
rag-search - Access to a compatible embedding endpoint
- Docker and Docker Compose, if using the container workflow
PDF, Office, OCR, and legacy document extraction may additionally require system tools such as Poppler, Tesseract, LibreOffice, and Antiword.
git clone https://github.com/Today-Hbw/rag-platform.git
cd rag-platform
uv sync --all-extras --dev
cp .env.example .envOn Windows PowerShell, use Copy-Item .env.example .env instead of cp.
Edit .env with your PostgreSQL, Qdrant, Redis, embedding, and API credentials. The file is ignored by Git and must never be committed.
The Compose file includes PostgreSQL, Qdrant, and Redis. The embedding provider remains an external dependency.
docker compose -f deploy/docker-compose.yml --env-file .env up -d postgres qdrant redisThe Compose PostgreSQL service applies these files automatically when it initializes a new postgres_data volume. When using an external PostgreSQL instance, apply them manually in order:
The initialization scripts only run when the PostgreSQL data directory is empty.
Start the versioned API:
rag-api serveStart the hybrid-search service in another terminal:
rag-search serve --host 0.0.0.0 --port 8090Default URLs:
- Agent-facing API:
http://localhost:8001 - Hybrid-search API:
http://localhost:8090 - API documentation:
http://localhost:8001/docs
The pipeline is split into explicit stages so each step can be retried and operated independently.
# Preview source changes without writing files or updating the database
rag-pipeline sync --source yuque --dry-run
# Download changed source documents
rag-pipeline sync --source yuque
# Convert downloaded content into normalized Markdown
rag-pipeline clean
# Chunk, embed, and index cleaned documents
rag-pipeline vectorizeUseful options:
rag-pipeline sync --source yuque --scope <scope-id> --full
# After setting RAG_LOCAL__ROOT to a directory containing Markdown files
rag-pipeline sync --source local
rag-pipeline reset-stuck --statuses vec_failed --to-status cleanedrag-pipeline vectorize --recreate deletes and recreates the configured Qdrant collection. Use it only when a full rebuild is intentional.
To add a source, implement the connector contract in packages/rag-pipeline/src/rag_pipeline/connectors and register it with the connector registry. The downstream pipeline does not need source-specific changes.
rag-api is designed for applications and AI agents that need isolated document lifecycle operations and citation-ready vector retrieval.
All endpoints except health and API documentation require a bearer API key. Configure either a multi-tenant mapping:
RAG_API__TENANT_KEYS={"replace-me":"tenant-a"}or a single-tenant key:
RAG_API__SINGLE_TENANT_KEY=replace-me
RAG_API__SINGLE_TENANT_ID=tenant-aThe server resolves the tenant from the key. Clients cannot override the tenant in request bodies or search filters.
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/health |
Check PostgreSQL and Qdrant availability |
POST |
/api/v1/knowledge-bases/{kb_id}/documents |
Submit an asynchronous text or Markdown ingestion job |
GET |
/api/v1/knowledge-bases/{kb_id}/documents/{doc_id} |
Read document metadata and status |
GET |
/api/v1/ingestion-jobs/{job_id} |
Read ingestion or update job status |
POST |
/api/v1/knowledge-bases/{kb_id}/search |
Search within the authenticated tenant and selected knowledge base |
PUT |
/api/v1/knowledge-bases/{kb_id}/documents/{doc_id} |
Submit an idempotent asynchronous replacement update |
DELETE |
/api/v1/knowledge-bases/{kb_id}/documents/{doc_id} |
Idempotently delete a document using Idempotency-Key |
Example search:
curl --request POST "http://localhost:8001/api/v1/knowledge-bases/demo/search" \
--header "Authorization: Bearer ${RAG_API_KEY}" \
--header "Content-Type: application/json" \
--data '{
"query": "How does tenant isolation work?",
"top_k": 5,
"score_threshold": 0.6,
"include_content": true
}'Write operations use idempotency keys. Reusing a key with a different request returns 409 Conflict; safe retries of the same request return the original job or result.
rag-search provides the existing hybrid retrieval surface:
POST /search— text retrievalPOST /image— image-oriented retrievalPOST /multimodal— combined text and image retrievalGET /health— service health
It combines vector, BM25, and title scores using reciprocal rank fusion and caches results in Redis.
RBAC is disabled by default for backward compatibility:
RAG_RBAC__ENABLED=falseWhen enabled, visible results are computed from role-to-resource assignments, public collections, and optional document exceptions. Production deployments should configure RAG_RBAC__INTROSPECT_URL so user tokens are resolved by a trusted identity service.
If the introspection URL is empty, the service trusts the configured role header directly. This mode is intended for local development and must not be exposed as a production authorization boundary.
rag-mcp wraps the remote hybrid-search service as two MCP tools: search_knowledge_base and check_service_health.
# Optional out-of-band login; the token is stored locally and never passed through a model
rag-mcp login --login-url https://example.com/api/login --server http://localhost:8090
# Start the MCP stdio server
rag-mcp --server http://localhost:8090The client is intentionally independent of rag-core, so it can be installed and distributed separately.
The top-level eval package evaluates retrieval using document-anchored hit rate, recall, precision, MRR, and NDCG.
# Evaluate the configured services with the example dataset
python -m eval run --dataset eval/datasets/smoke.example.jsonl --out runs/baseline.json
# Compare two runs and fail on regressions
python -m eval compare \
--before runs/baseline.json \
--after runs/candidate.json \
--fail-on-regressSee eval/datasets/README.md for dataset and annotation conventions.
Build or start the deployable services with Compose:
docker compose -f deploy/docker-compose.yml --env-file .env build
docker compose -f deploy/docker-compose.yml --env-file .env up -d api search
docker compose -f deploy/docker-compose.yml --env-file .env run --rm pipeline sync --source yuqueContainer images are published by GitHub Actions to:
ghcr.io/today-hbw/rag-apighcr.io/today-hbw/rag-searchghcr.io/today-hbw/rag-pipelineghcr.io/today-hbw/rag-mcp
Examples for scheduled synchronization are available under deploy/systemd and deploy/cron. Synchronization locks support file, Redis, PostgreSQL advisory locks, and disabled backends through configuration.
Runtime configuration uses environment variables with the RAG_ prefix and __ for nesting. For example, RAG_POSTGRESQL__PASSWORD maps to settings.postgresql.password.
| Prefix | Purpose |
|---|---|
RAG_POSTGRESQL__* |
Metadata, state, job, and permission database |
RAG_QDRANT__* |
Vector store URL and collection |
RAG_REDIS__* |
Hybrid-search cache |
RAG_EMBEDDING__* |
Embedding endpoint, model, credentials, and vector dimension |
RAG_PIPELINE__* |
Chunking and synchronization locking |
RAG_PATHS__* |
Pipeline data directory |
RAG_YUQUE__* / RAG_LOCAL__* |
Connector settings |
RAG_SEARCH__* |
Recall and fusion parameters |
RAG_API__* |
Versioned API authentication and worker behavior |
RAG_RBAC__* |
Role resolution, introspection, caching, and service tokens |
See .env.example for the complete reference. Secrets are represented with Pydantic SecretStr, redacted from application logs, and scanned with Gitleaks in CI and pre-commit hooks.
Install all workspace packages and development tools:
uv sync --all-extras --devRun the checks used by CI:
uv run ruff check .
uv run mypy packages/rag-core/src packages/rag-pipeline/src packages/rag-search/src packages/rag-api/src
uv run pytest
gitleaks detect --source . --config .gitleaks.toml --redact --no-bannerTests use in-memory or mocked Qdrant, Redis, HTTP, and PostgreSQL dependencies where possible. Real-provider evaluation and deployment validation require appropriately configured external services.
The project underwent a major version update on September 1, 2026 — landing the P0 versioned API layer, migrating the database from SQLite to PostgreSQL, adding Qdrant API-key authentication, and publishing an English README under the MIT license. Within two weeks it has reached 99 clones from 24 unique cloners (data as of September 3, 2026).
Issues, pull requests, and discussions are all welcome. A typical contribution flow is:
- Fork the repository and create a focused branch.
- Add or update tests for the behavior you are changing.
- Run linting, type checking, tests, and Gitleaks locally.
- Update documentation when configuration or public behavior changes.
- Open a pull request describing the problem, approach, compatibility impact, and verification performed.
Please keep connectors source-specific and keep shared payload, embedding, and database behavior in rag-core. Avoid introducing credentials, private endpoints, proprietary datasets, or organization-specific assumptions into examples and tests. For substantial API or architecture changes, open an issue before investing in a large implementation so the design can be discussed first.
If you find this project useful, a ⭐ Star is greatly appreciated!
- Never commit API keys, database credentials, cookies, user tokens, or populated
.envfiles. - Revoke a credential immediately if it may have been exposed; removing it from the latest commit is not sufficient.
- Use least-privilege database accounts and scoped service tokens in production.
- Keep tenant and knowledge-base filters server-controlled.
- Do not expose RBAC's header-trusting development mode to untrusted callers.
- Report suspected vulnerabilities privately through GitHub's security reporting features when available instead of opening a public issue.
RAG Platform evolved from Today-Hbw/dgr_yuque_qrant and was redesigned as a source-agnostic, modular workspace with independently deployable services.
RAG Platform is licensed under the MIT License.
