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.
Un plugin **Discourse** + un pipeline RAG auto-hébergé, conçu pour un forum
associatif francophone (Depiedencap — souliers pour homme, ~428k messages).
Principe produit : **le bot oriente vers les fils existants, il ne répond
pas à la place des membres.**
associatif francophone (Depiedencap — souliers pour homme, 424 835 messages
/ 22 230 sujets). Principe produit : **le bot oriente vers les fils
existants, il ne répond pas à la place des membres.**
## Ce que fait le plugin
- Retrieval au **grain post** (et non au grain topic) : embeddings `bge-m3`
dans `ai_posts_embeddings` (pgvector dans la Postgres du forum), top-25
par cosine puis **rerank cross-encoder** (TEI), déduplication par fil.
dans `ai_posts_embeddings` (pgvector dans la Postgres du forum, **sur le
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
précédents sont condensés en une requête autonome par un petit modèle
dédié avant retrieval (pattern *condense question* / history-aware
retriever).
précédents sont condensés en une requête autonome (pattern *condense
question* / history-aware retriever). Même modèle résident que la
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
`[[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,
@ -33,18 +35,41 @@ pas à la place des membres.**
## 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 → gate → réécriture (modèle léger) → embed → pgvector → rerank → LLM → sanitizer
▼
Mini PC auto-hébergé (k3s) VPS
Ollama : bge-m3 (embeddings) Discourse + Postgres/pgvector
qwen3 30B MoE (~3B actifs) (~428k posts embeddés)
— génération ET réécriture de
requête (un seul modèle résident)
TEI : cross-encoder mmarco-mMiniLMv2 (rerank)
Question membre
│
▼
[VPS] Discourse + ce plugin Ruby
│
├─ 1. gate regex locale, 0 LLM
│
├─ 2. réécriture ──Tailscale──► [mini PC] Ollama qwen3:30b-a3b-q6k
│
├─ 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
linéaire en code direct, instrumenté pour l'évaluation. Le raisonnement
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.
A **Discourse plugin** + a self-hosted RAG pipeline, built for a French
community forum (Depiedencap — men's shoes, ~428k posts). Product
principle: **the bot points members to existing threads instead of
answering in their place.**
community forum (Depiedencap — men's shoes, 424 835 posts / 22 230
topics). Product principle: **the bot points members to existing threads
instead of answering in their place.**
## What it does
- **Post-grain retrieval** (not topic-grain): `bge-m3` embeddings in
`ai_posts_embeddings` (pgvector inside the forum's Postgres), top-25 by
cosine then **cross-encoder rerank** (TEI), per-thread dedup.
`ai_posts_embeddings` (pgvector inside the forum's Postgres on the VPS),
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
are condensed into a standalone query by a small dedicated model before
retrieval (*condense question* / history-aware retriever pattern).
are condensed into a standalone query (*condense question* /
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]]`
markers; the plugin maps them to `/t/slug/id/post_number` links and
discards any link that does not actually exist in Postgres (URL
@ -31,18 +34,41 @@ answering in their place.**
## 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)
│ question → gate → rewrite (small model) → embed → pgvector → rerank → LLM → sanitizer
▼
Self-hosted mini PC (k3s) VPS
Ollama : bge-m3 (embeddings) Discourse + Postgres/pgvector
qwen3 30B MoE (~3B active) (~428k embedded posts)
— generation AND query rewriting
(single resident model)
TEI : mmarco-mMiniLMv2 cross-encoder (rerank)
Member question
│
▼
[VPS] Discourse + this Ruby plugin
│
├─ 1. gate regex, local, 0 LLM
│
├─ 2. rewrite ──Tailscale──► [mini PC] Ollama qwen3:30b-a3b-q6k
│
├─ 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
pipeline in direct code, instrumented for evaluation. Full reasoning:
[`docs/rag/DESIGN.md`](docs/rag/DESIGN.md) §9.

View file

@ -1,11 +1,12 @@
# Design — RAG grain post (Depiedencap)
> **Dernière màj** : 14/09/2026 · **Statut** : toutes les configs A-G
> mesurées, config gagnante identifiée (E : posts natifs + rerank),
> sécurité testée à l'échelle. **Rien déployé en prod.**
> **Dernière màj** : 17/09/2026 · **Statut** : config gagnante (E : posts
> natifs + rerank) mesurée ; code en prod sur le VPS, flags membre encore
> **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) ·
> Architecture : [`PIPELINE.md`](PIPELINE.md)
> Références techniques : [`REFERENCES.md`](REFERENCES.md)
> Références : [`REFERENCES.md`](REFERENCES.md)
## 0. État actuel (rappel)

View file

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

View file

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