Skip to content

zeabur/rag-service

Repository files navigation

RAG Service

A self-hosted RAG service with hybrid search (semantic + BM25), admin dashboard, and Claude Code plugin for agent integration.

Architecture

                          rag-service
 ┌─────────────────────────────────────────────────────────┐
 │                                                         │
 │  ┌─────────┐   ┌──────────┐   ┌───────────────────┐    │
 │  │ Adapter  │──>│ Screening│──>│ Chunk + Embed      │    │
 │  │ (json,md)│   │ + Redact │   │ (configurable API)│    │
 │  └─────────┘   └──────────┘   └────────┬───────────┘    │
 │       ^                                 │                │
 │       │ --input / --dry-run             │                │
 │       │ --dry-run                       │                │
 │                                         v                │
 │  ┌──────────────────────────────────────────────┐       │
 │  │       Direct PostgreSQL + pgvector             │       │
 │  │                                               │       │
 │  │  kb_chunks                                    │       │
 │  │  ┌─────┬──────┬───────────┬──────────┐       │       │
 │  │  │ id  │ text │ embedding │ source/tags│      │       │
 │  │  └─────┴──────┴───────────┴──────────┘       │       │
 │  │                                               │       │
 │  │  ACL, audit log, feedback, and query signals   │       │
 │  └──────────────────────────────────────────────┘       │
 │                         │                                │
 │              ┌──────────┴──────────┐                     │
 │              v                     v                     │
 │  ┌─────────────────┐   ┌──────────────────┐             │
 │  │   BM25 search   │   │ Semantic search  │             │
 │  │ (Intl.Segmenter │   │ (pgvector cosine)│             │
 │  │  + Porter stem)  │   └────────┬─────────┘             │
 │  └────────┬────────┘            │                        │
 │           └──────────┬──────────┘                        │
 │                      v                                   │
 │             ┌─────────────────┐                          │
 │             │   RRF Fusion    │                          │
 │             │ (keyword 0.25 + │                          │
 │             │  semantic 0.75) │                          │
 │             └────────┬────────┘                          │
 │                      v                                   │
 │  ┌────────────────────────────────────────────┐         │
 │  │  /api/query  →  chunks + optional LLM RAG  │         │
 │  │  /api/learn  →  add chunk (unverified)      │         │
 │  │  /api/report →  flag content issue          │         │
 │  │  /dashboard  →  admin UI                    │         │
 │  └────────────────────────────────────────────┘         │
 └─────────────────────────────────────────────────────────┘

            Agent Curation Loop
 ┌───────────────────────────────────────┐
 │                                       │
 │  search ──> learn/report              │
 │               │                       │
 │               v                       │
 │  triage ──> inspect ──> edit/verify   │
 │               │                       │
 │               v                       │
 │  curate (walks queue item-by-item)    │
 │               │                       │
 │               v                       │
 │  improved KB ──> better search ──>    │
 └───────────────────────────────────────┘

Getting Started

1. Create a Zeabur Project

Go to Zeabur Dashboard and create a new project.

2. Add PostgreSQL with pgvector

The Marketplace template provisions pgvector/pgvector automatically. For a manual deployment, provide a PostgreSQL database where the service account can run CREATE EXTENSION vector and schema migrations.

3. Configure model credentials

