Docs: show VPS vs mini PC RAG flow (Discourse lives on the VPS)

The old README diagram stacked Discourse above the mini PC and left the VPS as an orphan column. Rewrite is also the resident 30B MoE, not a small dedicated model.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
grgmro 2026-09-17 23:05:22 +02:00
parent 62d29468d4
commit 83805bf9cd
5 changed files with 100 additions and 47 deletions

View file

@ -8,19 +8,21 @@
> publiée. > publiée.
Un plugin **Discourse** + un pipeline RAG auto-hébergé, conçu pour un forum Un plugin **Discourse** + un pipeline RAG auto-hébergé, conçu pour un forum
associatif francophone (Depiedencap — souliers pour homme, ~428k messages). associatif francophone (Depiedencap — souliers pour homme, 424 835 messages
Principe produit : **le bot oriente vers les fils existants, il ne répond / 22 230 sujets). Principe produit : **le bot oriente vers les fils
pas à la place des membres.** existants, il ne répond pas à la place des membres.**
## Ce que fait le plugin ## Ce que fait le plugin
- Retrieval au **grain post** (et non au grain topic) : embeddings `bge-m3` - Retrieval au **grain post** (et non au grain topic) : embeddings `bge-m3`
dans `ai_posts_embeddings` (pgvector dans la Postgres du forum), top-25 dans `ai_posts_embeddings` (pgvector dans la Postgres du forum, **sur le
par cosine puis **rerank cross-encoder** (TEI), déduplication par fil. VPS**), top-25 par cosine puis **rerank cross-encoder** (TEI **sur le mini
PC**), déduplication par fil.
- **Réécriture de requête multi-tour** : le dernier message + les tours - **Réécriture de requête multi-tour** : le dernier message + les tours
précédents sont condensés en une requête autonome par un petit modèle précédents sont condensés en une requête autonome (pattern *condense
dédié avant retrieval (pattern *condense question* / history-aware question* / history-aware retriever). Même modèle résident que la
retriever). génération : `qwen3:30b-a3b-q6k` (MoE, ~3B actifs, `think:false`) — plus
de petit modèle dédié à la réécriture.
- **Citations vérifiées côté serveur** : le LLM n'écrit que des marqueurs - **Citations vérifiées côté serveur** : le LLM n'écrit que des marqueurs
`[[n]]` ; le plugin les transforme en liens `/t/slug/id/post_number` et `[[n]]` ; le plugin les transforme en liens `/t/slug/id/post_number` et
jette tout lien qui n'existe pas réellement en base (whitelist Postgres, jette tout lien qui n'existe pas réellement en base (whitelist Postgres,
@ -33,18 +35,41 @@ pas à la place des membres.**
## Architecture ## Architecture
Le plugin tourne **dans Discourse, sur le VPS**. Le mini PC n'est que le
backend d'inférence (Ollama + TEI), joignable via Tailscale. Le gate,
pgvector et le sanitizer de citations ne quittent jamais le VPS.
``` ```
Discourse (plugin Ruby, dans le process du forum) Question membre
│ question → gate → réécriture (modèle léger) → embed → pgvector → rerank → LLM → sanitizer │
▼ ▼
Mini PC auto-hébergé (k3s) VPS [VPS] Discourse + ce plugin Ruby
Ollama : bge-m3 (embeddings) Discourse + Postgres/pgvector │
qwen3 30B MoE (~3B actifs) (~428k posts embeddés) ├─ 1. gate regex locale, 0 LLM
— génération ET réécriture de │
requête (un seul modèle résident) ├─ 2. réécriture ──Tailscale──► [mini PC] Ollama qwen3:30b-a3b-q6k
TEI : cross-encoder mmarco-mMiniLMv2 (rerank) │
├─ 3. embed ──Tailscale──► [mini PC] Ollama bge-m3
│
├─ 4. pgvector Postgres locale, 424 835 posts, cosine top-25
│
├─ 5. rerank ──Tailscale──► [mini PC] TEI mmarco-mMiniLMv2
│
├─ 6. génération ──Tailscale──► [mini PC] Ollama qwen3:30b-a3b-q6k
│
└─ 7. sanitizer whitelist URL, Postgres locale
│
▼
réponse + liens /t/slug/id/post_number
``` ```
| Sur le VPS | Sur le mini PC (k3s, iGPU) |
|---|---|
| Discourse, ce plugin, Postgres/pgvector | Ollama (`bge-m3` + `qwen3:30b-a3b-q6k`) et TEI (`mmarco`) |
`qwen3:30b-a3b-q6k` est un seul MoE résident (~3B actifs) pour la
réécriture (`think:false`) **et** la génération.
Le choix délibéré : **pas de framework RAG** (LangChain & co.) — pipeline Le choix délibéré : **pas de framework RAG** (LangChain & co.) — pipeline
linéaire en code direct, instrumenté pour l'évaluation. Le raisonnement linéaire en code direct, instrumenté pour l'évaluation. Le raisonnement
complet : [`docs/rag/DESIGN.md`](docs/rag/DESIGN.md) §9. complet : [`docs/rag/DESIGN.md`](docs/rag/DESIGN.md) §9.

View file

@ -7,18 +7,21 @@
> This repository documents the work — no demo URL is published. > This repository documents the work — no demo URL is published.
A **Discourse plugin** + a self-hosted RAG pipeline, built for a French A **Discourse plugin** + a self-hosted RAG pipeline, built for a French
community forum (Depiedencap — men's shoes, ~428k posts). Product community forum (Depiedencap — men's shoes, 424 835 posts / 22 230
principle: **the bot points members to existing threads instead of topics). Product principle: **the bot points members to existing threads
answering in their place.** instead of answering in their place.**
## What it does ## What it does
- **Post-grain retrieval** (not topic-grain): `bge-m3` embeddings in - **Post-grain retrieval** (not topic-grain): `bge-m3` embeddings in
`ai_posts_embeddings` (pgvector inside the forum's Postgres), top-25 by `ai_posts_embeddings` (pgvector inside the forum's Postgres on the VPS),
cosine then **cross-encoder rerank** (TEI), per-thread dedup. top-25 by cosine then **cross-encoder rerank** (TEI on the mini PC),
per-thread dedup.
- **Multi-turn query rewriting**: the latest message plus earlier turns - **Multi-turn query rewriting**: the latest message plus earlier turns
are condensed into a standalone query by a small dedicated model before are condensed into a standalone query (*condense question* /
retrieval (*condense question* / history-aware retriever pattern). history-aware retriever). Same resident model as generation:
`qwen3:30b-a3b-q6k` (MoE, ~3B active, `think:false`) — not a separate
small rewriter.
- **Server-side verified citations**: the LLM only emits bare `[[n]]` - **Server-side verified citations**: the LLM only emits bare `[[n]]`
markers; the plugin maps them to `/t/slug/id/post_number` links and markers; the plugin maps them to `/t/slug/id/post_number` links and
discards any link that does not actually exist in Postgres (URL discards any link that does not actually exist in Postgres (URL
@ -31,18 +34,41 @@ answering in their place.**
## Architecture ## Architecture
The plugin runs **inside Discourse on the VPS**. The mini PC is only the
inference backend (Ollama + TEI), reached over Tailscale. Gate, pgvector
and the citation sanitizer never leave the VPS.
``` ```
Discourse (Ruby plugin, inside the forum process) Member question
│ question → gate → rewrite (small model) → embed → pgvector → rerank → LLM → sanitizer │
▼ ▼
Self-hosted mini PC (k3s) VPS [VPS] Discourse + this Ruby plugin
Ollama : bge-m3 (embeddings) Discourse + Postgres/pgvector │
qwen3 30B MoE (~3B active) (~428k embedded posts) ├─ 1. gate regex, local, 0 LLM
— generation AND query rewriting │
(single resident model) ├─ 2. rewrite ──Tailscale──► [mini PC] Ollama qwen3:30b-a3b-q6k
TEI : mmarco-mMiniLMv2 cross-encoder (rerank) │
├─ 3. embed ──Tailscale──► [mini PC] Ollama bge-m3
│
├─ 4. pgvector Postgres local, 424 835 posts, cosine top-25
│
├─ 5. rerank ──Tailscale──► [mini PC] TEI mmarco-mMiniLMv2
│
├─ 6. generate ──Tailscale──► [mini PC] Ollama qwen3:30b-a3b-q6k
│
└─ 7. sanitizer URL whitelist, local Postgres
│
▼
answer + /t/slug/id/post_number links
``` ```
| Lives on the VPS | Lives on the mini PC (k3s, iGPU) |
|---|---|
| Discourse, this plugin, Postgres/pgvector | Ollama (`bge-m3` + `qwen3:30b-a3b-q6k`) and TEI (`mmarco`) |
`qwen3:30b-a3b-q6k` is a single resident MoE (~3B active) used for both
rewrite (`think:false`) and generation.
Deliberate choice: **no RAG framework** (LangChain & co.) — a linear Deliberate choice: **no RAG framework** (LangChain & co.) — a linear
pipeline in direct code, instrumented for evaluation. Full reasoning: pipeline in direct code, instrumented for evaluation. Full reasoning:
[`docs/rag/DESIGN.md`](docs/rag/DESIGN.md) §9. [`docs/rag/DESIGN.md`](docs/rag/DESIGN.md) §9.

View file

@ -1,11 +1,12 @@
# Design — RAG grain post (Depiedencap) # Design — RAG grain post (Depiedencap)
> **Dernière màj** : 14/09/2026 · **Statut** : toutes les configs A-G > **Dernière màj** : 17/09/2026 · **Statut** : config gagnante (E : posts
> mesurées, config gagnante identifiée (E : posts natifs + rerank), > natifs + rerank) mesurée ; code en prod sur le VPS, flags membre encore
> sécurité testée à l'échelle. **Rien déployé en prod.** > **OFF**, agent limité à un groupe démo. Le §0 ci-dessous est un
> **instantané historique** (grain topic) — l'architecture actuelle :
> [`README.md`](../../README.md) · [`PIPELINE.md`](PIPELINE.md).
> Résultats : [`EVAL.md`](EVAL.md) · Sécurité : [`SAFETY.md`](SAFETY.md) · > Résultats : [`EVAL.md`](EVAL.md) · Sécurité : [`SAFETY.md`](SAFETY.md) ·
> Architecture : [`PIPELINE.md`](PIPELINE.md) > Références : [`REFERENCES.md`](REFERENCES.md)
> Références techniques : [`REFERENCES.md`](REFERENCES.md)
## 0. État actuel (rappel) ## 0. État actuel (rappel)

View file

@ -1,11 +1,12 @@
# Chantier RAG — granularité post, retrieval avancé, évaluation # Chantier RAG — granularité post, retrieval avancé, évaluation
> **Statut (15/09/2026)** : **déploiement prod en cours**, flags encore **OFF** > **Statut (17/09/2026)** : plugin + pipeline en prod sur le **VPS**
> (comportement live inchangé). Code v2 déployé, reranker en service, backfill > (Discourse + Postgres/pgvector, 424 835 posts). Inférence sur le **mini
> natif posts en cours sur le VPS. Reprise / prochaine étape exacte : > PC** (Ollama `bge-m3` + `qwen3:30b-a3b-q6k`, TEI mmarco) via Tailscale.
> [`PLAN_RAG_PROD_VPS.md`](../PLAN_RAG_PROD_VPS.md) § « Reprise session » (tout en haut). > Flags membre encore **OFF** ; agent limité à un groupe démo. Schéma
> Éval locale : config gagnante mesurée (posts+rerank, Hit@5 0.97), sécurité > public : [`README.md`](../../README.md). Reprise :
> testée (red-team + gate + clause) — `EVAL.md` / `SAFETY.md`. > [`PLAN_RAG_PROD_VPS.md`](../PLAN_RAG_PROD_VPS.md) § « Reprise session ».
> Éval : posts+rerank, Hit@5 0.97 — `EVAL.md` / `SAFETY.md`.
## Pourquoi ce chantier ## Pourquoi ce chantier

View file

@ -25,9 +25,9 @@ module DepiedencapAiCitations
RERANK_READ_TIMEOUT = 4 RERANK_READ_TIMEOUT = 4
POSTS_SQL_LIMIT = 25 POSTS_SQL_LIMIT = 25
# Réécriture de requête multi-tour (condense_query) — modèle léger dédié, # Réécriture de requête multi-tour (condense_query) — même LLM résident
# distinct du LLM de génération (Qwen 30B) : latence courte (~2-3s mesurées), # que la génération (qwen3:30b-a3b-q6k, MoE ~3B actifs, ~1 s à chaud).
# pas de mode "thinking" à filtrer (contrairement à qwen3:4b sur ce serving). # think:false pour éviter le budget englouti dans reasoning (vu sur qwen3:4b).
HISTORY_TURNS = 3 HISTORY_TURNS = 3
CONDENSE_MAX_TOKENS = 80 CONDENSE_MAX_TOKENS = 80
CONDENSE_OPEN_TIMEOUT = 2 CONDENSE_OPEN_TIMEOUT = 2