PixelRAG Documentation

repository·main·Indexed 25 days ago

https://github.com/startrail-org/pixelrag

A Visual Retrieval-Augmented Generation system that renders documents as screenshot tiles to preserve visual structures like tables and charts. Includes a reproduction harness (pixelrag-repro v0.1.0), high-performance Chromium screenshot patches via CDP, and a production topology featuring a Search API with blue-green deployment and a Node.js chat agent. Provides tools for benchmarking against public or self-hosted APIs using models like Qwen3.5-4B and MiniMax-M3, and a pixelbrowse plugin for Claude Code.

Tokens
34.7K
Snippets
78
Records
186
Agent score
94%

What's inside PixelRAG

  1. Overview of the Synthetic Data Generation Pipeline

    main

    The Synthetic Data Generation Pipeline is an end-to-end process for generating the screenshot-training-natural-filtered-v2 dataset. This dataset is used to fine-tune Qwen3-VL-Embedding-2B for visual document retrieval. The pipeline produces approximately 115K high-quality query→screenshot-chunk pairs including hard negatives, starting from raw Wikipedia screenshot tiles.

    There are two main pipelines:

    1. Visual Query Pipeline

    Processes Wikipedia screenshot tiles (kiwix_tiles/) through several stages:

    • Generation: Gemini reads screenshot chunks to generate Q/A pairs.
    • Filtering: GPT-4o removes queries that are not self-contained.
    • Hard Negative Mining: A Search API retrieves confusable chunks.
    • VQA Filtering: A VLM removes false negatives (chunks that actually answer the query).
    • Cleaning: Gemini scores queries for naturalness and factoid style.
    • Export & Splitting: Final data is filtered by score, split into train/eval/test sets, and packaged for Hugging Face.

    2. Text Warmup Pipeline

    Processes text passages (text_baseline.db) to create a text-qa-pair dataset used for --text-warmup-steps:

    • Generation: Gemini reads text passages to generate Q/A pairs.
    • Filtering: Removes non-self-contained queries.
    • Mining: Text search API retrieves confusable passages.
    • LLM Filtering: LLM removes false negatives.
  2. Understand PixelRAG Production Topology

    main

    The PixelRAG production environment consists of three main components:

    • Frontend: Located in the web/ directory, deployed automatically on Vercel from the main branch.
    • Search API: Located in the serve/ directory, run via pixelrag serve behind an nginx proxy. It uses a blue-green deployment strategy with two independent slots to ensure zero downtime.
    • Chat Agent: Located at web/agent-server.mjs, this is a Node.js service that communicates with the Search API.
  3. Filter hard negatives using VQA

    main

    Use filter_hard_negatives_vqa.py to remove false negatives from mined retrieval candidates. The script uses a VLM to verify if a candidate image actually contains the answer to the query. If the VLM determines the answer is CORRECT for a candidate, that candidate is treated as a false negative and skipped. If the verdict is WRONG or CANNOT_ANSWER, it is kept as a hard negative.

    Key Arguments:

    • --input: Path to the input JSONL file (must contain retrieve_top20 field).
    • --output: Path to the filtered JSONL file.
    • --reviews-output: Path to save candidate reviews.
    • --summary-output: Path to save the summary JSON.
    • --candidate-k: Maximum number of non-positive candidates to check (default 10).
    • --num-hard-negatives: Target number of hard negatives to collect per example.
    • --concurrency: Number of concurrent VLM requests.
    OPENAI_API_KEY=... python filter_hard_negatives_vqa.py \
        --input /tmp/sample_100_hn.jsonl \
        --output /tmp/sample_100_hn_v2.jsonl \
        --reviews-output /tmp/sample_100_hn_v2.reviews.jsonl \
        --summary-output /tmp/sample_100_hn_v2.summary.json \
        --candidate-k 10 \
        --num-hard-negatives 2 \
        --concurrency 8
  4. Use Qdrant as a vector search backend

    main

    For high-performance or large-scale needs, use Qdrant instead of the default FAISS. You can configure quantization via a JSON file to reduce memory usage.

    To build an index against Qdrant:

    1. Start a Qdrant server (e.g., via Docker).
    2. Run pixelrag build-index with --backend qdrant and required connection flags.
    3. Use pixelrag serve with --qdrant-url and --qdrant-client-config to query the collection.
    # Build against Qdrant
    pixelrag build-index --embeddings-dir ./embeddings --output-dir ./index \
        --backend qdrant --qdrant-url http://localhost:6333 --collection pixelrag \
        --qdrant-quantization-config ./quantization.json
    
    # Serve the collection
    pixelrag serve --index-dir ./index --qdrant-url http://localhost:6333 \
        --qdrant-client-config ./qdrant-client.json --port 30001
  5. Serve a PixelRAG index

    main

    After building an index (or using a pre-built one), you can serve it using the pixelrag serve command. You must provide the index directory and the articles JSON via environment variables.

    Environment Variables:

    • PIXELRAG_INDEX_DIR: Path to the index directory.
    • PIXELRAG_ARTICLES_JSON: Path to the articles.json file.

    Example (Serving a custom index):

    PIXELRAG_INDEX_DIR=./my_index PIXELRAG_ARTICLES_JSON=./my_index/articles.json \
    uv run pixelrag serve --port 31337

    Example (Serving a pre-built Wikipedia index):

    PIXELRAG_INDEX_DIR=/home/yichuan/pixelrag-data/text_search_index_1024 \
    PIXELRAG_ARTICLES_JSON=/home/yichuan/pixelrag-data/articles.json \
    uv run pixelrag serve --port 30001 &
  6. Use the Universal LoRA adapter for multiple compression levels

    main

    Instead of shipping multiple specialized adapters, you can use a single universal adapter trained on mixed-compression data (2x, 3x, 5x, and 9x). This adapter handles any of these compression levels with near-specialized accuracy (within 0.2–7.6 LLM-judge points).

    Universal Adapter Configuration:

    • Training Data: Concatenated 2x+3x+5x+9x samples.
    • Recommended Config: r=256, lr=1e-5, with ViT unfrozen.
    • Deployment: Ship as a single ~1.28 GB LoRA and merge at load time.
  7. Configure the PixelRAG reader and retrieval services

    main

    Before running reproduction scripts, you must ensure the necessary services are running. The topology typically includes a reader (serving the LLM) and various retrieval services (serving FAISS indexes).

    Reader Service

    • Model: Qwen/Qwen3.5-4B using vLLM 0.19.0.
    • Setup: Install via uv sync --extra reader, then run: CUDA_VISIBLE_DEVICES=0 HF_HOME=… .venv/bin/vllm serve Qwen/Qwen3.5-4B --port 8010.

    Retrieval Services

    roledefault portindex / modelnotes
    base pixel:30088search_index_normed_v2multimodal query
    lora pixel:30096wiki lora-vit-ckpt200multimodal query
    traf text:30097text_search_index_1024_normedtext query
    news pixel:30095news_image_search_indexLiveVQA only

    Note: All pixel/text services are direct_gpu. The reader sends the raw query, and the service performs the encoding. Do not POST precomputed embeddings.

  8. Build the patched headless_shell for turbo capture

    main

    The pixelshot turbo capture path (fast_cdp) requires a custom headless_shell binary containing specific CDP additions for high-throughput screenshotting. Without this binary, pixelshot falls back to a standard portable capture path (in-Chrome JPEG over CDP) which is slower.

    Prerequisites:

    • depot_tools must be on your PATH.
    • A Chromium 150 checkout (the patch is a git diff from upstream commit 4deaeccb7c).
    • ~100 GB free disk space.
    • Platform toolchain: Linux (clang via runhooks), macOS (Xcode), or Windows (VS/MSVC).

    Build Steps:

    1. Fetch dependencies:

      gclient sync --with_branch_heads --with_tags --delete_unversioned_trees -j 32
      gclient runhooks
    2. Apply the patch:

      git -C src apply ../render/chrome-build/pixelrag-chrome.patch
    3. Configure and build:

      mkdir -p src/out/Headless
      cat > src/out/Headless/args.gn <<'EOF'
      import("//build/args/headless.gn")
      is_official_build = true
      is_debug = false
      symbol_level = 0
      blink_symbol_level = 0
      chrome_pgo_phase = 0
      EOF
      ( cd src && gn gen out/Headless && autoninja -C out/Headless headless_shell )
    # 1. fetch deps for the checkout
    gclient sync --with_branch_heads --with_tags --delete_unversioned_trees -j 32
    gclient runhooks
    
    # 2. apply the patch
    git -C src apply ../render/chrome-build/pixelrag-chrome.patch     # or: git am / patch -p1
    
    # 3. configure (headless, optimized) and build
    mkdir -p src/out/Headless
    cat > src/out/Headless/args.gn <<'EOF'
    import("//build/args/headless.gn")
    is_official_build = true
    is_debug = false
    symbol_level = 0
    blink_symbol_level = 0
    chrome_pgo_phase = 0
    EOF
    ( cd src && gn gen out/Headless && autoninja -C out/Headless headless_shell )
  9. Run PixelRAG benchmark reproduction cells

    main

    Use the reproduce.sh script to run specific benchmark cells. This script targets self-hosted serves on localhost. It includes a preflight check that verifies if the required reader and retrieval services are up and running with the correct index.

    Usage: bash reproduce.sh <bench> <retrieval>

    Arguments:

    • <bench>: nq | nqt | sqa | mms | evqa | livevqa
    • <retrieval>: naive | traf | base | lora

    Examples:

    bash reproduce.sh evqa base       # Runs EVQA with base retrieval
    bash reproduce.sh mms lora       # Runs MMSearch with LoRA retrieval
    NUM=20 bash reproduce.sh nq traf # Runs NQ with Trafilatura retrieval for 20 examples
    bash reproduce.sh evqa base
    bash reproduce.sh mms lora
    NUM=20 bash reproduce.sh nq traf
  10. Set up prerequisites for the Wikipedia Demo

    main

    Before running the demo, ensure you have the following files and binaries in place:

    1. Simple English Wikipedia ZIM file: Must be located at ~/pixelrag-data/zim/wikipedia_en_simple.zim. You can download it using:
      curl -L https://download.kiwix.org/zim/wikipedia/wikipedia_en_simple_all_nopic_2026-05.zim -o ~/pixelrag-data/zim/wikipedia_en_simple.zim
    2. kiwix-serve binary: Must be located at .local/bin/kiwix-serve.
    curl -L https://download.kiwix.org/zim/wikipedia/wikipedia_en_simple_all_nopic_2026-05.zim -o ~/pixelrag-data/zim/wikipedia_en_simple.zim
  11. Platform-specific notes for building headless_shell

    main

    The build process uses gn to build for the host OS/arch by default.

    • linux-x64: The current primary release target. Build time is ~1–2 hours on many-core systems.
    • macOS (arm64/x64): Build on macOS. Note that the unsigned headless_shell will be blocked by Gatekeeper; you must codesign + notarize using an Apple Developer account, or users must manually clear the quarantine attribute.
    • Windows (x64): Build on Windows using the VS toolchain. You must ship headless_shell.exe along with its required runtime DLLs and .pak files.
  12. Crop and zoom into screenshot tiles

    main

    If text or details (like tables, small labels, or chart axes) are too small to read, you can crop a region of interest using Python's Pillow library to re-read it at full resolution.

    Coordinates (x1, y1, x2, y2) are in pixels from the top-left corner of the tile. For maximum clarity, aim for a crop size of roughly 800x800 or smaller.

    python3 -c "from PIL import Image; Image.open('<tile_path>').crop((x1, y1, x2, y2)).save('/tmp/pixelbrowse/crop.png')"