Provide an OpenAI-compatible embedding endpoint and key. Answer generation is optional and uses an OpenAI-compatible chat-completions endpoint.

  • EMBEDDING_API_KEY (or ZEABUR_AI_HUB_API_KEY)
  • EMBEDDING_API_URL (optional; defaults to https://api.openai.com/v1)
  • LLM_API_KEY / LLM_API_URL for RAG answers and optional LLM features

4. Deploy RAG Service

In your project, click Add Service → Marketplace, search for RAG Service, and fill in:

  • The PostgreSQL connection is injected automatically by the template
  • A Zeabur AI Hub key for answer generation
  • A Gemini API key for the Marketplace template's default embedding provider, or credentials for another OpenAI-compatible embedding provider when deploying manually

Deploy on Zeabur

Schema migrations run automatically on first startup — no manual setup needed.

API Endpoints

Endpoint Method Auth Description
/api/query POST API key Search knowledge base + optional RAG answer
/api/learn POST API key Add new knowledge chunks
/api/report POST API key Report content issues
/api/feedback POST API key Submit result feedback
/api/admin/* GET/POST API key (admin) API key management, signals, reports, chunks
/dashboard GET Basic Auth Admin dashboard

Search Example

curl -X POST "https://your-rag-service/api/query" \
  -H "Authorization: Bearer $RAG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "how to deploy", "mode": "hybrid", "top_k": 5}'

Search modes are hybrid (default), semantic, bm25, and sql-hybrid. Hybrid mode retrieves a wider BM25 and semantic candidate pool, combines ranks with RRF, applies optional per-source weights and recency decay, and can optionally send the candidate pool through an LLM reranker.

Deployment-specific search tuning stays in environment variables rather than source code:

RAG_QUERY_EXPANSIONS='[{"match":"object storage","append":"s3 bucket"}]'
RAG_SOURCE_WEIGHTS='{"docs":1.0,"learned":0.8}'

Learn Example

curl -X POST "https://your-rag-service/api/learn" \
  -H "Authorization: Bearer $RAG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title": "Deploy with Docker", "content": "Steps to deploy..."}'

Agent Integration (Claude Code Plugin)

Add the marketplace and install the plugin:

claude plugin marketplace add zeabur/rag-service && claude plugin install rag-service@rag-service

Then configure the connection:

claude
# Inside Claude Code, run:
/zeabur-rag-setup

This sets ZEABUR_RAG_URL and RAG_API_KEY in your Claude Code settings. The agent will automatically use the search skill when answering questions.

Available Skills

The plugin ships 8 skills split into two tiers: everyday use, and maintenance of the knowledge base itself.

Using the knowledge base — any agent with an API key can invoke these:

Skill When it triggers
zeabur-rag-setup First-time setup — asks for ZEABUR_RAG_URL and RAG_API_KEY, writes them to Claude Code settings
zeabur-rag-search Any technical question that might be answered by the KB (fires automatically, even without an explicit "search" instruction)
zeabur-rag-learn After solving a problem or discovering something the KB didn't cover — contributes a new chunk marked unverified until an admin reviews it
zeabur-rag-report A search result looks wrong, outdated, or a topic is missing — files an issue against a chunk without needing write access

Curating the knowledge base — admin-scope skills that form a self-improving loop:

Skill Role in the loop
zeabur-rag-triage Lists everything pending: open reports, unverified learn chunks, low-similarity queries, negative feedback
zeabur-rag-inspect Pulls one chunk's full content + related reports + search signals + edit history — the "look before you touch" step
zeabur-rag-edit Patches an existing chunk in place (title, content, tags, status). Requires admin scope
zeabur-rag-curate Orchestrator — walks through every triage item one by one with the user, deciding fix / reject / learn-new for each

The Curation Loop

Every agent interaction feeds a signal back into the KB, and the curation skills close the loop:

  1. Agents run search → every query is logged with its top similarity score. Low-similarity queries (< 0.4) become signals that the KB may have a gap.
  2. Agents run learn / report → new unverified chunks and open reports pile up in a queue.
  3. An admin runs curate (or triage for a quick read-only view) → the skill walks the queue item-by-item:
    • For a report: inspect the chunk → edit to fix, or learn new content if it's a gap → close the report.
    • For an unverified learn chunk: inspect → verify, reject, or edit before verifying.
    • For a low-similarity query: search to reproduce → learn new content if it's a true gap, or edit an existing chunk to rank better.
    • For negative feedback: inspect the returned chunks → fix whichever one misled the agent.
  4. Verified chunks rank at full weight; rejected ones are removed. The next round of searches sees an improved KB.

Agents and admins operate on the same data — the skills just expose different capabilities depending on scope. Point the plugin at any RAG service deployment and the loop works out of the box.

Environment Variables

Variable Required Description
DATABASE_URL Yes Direct PostgreSQL connection string; the database must support pgvector
EMBEDDING_API_KEY Yes* Embedding provider key; falls back to LLM_API_KEY or ZEABUR_AI_HUB_API_KEY
EMBEDDING_API_URL No OpenAI-compatible embedding base URL
EMBEDDING_MODEL No Embedding model (default: text-embedding-3-small)
EMBEDDING_DIMENSIONS No Vector dimension shared by the model and pgvector schema (default: 1536)
LLM_API_KEY / LLM_API_URL No Required only for generated answers, rewriting, LLM screening, or reranking
ZEABUR_AI_HUB_API_KEY No May be used as the embedding and LLM key on Zeabur
RAG_API_KEY Auto API key for service access (auto-generated on Zeabur)
RAG_BASIC_AUTH Auto Dashboard auth (format: admin:password)
RAG_MODEL No LLM model (default: gemini-2.5-flash-lite)
CORS_ORIGIN No CORS origin (default: *)
RAG_QUERY_EXPANSIONS No JSON array of literal query expansion rules
RAG_SOURCE_WEIGHTS No JSON object mapping source names to ranking multipliers
RERANK_API_URL / RERANK_API_KEY / RERANK_MODEL No Optional second-stage reranker configuration
SCREENER_ENABLED No Enable regex ingestion screening (default: true)
SCREENER_LLM_ENABLED No Opt in to sending chunks through the LLM sensitivity review (default: false)
SCREENER_CUSTOM_RULES_FILE No Path to deployment-private screening rules; keep this file outside the public repository

See .env.example for a complete template.

Embedding Model

The default is text-embedding-3-small with 1536 dimensions through an OpenAI-compatible endpoint. EMBEDDING_MODEL and EMBEDDING_DIMENSIONS are configurable, but existing vectors must be re-embedded before changing the dimension. Startup rejects a mismatched pgvector column instead of silently mixing incompatible embeddings.

This model is used for:

  • Query-time embedding — vectorizing search queries (src/embedding.ts)
  • Batch embedding — vectorizing chunks during pipeline upload (src/embedding.ts)

Local Development

cp .env.example .env
# Fill in your credentials
bun install
bun run start

Pipeline

Import data into the knowledge base using adapters.

Before embedding or upload, imported chunks pass through a sensitive-content screen. Known secrets are rejected; personal email addresses, phone numbers, private IPs, internal hostnames, and credential values are masked. This also applies to --dry-run output. The optional LLM phase can enforce private organization rules without committing those rules to this repository.

Quick start

# Import from a JSON file
bun run pipeline --adapter json --input data/my-chunks.json

# Import from a directory of Markdown files
bun run pipeline --adapter markdown --input ./docs/

# List available adapters
bun run pipeline --list

CLI options

Option Description
--adapter <name> Run specific adapter(s). Repeat for multiple. Omit for all.
--input <path> Set INPUT_PATH in adapter config.
--replace Delete all chunks before import (full rebuild).
--dry-run Export chunks as JSON to stdout; skip embed/upload.
--list List available adapters and exit.
--help Show help.

Writing a custom adapter

Create a .ts file that exports a SourceAdapter:

import type { SourceAdapter, Chunk } from "rag-service/pipeline/types";

export default {
  name: "my-source",
  description: "Import from my custom source",

  async export(config) {
    // config contains all env vars + CLI --input as INPUT_PATH
    const chunks: Chunk[] = [];
    // ... fetch data, chunk it, push to chunks[] ...
    return chunks;
  },

  // Optional: called after successful upload with the uploaded chunks
  async afterUpload(chunks, config) {
    // e.g. write back chunk IDs to source files
  },
} satisfies SourceAdapter;

Load external adapters by setting RAG_ADAPTERS_PATH:

RAG_ADAPTERS_PATH=./my-adapters bun run pipeline

Built-in adapters

json — Import from a JSON file. Accepts Chunk[] or arrays of objects with id + content fields.

markdown — Scan a directory for .md/.mdx files, parse frontmatter, split by ## headings (~800 chars per chunk).

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages