البرنامج التعليمي: استكشاف تضمينات خدمة Azure OpenAI والبحث عن المستندات

سيرشدك هذا البرنامج التعليمي خلال استخدام واجهة برمجة تطبيقات تضمين Azure OpenAI لإجراء البحث في المستندات حيث ستقوم بالاستعلام عن قاعدة المعارف (KB) للعثور على المستند الأكثر صلة.

في هذا البرنامج التعليمي، تتعلم كيفية:

  • تثبيت Azure OpenAI.
  • قم بتنزيل عينة من مجموعة البيانات وإعدادها للتحليل.
  • إنشاء متغيرات البيئة لنقطة نهاية الموارد ومفتاح API.
  • استخدام نموذج text-embedding-ada-002 (الإصدار 2)
  • استخدم تشابه التمام لترتيب نتائج البحث.

المتطلبات الأساسية

  • اشتراك Azure - إنشاء اشتراك مجاناً
  • الوصول الممنوح إلى Azure OpenAI في اشتراك Azure المطلوب. حاليًا، يتم منح الوصول إلى هذه الخدمة فقط بواسطة التطبيق. يمكنك التقدم بطلب للوصول إلى Azure OpenAI عن طريق إكمال النموذج في https://aka.ms/oai/access. افتح مشكلة على هذا المستودع للاتصال بنا إذا كانت لديك مشكلة.
  • مورد Azure OpenAI مع نشر نموذج text-embedding-ada-002 (الإصدار 2 ). يتوفر هذا النموذج حاليا فقط في مناطق معينة. إذا لم يكن لديك مورد، يتم توثيق عملية إنشاء مورد في دليل توزيع الموارد.
  • Python 3.8 أو إصدار أحدث
  • مكتبات Python التالية: openai، num2words، matplotlib، plotly، scipy، scikit-learn، pandas، tiktoken.
  • Jupyter Notebooks

الإعداد

مكتبات Python

إذا لم تكن قد قمت بالفعل، فستحتاج إلى تثبيت المكتبات التالية:

pip install openai num2words matplotlib plotly scipy scikit-learn pandas tiktoken

تنزيل مجموعة بيانات BillSum

BillSum هي مجموعة بيانات من فواتير الولايات المتحدة في الكونغرس وكاليفورنيا. لأغراض التوضيح، سننظر فقط في فواتير الولايات المتحدة. تتكون المجموعة من مشاريع قوانين من جلسات الكونغرس من 103 إلى 115 (1993-2018). وقسمت البيانات إلى 949 18 فاتورة قطار و 269 3 فاتورة اختبار. وتركز مجموعة بيلسوم على التشريع متوسط الطول من 000 5 إلى 000 20 حرف في الطول. يمكن العثور على مزيد من المعلومات حول المشروع والورقة الأكاديمية الأصلية حيث يتم اشتقاق مجموعة البيانات هذه في مستودع GitHub لمشروع BillSum

يستخدم bill_sum_data.csv هذا البرنامج التعليمي الملف الذي يمكن تنزيله من بيانات نموذج GitHub.

يمكنك أيضا تنزيل نموذج البيانات عن طريق تشغيل الأمر التالي على جهازك المحلي:

curl "https://raw.githubusercontent.com/Azure-Samples/Azure-OpenAI-Docs-Samples/main/Samples/Tutorials/Embeddings/data/bill_sum_data.csv" --output bill_sum_data.csv

قم باسترداد المفتاح ونقطة النهاية

لإجراء مكالمة بنجاح مقابل Azure OpenAI، تحتاج إلى نقطة نهاية ومفتاح.

اسم المتغير القيمة‬
ENDPOINT يمكن العثور على هذه القيمة في قسم المفاتيح ونقطة النهاية عند فحص المورد من مدخل Microsoft Azure. بدلاً من ذلك، يمكنك العثور على القيمة في Azure OpenAI Studio>Playground>Code View. مثال على نقطة النهاية هو: https://docs-test-001.openai.azure.com/.
API-KEY يمكن العثور على هذه القيمة في قسم المفاتيح ونقطة النهاية عند فحص المورد من مدخل Microsoft Azure. يمكنك استخدام إما KEY1 أو KEY2.

