Note
This tutorial uses the Semantic Kernel C# library. For a tutorial that uses the Python library see Get Started with the Semantic Kernel Python Integration.
You can integrate Atlas Vector Search with Microsoft Semantic Kernel to build AI applications and implement retrieval-augmented generation (RAG). This tutorial demonstrates how to start using Atlas Vector Search with Semantic Kernel to perform semantic search on your data and build a RAG implementation. Specifically, you perform the following actions:
Set up the environment.
Store custom data on Atlas.
Create an Atlas Vector Search index on your data.
Run a semantic search query on your data.
Implement RAG by using Atlas Vector Search to answer questions on your data.
Background
Semantic Kernel is an open-source SDK that allows you to combine various AI services and plugins with your applications. You can use Semantic Kernel for a variety of AI use cases, including RAG.
By integrating Atlas Vector Search with Semantic Kernel, you can use Atlas as a vector database and use Atlas Vector Search to implement RAG by retrieving semantically similar documents from your data. To learn more about RAG, see Retrieval-Augmented Generation (RAG) with Atlas Vector Search.
Prerequisites
To complete this tutorial, you must have the following:
An Atlas account with a cluster running MongoDB version 6.0.11, 7.0.2, or later (including RCs). Ensure that your IP address is included in your Atlas project's access list. To learn more, see Create a Cluster.
An OpenAI API Key. You must have an OpenAI account with credits available for API requests. To learn more about registering an OpenAI account, see the OpenAI API website.
A terminal and code editor to run your .NET application.
C#/.NET installed.
Set Up the Environment
You must first set up the environment for this tutorial. To set up your environment, complete the following steps.
Install dependencies.
In your terminal, run the following commands to install the packages for this tutorial.
dotnet add package Microsoft.SemanticKernel dotnet add package Microsoft.SemanticKernel.Connectors.MongoDB --prerelease dotnet add package Microsoft.SemanticKernel.Connectors.OpenAI dotnet add package Microsoft.Extensions.AI dotnet add package Microsoft.Extensions.AI.OpenAI dotnet add package Microsoft.Extensions.AI.Abstractions dotnet add package Microsoft.Extensions.VectorData.Abstractions dotnet add package SemanticKernelPooling.Connectors.OpenAI
Define environment variables.
In your terminal, run the following commands to add your Atlas cluster's SRV connection string and OpenAI API Key to your environment.
export OPENAI_API_KEY="<Your OpenAI API Key>" export ATLAS_CONNECTION_STRING="<Your MongoDB Atlas SRV Connection String>"
Note
Your connection string should use the following format:
mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net
Store Custom Data in Atlas
In this section, you initialize the kernel, which is the main interface used to manage your application's services and plugins. Through the kernel, you configure your AI services, instantiate Atlas as a vector database (also called a memory store), and load custom data into your Atlas cluster.
Copy and paste the following code into your application's Program.cs
file.
This code performs the following actions:
Imports Semantic Kernel and all the required packages.
Connects to your Atlas cluster by retrieving your SRV connection string from the environment.
Retrieves your OpenAI API key from the environment and creates an instance of OpenAI's
text-embedding-ada-002
embedding model.Instantiates Atlas as a memory store and specifies the following parameters:
semantic_kernel_db.records
as the collection to store the documents.vector_index
as the index to use for querying the memory store.
Populates the
semantic_kernel_db.records
collection with sample documents by calling theCreateCollectionFromListAsync
method.Defines a variable
recordCollection
containing thesemantic_kernel_db.records
collection.Creates two helper methods to help store and retrieve text in memory:
CreateRecord
: A factory to create a newDataModel
object.CreateCollectionFromListAsync
: A method to take string entries, generate embeddings for the strings, create corresponding records, and then upsert those records into a collection in your Atlas cluster.
Creates a
DataModel
class that defines the structure of documents stored in the MongoDB collection.
using Microsoft.Extensions.AI; using Microsoft.Extensions.VectorData; using Microsoft.SemanticKernel.Connectors.MongoDB; using Microsoft.SemanticKernel.Data; using MongoDB.Bson; using MongoDB.Driver; using OpenAI; static class Program { static async Task Main(string[] args) { // Get connection string and OpenAI API Key var connectionString = Environment.GetEnvironmentVariable("ATLAS_CONNECTION_STRING"); if (connectionString == null) { Console.WriteLine("You must set your 'ATLAS_CONNECTION_STRING' environment variable."); Environment.Exit(0); } var openAIKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY"); if (openAIKey == null) { Console.WriteLine("You must set your 'OPENAPI_KEY' environment variable."); Environment.Exit(0); } // Create new OpenAI API Embedding Model var embeddingGenerator = new OpenAIClient(openAIKey) .GetEmbeddingClient("text-embedding-ada-002") .AsIEmbeddingGenerator(); // Instantiate Atlas as a vector store var mongoClient = new MongoClient(connectionString); var options = new MongoVectorStoreOptions { EmbeddingGenerator = embeddingGenerator }; var vectorStore = new MongoVectorStore(mongoClient.GetDatabase("semantic_kernel_db"), options); // Sample data string[] lines = [ "I am a developer", "I started using MongoDB two years ago", "I'm using MongoDB Vector Search with Semantic Kernel to implement RAG", "I like coffee" ]; // Populate database with sample data await CreateCollectionFromListAsync<string, DataModel>(vectorStore, "records", lines, embeddingGenerator, CreateRecord); // Get the specific collection from the vector store var recordCollection = vectorStore.GetCollection<string, DataModel>("records"); } static DataModel CreateRecord(string text, ReadOnlyMemory<float> embedding) => new() { Key = ObjectId.GenerateNewId().ToString(), Text = text, Embedding = embedding }; static async Task CreateCollectionFromListAsync<TKey, TRecord>( this VectorStore vectorStore, string collectionName, string[] entries, IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator, Func<string, ReadOnlyMemory<float>, TRecord> createRecord) where TKey : notnull where TRecord : class { // Get and create collection if it doesn't exist var collection = vectorStore.GetCollection<TKey, TRecord>(collectionName); await collection.EnsureCollectionExistsAsync().ConfigureAwait(false); // Create records and generate embeddings for them var embeddings = await embeddingGenerator.GenerateAsync(entries); var records = entries.Zip(embeddings, (entry, embedding) => createRecord(entry, embedding.Vector)); // Add them to the database await collection.UpsertAsync(records).ConfigureAwait(false); } internal sealed class DataModel { [ ] [ ] public required String Key { get; init; } [ ] [ ] public required string Text { get; init; } [ ] public ReadOnlyMemory<float> Embedding { get; init; } } }
Save the file, then run the following command to load your data into Atlas:
dotnet run
Tip
After running the sample code, you can
view your vector embeddings and index in the Atlas UI
by navigating to the semantic_kernel_db.test
collection in your cluster.
Run Vector Search Queries
Once you've created your vector embeddings, you can run vector search queries on your data.
At the end of the Program
class in your Program.cs
file, add the following code to perform a basic semantic
search for the string What is my job title?
. It prints the most
relevant document.
1 // Create a text search instance using the InMemory vector store. 2 var textSearch = new VectorStoreTextSearch<DataModel>(recordCollection, embeddingGenerator); 3 4 // Search and return results as TextSearchResult items 5 var query = "What is my job title?"; 6 KernelSearchResults<TextSearchResult> textResults = await textSearch.GetTextSearchResultsAsync(query, new() { Top = 2, Skip = 0 }); 7 await foreach (TextSearchResult result in textResults.Results) 8 { 9 Console.WriteLine($"Answer: {result.Value}"); 10 } 11 Console.WriteLine("Search completed.");
Save the file, then run the following command to see the results of the semantic search:
dotnet run
Answer: I am a developer Search completed.
Answer Questions on Your Data
This section shows an example RAG implementation
with Atlas Vector Search and Semantic Kernel. Now that you've used Atlas Vector Search
to retrieve semantically similar documents, paste the following code example
at the end of the Program
class in your Program.cs
to prompt the LLM
to answer questions based on those documents.
This code performs the following actions:
Creates a new kernel using OpenAI's
gpt-4o
as the chat model to generate responses.Creates a new text search instance using the vector store.
Defines a question to ask the chat model and initializes the variable
retrievedContext
to hold context from the vector store.Performs a semantic search in the
recordCollection
for the questionWhen did I start using MongoDB?
and returns the most relevant search result.Builds a prompt template that instructs the AI model to answer the question based only on the retrieved context.
Creates a function named
ragFunction
from the chat prompt using the kernel'sCreateFunctionFromPrompt
function.Prepares arguments for the RAG prompt by creating a new object to hold the question and context.
Calls the kernel's
InvokeAsync
function to generate a response from the chat model using the following parameters:The
ragFunction
that configures the prompt template.The
ragArguments
that contains the question and context.
Prints the question and generated response.
// Create a kernel with OpenAI chat completion IKernelBuilder kernelBuilder = Kernel.CreateBuilder(); kernelBuilder.AddOpenAIChatCompletion( modelId: "gpt-4o", apiKey: openAIKey); Kernel kernel = kernelBuilder.Build(); // Create a text search instance using the vector store collection. var textSearch = new VectorStoreTextSearch<DataModel>(recordCollection, embeddingGenerator); // --- Modified RAG Section --- var userQuestion = "When did I start using MongoDB?"; string retrievedContext = "No relevant context found."; // Default // 1. Perform search to get context var searchResults = await textSearch.GetTextSearchResultsAsync(userQuestion, new() { Top = 1 }); // Get most relevant result await foreach (var result in searchResults.Results) { if (result.Value != null) { retrievedContext = result.Value; // Use the text from the search result as context break; // Take the most relevant result } } // 2. Define a prompt template that uses the retrieved context const string ragPromptTemplate = @" Context: {{$context}} Question: {{$question}} Based *only* on the context provided, answer the question. Answer: "; // 3. Create a function from the RAG prompt template var ragFunction = kernel.CreateFunctionFromPrompt(ragPromptTemplate); // 4. Prepare arguments for the RAG prompt var ragArguments = new KernelArguments { ["question"] = userQuestion, ["context"] = retrievedContext }; // 5. Invoke the RAG prompt var ragResult = await kernel.InvokeAsync(ragFunction, ragArguments); Console.WriteLine($"Question: {userQuestion}"); Console.WriteLine($"Retrieved Context: {retrievedContext}"); Console.WriteLine($"Answer: {ragResult.GetValue<string>()}"); // --- End of Modified RAG Section ---
Save the file, then run the following command to generate a response:
dotnet run
Question: When did I start using MongoDB? Retrieved Context: I started using MongoDB two years ago Answer: Two years ago.
Tip
You can add your own data and replace the following part of the code to generate responses on a different question:
var userQuestion = "When did I start using MongoDB?"
Next Steps
MongoDB also provides the following developer resources: