Megosztás:


Az Azure OpenAI implementálása RAG használatával vektoros kereséssel egy .NET-alkalmazásban

Ez az oktatóanyag bemutatja a RAG-minta integrációját open AI-modellek és vektorkeresési képességek használatával egy .NET-alkalmazásban. A mintaalkalmazás vektorkereséseket végez a MongoDB-hez készült Azure Cosmos DB-ben tárolt egyéni adatokon, és tovább finomítja a válaszokat generatív AI-modellek, például a GPT-35 és a GPT-4 használatával. Az alábbi szakaszokban beállít egy mintaalkalmazást, és megismeri az ezeket a fogalmakat szemléltető kulcskód-példákat.

Előfeltételek

Alkalmazás áttekintése

A Cosmos Receptútmutató alkalmazás lehetővé teszi vektoros és AI-alapú keresések elvégzését receptadatok halmaza alapján. Közvetlenül megkeresheti a rendelkezésre álló recepteket, vagy kérheti, hogy az alkalmazás összetevőnevekkel keresse meg a kapcsolódó recepteket. Az alkalmazás és az előtte álló szakaszok végigvezetik az alábbi munkafolyamaton az ilyen típusú funkciók bemutatásához:

  1. Mintaadatok feltöltése egy MongoDB-adatbázishoz készült Azure Cosmos DB-adatbázisba.

  2. Beágyazásokat és vektorindexet hozhat létre a feltöltött mintaadatokhoz az Azure OpenAI-modell text-embedding-ada-002 használatával.

  3. A felhasználói kérések alapján végezzen vektoros hasonlósági keresést.

  4. Az Azure OpenAI-kiegészítési gpt-35-turbo modell használatával értelmesebb válaszokat írhat a keresési eredmények adatai alapján.

    Képernyőkép a futó mintaalkalmazásról.

Első lépések

  1. Klónozza a következő GitHub-adattárat:

    git clone https://github.com/microsoft/AzureDataRetrievalAugmentedGenerationSamples.git
    
  2. A C#/CosmosDB-MongoDBvCore mappában nyissa meg a CosmosRecipeGuide.sln fájlt.

  3. A appsettings.json fájlban cserélje le a következő konfigurációs értékeket az Azure OpenAI és az Azure CosmosDB for MongoDb értékekre:

    "OpenAIEndpoint": "https://<your-service-name>.openai.azure.com/",
    "OpenAIKey": "<your-API-key>",
    "OpenAIEmbeddingDeployment": "<your-ADA-deployment-name>",
    "OpenAIcompletionsDeployment": "<your-GPT-deployment-name>",
    "MongoVcoreConnection": "<your-Mongo-connection-string>"
    
  4. Indítsa el az alkalmazást a Visual Studio tetején található Start gombra kattintva.

Az alkalmazás felfedezése