انتقل إلى مجموعة الموارد في مدخل Microsoft Azure. يمكن العثور على قسم Keys & Endpoint في قسم Resource Management . انسخ نقطة النهاية ومفتاح الوصول حيث ستحتاج إلى كليهما لمصادقة استدعاءات واجهة برمجة التطبيقات. يمكنك استخدام إما KEY1 أو KEY2. يسمح لك وجود مفتاحين دائماً بتدوير المفاتيح وإعادة إنشائها بأمان دون التسبب في تعطيل الخدمة.

لقطة شاشة لواجهة مستخدم النظرة العامة لمورد Azure OpenAI في مدخل Microsoft Azure مع وضع نقطة النهاية وموقع مفاتيح الوصول في دائرة باللون الأحمر.

متغيرات البيئة

setx AZURE_OPENAI_API_KEY "REPLACE_WITH_YOUR_KEY_VALUE_HERE" 
setx AZURE_OPENAI_ENDPOINT "REPLACE_WITH_YOUR_ENDPOINT_HERE" 

بعد تعيين متغيرات البيئة، قد تحتاج إلى إغلاق دفاتر ملاحظات Jupyter وإعادة فتحها أو أي IDE تستخدمه حتى يمكن الوصول إلى متغيرات البيئة. بينما نوصي بشدة باستخدام Jupyter Notebooks، إذا كان لسبب ما لا يمكنك تعديل أي تعليمة برمجية تقوم بإرجاع إطار بيانات Pandas باستخدام print(dataframe_name) بدلا من مجرد استدعاء dataframe_name مباشرة كما هو الحال غالبا في نهاية كتلة التعليمات البرمجية.

قم بتشغيل التعليمات البرمجية التالية في Python IDE المفضل لديك:

استيراد مكتبات

import os
import re
import requests
import sys
from num2words import num2words
import os
import pandas as pd
import numpy as np
import tiktoken
from openai import AzureOpenAI

الآن نحن بحاجة إلى قراءة ملف csv الخاص بنا وإنشاء pandas DataFrame. بعد إنشاء DataFrame الأولي، يمكننا عرض محتويات الجدول عن طريق تشغيل df.

df=pd.read_csv(os.path.join(os.getcwd(),'bill_sum_data.csv')) # This assumes that you have placed the bill_sum_data.csv in the same directory you are running Jupyter Notebooks
df

إخراج:

لقطة شاشة لنتائج جدول DataFrame الأولي من ملف csv.

يحتوي الجدول الأولي على أعمدة أكثر مما نحتاج إليه سنقوم بإنشاء DataFrame أصغر جديد يسمى df_bills والذي سيحتوي فقط على أعمدة و textsummaryوtitle.

df_bills = df[['text', 'summary', 'title']]
df_bills

إخراج:

لقطة شاشة لنتائج جدول DataFrame الأصغر مع عرض أعمدة النص والملخص والعنوان فقط.

بعد ذلك سنقوم بإجراء بعض تنظيف البيانات الخفيفة عن طريق إزالة المسافة البيضاء المكررة وتنظيف علامات الترقيم لإعداد البيانات للرمز المميز.

pd.options.mode.chained_assignment = None #https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#evaluation-order-matters

# s is input text
def normalize_text(s, sep_token = " \n "):
    s = re.sub(r'\s+',  ' ', s).strip()
    s = re.sub(r". ,","",s)
    # remove all instances of multiple spaces
    s = s.replace("..",".")
    s = s.replace(". .",".")
    s = s.replace("\n", "")
    s = s.strip()
    
    return s

df_bills['text']= df_bills["text"].apply(lambda x : normalize_text(x))

الآن نحن بحاجة إلى إزالة أي فواتير طويلة جدا للحد الأقصى للرمز المميز (8192 الرموز المميزة).

tokenizer = tiktoken.get_encoding("cl100k_base")
df_bills['n_tokens'] = df_bills["text"].apply(lambda x: len(tokenizer.encode(x)))
df_bills = df_bills[df_bills.n_tokens<8192]
len(df_bills)
20

إشعار

