Generar y procesar respuestas RAG
Un cliente quiere saber qué pedales caben en su bicicleta Mountain-500. La búsqueda vectorial se usa para buscar los productos pertinentes, darles el formato JSON y crear una solicitud con instrucciones de base. Los pasos de recuperación y aumento se han completado. Ahora viene el "G" en RAG: generación. Este paso es donde se envía todo a un modelo de lenguaje y se devuelve una respuesta.
Piense en esto: ha recopilado todos los ingredientes (datos recuperados), preparó la receta (petición aumentada) y ahora lo pone en el horno (llamando al modelo) para hornear el plato final (la respuesta). El modelo usa el contexto proporcionado para generar una respuesta con base.
Llamada al modelo desde SQL
Puede pensar que este paso requiere dejar T-SQL y escribir código de aplicación. Pero SQL Server y Azure SQL Database pueden llamar a puntos de conexión REST directamente mediante sp_invoke_external_rest_endpoint. Este procedimiento almacenado envía solicitudes HTTPS a servicios externos y devuelve la respuesta. El procedimiento almacenado está habilitado de forma predeterminada en Azure SQL Database y se puede habilitar en SQL Server 2025 mediante sp_configure.
Este es el patrón básico:
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;
El @url parámetro apunta al punto de conexión de implementación de Azure OpenAI.
@method normalmente es "POST" para las solicitudes de generación.
@payload contiene el mensaje JSON que creó anteriormente. El @credential hace referencia a una credencial delimitada al ámbito de la base de datos que contiene tus detalles de autenticación. El @response parámetro de salida captura la respuesta del modelo.
Al llamar al punto de conexión de implementación de su modelo con la solicitud aumentada, el modelo lo procesa y devuelve el resultado. El procedimiento almacenado devuelve 0 cuando la llamada HTTP se realiza correctamente con un código de estado 2xx o el código de estado HTTP real cuando se produce un error.
Autenticación con el punto de conexión
El @credential parámetro hace referencia a una credencial con ámbito de base de datos que contiene los detalles de autenticación. Estas credenciales se configuran al crear un modelo externo mediante una identidad administrada o una clave de API. La misma credencial funciona tanto para las llamadas de modelo externo como para las llamadas directas al punto de conexión REST con sp_invoke_external_rest_endpoint.
Extraer la respuesta de la respuesta dada
Cuando el modelo termine de procesar el mensaje, sp_invoke_external_rest_endpoint devuelve el resultado envuelto en un sobre estándar. El procedimiento almacenado agrega metadatos sobre la transacción HTTP y, a continuación, anida la respuesta de API real dentro de una result propiedad :
{
"response": {
"status": {
"http": {
"code": 200,
"description": "OK"
}
}
},
"result": {
"choices": [
{
"message": {
"role": "assistant",
"content": "The Mountain-500 is compatible with several pedal options..."
}
}
]
}
}
La respuesta que quieres vive en $.result.choices[0].message.content. Para llegar allí, use JSON_VALUE, que extrae valores escalares 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 alguna vez necesita extraer un objeto JSON o una matriz en lugar de un solo valor, use JSON_QUERY en su lugar. Para la mayoría de los escenarios RAG, JSON_VALUE en el contenido del mensaje es todo lo que necesitas.
Administración de errores y reintentos
Llamar a un servicio externo desde una base de datos puede introducir modos de error que no se encuentran con consultas locales. Es posible que el punto de conexión no esté disponible temporalmente, es posible que la solicitud obtenga una velocidad limitada o que se produzca un error en la autenticación. Las consultas SQL deben controlar estas condiciones correctamente.
El valor devuelto de sp_invoke_external_rest_endpoint indica lo que ha ocurrido. Un 0 significa que la llamada HTTP se realizó correctamente con un estado 2xx. En el caso de los errores, el valor devuelto es el propio código de estado HTTP. Por ejemplo, un estado 429 significa que el servicio limita las solicitudes. Un 401 o 403 apunta a problemas de credenciales:
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
En el caso de errores transitorios, como tiempos de espera o falta de disponibilidad temporal del servicio, el procedimiento almacenado puede reintentar automáticamente. Agregue el @retry_count parámetro y, en este ejemplo, intenta la llamada hasta tres veces antes de abandonar:
EXECUTE @returnValue = sp_invoke_external_rest_endpoint
@url = @url,
@payload = @payload,
@credential = @credentialName,
@retry_count = 3,
@response = @response OUTPUT;
Este parámetro controla el caso común en el que se produce un error en una solicitud una vez, pero se realiza correctamente en el siguiente intento.
Crear un procedimiento almacenado RAG completo
Ahora conoces cada pieza del rompecabezas RAG. Vamos a reunirlos en un único procedimiento almacenado al que podría llamar una aplicación de soporte técnico al cliente. El procedimiento acepta una pregunta de lenguaje natural y devuelve una respuesta fundamentada en los datos del producto.
Esto es lo que hace el procedimiento:
- Convierte la pregunta en una inserción para que pueda compararla con las descripciones del producto.
- Busca los productos más relevantes utilizando la distancia vectorial para comparar la inserción de la pregunta con la inserción precalculada de la descripción de cada producto.
- Construye el aviso con un mensaje del sistema que indica al modelo que se ciña a los datos proporcionados y un mensaje de usuario que integra los productos recuperados con la pregunta original.
- Envía todo a Azure OpenAI.
- Extrae la respuesta del resultado y la devuelve al llamante.
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;
Ha completado la canalización RAG en T-SQL. Un cliente pregunta "¿Qué pedales funcionan con la Montaña-500?" El procedimiento convierte esa pregunta en un vector, busca los productos más relevantes, los envía al modelo con instrucciones de puesta a tierra y devuelve una respuesta basada en el inventario real. Ningún middleware, ningún código de aplicación externo, solo SQL que llama a AI y devuelve resultados.
Conclusiones clave
El paso de generación es donde la canalización RAG ofrece valor. El mensaje aumentado se envía a Azure OpenAI mediante sp_invoke_external_rest_endpoint, que controla la comunicación HTTP sin salir de T-SQL. La autenticación usa las mismas credenciales de ámbito de base de datos que configuró al crear modelos externos. Cuando la respuesta vuelva, JSON_VALUE extrae la respuesta del asistente. Incorpora el manejo de errores porque las llamadas de red fallan de maneras en las que las consultas locales no lo hacen. Con los tres pasos RAG que se ejecutan en T-SQL, la base de datos se convierte en más que almacenamiento. Se convierte en un servicio inteligente que responde a preguntas mediante los datos.