Swelv· Article 1 sur 2
Un RAG sans vecteurs : le modèle cherche lui-même ses livres
Chaque mois, cinq livres choisis pour chaque lecteur. Le modèle n'invente aucun titre : il interroge le catalogue avec des outils, et le code vérifie tout.
6 min de lectureRead in English
Chaque mois, Swelv compose pour chaque lecteur une sélection de cinq livres, à partir de ce qu'il lit, aime et abandonne. Chaque livre arrive avec une accroche et un « pourquoi » qui cite ses propres lectures.
Une sélection qui te ressemble
Sélection mensuelle de Léa octobre 2026
Le Comte de Monte-Cristo
Alexandre Dumas
Classique · Aventure · Historique
Camille, Léo ont aimé
Parce que tu as adoré Les Trois Mousquetaires
Pourquoi pour toi
Tu as adoré Les Trois Mousquetaires : même souffle, même Dumas, et une vengeance patiente qui tient sur mille pages.
- 1Un livre qui existeTrouvé dans le catalogue, jamais cité de mémoire.
- 2Un axeSur cinq : trois sûrs, un voisin, une découverte.
- 3Une accroche cadréeQuelques tournures imposées, toujours au tutoiement.
- 4Un pourquoi vérifiableIl cite un livre du profil, et un lien réel entre les deux.
RAG : récupérer, puis générer
Un modèle de langage écrit très bien, mais il ne connaît pas notre catalogue, et il peut citer avec aplomb un livre qui n'existe pas. Le RAG (Retrieval-Augmented Generation) corrige ça : avant de répondre, on va chercher les bonnes informations, et le modèle écrit à partir d'elles.
La version la plus connue passe par des embeddings : on transforme les textes en vecteurs, et on cherche ceux qui sont les plus proches de la question. C'est idéal pour retrouver un passage dans une documentation. Pour recommander un livre, beaucoup moins : les vraies contraintes sont exactes (la bonne langue, pas un livre déjà lu, pas deux fois le même auteur), et une proximité de vecteurs n'en garantit aucune.
Swelv fait donc l'inverse : le modèle reçoit des outils, et cherche lui-même dans le catalogue, comme on interroge une base de données. C'est ce qu'on appelle le function calling : le modèle ne répond pas directement, il demande à appeler une fonction, le serveur l'exécute, et lui rend le résultat.
- + idéal pour retrouver du texte libre
- − aucune garantie sur une règle exacte
- + filtres exacts, résultats vérifiables
- − plus d'allers-retours, donc plus lent
Le défi. Laisser au modèle ce qu'il fait bien (comprendre des goûts, formuler des hypothèses, écrire) et garder dans le code tout ce qui doit être exact. Un modèle peut oublier une consigne ; une clause SQL, jamais.
La boucle
Le modèle reçoit le profil du lecteur, puis enchaîne les recherches : il formule une hypothèse (« il aime les grandes aventures historiques »), cherche, croise les résultats, élimine. Quand il propose ses cinq livres, le code relit chacun, et le renvoie au travail si l'un ne passe pas.
- livres adorés, coups de cœur
- livres abandonnés, et pourquoi
- activité récente
- langues lues
Jamais les notes privées.
Il formule des hypothèses, cherche, croise des listes, élimine.
- le livre existe ?
- déjà lu, déjà proposé ?
- bonne langue ?
- même auteur deux fois ?
✗ → le modèle est relancé
Chaque recherche est un petit filtre que le modèle écrit, et que le serveur traduit en SQL. Le serveur y ajoute toujours ce que le modèle n'a pas à gérer : les livres déjà lus, et les langues du lecteur.
chercher({ genre: "aventure", thèmes: ["vengeance", "trahison"], garder_sous: "comme_dumas"})
SELECT titre, auteur, id FROM catalogueWHERE genre = $1 AND résumé ~* $2 AND id NOT IN (:déjà_lus) AND langue IN (:langues_du_lecteur)ORDER BY note DESC LIMIT 20
Le modèle reçoit → une poignée de livres, chacun avec un identifiant à recopier tel quel, et la liste gardée sous un nom pour la croiser ensuite.
La langue, apprise à la dure
Les premiers incidents de la fonctionnalité ont tous eu la même cause : la langue. Chacun a fait passer une règle du prompt vers le code.
Trois portes
- 01À chaque rechercheFiltrée d'office sur les langues du lecteur.
- 02Dans la boucleUn choix hors langue est refusé, le modèle relancé.
- 03À la validationDernier filet : le livre est retiré, puis remplacé.
Ce qui l'a appris
- 1Une lectrice francophone reçoit des livres dans d'autres langues.→ La langue n'était qu'un conseil dans le prompt : elle devient un filtre, à trois niveaux.
- 2L'accroche dans une langue, l'explication dans une autre.→ La langue de sortie est décidée par le code, et imposée au modèle.
- 3Des éditions mal étiquetées par la source des données.→ Un quota de langues par sélection, et la langue vérifiée par recoupement.
Le bon modèle n'est pas le moins cher
Avant de changer de modèle, on rejoue de vraies sélections sur les mêmes profils, et on compare : le coût, la durée, le nombre de corrections, et surtout la qualité des textes.
Coût par sélection, relatif
Le prompt système est le même pour tous les lecteurs : le fournisseur le sert presque entièrement depuis son cache.
Ce que j'en retiens
- Donner des outils plutôt que des souvenirs. Le modèle ne propose que ce qu'il a trouvé.
- Ce qui compte va dans le code. Une consigne de prompt finit toujours par être oubliée.
- Choisir le modèle sur ce que lit l'utilisateur, pas seulement sur la facture.