Améliorer les suggestions de complétion dans les chats grâce aux représentations vectorielles et à la technologie RAG

Découvrez comment utiliser les représentations vectorielles de l’API Red Hat AI Inference on IBM Cloud pour créer un pipeline de génération augmentée par la recherche (RAG) qui fournit à un modèle de chat des réponses précises et fondées issues de votre propre base de connaissances.

Les grands modèles linguistiques sont puissants, mais ils n'ont aucune connaissance des faits qui ne figurent pas dans leurs données d'entraînement. Lorsque vous avez besoin d’un modèle pour effectuer des raisonnements sur une base de connaissances spécifique — telle qu’un catalogue de produits, l’historique d’un client ou un corpus de domaine —, vous devez disposer d’un moyen de faire ressortir les informations pertinentes au moment de la requête. Ce tutoriel présente le cycle RAG complet : intégrer une base de connaissances, la stocker dans un index vectoriel, puis injecter les informations les plus pertinentes dans une requête de complétion de chat afin que la réponse du modèle s'appuie sur vos données.

Pour plus d’informations sur les cas d’utilisation des représentations vectorielles, tels que la recherche, le clustering et la classification, consultez la section Cas d’utilisation des représentations vectorielles.

Objectifs

Dans ce tutoriel, vous allez effectuer les tâches suivantes :

  • Authentification auprès de l'API Red Hat AI Inference.
  • Intégrer une base de connaissances à l'aide du modèle granite-embedding-278m-multilingual.
  • Stockez les représentations vectorielles dans un index vectoriel en mémoire et effectuez des recherches à l'aide de la similarité cosinus.
  • Interrogez le modèle de chat gpt-oss-120b avec et sans contexte récupéré pour constater par vous-même l'amélioration de la précision.

L'application que vous développez présente l'architecture suivante :

  1. Chaque information de votre base de connaissances est convertie en un vecteur d'encodage et ajoutée à un fichier MemoryStore.
  2. Une question d'utilisateur est intégrée selon le même modèle.
  3. La similarité cosinus classe les vecteurs stockés par rapport au vecteur de la question, et les trois faits les plus pertinents sont extraits.
  4. Ces informations sont intégrées dans l'invite système envoyée au modèle de chat.
  5. Le modèle fournit une réponse bien fondée.

Avant de commencer

Assurez-vous de disposer des éléments suivants :

  • Un compte IBM Cloud en paiement à l'utilisation ou par abonnement. Les comptes d'essai ne sont pas pris en charge. Pour plus d'informations ou pour mettre à niveau votre compte, consultez la section Types de comptes.

  • Un projet d' Red Hat AI Inference.

  • Rôle Writer ou supérieur sur le service Red Hat AI Inference. Pour plus d'informations, consultez la section Gestion des accès IAM.

  • Python 3.9 ou une version ultérieure installée sur votre poste de travail.

  • Le openai pack Python. Installez-le en exécutant la commande suivante :

    pip install openai
    

Configurer l'authentification API

Avant de pouvoir appeler l'API Red Hat AI Inference, vous devez authentifier vos requêtes à l'aide d'un identifiant de service et d'une clé API. Si vous avez déjà suivi le tutoriel de prise en main, vous pouvez réutiliser l'ID de service et la clé API que vous y avez créés et passer directement à la section Obtenir votre ID de projet et votre point de terminaison.

Créer un identifiant de service et attribuer des droits d'accès

Un identifiant de service est un moyen pratique de contrôler et de gérer les accès aux projets d' Red Hat AI Inference. Créez l'identifiant de service, puis attribuez-lui un accès à votre projet.

  1. Dans la console d' IBM Cloud, accédez à Gérer > Accès (IAM) > Identifiants de service, puis cliquez sur Créer.

  2. Saisissez un nom et une description pour votre identifiant de service, puis cliquez sur Créer.

  3. Depuis la page d'identification du service, cliquez sur Attribuer un accès.

  4. Sélectionnez Red Hat AI Inference comme service.

  5. Dans la section Ressources, sélectionnez Ressources spécifiques, puis choisissez votre projet.

  6. Dans la section Rôles et actions, sélectionnez Rédacteur comme rôle d'accès au service.

    Aucun rôle d'accès à la plateforme n'est requis pour accéder à l'API.

  7. Consultez le récapitulatif des droits d'accès, puis cliquez sur Attribuer.