في هذه الحالة، تكون جميع الفواتير ضمن حد رمز إدخال نموذج التضمين، ولكن يمكنك استخدام التقنية أعلاه لإزالة الإدخالات التي قد تتسبب في فشل التضمين. عندما تواجه محتوى يتجاوز حد التضمين، يمكنك أيضا تقسيم المحتوى إلى أجزاء أصغر ثم تضمينها كل على حدة.

سنفحص df_bills مرة أخرى.

df_bills

إخراج:

لقطة شاشة ل DataFrame مع عمود جديد يسمى n_tokens.

لفهم عمود n_tokens أكثر قليلا بالإضافة إلى كيفية ترميز النص في نهاية المطاف، قد يكون من المفيد تشغيل التعليمات البرمجية التالية:

sample_encode = tokenizer.encode(df_bills.text[0]) 
decode = tokenizer.decode_tokens_bytes(sample_encode)
decode

بالنسبة إلى مستنداتنا، نقوم عن قصد باقتطاع الإخراج، ولكن تشغيل هذا الأمر في بيئتك سيعيد النص الكامل من الفهرس صفر رمزي إلى مجموعات. يمكنك أن ترى أنه في بعض الحالات يتم تمثيل كلمة بأكملها برمز مميز واحد بينما في أجزاء أخرى من الكلمات يتم تقسيمها عبر رموز مميزة متعددة.

