Tutorial: Exploración de las inserciones de Azure OpenAI Service y la búsqueda de documentos

En este tutorial aprenderá a usar la API de inserciones de Azure OpenAI para realizar la búsqueda de documentos, donde consultará una knowledge base para encontrar el documento más relevante.

En este tutorial, aprenderá a:

  • Instale Azure OpenAI.
  • Descargue el conjunto de datos de ejemplo y prepárelo para su análisis.
  • Cree variables de entorno para el punto de conexión de recursos y la clave de API.
  • Use el modelo text-embedding-ada-002 (versión 2)
  • Use la similitud coseno para clasificar los resultados de búsqueda.

Requisitos previos

  • Una suscripción a Azure: cree una cuenta gratuita.
  • Acceso concedido a Azure OpenAl en la suscripción de Azure que quiera. Actualmente, solo la aplicación concede acceso a este servicio. Para solicitar acceso a Azure OpenAI, rellene el formulario en https://aka.ms/oai/access. Si tiene algún problema, abra una incidencia en este repositorio para ponerse en contacto con nosotros.
  • Un recurso de Azure OpenAI con el modelo text-embedding-ada-002 (versión 2) implementado. Este modelo solo está disponible actualmente en determinadas regiones. Si no cuenta con un recurso o modelo, el proceso de crear uno está documentado en nuestra guía de implementación de recursos.
  • Python 3.8 o una versión posterior
  • Las siguientes bibliotecas de Python: openai, num2words, matplotlib, plotly, scipy, scikit-learn, pandas y tiktoken.
  • Jupyter Notebooks

Configuración

Bibliotecas de Python

Si aún no lo ha hecho, debe instalar las siguientes bibliotecas:

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

Descarga del conjunto de datos BillSum

BillSum es un conjunto de datos de las facturas estatales del Congreso de Estados Unidos y California. Con fines ilustrativos, solo veremos las facturas estadounidenses. El corpus consta de facturas de las sesiones 103 a 115 (1993-2018) del Congreso. Los datos se dividieron en 18 949 facturas de entrenamiento y 3269 facturas de prueba. El corpus de BillSum se centra en la legislación de longitud media de 5000 a 20 000 caracteres. Puede encontrar más información sobre el proyecto y el documento académico original del que se deriva este conjunto de datos en el repositorio de GitHub del proyecto BillSum.

En este inicio rápido se usa el archivo bill_sum_data.csv que se puede descargar de los datos de ejemplo de GitHub

También puede descargar los datos de ejemplo ejecutando el siguiente comando en el equipo local:

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

Recuperación de la clave y el punto de conexión

Para realizar correctamente una llamada en Azure OpenAI, necesita un punto de conexión y una clave.

Nombre de la variable Valor
ENDPOINT Este valor se puede encontrar en la sección Claves y punto de conexión al examinar el recurso en Azure Portal. Como alternativa, puede encontrar el valor en Azure OpenAI Studio>Playground>Code View. Punto de conexión de ejemplo: https://docs-test-001.openai.azure.com/.
API-KEY Este valor se puede encontrar en la sección Claves y punto de conexión al examinar el recurso en Azure Portal. Puede usar KEY1 o KEY2.

Vaya al recurso en Azure Portal. La sección Claves y puntos de conexión se puede encontrar en la sección Administración de recursos. Copie el punto de conexión y la clave de acceso, ya que los necesitará para autenticar las llamadas API. Puede usar KEY1 o KEY2. Tener siempre dos claves permite rotar y regenerar las claves de forma segura sin provocar una interrupción del servicio.

Captura de pantalla de la interfaz de usuario de información general de un recurso de Azure OpenAI en Azure Portal con el punto de conexión y la ubicación de las claves de acceso en círculo en rojo.

Variables de entorno

setx AZURE_OPENAI_API_KEY "REPLACE_WITH_YOUR_KEY_VALUE_HERE" 
setx AZURE_OPENAI_ENDPOINT "REPLACE_WITH_YOUR_ENDPOINT_HERE" 

Después de establecer las variables de entorno, es posible que tenga que cerrar y volver a abrir las instancias de Jupyter Notebook o cualquier IDE que use para que se pueda acceder a las variables de entorno. Aunque se recomienda encarecidamente usar instancias de Jupyter Notebook, si por alguna razón no es posible, deberá modificar cualquier código que devuelva un dataframe de Pandas mediante print(dataframe_name) en lugar de llamar directamente a dataframe_name, como se hace a menudo al final de un bloque de código.

Ejecute el código siguiente en el IDE de Python preferido:

Importación de bibliotecas

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

Ahora necesitamos leer nuestro archivo .csv y crear un dataframe de Pandas. Una vez creado el dataframe inicial, podemos ver el contenido de la tabla mediante la ejecución de 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

Salida:

Captura de pantalla de los resultados iniciales de la tabla DataFrame del archivo CSV.

La tabla inicial tiene más columnas de las que necesitamos crearemos una dataframe más pequeña denominada df_bills que contendrá solo las columnas de text, summary y title.

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

Salida:

Captura de pantalla de los resultados de la tabla DataFrame más pequeños con solo las columnas de texto, resumen y título que se muestran.

A continuación, realizaremos una limpieza de datos ligeros quitando el espacio en blanco redundante y limpiando la puntuación para preparar los datos para la tokenización.

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))

Ahora es necesario quitar las facturas que sean demasiado largas para el límite de tokens (8192 tokens).

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

Nota:

En este caso, todas las facturas están por debajo del límite de tokens de entrada del modelo de inserción, pero puede usar la técnica anterior para quitar entradas que, de lo contrario, provocarían un error en la inserción. Cuando se enfrente a contenido que supere el límite de inserción, también podrá fragmentar el contenido en partes más pequeñas y, a continuación, insertarlos de uno en uno.

Volveremos a examinar df_bills.

df_bills

Salida:

Captura de pantalla de DataFrame con una nueva columna denominada n_tokens.

Para comprender la columna n_tokens un poco más, así como cómo se tokeniza el texto en última instancia, podría resultar útil ejecutar el código siguiente:

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

Para nuestros documentos, truncamos intencionadamente la salida, pero la ejecución de este comando en el entorno devolverá el texto completo del índice cero tokenizado en fragmentos. Puede ver que, en algunos casos, una palabra completa se representa con un único token, mientras que en otros, partes de palabras se dividen entre varios tokens.

[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',

Si, después, comprueba la longitud de la variable decode, encontrará que coincide con el primer número de la columna n_tokens.

len(decode)
1466

Ahora que entendemos más sobre cómo funciona la tokenización, podemos pasar a la inserción. Es importante tener en cuenta que aún no hemos tokenizado los documentos. La columna n_tokens es simplemente una manera de asegurarse de que ninguno de los datos que pasamos al modelo para la tokenización y la inserción supere el límite de tokens de entrada de 8192. Cuando pasemos los documentos al modelo de incrustaciones, este dividirá los documentos en tokens similares (aunque no necesariamente idénticos) a los ejemplos anteriores y, a continuación, convertirá los tokens en una serie de números de punto flotante que serán accesibles a través de la búsqueda vectorial. Estas inserciones se pueden almacenar localmente o en una base de datos de Azure para admitir los vectores de búsqueda. Como resultado, cada factura tendrá su propio vector de incrustación correspondiente en la nueva columna ada_v2 del lado derecho del DataFrame.

En el ejemplo siguiente, llamamos al modelo de inserción una vez por cada elemento que queremos insertar. Al trabajar con proyectos de inserción de gran tamaño, también puede pasar al modelo una matriz de entradas para insertar en lugar de una entrada a la vez. Al pasar el modelo una matriz de entradas, el número máximo de elementos de entrada por llamada al punto de conexión de inserción es 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

Salida:

Captura de pantalla de los resultados con formato del comando df_bills.

A medida que ejecutemos el bloque de código de búsqueda siguiente, insertaremos la consulta de búsqueda "¿Puedo obtener información sobre la recaudación tributaria de la empresa de cable?" con el mismo modelo text-embedding-ada-002 (versión 2). A continuación, veremos la inserción de facturas más cercana al texto recién insertado de nuestra consulta clasificada por similitud de coseno.

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)

Salida:

Captura de pantalla de los resultados con formato de res una vez que se ha ejecutado la consulta de búsqueda.

Por último, mostraremos el resultado superior de la búsqueda de documentos en función de la consulta del usuario en toda la knowledge base. Esto devuelve el resultado superior del "Derecho del contribuyente a ver la ley de 1993". Este documento tiene una puntuación de similitud de coseno de 0,76 entre la consulta y el documento:

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."

Requisitos previos

  • Una suscripción a Azure: cree una cuenta gratuita.

  • Acceso concedido a Azure OpenAl en la suscripción de Azure que quiera.

    Actualmente, solo la aplicación concede acceso a este servicio. Para solicitar acceso a Azure OpenAI, rellene el formulario en https://aka.ms/oai/access. Si tiene algún problema, abra una incidencia en este repositorio para ponerse en contacto con nosotros.

  • Un recurso de Azure OpenAI con el modelo text-embedding-ada-002 (versión 2) implementado.

    Este modelo solo está disponible actualmente en determinadas regiones. Si no cuenta con un recurso o modelo, el proceso de crear uno está documentado en nuestra guía de implementación de recursos.

  • PowerShell 7.4

Nota:

Muchos ejemplos de este tutorial reutilizan variables de un paso a otro. Mantenga abierta la misma sesión de terminal durante todo el proceso. Si las variables establecidas en un paso anterior se pierden debido al cierre del terminal, debe comenzar de nuevo desde el principio.

Recuperación de la clave y el punto de conexión

Para realizar correctamente una llamada en Azure OpenAI, necesita un punto de conexión y una clave.

Nombre de la variable Valor
ENDPOINT Este valor se puede encontrar en la sección Claves y punto de conexión al examinar el recurso en Azure Portal. Como alternativa, puede encontrar el valor en Azure OpenAI Studio>Playground>Code View. Punto de conexión de ejemplo: https://docs-test-001.openai.azure.com/.
API-KEY Este valor se puede encontrar en la sección Claves y punto de conexión al examinar el recurso en Azure Portal. Puede usar KEY1 o KEY2.

Vaya al recurso en Azure Portal. La sección Claves y puntos de conexión se puede encontrar en la sección Administración de recursos. Copie el punto de conexión y la clave de acceso, ya que los necesitará para autenticar las llamadas API. Puede usar KEY1 o KEY2. Tener siempre dos claves permite rotar y regenerar las claves de forma segura sin provocar una interrupción del servicio.

Captura de pantalla de la interfaz de usuario de información general de un recurso de Azure OpenAI en Azure Portal con el punto de conexión y la ubicación de las claves de acceso en círculo en rojo.

Cree y asigne variables de entorno persistentes para la clave y el punto de conexión.

Variables de entorno

setx AZURE_OPENAI_API_KEY "REPLACE_WITH_YOUR_KEY_VALUE_HERE" 
setx AZURE_OPENAI_ENDPOINT "REPLACE_WITH_YOUR_ENDPOINT_HERE" 

En este tutorial, se usa la documentación de referencia de PowerShell 7.4 como un conjunto de datos de ejemplo conocido y seguro. Como alternativa, puede optar por explorar los conjuntos de datos de ejemplo de las herramientas de Microsoft Research.

Cree una carpeta donde quiera almacenar el proyecto. Establezca la ubicación en la carpeta del proyecto. Descargue el conjunto de datos en la máquina local mediante el comando Invoke-WebRequest y, a continuación, expanda el archivo. Por último, establezca la ubicación en la subcarpeta que contiene información de referencia para PowerShell versión 7.4.

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/'

Estamos trabajando con una gran cantidad de datos en este tutorial, por lo que usamos un objeto de tabla de datos de .NET para lograr un rendimiento eficaz. La tabla de datos tiene las columnas title, content, prep, uri, filey vectors. La columna title es la clave principal.

En el paso siguiente, cargamos el contenido de cada archivo Markdown en la tabla de datos. También se usa el operador -match de PowerShell para capturar líneas conocidas de texto title: y online version:, y almacenarlas en columnas distintas. Algunos de los archivos no contienen las líneas de texto de metadatos, pero como son páginas de información general y no documentos de referencia detallados, los excluimos de la tabla de datos.

# 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)
    }
}

Consulte los datos con el comando out-gridview (no disponible en Cloud Shell).

$Datatable | out-gridview

Salida:

Captura de pantalla de los resultados iniciales de DataTable.

A continuación, realice algunas limpiezas de datos ligeras quitando caracteres adicionales, espacios vacíos y otras notaciones de documentos, para preparar los datos para la tokenización. La función de ejemplo Invoke-DocPrep muestra cómo usar el operador -replace de PowerShell para recorrer en iteración una lista de caracteres que le gustaría quitar del contenido.

# 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
}

