Bagikan melalui


Menerapkan Azure OpenAI dengan RAG menggunakan pencarian vektor di aplikasi .NET

Tutorial ini mengeksplorasi integrasi pola RAG menggunakan model Open AI dan kemampuan pencarian vektor di aplikasi .NET. Aplikasi sampel melakukan pencarian vektor pada data kustom yang disimpan di Azure Cosmos DB untuk MongoDB dan lebih menyempurnakan respons menggunakan model AI generatif, seperti GPT-35 dan GPT-4. Di bagian berikut, Anda akan menyiapkan aplikasi sampel dan menjelajahi contoh kode kunci yang menunjukkan konsep ini.

Prasyarat

Gambaran umum aplikasi

Aplikasi Cosmos Recipe Guide memungkinkan Anda melakukan pencarian yang didorong vektor dan AI terhadap sekumpulan data resep. Anda dapat mencari resep yang tersedia secara langsung atau meminta aplikasi dengan nama bahan untuk menemukan resep terkait. Aplikasi dan bagian di depan memandu Anda melalui alur kerja berikut untuk menunjukkan jenis fungsionalitas ini:

  1. Unggah data sampel ke database Azure Cosmos DB for MongoDB.

  2. Buat penyematan dan indeks vektor untuk data sampel yang diunggah menggunakan model Azure OpenAI text-embedding-ada-002 .

  3. Lakukan pencarian kesamaan vektor berdasarkan perintah pengguna.

  4. Gunakan model penyelesaian Azure OpenAI gpt-35-turbo untuk menyusun jawaban yang lebih bermakna berdasarkan data hasil pencarian.

    Cuplikan layar memperlihatkan aplikasi sampel yang sedang berjalan.

Get started

  1. Kloning repositori GitHub berikut:

    git clone https://github.com/microsoft/AzureDataRetrievalAugmentedGenerationSamples.git
    
  2. Di folder C#/CosmosDB-MongoDBvCore , buka file CosmosRecipeGuide.sln .

  3. Dalam file appsettings.json , ganti nilai konfigurasi berikut dengan nilai Azure OpenAI dan Azure CosmosDB untuk MongoDb Anda:

    "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. Luncurkan aplikasi dengan menekan tombol Mulai di bagian atas Visual Studio.

Menjelajahi aplikasi

Saat Anda menjalankan aplikasi untuk pertama kalinya, aplikasi terhubung ke Azure Cosmos DB dan melaporkan bahwa belum ada resep yang tersedia. Ikuti langkah-langkah yang ditampilkan oleh aplikasi untuk memulai alur kerja inti.

  1. Pilih Unggah resep ke Cosmos DB dan tekan Enter. Perintah ini membaca sampel file JSON dari proyek lokal dan mengunggahnya ke akun Cosmos DB.

    Kode dari kelas Utility.cs mengurai file JSON lokal.

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

    Metode UpsertVectorAsync dalam file VCoreMongoService.cs mengunggah dokumen ke Azure Cosmos DB untuk MongoDB.

    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. Pilih Vektorisasi resep dan simpan di Cosmos DB.

    Item JSON yang diunggah ke Cosmos DB tidak berisi penyematan dan oleh karena itu tidak dioptimalkan untuk RAG melalui pencarian vektor. Penyematan adalah representasi numerik yang padat informasi dari arti semantik dari sepotong teks. Pencarian vektor dapat menemukan item dengan penyematan serupa secara kontekstual.

    Metode GetEmbeddingsAsync dalam file OpenAIService.cs membuat penyematan untuk setiap item dalam database.

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

    dalam CreateVectorIndexIfNotExists file VCoreMongoService.cs membuat indeks vektor, yang memungkinkan Anda melakukan pencarian kesamaan vektor.

    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. Pilih opsi Minta Asisten AI (cari resep berdasarkan nama atau deskripsi, atau ajukan pertanyaan) di aplikasi untuk menjalankan kueri pengguna.

    Kueri pengguna dikonversi ke penyematan menggunakan layanan Open AI dan model penyematan. Penyematan kemudian dikirim ke Azure Cosmos DB untuk MongoDB dan digunakan untuk melakukan pencarian vektor. Metode VectorSearchAsync dalam file VCoreMongoService.cs melakukan pencarian vektor untuk menemukan vektor yang dekat dengan vektor yang disediakan dan mengembalikan daftar dokumen dari Azure Cosmos DB untuk MongoDB vCore.

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

    Metode ini GetChatCompletionAsync menghasilkan respons penyelesaian obrolan yang ditingkatkan berdasarkan permintaan pengguna dan hasil pencarian vektor terkait.

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

    Aplikasi ini juga menggunakan rekayasa cepat untuk memastikan batas layanan dari Open AI dan memformat respons sesuai dengan resep yang disediakan.

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