[b'SECTION',
 b' ',
 b'1',
 b'.',
 b' SHORT',
 b' TITLE',
 b'.',
 b' This',
 b' Act',
 b' may',
 b' be',
 b' cited',
 b' as',
 b' the',
 b' ``',
 b'National',
 b' Science',
 b' Education',
 b' Tax',
 b' In',
 b'cent',
 b'ive',
 b' for',
 b' Businesses',
 b' Act',
 b' of',
 b' ',
 b'200',
 b'7',
 b"''.",
 b' SEC',
 b'.',
 b' ',
 b'2',
 b'.',
 b' C',
 b'RED',
 b'ITS',
 b' FOR',
 b' CERT',
 b'AIN',
 b' CONTRIBUT',
 b'IONS',
 b' BEN',
 b'EF',
 b'IT',
 b'ING',
 b' SC',

إذا قمت بعد ذلك بالتحقق من decode طول المتغير، فستجد أنه يطابق الرقم الأول في العمود n_tokens.

len(decode)
1466

الآن بعد أن فهمنا المزيد حول كيفية عمل الرمز المميز يمكننا الانتقال إلى التضمين. من المهم ملاحظة أننا لم نرمز المستندات بعد. n_tokens العمود هو ببساطة طريقة للتأكد من عدم تجاوز أي من البيانات التي نمررها إلى النموذج للرمز المميز والتضمين حد الرمز المميز للإدخال وهو 8192. عندما نمرر المستندات إلى نموذج التضمينات، فإنه سيتم تقسيم المستندات إلى رموز مميزة مشابهة (وإن لم تكن متطابقة بالضرورة) للأمثلة أعلاه ثم تحويل الرموز المميزة إلى سلسلة من أرقام النقاط العائمة التي يمكن الوصول إليها عبر البحث المتجه. يمكن تخزين هذه التضمينات محليا أو في قاعدة بيانات Azure لدعم البحث في المتجهات. ونتيجة لذلك، سيكون لكل فاتورة متجه تضمين مطابق لها في العمود الجديد ada_v2 على الجانب الأيسر من DataFrame.

في المثال أدناه، نستدعي نموذج التضمين مرة واحدة لكل عنصر نريد تضمينه. عند العمل مع مشاريع التضمين الكبيرة، يمكنك بدلا من ذلك تمرير النموذج مجموعة من المدخلات للتضمين بدلا من إدخال واحد في كل مرة. عند تمرير النموذج، يكون الحد الأقصى لعدد عناصر الإدخال لكل استدعاء إلى نقطة نهاية التضمين هو 2048.

client = AzureOpenAI(
  api_key = os.getenv("AZURE_OPENAI_API_KEY"),  
  api_version = "2024-02-01",
  azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT")
)

def generate_embeddings(text, model="text-embedding-ada-002"): # model = "deployment_name"
    return client.embeddings.create(input = [text], model=model).data[0].embedding

df_bills['ada_v2'] = df_bills["text"].apply(lambda x : generate_embeddings (x, model = 'text-embedding-ada-002')) # model should be set to the deployment name you chose when you deployed the text-embedding-ada-002 (Version 2) model
df_bills

إخراج:

لقطة شاشة للنتائج المنسقة من الأمر df_bills.

أثناء تشغيل كتلة التعليمات البرمجية للبحث أدناه، سنقوم بتضمين استعلام البحث "هل يمكنني الحصول على معلومات حول إيرادات ضريبة شركة الكابلات؟" بنفس نموذج text-embedding-ada-002 (الإصدار 2). بعد ذلك، سنجد أقرب فاتورة يتم تضمينها في النص المضمن حديثا من استعلامنا المصنف حسب تشابه جيب التمام.

def cosine_similarity(a, b):
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

def get_embedding(text, model="text-embedding-ada-002"): # model = "deployment_name"
    return client.embeddings.create(input = [text], model=model).data[0].embedding

def search_docs(df, user_query, top_n=4, to_print=True):
    embedding = get_embedding(
        user_query,
        model="text-embedding-ada-002" # model should be set to the deployment name you chose when you deployed the text-embedding-ada-002 (Version 2) model
    )
    df["similarities"] = df.ada_v2.apply(lambda x: cosine_similarity(x, embedding))

    res = (
        df.sort_values("similarities", ascending=False)
        .head(top_n)
    )
    if to_print:
        display(res)
    return res


res = search_docs(df_bills, "Can I get information on cable company tax revenue?", top_n=4)

إخراج:

لقطة شاشة لنتائج res المنسقة بمجرد تشغيل استعلام البحث.

وأخيرا، سنعرض النتيجة العليا من البحث في المستندات استنادا إلى استعلام المستخدم مقابل قاعدة المعارف (KB) بأكمله. وهذا يرجع النتيجة العليا "قانون حق دافع الضرائب في العرض لعام 1993". يحتوي هذا المستند على درجة تشابه جيب تمام تبلغ 0.76 بين الاستعلام والمستند:

res["summary"][9]
"Taxpayer's Right to View Act of 1993 - Amends the Communications Act of 1934 to prohibit a cable operator from assessing separate charges for any video programming of a sporting, theatrical, or other entertainment event if that event is performed at a facility constructed, renovated, or maintained with tax revenues or by an organization that receives public financial support. Authorizes the Federal Communications Commission and local franchising authorities to make determinations concerning the applicability of such prohibition. Sets forth conditions under which a facility is considered to have been constructed, maintained, or renovated with tax revenues. Considers events performed by nonprofit or public organizations that receive tax subsidies to be subject to this Act if the event is sponsored by, or includes the participation of a team that is part of, a tax exempt organization."

المتطلبات الأساسية

  • اشتراك Azure - إنشاء اشتراك مجاناً

  • الوصول الممنوح إلى Azure OpenAI في اشتراك Azure المطلوب.

    حاليًا، يتم منح الوصول إلى هذه الخدمة فقط بواسطة التطبيق. يمكنك التقدم بطلب للوصول إلى Azure OpenAI عن طريق إكمال النموذج في https://aka.ms/oai/access. افتح مشكلة على هذا المستودع للاتصال بنا إذا كانت لديك مشكلة.

  • مورد Azure OpenAI مع نشر نموذج text-embedding-ada-002 (الإصدار 2 ).

    يتوفر هذا النموذج حاليا فقط في مناطق معينة. إذا لم يكن لديك مورد، يتم توثيق عملية إنشاء مورد في دليل توزيع الموارد.

  • PowerShell 7.4

إشعار

العديد من الأمثلة في هذا البرنامج التعليمي إعادة استخدام المتغيرات من خطوة إلى خطوة. احتفظ بجلسة المحطة الطرفية نفسها مفتوحة طوال الوقت. إذا فقدت المتغيرات التي قمت بتعيينها في خطوة سابقة بسبب إغلاق المحطة الطرفية، يجب أن تبدأ مرة أخرى من البداية.

قم باسترداد المفتاح ونقطة النهاية

لإجراء مكالمة بنجاح مقابل Azure OpenAI، تحتاج إلى نقطة نهاية ومفتاح.

اسم المتغير القيمة‬
ENDPOINT يمكن العثور على هذه القيمة في قسم المفاتيح ونقطة النهاية عند فحص المورد من مدخل Microsoft Azure. بدلاً من ذلك، يمكنك العثور على القيمة في Azure OpenAI Studio>Playground>Code View. مثال على نقطة النهاية هو: https://docs-test-001.openai.azure.com/.
API-KEY يمكن العثور على هذه القيمة في قسم المفاتيح ونقطة النهاية عند فحص المورد من مدخل Microsoft Azure. يمكنك استخدام إما KEY1 أو KEY2.

انتقل إلى مجموعة الموارد في مدخل Microsoft Azure. يمكن العثور على قسم Keys & Endpoint في قسم Resource Management . انسخ نقطة النهاية ومفتاح الوصول حيث ستحتاج إلى كليهما لمصادقة استدعاءات واجهة برمجة التطبيقات. يمكنك استخدام إما KEY1 أو KEY2. يسمح لك وجود مفتاحين دائماً بتدوير المفاتيح وإعادة إنشائها بأمان دون التسبب في تعطيل الخدمة.

لقطة شاشة لواجهة مستخدم النظرة العامة لمورد Azure OpenAI في مدخل Microsoft Azure مع وضع نقطة النهاية وموقع مفاتيح الوصول في دائرة باللون الأحمر.

إنشاء متغيرات بيئة ثابتة وتعيينها للمفتاح ونقطة النهاية.

متغيرات البيئة

setx AZURE_OPENAI_API_KEY "REPLACE_WITH_YOUR_KEY_VALUE_HERE" 
setx AZURE_OPENAI_ENDPOINT "REPLACE_WITH_YOUR_ENDPOINT_HERE" 

بالنسبة لهذا البرنامج التعليمي، نستخدم وثائق PowerShell 7.4 المرجعية كمجموعة بيانات نموذجية معروفة وآمنة. كبديل، قد تختار استكشاف مجموعات بيانات عينة أدوات Microsoft Research.

أنشئ مجلدا تريد تخزين مشروعك فيه. قم بتعيين موقعك إلى مجلد المشروع. قم بتنزيل مجموعة البيانات إلى جهازك المحلي باستخدام Invoke-WebRequest الأمر ثم قم بتوسيع الأرشيف. وأخيرا، قم بتعيين موقعك إلى المجلد الفرعي الذي يحتوي على معلومات مرجعية للإصدار 7.4 من PowerShell.

New-Item '<FILE-PATH-TO-YOUR-PROJECT>' -Type Directory
Set-Location '<FILE-PATH-TO-YOUR-PROJECT>'

$DocsUri = 'https://github.com/MicrosoftDocs/PowerShell-Docs/archive/refs/heads/main.zip'
Invoke-WebRequest $DocsUri -OutFile './PSDocs.zip'

Expand-Archive './PSDocs.zip'
Set-Location './PSDocs/PowerShell-Docs-main/reference/7.4/'

نحن نعمل مع كمية كبيرة من البيانات في هذا البرنامج التعليمي، لذلك نستخدم كائن جدول بيانات .NET لأداء فعال. يحتوي جدول البيانات على عنوان أعمدة ومحتوى وإعداد وuri وملف وخطوط متجهة. عمود العنوان هو المفتاح الأساسي.

في الخطوة التالية، نقوم بتحميل محتوى كل ملف markdown في جدول البيانات. نستخدم أيضا عامل تشغيل PowerShell -match لالتقاط أسطر النص title: المعروفة و online version:، وتخزينها في أعمدة مميزة. لا تحتوي بعض الملفات على أسطر بيانات التعريف للنص، ولكن نظرا لأنها صفحات نظرة عامة وليست مستندات مرجعية مفصلة، فإننا نستبعدها من جدول البيانات.

# make sure your location is the project subfolder

$DataTable = New-Object System.Data.DataTable

'title', 'content', 'prep', 'uri', 'file', 'vectors' | ForEach-Object {
    $DataTable.Columns.Add($_)
} | Out-Null
$DataTable.PrimaryKey = $DataTable.Columns['title']

$md = Get-ChildItem -Path . -Include *.md -Recurse

$md | ForEach-Object {
    $file       = $_.FullName
    $content    = Get-Content $file
    $title      = $content | Where-Object { $_ -match 'title: ' }
    $uri        = $content | Where-Object { $_ -match 'online version: ' }
    if ($title -and $uri) {
        $row                = $DataTable.NewRow()
        $row.title          = $title.ToString().Replace('title: ', '')
        $row.content        = $content | Out-String
        $row.prep           = '' # use later in the tutorial
        $row.uri            = $uri.ToString().Replace('online version: ', '')
        $row.file           = $file
        $row.vectors        = '' # use later in the tutorial
        $Datatable.rows.add($row)
    }
}

عرض البيانات باستخدام out-gridview الأمر (غير متوفر في Cloud Shell).

$Datatable | out-gridview

إخراج:

لقطة شاشة لنتائج DataTable الأولية.

بعد ذلك، قم بإجراء بعض التنظيف الخفيف للبيانات عن طريق إزالة أحرف إضافية ومساحة فارغة وعلامات مستندات أخرى، لإعداد البيانات للرمز المميز. توضح الدالة Invoke-DocPrep النموذجية كيفية استخدام عامل تشغيل PowerShell -replace للتكرار من خلال قائمة بالأحرف التي ترغب في إزالتها من المحتوى.

# sample demonstrates how to use `-replace` to remove characters from text content
function Invoke-DocPrep {
param(
    [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
    [string]$content
)
    # tab, line breaks, empty space
    $replace = @('\t','\r\n','\n','\r')
    # non-UTF8 characters
    $replace += @('[^\x00-\x7F]')
    # html
    $replace += @('<table>','</table>','<tr>','</tr>','<td>','</td>')
    $replace += @('<ul>','</ul>','<li>','</li>')
    $replace += @('<p>','</p>','<br>')
    # docs
    $replace += @('\*\*IMPORTANT:\*\*','\*\*NOTE:\*\*')
    $replace += @('<!','no-loc ','text=')
    $replace += @('<--','-->','---','--',':::')
    # markdown
    $replace += @('###','##','#','```')
    $replace | ForEach-Object {
        $content = $content -replace $_, ' ' -replace '  ',' '
    }
    return $content
}

بعد إنشاء الدالة Invoke-DocPrep ، استخدم ForEach-Object الأمر لتخزين المحتوى المعد في عمود الإعداد لكافة الصفوف في جدول البيانات. نحن نستخدم عمودا جديدا بحيث يتوفر التنسيق الأصلي إذا أردنا استرداده لاحقا.

$Datatable.rows | ForEach-Object { $_.prep = Invoke-DocPrep $_.content }

اعرض جدول البيانات مرة أخرى لرؤية التغيير.

$Datatable | out-gridview

عندما نمرر المستندات إلى نموذج التضمينات، فإنه يقوم بترميز المستندات في رموز مميزة ثم يقوم بإرجاع سلسلة من أرقام النقاط العائمة لاستخدامها في بحث تشابه التمام. يمكن تخزين هذه التضمينات محليا أو في خدمة مثل Vector Search في Azure الذكاء الاصطناعي Search. يحتوي كل مستند على متجه تضمين مطابق له في عمود المتجهات الجديدة.

يتكرر المثال التالي عبر كل صف في جدول البيانات، ويسترد المتجهات للمحتوى المعالج مسبقا، ويخزنها في عمود المتجهات . تقيس خدمة OpenAI الطلبات المتكررة، لذلك يتضمن المثال تراجعا أسيا كما هو مقترح في الوثائق.

بعد اكتمال البرنامج النصي، يجب أن يحتوي كل صف على قائمة محددة بفاصلة من 1536 متجها لكل مستند. إذا حدث خطأ وكان رمز الحالة هو 400، تتم إضافة مسار الملف والعنوان ورمز الخطأ إلى متغير يسمى $errorDocs لاستكشاف الأخطاء وإصلاحها. يحدث الخطأ الأكثر شيوعا عندما يكون عدد الرموز المميزة أكثر من حد المطالبة للنموذج.

# Azure OpenAI metadata variables
$openai = @{
    api_key     = $Env:AZURE_OPENAI_API_KEY 
    api_base    = $Env:AZURE_OPENAI_ENDPOINT # should look like 'https://<YOUR_RESOURCE_NAME>.openai.azure.com/'
    api_version = '2024-02-01' # may change in the future
    name        = $Env:AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT # custom name you chose for your deployment
}

$headers = [ordered]@{
    'api-key' = $openai.api_key
}

$url = "$($openai.api_base)/openai/deployments/$($openai.name)/embeddings?api-version=$($openai.api_version)"

$Datatable | ForEach-Object {
    $doc = $_

    $body = [ordered]@{
        input = $doc.prep
    } | ConvertTo-Json

    $retryCount = 0
    $maxRetries = 10
    $delay      = 1
    $docErrors = @()

    do {
        try {
            $params = @{
                Uri         = $url
                Headers     = $headers
                Body        = $body
                Method      = 'Post'
                ContentType = 'application/json'
            }
            $response = Invoke-RestMethod @params
            $Datatable.rows.find($doc.title).vectors = $response.data.embedding -join ','
            break
        } catch {
            if ($_.Exception.Response.StatusCode -eq 429) {
                $retryCount++
                [int]$retryAfter = $_.Exception.Response.Headers |
                    Where-Object key -eq 'Retry-After' |
                    Select-Object -ExpandProperty Value

                # Use delay from error header
                if ($delay -lt $retryAfter) { $delay = $retryAfter++ }
                Start-Sleep -Seconds $delay
                # Exponential back-off
                $delay = [math]::min($delay * 1.5, 300)
            } elseif ($_.Exception.Response.StatusCode -eq 400) {
                if ($docErrors.file -notcontains $doc.file) {
                    $docErrors += [ordered]@{
                        error   = $_.exception.ErrorDetails.Message | ForEach-Object error | ForEach-Object message
                        file    = $doc.file
                        title   = $doc.title
                    }
                }
            } else {
                throw
            }
        }
    } while ($retryCount -lt $maxRetries)
}
if (0 -lt $docErrors.count) {
    Write-Host "$($docErrors.count) documents encountered known errors such as too many tokens.`nReview the `$docErrors variable for details."
}

لديك الآن جدول قاعدة بيانات محلي في الذاكرة من مستندات PowerShell 7.4 المرجعية.

استنادا إلى سلسلة بحث، نحتاج إلى حساب مجموعة أخرى من المتجهات حتى يتمكن PowerShell من تصنيف كل مستند حسب التشابه.

في المثال التالي، يتم استرداد المتجهات لسلسلة get a list of running processesالبحث .

$searchText = "get a list of running processes"

$body = [ordered]@{
    input = $searchText
} | ConvertTo-Json

$url = "$($openai.api_base)/openai/deployments/$($openai.name)/embeddings?api-version=$($openai.api_version)"

$params = @{
    Uri         = $url
    Headers     = $headers
    Body        = $body
    Method      = 'Post'
    ContentType = 'application/json'
}
$response = Invoke-RestMethod @params
$searchVectors = $response.data.embedding -join ','

وأخيرا، تقوم دالة العينة التالية، التي تقترض مثالا من مثال البرنامج النصي Measure-VectorSimilarity الذي كتبه Lee Holmes، بإجراء حساب تشابه تمام التمام ثم ترتيب كل صف في جدول البيانات.

# Sample function to calculate cosine similarity
function Get-CosineSimilarity ([float[]]$vector1, [float[]]$vector2) {
    $dot = 0
    $mag1 = 0
    $mag2 = 0

    $allkeys = 0..($vector1.Length-1)

    foreach ($key in $allkeys) {
        $dot  += $vector1[$key]  * $vector2[$key]
        $mag1 += ($vector1[$key] * $vector1[$key])
        $mag2 += ($vector2[$key] * $vector2[$key])
    }

    $mag1 = [Math]::Sqrt($mag1)
    $mag2 = [Math]::Sqrt($mag2)

    return [Math]::Round($dot / ($mag1 * $mag2), 3)
}

الأوامر في المثال التالي حلقة من خلال جميع الصفوف في $Datatable وحساب تشابه جيب التمام لسلسلة البحث. يتم فرز النتائج ويتم تخزين النتائج الثلاث الأولى في متغير يسمى $topThree. لا يرجع المثال الإخراج.

# Calculate cosine similarity for each row and select the top 3
$topThree = $Datatable | ForEach-Object {
    [PSCustomObject]@{
        title = $_.title
        similarity = Get-CosineSimilarity $_.vectors.split(',') $searchVectors.split(',')
    }
} | Sort-Object -property similarity -descending | Select-Object -First 3 | ForEach-Object {
    $title = $_.title
    $Datatable | Where-Object { $_.title -eq $title }
}

راجع إخراج $topThree المتغير، مع خصائص العنوان وعنوان URL فقط، في gridview.

$topThree | Select "title", "uri" | Out-GridView

إخراج:

لقطة شاشة للنتائج المنسقة بمجرد انتهاء استعلام البحث.

$topThree يحتوي المتغير على كافة المعلومات من الصفوف في جدول البيانات. على سبيل المثال، تحتوي خاصية المحتوى على تنسيق المستند الأصلي. يستخدم [0] للفهرسة في العنصر الأول في الصفيف.

$topThree[0].content

عرض المستند الكامل (مقتطع في مقتطف الإخراج لهذه الصفحة).

---
external help file: Microsoft.PowerShell.Commands.Management.dll-Help.xml
Locale: en-US
Module Name: Microsoft.PowerShell.Management
ms.date: 07/03/2023
online version: https://learn.microsoft.com/powershell/module/microsoft.powershell.management/get-process?view=powershell-7.4&WT.mc_id=ps-gethelp
schema: 2.0.0
title: Get-Process
---

# Get-Process

## SYNOPSIS
Gets the processes that are running on the local computer.

## SYNTAX

### Name (Default)

Get-Process [[-Name] <String[]>] [-Module] [-FileVersionInfo] [<CommonParameters>]
# truncated example

وأخيرا، بدلا من إعادة إنشاء عمليات التضمين في كل مرة تحتاج فيها إلى الاستعلام عن مجموعة البيانات، يمكنك تخزين البيانات على القرص واستدعائها في المستقبل. تبسط WriteXML() أساليب و ReadXML() من أنواع كائنات DataTable في المثال التالي العملية. يتطلب مخطط ملف XML أن يحتوي جدول البيانات على TableName.

استبدل <YOUR-FULL-FILE-PATH> بالمسار الكامل حيث تريد كتابة ملف XML وقراءته. يجب أن ينتهي المسار ب .xml.

# Set DataTable name
$Datatable.TableName = "MyDataTable"

# Writing DataTable to XML
$Datatable.WriteXml("<YOUR-FULL-FILE-PATH>", [System.Data.XmlWriteMode]::WriteSchema)

# Reading XML back to DataTable
$newDatatable = New-Object System.Data.DataTable
$newDatatable.ReadXml("<YOUR-FULL-FILE-PATH>")

أثناء إعادة استخدام البيانات، تحتاج إلى الحصول على متجهات كل سلسلة بحث جديدة (ولكن ليس جدول البيانات بأكمله). كتمرين تعليمي، حاول إنشاء برنامج نصي PowerShell لأتمتة Invoke-RestMethod الأمر باستخدام سلسلة البحث كمعلمة.

باستخدام هذا الأسلوب، يمكنك استخدام التضمينات كآلية بحث عبر المستندات في قاعدة المعارف (KB). يمكن للمستخدم بعد ذلك أخذ أعلى نتيجة بحث واستخدامها لمهمة انتقال البيانات من الخادم، والتي دفعت الاستعلام الأولي الخاص به.

تنظيف الموارد

إذا قمت بإنشاء مورد Azure OpenAI فقط لإكمال هذا البرنامج التعليمي وتريد تنظيف مورد Azure OpenAI وإزالته، فستحتاج إلى حذف النماذج المنشورة، ثم حذف المورد أو مجموعة الموارد المقترنة إذا كان مخصصا لمورد الاختبار الخاص بك. يؤدي حذف مجموعة الموارد إلى حذف أية موارد أخرى مقترنة بها أيضًا.

الخطوات التالية

تعرف على المزيد حول نماذج Azure OpenAI: