feat(chatbot): add corpus param (pensees/projets/both) with adaptive preface

- New corpus param defaults to 'both' (projet-centered crossing)
- 3 preface modes for LightRAG query orientation
- Smoke tested via SSH direct LightRAG VPS -- pondération validée

V2 Phase 2.1 -- backend only, frontend toggle pending B.2

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jules Neny
2026-05-11 19:23:23 +02:00
parent 586742d90e
commit 8d673482b6

View File

@@ -4,6 +4,7 @@ import { checkRateLimitJson } from '~/server/utils/rateLimitJson'
interface ChatbotPenseesRequest { interface ChatbotPenseesRequest {
query: string query: string
mode?: 'hybrid' | 'local' | 'global' | 'naive' | 'mix' mode?: 'hybrid' | 'local' | 'global' | 'naive' | 'mix'
corpus?: 'pensees' | 'projets' | 'both'
filter_couche?: 'fond' | 'forme' | 'structure' | null filter_couche?: 'fond' | 'forme' | 'structure' | null
filter_ecole?: string | null filter_ecole?: string | null
history?: Array<{ role: 'user' | 'assistant'; content: string }> history?: Array<{ role: 'user' | 'assistant'; content: string }>
@@ -13,7 +14,7 @@ interface LightRAGQueryResponse {
response: string response: string
} }
const SYSTEM_PREFACE = `Tu es un agent du RAG Pensées Écologiques, infrastructure militante du collectif trans-former.fr. const SYSTEM_PREFACE_PENSEES = `Tu es un agent du RAG Pensées Écologiques, infrastructure militante du collectif trans-former.fr.
Tu réponds en t'appuyant STRICTEMENT sur le corpus ingéré (auteurs FRACAS Bonpote : écosocialisme, éco-anarchisme, écoféminismes, écologies décoloniales, technocritique, pensées du vivant, décroissance...). Tu réponds en t'appuyant STRICTEMENT sur le corpus ingéré (auteurs FRACAS Bonpote : écosocialisme, éco-anarchisme, écoféminismes, écologies décoloniales, technocritique, pensées du vivant, décroissance...).
Règles : Règles :
@@ -23,6 +24,29 @@ Règles :
- Réponse en français, dense, sans délayage. - Réponse en français, dense, sans délayage.
- Distingue les positions selon les écoles quand elles divergent.` - Distingue les positions selon les écoles quand elles divergent.`
const SYSTEM_PREFACE_PROJETS = `Tu es un agent du RAG Projets de Jules Nény (architecte, collectif trans-former.fr).
Tu réponds STRICTEMENT à partir des documents projet (fichiers butte-pinson__*.md et autres projets archi de Jules).
N'utilise PAS le corpus FRACAS Pensées Écologiques pour répondre, sauf si l'usager te le demande explicitement.
Règles :
- Cite les sources (nom de projet, document) à chaque assertion importante.
- Si la question dépasse le corpus projet, dis-le clairement. Pas d'hallucination.
- Ton praticien réflexif : 1ère personne quand pertinent, narration située.
- Réponse en français, dense, sans délayage.`
const SYSTEM_PREFACE_BOTH = `Tu es un agent du RAG croisé Pensées x Projets de Jules Nény (architecte militant, collectif trans-former.fr).
CENTRE TA RÉPONSE sur les documents PROJETS (fichiers butte-pinson__*.md et autres projets archi).
Mobilise le corpus FRACAS Pensées (autres fichiers) UNIQUEMENT pour éclairer théoriquement les partis pris des projets, jamais l'inverse.
Pondération attendue : ~70% ancrage projet concret, ~30% éclairage théorique FRACAS.
Règles :
- Cite les sources (auteur ou nom de projet, document) à chaque assertion.
- Si un thème n'est pas couvert par les projets, dis-le clairement avant d'éventuellement étendre au corpus Pensées.
- Pas d'hallucination, pas d'extrapolation hors corpus.
- Ton praticien militant : direct, pas neutre, ancré dans la pratique architecturale.
- Réponse en français, dense, sans délayage.`
export default defineEventHandler(async (event: H3Event) => { export default defineEventHandler(async (event: H3Event) => {
const config = useRuntimeConfig(event) const config = useRuntimeConfig(event)
@@ -45,8 +69,17 @@ export default defineEventHandler(async (event: H3Event) => {
const query = body.query.trim() const query = body.query.trim()
const mode = body.mode || 'hybrid' const mode = body.mode || 'hybrid'
const corpus = body.corpus || 'both'
const ragUrl = (config.ragPeUrl as string) || 'http://localhost:9621' const ragUrl = (config.ragPeUrl as string) || 'http://localhost:9621'
// Préface adaptative selon corpus demandé
const systemPreface =
corpus === 'pensees'
? SYSTEM_PREFACE_PENSEES
: corpus === 'projets'
? SYSTEM_PREFACE_PROJETS
: SYSTEM_PREFACE_BOTH
// 3. Health guard — LightRAG down = erreur claire, pas de fallback hallucinatoire // 3. Health guard — LightRAG down = erreur claire, pas de fallback hallucinatoire
try { try {
await $fetch(`${ragUrl}/health`, { timeout: 5000 }) await $fetch(`${ragUrl}/health`, { timeout: 5000 })
@@ -58,7 +91,7 @@ export default defineEventHandler(async (event: H3Event) => {
} }
// 4. Call LightRAG VPS — préface système injectée dans la query // 4. Call LightRAG VPS — préface système injectée dans la query
const ragQuery = `${SYSTEM_PREFACE}\n\nQuestion : ${query}` const ragQuery = `${systemPreface}\n\nQuestion : ${query}`
let ragResponse: LightRAGQueryResponse let ragResponse: LightRAGQueryResponse
try { try {
@@ -79,6 +112,7 @@ export default defineEventHandler(async (event: H3Event) => {
return { return {
response: ragResponse.response ?? '', response: ragResponse.response ?? '',
mode, mode,
corpus,
filter: { couche: body.filter_couche ?? null, ecole: body.filter_ecole ?? null }, filter: { couche: body.filter_couche ?? null, ecole: body.filter_ecole ?? null },
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
} }