יצירת ועיבוד תגובות RAG
לקוח רוצה לדעת אילו פדלים מתאימים לאופני Mountain-500 שלו. אתה משתמש בחיפוש וקטורי כדי למצוא את המוצרים הרלוונטיים, פורמט אותם כ-JSON, ובונה פרומפט עם הוראות הארקה. שלבי השליפה וההרחבה הושלמו. עכשיו מגיע ה-"G" ב-RAG: יצירת ה-Age. השלב הזה הוא שבו שולחים הכל למודל שפה ומקבלים תשובה.
תחשוב על זה כך: אספת את כל המרכיבים (נתונים שנאספו), הכנת את המתכון (הנחיה משודרגת), ועכשיו אתה שם אותו בתנור (קורא לדגם) כדי לאפות את המנה הסופית (התגובה). המודל משתמש בהקשר שסיפקת כדי לייצר תשובה מבוססת.
קרא למודל מ-SQL
אולי תחשוב שהשלב הזה דורש לעזוב את T-SQL ולכתוב קוד יישומים. אבל SQL Server ו-Azure SQL Database יכולים לקרוא לנקודות קצה REST ישירות באמצעות sp_invoke_external_rest_endpoint. פרוצדורה מאוחסנת זו שולחת בקשות HTTPS לשירותים חיצוניים ומחזירה את התגובה. הפרוצדורה המאוחסנת מופעלת כברירת מחדל ב-Azure SQL Database וניתן להפעיל אותה ב-SQL Server 2025 באמצעות sp_configure.
הנה הדפוס הבסיסי:
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;
הפרמטר @url מצביע על נקודת הקצה של פריסת Azure OpenAI שלך.
@method בדרך כלל זה 'POST' לבקשות דור. הוא @payload מכיל את הפרומפט JSON שבנית קודם. ההודעה @credential מתייחסת לאישור מבוסס במסד הנתונים שמכיל את פרטי האימות שלך. פרמטר הפלט @response תופס את תגובת המודל.
כשאתה קורא לנקודת הקצה של הפריסה של המודל שלך עם הprompt המורחב, המודל מעבד אותה ומחזיר את התוצאה. הפרוצדורה המאוחסנת מחזירה 0 כאשר קריאת ה-HTTP מצליחה עם קוד סטטוס 2xx, או את קוד הסטטוס של ה-HTTP עצמו כאשר היא נכשלת.
אימות עם נקודת הקצה
הפרמטר @credential מתייחס לאישור ממוקד במסד הנתונים שמכיל את פרטי האימות שלך. אתה מגדיר את האישורים האלה כשאתה יוצר מודל חיצוני, באמצעות זהות מנוהלת או מפתח API. אותו אישור עובד גם לקריאות מודל חיצוניות וגם לקריאות REST ישירות בנקודת קצה עם sp_invoke_external_rest_endpoint.
הוצא את התשובה מהתשובה
כאשר המודל מסיים לעבד את ההנחיה שלך, sp_invoke_external_rest_endpoint הוא מחזיר את התוצאה עטופה במעטפה סטנדרטית. הפרוצדורה המאוחסנת מוסיפה מטא-דאטה על עסקת ה-HTTP, ואז מקננת את תגובת ה-API בפועל בתוך result תכונה:
{
"response": {
"status": {
"http": {
"code": 200,
"description": "OK"
}
}
},
"result": {
"choices": [
{
"message": {
"role": "assistant",
"content": "The Mountain-500 is compatible with several pedal options..."
}
}
]
}
}
התשובה שאתה רוצה נמצאת ב- $.result.choices[0].message.content. כדי להגיע לשם, השתמש JSON_VALUEב-, שמחלץ ערכים סקלריים מ-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
אם אי פעם תצטרך לחלץ אובייקט או מערך JSON במקום ערך יחיד, השתמש JSON_QUERY במקום. ברוב תרחישי RAG, JSON_VALUE תוכן ההודעה זה כל מה שצריך.
ניהול שגיאות וניסיונות חוזרים
קריאה לשירות חיצוני ממסד נתונים יכולה להכניס מצבי כשל שלא נתקלים בהם בשאילתות מקומיות. נקודת הקצה עלולה להיות לא זמינה זמנית, הבקשה שלך עלולה לקבל הגבלת קצב, או שהאימות עלול להיכשל. שאילתות ה-SQL שלך צריכות לטפל בתנאים האלה בצורה אלגנטית.
ערך ההחזרה מ-Tweet sp_invoke_external_rest_endpoint מספר לך מה קרה. 0 פירושו שהקריאה ב-HTTP הצליחה עם סטטוס 2xx. לגבי כשלות, ערך ההחזרה הוא קוד הסטטוס של HTTP עצמו. לדוגמה, סטטוס 429 אומר שהשירות מאט את הבקשות שלך. טופס 401 או 403 מצביע על בעיות תעודה:
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
במקרה של כשלים זמניים כמו פסקי זמן או חוסר זמינות זמנית של שירות, ההליך המאוחסן יכול לנסות שוב אוטומטית. מוסיפים את הפרמטר @retry_count ובדוגמה הזו, הוא מנסה את הקריאה עד שלוש פעמים לפני שהוא מוותר:
EXECUTE @returnValue = sp_invoke_external_rest_endpoint
@url = @url,
@payload = @payload,
@credential = @credentialName,
@retry_count = 3,
@response = @response OUTPUT;
פרמטר זה מטפל במקרה הנפוץ שבו בקשה נכשלת פעם אחת אך מצליחה בניסיון הבא.
בנה פרוצדורה מאוחסנת RAG מלאה
עכשיו אתה יודע כל חלק בפאזל RAG. בואו נחבר אותם לפרוצדורה מאוחסנת אחת שאפליקציית שירות לקוחות תוכל לקרוא לה. ההליך מקבל שאלה בשפה טבעית ומחזיר תשובה המבוססת על נתוני המוצר שלך.
הנה מה שההליך עושה:
- ממיר את השאלה להטמעה כדי שתוכל להשוות אותה לתיאורי המוצר שלך.
- מוצא את המוצרים הרלוונטיים ביותר באמצעות מרחק וקטורי כדי להשוות את הטמעת השאלה מול ההטמעה המתוכננת מראש של כל מוצר.
- בונה את ההנחיה עם הודעת מערכת שאומרת למודל להיצמד לנתונים שסופקו, והודעת משתמש שמשלבת את המוצרים שנשלפו עם השאלה המקורית.
- שולח הכל ל-Azure OpenAI.
- מוציא את התשובה מהתגובה ומחזיר אותה למתקשר.
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;
השלמת את צינור ה-RAG ב-T-SQL. לקוח שואל "אילו פדלים מתאימים ל-Mountain-500?" הפרוצדורה שלך ממירה את השאלה הזו לווקטור, מוצאת את המוצרים הרלוונטיים ביותר, שולחת אותם למודל עם הוראות הארקה, ומחזירה תשובה המבוססת על המלאי האמיתי שלך. אין תוכנת ביניים, אין קוד חיצוני של אפליקציה, רק SQL קורא לבינה מלאכותית ומחזיר תוצאות.
נקודות מפנה
שלב הייצור הוא המקום שבו צינור ה-RAG שלך מספק ערך. אתה שולח את ההנחיה המורחבת ל-Azure OpenAI באמצעות sp_invoke_external_rest_endpoint, שמטפל בתקשורת HTTP מבלי לעזוב את T-SQL. אימות משתמש באותם אישורי טווח מסד נתונים שהגדרת בעת יצירת מודלים חיצוניים. כשהתשובה חוזרת, JSON_VALUE הוא מוציא את התשובה של העוזר. תבנה טיפול בשגיאות כי שיחות רשת נכשלות בדרכים ששאילתות מקומיות לא. עם שלושת שלבי ה-RAG שרצים ב-T-SQL, מסד הנתונים שלך הופך ליותר מאחסון. הוא הופך לשירות חכם שעונה על שאלות באמצעות הנתונים שלך.