feat(chatbot-pensees): garde-fou serveur des citations (guillemets retirés si introuvables)
La route demande include_references + include_chunk_content à LightRAG et vérifie
chaque segment >= 6 mots entre « » / "…" / “…” dans les chunks réellement
récupérés (normalisation accents, casse, apostrophes, ponctuation, élisions [...]).
Un segment introuvable perd ses guillemets (texte conservé) ; champ optionnel
citations: { gardees, retirees }. Fonction pure dans server/utils/citations.ts,
réutilisée par scripts/verif-citations.mjs (qui gagne --max-total-tokens) ;
scripts/test-citations.mjs couvre les cas limites.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015vcVGz8i4WpZm2dixKUiSD
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
52b79e1e8e
commit
85cd31d201
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env node
|
||||
// Test du garde-fou des citations (server/utils/citations.ts). Usage : node scripts/test-citations.mjs
|
||||
import assert from 'node:assert/strict'
|
||||
import { verifierCitations, contenusChunks } from '../server/utils/citations.ts'
|
||||
|
||||
const ctx = ["La technique est devenue autonome, et forme un monde dévorant.\r\nIl n’y a plus de liberté de choix. Plus on parle d’une valeur, plus c'est le signe de son absence."]
|
||||
const t = (rep, c = ctx) => verifierCitations(rep, c)
|
||||
|
||||
// retrouvée (apostrophes, casse, ponctuation de bord, retours chariot) : gardée
|
||||
let r = t('Ellul écrit : « Il n\'y a plus de liberté de choix ! » donc.')
|
||||
assert.equal(r.gardees, 1); assert.equal(r.retirees, 0); assert.ok(r.texte.includes('«'))
|
||||
// introuvable : guillemets retirés, texte conservé
|
||||
r = t('Il dit : « La technique est une machine à fabriquer du pouvoir social inévitable ». Fin.')
|
||||
assert.equal(r.retirees, 1); assert.equal(r.texte, 'Il dit : La technique est une machine à fabriquer du pouvoir social inévitable. Fin.')
|
||||
// guillemets droits et anglais
|
||||
r = t('"Une phrase inventée qui ne figure nulle part dans le texte" et “Une autre phrase inventée qui ne figure nulle part ici”')
|
||||
assert.equal(r.retirees, 2); assert.ok(!/["“”]/.test(r.texte))
|
||||
// court (< 6 mots) : intact, non compté
|
||||
r = t('Le « rift métabolique » et la « ville intelligente ».')
|
||||
assert.equal(r.gardees + r.retirees, 0); assert.ok(r.texte.includes('« rift métabolique »'))
|
||||
// élision [...] : chaque morceau doit exister
|
||||
r = t('« Plus on parle d\'une valeur [...] le signe de son absence »')
|
||||
assert.equal(r.gardees, 1)
|
||||
r = t('« Plus on parle d\'une valeur [...] le signe de sa présence éclatante »')
|
||||
assert.equal(r.retirees, 1)
|
||||
// pas de correspondance en milieu de mot
|
||||
r = t('« ormation autonome, et forme un monde dévorant »')
|
||||
assert.equal(r.retirees, 1)
|
||||
// aucun contexte : rien de long n'est gardé
|
||||
r = t('« Il n\'y a plus de liberté de choix »', [])
|
||||
assert.equal(r.retirees, 1)
|
||||
// contenusChunks : tableau de chaînes ou chaîne
|
||||
assert.deepEqual(contenusChunks([{ content: ['a', 'b'] }, { content: 'c' }, {}, null]), ['a', 'b', 'c'])
|
||||
assert.deepEqual(contenusChunks(undefined), [])
|
||||
console.log('citations : tous les cas passent')
|
||||
Reference in New Issue
Block a user