Después de crear la función Invoke-DocPrep, use el comando ForEach-Object para almacenar el contenido preparado en la columna prep, para todas las filas de la tabla de datos. Estamos utilizando una nueva columna para que el formato original esté disponible si queremos recuperarlo más adelante.

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

Vuelva a consultar la tabla de datos para ver el cambio.

$Datatable | out-gridview

Cuando pasamos los documentos al modelo de inserciones, codifica los documentos en tokens y, a continuación, devuelve una serie de números de punto flotante que se van a usar en una búsqueda de similitud coseno. Estas inserciones se pueden almacenar localmente o en un servicio como el vector de búsqueda de Búsqueda de Azure AI. Cada documento tiene su propio vector de inserción correspondiente en la nueva columna vectors.

En el ejemplo siguiente se recorre en bucle cada fila de la tabla de datos, se recuperan los vectores del contenido preprocesado y se almacenan en la columna vectors. El servicio OpenAI limita las solicitudes frecuentes, por lo que en el ejemplo se incluye un retroceso exponencial, como sugiere la documentación.

Una vez completado el script, cada fila debe tener una lista delimitada por comas de 1536 vectores para cada documento. Si se produce un error y el código de estado es 400, la ruta de acceso del archivo, el título y el código de error se agregan a una variable denominada $errorDocs para la solución de problemas. El error más común se produce cuando el recuento de tokens es mayor que el límite de solicitudes del modelo.

# 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."
}

Ahora tiene una tabla de base de datos local en memoria de los documentos de referencia de PowerShell 7.4.

Basándonos en una cadena de búsqueda, necesitamos calcular otro conjunto de vectores para que PowerShell pueda clasificar cada documento por similitud.

En el ejemplo siguiente, los vectores se recuperan para la cadena de búsqueda 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 ','

Por último, la siguiente función de ejemplo, que toma un ejemplo del script de ejemplo Measure-VectorSimilarity escrito por Lee Holmes, realiza un cálculo de similitud coseno y, a continuación, clasifica cada fila en la tabla de datos.

# 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)
}

Los comandos del ejemplo siguiente recorren en bucle todas las filas de $Datatable y calculan la similitud coseno con la cadena de búsqueda. Los resultados se ordenan y los tres primeros resultados se almacenan en una variable denominada $topThree. El ejemplo no devuelve la salida.

# 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 }
}

Revise la salida de la variable $topThree, con solo las propiedades title y url, en la vista de cuadrícula.

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

Salida:

Captura de pantalla de los resultados con formato una vez finalizada la consulta de búsqueda.

La variable $topThree contiene toda la información de las filas de la tabla de datos. Por ejemplo, la propiedad content contiene el formato de documento original. Use [0] para indexar en el primer elemento de la matriz.

$topThree[0].content

Consulte el documento completo (truncado en el fragmento de código de salida de esta página).

---
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

Por último, en lugar de volver a generar las inserciones cada vez que necesite consultar el conjunto de datos, puede almacenar los datos en el disco y recuperarlos en el futuro. Los métodos WriteXML() y ReadXML() de los tipos de objeto DataTable del ejemplo siguiente simplifican el proceso. El esquema del archivo XML requiere que la tabla de datos tenga TableName.

Reemplace <YOUR-FULL-FILE-PATH> por la ruta de acceso completa donde desea escribir y leer el archivo XML. La ruta de acceso debe terminar con .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>")

A medida que reutilice los datos, debe obtener los vectores de cada nueva cadena de búsqueda (pero no toda la tabla de datos). Como ejercicio de aprendizaje, intente crear un script de PowerShell para automatizar el comando Invoke-RestMethod con la cadena de búsqueda como parámetro.

Con este enfoque, puede usar inserciones como un mecanismo de búsqueda entre los documentos de una knowledge base. A continuación, el usuario puede tomar el resultado de la búsqueda superior y usarlo para su tarea de bajada, que le pedirá su consulta inicial.

Limpieza de recursos

Si ha creado un recurso de Azure OpenAI únicamente para completar este tutorial y quiere limpiar y quitar un recurso de Azure OpenAI, deberá eliminar los modelos implementados y a después, eliminar el recurso o el grupo de recursos asociado si está dedicado al recurso de prueba. Al eliminar el grupo de recursos, también se elimina cualquier otro recurso que esté asociado a él.

Pasos siguientes

Más información sobre los modelos de Azure OpenAI: