AI & RAG · lab d'ingénierie
Construire un moteur RAG en C# à partir de zéro
Suivez le document du découpage à la génération ancrée. Comprenez chaque frontière avant d'ajouter un framework.
// avant de commencer
- Collections C#, entrées-sorties asynchrones et leçon sur les embeddings
- 1Comprendre
- 2Réfléchir
- 3Concevoir
- 4Implémenter
- 5Tester
- 6Améliorer
- 7Comparer à l'IA
01 / Comprendre le problème
Créez une chaîne de questions-réponses sur des documents. À partir de textes approuvés et d'une question, recherchez les passages pertinents et construisez une consigne appuyée sur les sources. L'indexation et la recherche déterministes se testent sans modèle.
02 / Réfléchir
Définissez les contrats du chargeur, du découpeur, du modèle d'embedding, du stockage vectoriel, du moteur de recherche et du générateur. Un document ne doit jamais décider des autorisations d'un visiteur.
Indice : deux cycles de vie
Indexez quand les documents changent. Recherchez pour chaque question. Conservez les versions du document et du modèle pour garder les deux chemins compatibles.
03 / Concevoir
Indexation : document → texte validé → fragments identifiés → embeddings → index soumis aux autorisations.
Question : question autorisée → vecteur → meilleurs fragments admissibles → contexte limité avec sources → réponse et citations vérifiées.
04 / Implémenter
Révéler le découpeur par caractères
public static IEnumerable<string> Chunk(
string text, int size = 800, int overlap = 100)
{
if (size <= 0 || overlap < 0 || overlap >= size)
throw new ArgumentOutOfRangeException(nameof(overlap));
for (int start = 0; start < text.Length; start += size - overlap)
{
int length = Math.Min(size, text.Length - start);
yield return text.Substring(start, length);
if (start + length == text.Length) yield break;
}
}C'est une version pédagogique. Un nombre de caractères n'est pas un nombre de tokens. En production, tenez compte du tokenizer, des paragraphes, des titres et de la langue. Le chevauchement conserve le contexte mais augmente le stockage.
Révéler la similarité cosinus
public static double Cosine(double[] a, double[] b)
{
if (a.Length == 0 || a.Length != b.Length)
throw new ArgumentException("Equal non-empty dimensions required.");
double dot = 0, aa = 0, bb = 0;
for (int i = 0; i < a.Length; i++)
{
dot += a[i] * b[i];
aa += a[i] * a[i];
bb += b[i] * b[i];
}
if (aa == 0 || bb == 0) return 0;
return dot / (Math.Sqrt(aa) * Math.Sqrt(bb));
}Classez les candidats autorisés par score décroissant. Conservez les identifiants du document et les positions de chaque fragment.
Construire la frontière de génération
Délimitez clairement les textes sources. Exigez des réponses justifiées et une abstention explicite sans preuve. Ne mettez ni secrets ni consignes système dans le corpus. Une instruction dans un document n'est jamais une commande privilégiée.
05 / Tester
- Un document vide ne produit aucun fragment.
- Le chevauchement reste inférieur à la taille d'un fragment.
- Les dimensions incompatibles sont rejetées.
- Un utilisateur ne recherche pas les documents d'une autre organisation.
- Une question sans réponse peut rester sans réponse.
- Chaque citation correspond à une source réellement trouvée.
06 / Améliorer
Mesurez le rappel sur un petit ensemble de questions annotées. Ajoutez déduplication, reclassement, limites d'ingestion et suivi des versions. Évaluez séparément la fidélité des réponses.
07 / Comparer avec l'IA
Vérifiez les autorisations absentes, les instructions malveillantes, les vecteurs périmés et les citations inventées. Une démonstration convaincante ne prouve pas la sécurité du système.
Pause. Essayez d'abord vous-même.
Ouvrez votre éditeur et accordez-vous quinze minutes honnêtes. Revenez ensuite ouvrir un indice à la fois.