Création d'une clé d'API

Maintenant que votre identifiant de service a accès à votre projet, créez une clé API pour cet identifiant de service.

  1. Depuis la page d'identification du service, cliquez sur Clés API.

  2. Cliquez sur Créer et saisissez un nom pour votre clé API.

  3. Pour la gestion des clés compromises, sélectionnez Désactiver la clé compromise afin de désactiver automatiquement la clé si une fuite est détectée.

  4. Définissez une date d'expiration pour la clé. Pour des raisons de sécurité, il est recommandé de procéder régulièrement à une rotation des clés.

  5. Cliquez sur Créer.

  6. Copiez la clé API et enregistrez-la dans un emplacement sécurisé. La clé ne peut plus être consultée.

Récupérez votre identifiant de projet et votre point de terminaison API

Votre identifiant de projet est obligatoire pour toutes les requêtes API.

  1. Accéder aux projets Red Hat AI Inference.

  2. Ouvrez votre projet.

  3. Copiez l'identifiant de votre projet et conservez-le pour les étapes suivantes.

L' URL de base pour toutes les requêtes API de ce tutoriel se présente sous la forme suivante :

https://us-east.rhai.ibm.com/v1/projects/<project_id>

Remplacez <project_id> par l'ID de projet que vous avez copié.

Définissez les variables d'environnement

Définissez votre point de terminaison et stockez votre clé API dans une variable :

export ENDPOINT="https://us-east.rhai.ibm.com/v1/projects/<project_id>"
export API_KEY="<your-api-key>"

Remplacez <project_id> par votre identifiant de projet et <your-api-key> par la clé API que vous avez créée précédemment.

Ne jamais coder en dur les identifiants dans les fichiers source. Utilisez des variables d'environnement ou un gestionnaire de secrets tel que IBM Cloud Secrets Manager.

Générer un jeton porteur

L'API nécessite un jeton IAM de type bearer. Échangez votre clé API contre un jeton IAM de type bearer en exécutant la commande suivante :

export AUTH=$(curl -s -X POST "https://iam.cloud.ibm.com/identity/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ibm:params:oauth:grant-type:apikey" \
  --data-urlencode "apikey=$API_KEY" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

Vérifiez que le jeton a bien été émis. Un jeton valide commence par eyJ:

echo $AUTH | cut -c1-4

Les jetons IAM expirent au bout d'une heure. Relancez la commande d'échange de jeton si vous recevez une erreur 401 lors de l'exécution du script.

Créer le magasin de vecteurs

Le magasin de vecteurs est un index en mémoire qui stocke du texte ainsi que son vecteur d'embedding. L'implémentation suivante d' Python utilise la similarité cosinus pour trouver les entrées les plus proches, d'un point de vue sémantique, d'un vecteur de requête.

Créez un fichier nommé et store.py ajoutez-y le code suivant :

import math
def cosine_similarity(a: list[float], b: list[float]) -> float:
    """Return the cosine similarity between two vectors."""
    dot = sum(x * y for x, y in zip(a, b))
    mag_a = math.sqrt(sum(x * x for x in a))
    mag_b = math.sqrt(sum(x * x for x in b))
    if mag_a == 0 or mag_b == 0:
        return 0.0
    return dot / (mag_a * mag_b)
class MemoryStore:
    """In-memory vector store with cosine similarity search."""
    def __init__(self) -> None:
        self._entries: list[dict] = []
    def add(self, text: str, vector: list[float]) -> None:
        """Store a text/vector pair."""
        self._entries.append({"text": text, "vector": vector})
    def search(self, query: list[float], top_k: int) -> list[dict]:
        """Return the top_k entries most similar to the query vector."""
        scored = [
            {"text": e["text"], "score": cosine_similarity(query, e["vector"])}
            for e in self._entries
        ]
        scored.sort(key=lambda x: x["score"], reverse=True)
        return scored[:top_k]

La similarité cosinus mesure l'angle entre deux vecteurs, et non leur amplitude. Cela en fait un outil particulièrement adapté à la comparaison des représentations textuelles, car il permet de saisir l'orientation sémantique indépendamment de la longueur du texte.

Créer le client API

Le client API encapsule deux points de terminaison d' Red Hat AI Inference: le point de terminaison embeddings et le point de terminaison chat completions. Cette implémentation utilise le package openai, qui fournit un client Python pour toute API OpenAI-compatible.

Créez un fichier nommé et client.py ajoutez-y le code suivant :

import os
from openai import OpenAI
def _client() -> OpenAI:
    """Return an OpenAI client pointed at the InstructLab endpoint."""
    return OpenAI(
        api_key=os.environ["AUTH"],
        base_url=f"{os.environ['ENDPOINT']}/inference/v1",
    )
def embed_text(text: str) -> list[float]:
    """Call the embeddings endpoint and return the vector for the given text."""
    response = _client().embeddings.create(
        model="granite-embedding-278m-multilingual",
        input=text,
        encoding_format="float",
    )
    return response.data[0].embedding
def chat_complete(system_prompt: str, user_message: str, prefix: str = "") -> None:
    """Call the chat completions endpoint with streaming and print tokens as they arrive."""
    messages = []
    if system_prompt:
        messages.append({"role": "system", "content": system_prompt})
    messages.append({"role": "user", "content": user_message})
    stream = _client().chat.completions.create(
        model="gpt-oss-120b",
        messages=messages,
        stream=True,
    )
    print(prefix, end="", flush=True)
    for chunk in stream:
        content = chunk.choices[0].delta.content if chunk.choices else None
        if content:
            print(content.replace("\n", f"\n{prefix}"), end="", flush=True)
    print()

L'assistant _client() crée une instance OpenAI pointant vers l'API Red Hat AI Inference à l'aide de vos variables d'environnement AUTH et ENDPOINT. La fonction embed_text appelle le point de terminaison embeddings avec encoding_format="float" afin de garantir que la réponse soit renvoyée sous la forme d'une simple liste de nombres à virgule flottante. La fonction chat_complete appelle le point de terminaison des complétions de chat avec la diffusion en continu activée, de sorte que les jetons s'affichent sur le terminal au fur et à mesure de leur arrivée.

Initialiser le magasin de mémoire et lancer la démo

Une fois le magasin de données vectorielles et le client API mis en place, vous pouvez désormais écrire le script principal qui relie tous les éléments entre eux : il initialise le magasin, répond à chaque question sans recourir à la mémoire, puis fournit une réponse via RAG et affiche les deux réponses côte à côte à des fins de comparaison.

Créez un fichier nommé et main.py ajoutez-y le code suivant :

import os
import sys
from client import chat_complete, embed_text
from store import MemoryStore
# knowledge is the set of facts to embed into the vector store.
# This example uses IBM Cloud support FAQs as the knowledge base.
KNOWLEDGE = [
    "To reset your IBM Cloud account password, go to the IBM Cloud login page and click 'Forgot password' to receive a reset email.",
    "IBM Cloud Pay-As-You-Go accounts are charged monthly based on resource usage, with no upfront commitment required.",
    "IBM Cloud resource groups are used to organize account resources and manage access with IAM policies.",
    "You can monitor IBM Cloud service health and view planned maintenance on the IBM Cloud Status page at cloud.ibm.com/status.",
    "IBM Cloud Lite accounts are free and include a selection of always-free services, but do not support all paid features.",
    "To create an IBM Cloud API key, go to Manage > Access (IAM) > API keys in the IBM Cloud console.",
    "IBM Cloud support plans range from Basic (included with all accounts) to Advanced and Premium tiers with faster response times.",
    "IBM Cloud Object Storage stores data as objects across multiple regions for high availability and durability.",
]
QUESTIONS = [
    "How do I reset my IBM Cloud password?",
    "What is the difference between a Lite account and a Pay-As-You-Go account?",
]
TOP_K = 3
def build_system_prompt(entries: list[dict]) -> str:
    facts = "\n".join(f"{i + 1}. {e['text']}" for i, e in enumerate(entries))
    return (
        "You are a helpful assistant with access to the following verified facts.\n"
        "Use these facts to answer the user's question accurately and concisely.\n\n"
        f"Relevant facts:\n{facts}\n"
    )
def main() -> None:
    if not os.environ.get("ENDPOINT") or not os.environ.get("AUTH"):
        print("ERROR: ENDPOINT and AUTH environment variables must be set.", file=sys.stderr)
        sys.exit(1)
    # Seed the vector store.
    print("Embedding knowledge base...")
    store = MemoryStore()
    for i, fact in enumerate(KNOWLEDGE):
        print(f"  [{i + 1}/{len(KNOWLEDGE)}] {fact[:60]}")
        store.add(fact, embed_text(fact))
    print(f"\nMemory store ready with {len(KNOWLEDGE)} entries.\n")
    # Run each question without memory, then with RAG.
    for question in QUESTIONS:
        print(f"Question: {question}\n")
        print("  WITHOUT memory:")
        chat_complete("", question, prefix="    ")
        print("\n  WITH memory (RAG):")
        query_vec = embed_text(question)
        relevant = store.search(query_vec, TOP_K)
        print("  Retrieved facts:")
        for i, entry in enumerate(relevant):
            print(f"    {i + 1}. {entry['text']}")
        print()
        chat_complete(build_system_prompt(relevant), question, prefix="    ")
        print("\n" + "─" * 70)
if __name__ == "__main__":
    main()

Exécutez le script Python :

python main.py

Le résultat affiche deux réponses à chaque question. La première réponse provient uniquement des connaissances acquises lors de l'entraînement général du modèle. La deuxième réponse s'appuie sur les faits extraits de votre base de données vectorielle :

Question: How do I reset my IBM Cloud password?
  WITHOUT memory:
    To reset your password, visit the account login page and look for a
    'Forgot password' or 'Reset password' option ...
  WITH memory (RAG):
  Retrieved facts:
    1. To reset your IBM Cloud account password, go to the IBM Cloud login
       page and click 'Forgot password' to receive a reset email.
    2. To create an IBM Cloud API key, go to Manage > Access (IAM) > API keys
       in the IBM Cloud console.
    3. IBM Cloud support plans range from Basic to Advanced and Premium tiers.
    To reset your IBM Cloud account password, go to the IBM Cloud login page
    and click 'Forgot password'. You will receive an email with instructions
    to set a new password.

La réponse du RAG est plus précise et cite directement les faits récupérés, car ceux-ci ont été intégrés dans la requête du système avant que le modèle de chat ne soit appelé.

Etapes suivantes

Maintenant que vous avez mis en place un pipeline RAG complet, voici quelques pistes pour l'étendre :

  • Apportez votre propre base de connaissances — Remplacez la liste KNOWLEDGE dans main.py par n'importe quelle liste de chaînes : documentation produit, tickets d'assistance, wikis internes ou données spécifiques à un domaine.
  • Persister le magasin vectoriel — Sérialisez les entrées du magasin sur le disque (par exemple, au format JSON) afin de ne pas avoir à les réintégrer à chaque exécution.
  • Réglage de la recherche — Ajustez TOP_K le paramètre main.py pour obtenir plus ou moins de résultats par requête et observez l'effet sur la qualité des réponses.