Amikor első alkalommal futtatja az alkalmazást, az csatlakozik az Azure Cosmos DB-hez, és arról számol be, hogy még nem állnak rendelkezésre receptek. Az alapvető munkafolyamat elindításához kövesse az alkalmazás által megjelenített lépéseket.

  1. Válassza a Recept feltöltése a Cosmos DB-be lehetőséget, és nyomja le az Enter billentyűt. Ez a parancs beolvassa a minta JSON-fájlokat a helyi projektből, és feltölti őket a Cosmos DB-fiókba.

    A Utility.cs osztály kódja elemzi a helyi JSON-fájlokat.

    public static List<Recipe> ParseDocuments(string Folderpath)
    {
        List<Recipe> recipes = new List<Recipe>();
    
        Directory.GetFiles(Folderpath)
            .ToList()
            .ForEach(f =>
            {
                var jsonString= System.IO.File.ReadAllText(f);
                Recipe recipe = JsonConvert.DeserializeObject<Recipe>(jsonString);
                recipe.id = recipe.name.ToLower().Replace(" ", "");
                recipes.Add(recipe);
            }
        );
    
        return recipes;
    }
    

    A UpsertVectorAsyncVCoreMongoService.cs fájl metódusa feltölti a dokumentumokat a MongoDB-hez készült Azure Cosmos DB-be.

    public async Task UpsertVectorAsync(Recipe recipe)
        {
            BsonDocument document = recipe.ToBsonDocument();
    
            if (!document.Contains("_id"))
            {
                Console.WriteLine("UpsertVectorAsync: Document does not contain _id.");
                throw new ArgumentException("UpsertVectorAsync: Document does not contain _id.");
            }
    
            string? _idValue = document["_id"].ToString();
    
            try
            {
                var filter = Builders<BsonDocument>.Filter.Eq("_id", _idValue);
                var options = new ReplaceOptions { IsUpsert = true };
                await _recipeCollection.ReplaceOneAsync(filter, document, options);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Exception: UpsertVectorAsync(): {ex.Message}");
                throw;
            }
        }
    
  2. Válassza a Receptek vektorizálása lehetőséget, és tárolja a Cosmos DB-ben.

    A Cosmos DB-be feltöltött JSON-elemek nem tartalmaznak beágyazásokat, ezért nem optimalizálhatók RAG-ra vektorkereséssel. A beágyazás egy szöveg szemantikai jelentésének információdús, numerikus ábrázolása. A vektorkeresések környezetfüggően hasonló beágyazású elemeket találnak.

    A GetEmbeddingsAsyncOpenAIService.cs fájlban lévő metódus beágyazást hoz létre az adatbázis minden eleméhez.

    public async Task<float[]?> GetEmbeddingsAsync(dynamic data)
    {
        try
        {
            EmbeddingsOptions options = new EmbeddingsOptions(data)
            {
                Input = data
            };
    
            var response = await _openAIClient.GetEmbeddingsAsync(openAIEmbeddingDeployment, options);
    
            Embeddings embeddings = response.Value;
            float[] embedding = embeddings.Data[0].Embedding.ToArray();
    
            return embedding;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"GetEmbeddingsAsync Exception: {ex.Message}");
            return null;
        }
    }
    

    A CreateVectorIndexIfNotExistsVCoreMongoService.cs fájlban létrehoz egy vektorindexet, amely lehetővé teszi a vektorok hasonlósági keresését.

    public void CreateVectorIndexIfNotExists(string vectorIndexName)
    {
        try
        {
            //Find if vector index exists in vectors collection
            using (IAsyncCursor<BsonDocument> indexCursor = _recipeCollection.Indexes.List())
            {
                bool vectorIndexExists = indexCursor.ToList().Any(x => x["name"] == vectorIndexName);
                if (!vectorIndexExists)
                {
                    BsonDocumentCommand<BsonDocument> command = new BsonDocumentCommand<BsonDocument>(
                        BsonDocument.Parse(@"
                            { createIndexes: 'Recipe',
                                indexes: [{
                                name: 'vectorSearchIndex',
                                key: { embedding: 'cosmosSearch' },
                                cosmosSearchOptions: {
                                    kind: 'vector-ivf',
                                    numLists: 5,
                                    similarity: 'COS',
                                    dimensions: 1536 }
                                }]
                            }"));
    
                    BsonDocument result = _database.RunCommand(command);
                    if (result["ok"] != 1)
                    {
                        Console.WriteLine("CreateIndex failed with response: " + result.ToJson());
                    }
                }
            }
        }
        catch (MongoException ex)
        {
            Console.WriteLine("MongoDbService InitializeVectorIndex: " + ex.Message);
            throw;
        }
    }
    
  3. Felhasználói lekérdezés futtatásához válassza az Ask AI Assistant (recept keresése név vagy leírás alapján, vagy kérdés feltevése) lehetőséget az alkalmazásban.

    A felhasználói lekérdezés beágyazássá alakul az Open AI szolgáltatás és a beágyazási modell használatával. A beágyazást ezután a rendszer elküldi a MongoDB-hez készült Azure Cosmos DB-nek, és vektorkeresés végrehajtására szolgál. A VectorSearchAsyncVCoreMongoService.cs fájl metódusa vektorkeresést végez a megadott vektorhoz közeli vektorok kereséséhez, és visszaadja a MongoDB virtuális maghoz készült Azure Cosmos DB-ből származó dokumentumok listáját.

    public async Task<List<Recipe>> VectorSearchAsync(float[] queryVector)
        {
            List<string> retDocs = new List<string>();
            string resultDocuments = string.Empty;
    
            try
            {
                //Search Azure Cosmos DB for MongoDB vCore collection for similar embeddings
                //Project the fields that are needed
                BsonDocument[] pipeline = new BsonDocument[]
                {
                    BsonDocument.Parse(
                        @$"{{$search: {{
                                cosmosSearch:
                                    {{ vector: [{string.Join(',', queryVector)}],
                                       path: 'embedding',
                                       k: {_maxVectorSearchResults}}},
                                       returnStoredSource:true
                                    }}
                                }}"),
                    BsonDocument.Parse($"{{$project: {{embedding: 0}}}}"),
                };
    
                var bsonDocuments = await _recipeCollection
                    .Aggregate<BsonDocument>(pipeline).ToListAsync();
    
                var recipes = bsonDocuments
                    .ToList()
                    .ConvertAll(bsonDocument =>
                        BsonSerializer.Deserialize<Recipe>(bsonDocument));
                return recipes;
            }
            catch (MongoException ex)
            {
                Console.WriteLine($"Exception: VectorSearchAsync(): {ex.Message}");
                throw;
            }
        }
    

    A GetChatCompletionAsync metódus továbbfejlesztett csevegés-befejezési választ hoz létre a felhasználói kérés és a kapcsolódó vektorkeresési eredmények alapján.

    public async Task<(string response, int promptTokens, int responseTokens)> GetChatCompletionAsync(string userPrompt, string documents)
    {
        try
        {
            ChatMessage systemMessage = new ChatMessage(
                ChatRole.System, _systemPromptRecipeAssistant + documents);
            ChatMessage userMessage = new ChatMessage(
                ChatRole.User, userPrompt);
    
            ChatCompletionsOptions options = new()
            {
                Messages =
                {
                    systemMessage,
                    userMessage
                },
                MaxTokens = openAIMaxTokens,
                Temperature = 0.5f, //0.3f,
                NucleusSamplingFactor = 0.95f,
                FrequencyPenalty = 0,
                PresencePenalty = 0
            };
    
            Azure.Response<ChatCompletions> completionsResponse =
                await openAIClient.GetChatCompletionsAsync(openAICompletionDeployment, options);
            ChatCompletions completions = completionsResponse.Value;
    
            return (
                response: completions.Choices[0].Message.Content,
                promptTokens: completions.Usage.PromptTokens,
                responseTokens: completions.Usage.CompletionTokens
            );
    
        }
        catch (Exception ex)
        {
            string message = $"OpenAIService.GetChatCompletionAsync(): {ex.Message}";
            Console.WriteLine(message);
            throw;
        }
    }
    

    Az alkalmazás emellett parancssori fejlesztéssel biztosítja az Open AI szolgáltatás korlátait, és formázni fogja a megadott receptekre adott választ.

    //System prompts to send with user prompts to instruct the model for chat session
    private readonly string _systemPromptRecipeAssistant = @"
        You are an intelligent assistant for Contoso Recipes.
        You are designed to provide helpful answers to user questions about
        recipes, cooking instructions provided in JSON format below.
    
        Instructions:
        - Only answer questions related to the recipe provided below.
        - Don't reference any recipe not provided below.
        - If you're unsure of an answer, say ""I don't know"" and recommend users search themselves.
        - Your response  should be complete.
        - List the Name of the Recipe at the start of your response followed by step by step cooking instructions.
        - Assume the user is not an expert in cooking.
        - Format the content so that it can be printed to the Command Line console.
        - In case there is more than one recipe you find, let the user pick the most appropriate recipe.";