Générer et traiter des réponses RAG

Effectué

Un client veut savoir quelles pédales correspondent à leur vélo Mountain-500. Vous utilisez la recherche vectorielle pour trouver les produits pertinents, les avez formatés en JSON et créé un message d'invite avec des instructions d'ancrage. Les phases de récupération et d’augmentation sont achevées. Vient maintenant le « G » dans RAG : génération. Cette étape est l’endroit où vous envoyez tout à un modèle de langage et récupérez une réponse.

Considérez la situation ainsi : vous avez réuni tous les ingrédients (données récupérées), préparé la recette (requête augmentée) et vous la placez désormais au four (appel du modèle) afin d’obtenir le plat final (la réponse). Le modèle utilise le contexte que vous avez fourni pour générer une réponse fondée.

Appeler le modèle à partir de SQL

Vous pouvez penser que cette étape nécessite de quitter T-SQL et d’écrire du code d’application. Toutefois, SQL Server et Azure SQL Database peuvent appeler des points de terminaison REST directement à l’aide de sp_invoke_external_rest_endpoint. Cette procédure stockée envoie des requêtes HTTPS aux services externes et retourne la réponse. La procédure stockée est activée par défaut dans Azure SQL Database et peut être activée dans SQL Server 2025 à l’aide de sp_configure.

Voici le modèle de base :

DECLARE @response NVARCHAR(MAX);
DECLARE @returnValue INT;

EXECUTE @returnValue = sp_invoke_external_rest_endpoint
    @url = N'https://<endpoint>.openai.azure.com/openai/deployments/<model>/chat/completions?api-version=<api-version>',
    @method = 'POST',
    @payload = @payload,
    @credential = [https://<endpoint>.openai.azure.com],
    @response = @response OUTPUT;

Le @url paramètre pointe vers votre point de terminaison de déploiement Azure OpenAI. Le @method est généralement 'POST' pour les demandes de génération. Le @payload contient l’invite JSON que vous avez créée précédemment. @credential désigne un identifiant de base de données contenant vos informations d’authentification. Le @response paramètre de sortie capture la réponse du modèle.

Lorsque vous appelez le point de terminaison de déploiement de votre modèle avec votre invite augmentée, le modèle le traite et retourne le résultat. La procédure stockée retourne 0 lorsque l’appel HTTP réussit avec un code d’état 2xx ou le code d’état HTTP réel lorsqu’il échoue.

S’authentifier auprès du point de terminaison

Le paramètre @credential fait référence à des identifiants liés à la portée de la base de données qui contiennent vos informations d’authentification. Vous configurez ces informations d’identification lorsque vous créez un modèle externe, à l’aide d’une identité managée ou d’une clé API. Les mêmes informations d’identification fonctionnent pour les appels de modèle externe et les appels de point de terminaison REST directs avec sp_invoke_external_rest_endpoint.

Extraire la réponse de la réplique

Lorsque le modèle termine le traitement de votre requête, sp_invoke_external_rest_endpoint retourne le résultat encapsulé dans une enveloppe standard. La procédure stockée ajoute des métadonnées sur la transaction HTTP, puis imbrique la réponse réelle de l’API à l’intérieur d’une result propriété.

{
  "response": {
    "status": {
      "http": {
        "code": 200,
        "description": "OK"
      }
    }
  },
  "result": {
    "choices": [
      {
        "message": {
          "role": "assistant",
          "content": "The Mountain-500 is compatible with several pedal options..."
        }
      }
    ]
  }
}

La réponse que vous cherchez se trouve à $.result.choices[0].message.content. Pour y parvenir, utilisez JSON_VALUE, qui extrait des valeurs scalaires à partir de JSON :

IF @returnValue = 0
BEGIN
    DECLARE @answer NVARCHAR(MAX);
    SET @answer = JSON_VALUE(@response, '$.result.choices[0].message.content');
    SELECT @answer AS AssistantResponse;
END
ELSE
BEGIN
    SELECT 
        @returnValue AS HttpStatus,
        JSON_VALUE(@response, '$.response.status.http.description') AS ErrorDescription;
END

Si vous avez besoin d’extraire un objet ou tableau JSON plutôt qu’une seule valeur, utilisez JSON_QUERY plutôt. Pour la plupart des scénarios RAG, JSON_VALUE sur le contenu du message est tout ce dont vous avez besoin.

Gérer les erreurs et les nouvelles tentatives

L’appel d’un service externe à partir d’une base de données peut introduire des modes d’échec que vous ne rencontrez pas avec des requêtes locales. Le point de terminaison peut être temporairement indisponible, votre demande peut obtenir un débit limité ou l’authentification peut échouer. Vos requêtes SQL doivent gérer ces conditions correctement.

La valeur de retour de sp_invoke_external_rest_endpoint vous indique ce qui s’est passé. Un 0 signifie que l’appel HTTP a réussi avec un état 2xx. Pour les échecs, la valeur de retour est le code d’état HTTP lui-même. Par exemple, un état 429 signifie que le service limite vos demandes. Une erreur 401 ou 403 signale un problème lié aux identifiants :

IF @returnValue = 0
    SET @answer = JSON_VALUE(@response, '$.result.choices[0].message.content');
ELSE IF @returnValue = 429
    RAISERROR('Service is busy. Try again later.', 16, 1);
ELSE IF @returnValue = 401 OR @returnValue = 403
    RAISERROR('Authentication failed. Check your credential configuration.', 16, 1);
ELSE
BEGIN
    DECLARE @errorMsg NVARCHAR(500) = 'API call failed with status ' + CAST(@returnValue AS NVARCHAR(10));
    RAISERROR(@errorMsg, 16, 1);
END

Pour les défaillances temporaires telles que les délais d’expiration ou l’indisponibilité temporaire du service, la procédure stockée peut réessayer automatiquement. Ajoutez le @retry_count paramètre et, dans cet exemple, il tente d’appeler jusqu’à trois fois avant d’abandonner :

EXECUTE @returnValue = sp_invoke_external_rest_endpoint
    @url = @url,
    @payload = @payload,
    @credential = @credentialName,
    @retry_count = 3,
    @response = @response OUTPUT;

Ce paramètre gère le cas courant où une requête échoue une fois, mais réussit lors de la prochaine tentative.

Créer une procédure stockée RAG complète

Vous connaissez maintenant chaque pièce du puzzle RAG. Nous allons les rassembler dans une procédure stockée unique qu’une application de support client peut appeler. La procédure accepte une question en langage naturel et retourne une réponse fondée sur vos données de produit.

Voici ce que fait la procédure :

  1. Convertit la question en incorporation afin de pouvoir la comparer à vos descriptions de produit.
  2. Recherche les produits les plus pertinents à l'aide de la distance vectorielle pour comparer l'encodage de la question contre l'encodage pré-calculé de la description de chaque produit.
  3. Génère l’invite avec un message système qui indique au modèle de respecter les données fournies et un message utilisateur qui associe les produits extraits à la question d’origine.
  4. Envoie tout à Azure OpenAI.
  5. Extrait la réponse de la réaction et la retourne à l'appelant.
CREATE PROCEDURE dbo.AskProductQuestion
    @Question NVARCHAR(1000),
    @Answer NVARCHAR(MAX) OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    
    DECLARE @questionVector VECTOR(1536);
    DECLARE @context NVARCHAR(MAX);
    DECLARE @payload NVARCHAR(MAX);
    DECLARE @response NVARCHAR(MAX);
    DECLARE @returnValue INT;
    
    -- Step 1:Convert question to embedding
    SELECT @questionVector = AI_GENERATE_EMBEDDINGS(@Question USE MODEL my_embedding_model);
    
    -- Step 2: Retrieve relevant products using vector search
    SET @context = (
        SELECT TOP 3 
            p.Name AS ProductName, 
            p.Color, 
            p.Size, 
            pm.Name AS Model
        FROM Production.Product p
        INNER JOIN Production.ProductModel pm ON p.ProductModelID = pm.ProductModelID
        ORDER BY VECTOR_DISTANCE('cosine', p.DescriptionVector, @questionVector)
        FOR JSON PATH
    );
    
    -- Step 3: Build augmented prompt
    SET @payload = JSON_OBJECT(
        'messages': JSON_ARRAY(
            JSON_OBJECT('role': 'system', 'content': 'You are an Adventure Works product assistant. Answer questions using only the provided product data.'),
            JSON_OBJECT('role': 'user', 'content': 'Products: ' + @context + ' Question: ' + @Question)
        ),
        'max_tokens': 500,
        'temperature': 0.5
    );
    
    -- Step 4: Call the model
    EXECUTE @returnValue = sp_invoke_external_rest_endpoint
        @url = N'https://adventureworks-openai.openai.azure.com/openai/deployments/gpt-5.2/chat/completions?api-version=2024-10-21',
        @method = 'POST',
        @payload = @payload,
        @credential = [https://adventureworks-openai.openai.azure.com],
        @response = @response OUTPUT;
    
    -- Extract and return the answer
    IF @returnValue = 0
        SET @Answer = JSON_VALUE(@response, '$.result.choices[0].message.content');
    ELSE
        SET @Answer = 'Unable to process your question. Please try again.';
END;

Vous avez terminé le pipeline RAG dans T-SQL. Un client demande « Quelles pédales fonctionnent avec la Montagne-500 ? » Votre procédure convertit cette question en vecteur, trouve les produits les plus pertinents, les envoie au modèle avec des instructions de base et retourne une réponse basée sur votre inventaire réel. Aucun intergiciel, aucun code d’application externe, juste SQL appelant l’IA et retournant des résultats.

Points clés à prendre

L’étape de génération consiste à fournir une valeur à votre pipeline RAG. Vous envoyez l’invite augmentée à Azure OpenAI à l’aide sp_invoke_external_rest_endpointde , qui gère la communication HTTP sans quitter T-SQL. L’authentification utilise les mêmes informations d’identification délimitées à la base de données que celles que vous avez configurées lors de la création de modèles externes. Lorsque la réponse revient, JSON_VALUE extrait la réponse de l’assistant. Intégrez un mécanisme de gestion des erreurs, les appels réseau échouant différemment des requêtes locales. Avec les trois étapes RAG exécutées dans T-SQL, votre base de données devient plus que le stockage. Il devient un service intelligent qui répond aux questions à l’aide de vos données.