Semantic Kernel
Semantic Kernel
Semantic Kernel is a lightweight, open-source development kit that lets you easily build
AI agents and integrate the latest AI models into your C#, Python, or Java codebase. It
serves as an efficient middleware that enables rapid delivery of enterprise-grade
solutions.
Enterprise ready
Microsoft and other Fortune 500 companies are already leveraging Semantic Kernel
because it’s flexible, modular, and observable. Backed with security enhancing
capabilities like telemetry support, and hooks and filters so you’ll feel confident you’re
delivering responsible AI solutions at scale.
Version 1.0+ support across C#, Python, and Java means it’s reliable, committed to non
breaking changes. Any existing chat-based APIs are easily expanded to support
additional modalities like voice and video.
Semantic Kernel was designed to be future proof, easily connecting your code to the
latest AI models evolving with the technology as it advances. When new models are
released, you’ll simply swap them out without needing to rewrite your entire codebase.
Automating business processes
Semantic Kernel combines prompts with existing APIs to perform actions. By describing
your existing code to AI models, they’ll be called to address requests. When a request is
made the model calls a function, and Semantic Kernel is the middleware translating the
model's request to a function call and passes the results back to the model.
In just a few steps, you can build your first AI agent with Semantic Kernel in either
Python, .NET, or Java. This guide will show you how to...
Bash
For the full list of Nuget packages, please refer to the supported languages article.
Bash
Bash
// Import packages
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
// Enable planning
OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new()
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};
The following back-and-forth chat should be similar to what you see in the console. The
function calls have been added below to demonstrate how the AI leverages the plugin
behind the scenes.
ノ Expand table
Role Message
🟢 Tool off
🟢 Tool on
If you're interested in understanding more about the code above, we'll break it down in
the next section.
Import packages
Add AI services
Enterprise components ::: zone-end
Build the kernel
Add memory (skipped)
Add plugins
Create kernel arguments (skipped)
Create prompts (skipped)
Planning
Invoke
1) Import packages
For this sample, we first started by importing the following packages:
C#
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
2) Add AI services
Afterwards, we add the most important part of a kernel: the AI services that you want to
use. In this example, we added an Azure OpenAI chat completion service to the kernel
builder.
7 Note
In this example, we used Azure OpenAI, but you can use any other chat completion
service. To see the full list of supported services, refer to the supported languages
article. If you need help creating a different service, refer to the AI services article.
There, you'll find guidance on how to use OpenAI or Azure OpenAI models as
services.
C#
// Create kernel
var builder = Kernel.CreateBuilder()
builder.AddAzureOpenAIChatCompletion(modelId, endpoint, apiKey);
C#
builder.Services.AddLogging(services =>
services.AddConsole().SetMinimumLevel(LogLevel.Trace));
C#
6) Add plugins
With plugins, can give your AI agent the ability to run your code to retrieve information
from external sources or to perform actions. In the above example, we added a plugin
that allows the AI agent to interact with a light bulb. Below, we'll show you how to
create this plugin.
In this example, we've created a plugin that can manipulate a light bulb. While this is a
simple example, this plugin quickly demonstrates how you can support both...
1. Retrieval Augmented Generation (RAG) by providing the AI agent with the state of
the light bulb
2. And task automation by allowing the AI agent to turn the light bulb on or off.
In your own code, you can create a plugin that interacts with any external service or API
to achieve similar results.
C#
using System.ComponentModel;
using System.Text.Json.Serialization;
using Microsoft.SemanticKernel;
[KernelFunction("get_lights")]
[Description("Gets a list of lights and their current state")]
[return: Description("An array of lights")]
public async Task<List<LightModel>> GetLightsAsync()
{
return lights;
}
[KernelFunction("change_state")]
[Description("Changes the state of the light")]
[return: Description("The updated state of the light; will return null if
the light does not exist")]
public async Task<LightModel?> ChangeStateAsync(int id, bool isOn)
{
var light = lights.FirstOrDefault(light => light.Id == id);
if (light == null)
{
return null;
}
return light;
}
}
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("is_on")]
public bool? IsOn { get; set; }
}
C#
9) Planning
Semantic Kernel leverages function calling–a native feature of most LLMs–to provide
planning. With function calling, LLMs can request (or call) a particular function to satisfy
a user's request. Semantic Kernel then marshals the request to the appropriate function
in your codebase and returns the results back to the LLM so the AI agent can generate a
final response.
To enable automatic function calling, we first need to create the appropriate execution
settings so that Semantic Kernel knows to automatically invoke the functions in the
kernel when the AI agent requests them.
C#
10) Invoke
Finally, we invoke the AI agent with the plugin. The sample code demonstrates how to
generate a non-streaming response, but you can also generate a streaming response by
using the GetStreamingChatMessageContentAsync method.
C#
Bash
dotnet run
Next steps
In this guide, you learned how to quickly get started with Semantic Kernel by building a
simple AI agent that can interact with an AI service and run your code. To see more
examples and learn how to build more complex AI agents, check out our in-depth
samples.
Deep dive into Semantic Kernel
Article • 10/03/2024
If you want to dive into deeper into Semantic Kernel and learn how to use more
advanced functionality not explicitly covered in our Learn documentation, we
recommend that you check out our concepts samples that individually demonstrate how
to use specific features within the SDK.
Each of the SDKs (Python, C#, and Java) have their own set of samples that walk through
the SDK. Each sample is modelled as a test case within our main repo, so you're always
guaranteed that the sample will work with the latest nightly version of the SDK! Below
are most of the samples you'll find in our concepts project.
" C#
" Python
" Java
While the overall architecture of the kernel is consistent across all languages, we made
sure the SDK for each language follows common paradigms and styles in each language
to make it feel native and easy to use.
C# packages
In C#, there are several packages to help ensure that you only need to import the
functionality that you need for your project. The following table shows the available
packages in C#.
ノ Expand table
There are other packages available (e.g., the memory connectors), but they are still
experimental and are not yet recommended for production use.
To install any of these packages, you can use the following command:
Bash
Python packages
In Python, there's a single package that includes everything you need to get started with
Semantic Kernel. To install the package, you can use the following command:
Bash
On PyPI under Provides-Extra the additional extras you can install are also listed and
when used that will install the packages needed for using SK with that specific connector
or service, you can install those with the square bracket syntax for instance:
Bash
Java packages
For Java, Semantic Kernel has the following packages; all are under the group Id
com.microsoft.semantic-kernel , and can be imported from maven.
XML
<dependency>
<groupId>com.microsoft.semantic-kernel</groupId>
<artifactId>semantickernel-api</artifactId>
</dependency>
A BOM is provided that can be used to define the versions of all Semantic Kernel
packages.
XML
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.microsoft.semantic-kernel</groupId>
<artifactId>semantickernel-bom</artifactId>
<version>${semantickernel.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
Below is an example POM XML for a simple project that uses OpenAI.
XML
<project>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.microsoft.semantic-kernel</groupId>
<artifactId>semantickernel-bom</artifactId>
<version>${semantickernel.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.microsoft.semantic-kernel</groupId>
<artifactId>semantickernel-api</artifactId>
</dependency>
<dependency>
<groupId>com.microsoft.semantic-kernel</groupId>
<artifactId>semantickernel-connectors-ai-openai</artifactId>
</dependency>
</dependencies>
</project>
Core capabilities
ノ Expand table
Native functions ✅ ✅ ✅
and plugins
Automatic function ✅ ✅ ✅
calling
Open Telemetry ✅ 🔄 ❌
logs
ノ Expand table
Handlebars ✅ ✅ ✅
Liquid ✅ ❌ ❌
Jinja2 ❌ ✅ ❌
ノ Expand table
YAML ✅ ✅ ✅
Prompty ❌ ✅ ❌
AI Services Modalities
ノ Expand table
AI Service Connectors
ノ Expand table
OpenAI ✅ ✅ ✅
Azure OpenAI ✅ ✅ ✅
Hugging Face Inference API 🔄 ❌ ❌ Coming soon to Python, not all scenarios
are covered for .NET
) Important
All of the existing memory connectors are currently experimental and will be
replaced by Vector Store connectors. These will provide more functionality via an
updated abstraction layer.
ノ Expand table
Memory Connectors C# Python Java Notes
Azure AI Search ✅ ✅ ✅
Chroma ✅ ✅ ❌
DuckDB ✅ ❌ ❌
Milvus 🔄 ✅ ❌
Pinecone ✅ ✅ ❌
Postgres ✅ ✅ ❌
Qdrant ✅ 🔄 ❌
Redis ✅ 🔄 ❌
Sqlite ✅ ❌ 🔄
Weaviate ✅ ✅ ❌
) Important
All of the existing Vector Store connectors are currently experimental and are
undergoing active development to improve the experience of using them. To
provide feedback on the latest proposal, please refer to the active Search and
Memory Connector ADRs.
For the list of out of the box vector store connectors and the language support for each,
refer to out of the box connectors.
Understanding the kernel
Article • 07/25/2024
The kernel is the central component of Semantic Kernel. At its simplest, the kernel is a
Dependency Injection container that manages all of the services and plugins necessary
to run your AI application. If you provide all of your services and plugins to the kernel,
they will then be seamlessly used by the AI as needed.
This is extremely powerful, because it means you as a developer have a single place
where you can configure, and most importantly monitor, your AI agents. Take for
example, when you invoke a prompt from the kernel. When you do so, the kernel will...
Throughout this entire process, you can create events and middleware that are triggered
at each of these steps. This means you can perform actions like logging, provide status
updates to users, and most importantly responsible AI. All from a single place.
Build a kernel with services and plugins
Before building a kernel, you should first understand the two types of components that
exist:
ノ Expand table
Components Description
1 Services These consist of both AI services (e.g., chat completion) and other services
(e.g., logging and HTTP clients) that are necessary to run your application. This
was modelled after the Service Provider pattern in .NET so that we could
support dependency ingestion across all languages.
2 Plugins These are the components that are used by your AI services and prompt
templates to perform work. AI services, for example, can use plugins to
retrieve data from a database or call an external API to perform actions.
To start creating a kernel, import the necessary packages at the top of your file:
C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Plugins.Core;
Next, you can add services and plugins. Below is an example of how you can add an
Azure OpenAI chat completion, a logger, and a time plugin.
C#
// Create a kernel with a logger and Azure OpenAI chat completion service
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(modelId, endpoint, apiKey);
builder.Services.AddLogging(c =>
c.AddDebug().SetMinimumLevel(LogLevel.Trace));
builder.Plugins.AddFromType<TimePlugin>();
Kernel kernel = builder.Build();
C#
using Microsoft.SemanticKernel;
KernelPluginFactory.CreateFromObject(serviceProvider.GetRequiredService<Ligh
tsPlugin>()),
KernelPluginFactory.CreateFromObject(serviceProvider.GetRequiredService<Spea
kerPlugin>())
]
);
// Finally, create the Kernel service with the service provider and plugin
collection
builder.Services.AddTransient((serviceProvider)=> {
KernelPluginCollection pluginCollection =
serviceProvider.GetRequiredService<KernelPluginCollection>();
Tip
For more samples on how to use dependency injection in C#, refer to the concept
samples.
Next steps
Now that you understand the kernel, you can learn about all the different AI services
that you can add to it.
One of the main features of Semantic Kernel is its ability to add different AI services to
the kernel. This allows you to easily swap out different AI services to compare their
performance and to leverage the best model for your needs. In this section, we will
provide sample code for adding different AI services to the kernel.
Within Semantic Kernel, there are interfaces for the most popular AI tasks. In the table
below, you can see the services that are supported by each of the SDKs.
ノ Expand table
Chat completion ✅ ✅ ✅
Text generation ✅ ✅ ✅
Text-to-image (Experimental) ✅ ❌ ❌
Image-to-text (Experimental) ✅ ❌ ❌
Text-to-audio (Experimental) ✅ ❌ ❌
Audio-to-text (Experimental) ✅ ❌ ❌
Tip
In most scenarios, you will only need to add chat completion to your kernel, but to
support multi-modal AI, you can add any of the above services to your kernel.
Next steps
To learn more about each of the services, please refer to the specific articles for each
service type. In each of the articles we provide sample code for adding the service to the
kernel across multiple AI service providers.
With chat completion, you can simulate a back-and-forth conversation with an AI agent.
This is of course useful for creating chat bots, but it can also be used for creating
autonomous agents that can complete business processes, generate code, and more. As
the primary model type provided by OpenAI, Google, Mistral, Facebook, and others,
chat completion is the most common AI service that you will add to your Semantic
Kernel project.
When picking out a chat completion model, you will need to consider the following:
What modalities does the model support (e.g., text, image, audio, etc.)?
Does it support function calling?
How fast does it receive and generate tokens?
How much does each token cost?
) Important
Of all the above questions, the most important is whether the model supports
function calling. If it does not, you will not be able to use the model to call your
existing code. Most of the latest models from OpenAI, Google, Mistral, and Amazon
all support function calling. Support from small language models, however, is still
limited.
Azure OpenAI
Bash
Azure OpenAI
Bash
C#
using Microsoft.SemanticKernel;
Azure OpenAI
C#
using Microsoft.SemanticKernel;
builder.Services.AddAzureOpenAIChatCompletion(
deploymentName: "NAME_OF_YOUR_DEPLOYMENT",
apiKey: "YOUR_API_KEY",
endpoint: "YOUR_AZURE_ENDPOINT",
modelId: "gpt-4", // Optional name of the underlying model if the
deployment name doesn't match the model name
serviceId: "YOUR_SERVICE_ID" // Optional; for targeting specific
services within Semantic Kernel
);
builder.Services.AddTransient((serviceProvider)=> {
return new Kernel(serviceProvider);
});
Azure OpenAI
C#
using Microsoft.SemanticKernel.Connectors.OpenAI;
C#
var chatCompletionService =
kernel.GetRequiredService<IChatCompletionService>();
Non-streaming: You wait for the service to generate an entire response before
returning it to the user.
Streaming: Individual chunks of the response are generated and returned to the
user as they are created.
Below are the two ways you can use a chat completion service to generate responses.
C#
C#
Next steps
Now that you've added chat completion services to your Semantic Kernel project, you
can start creating conversations with your AI agent. To learn more about using a chat
completion service, check out the following articles:
The chat history object is used to maintain a record of messages in a chat session. It is
used to store messages from different authors, such as users, assistants, tools, or the
system. As the primary mechanism for sending and receiving messages, the chat history
object is essential for maintaining context and continuity in a conversation.
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.ChatCompletion;
For example, to inject information about the current user in the chat history without
requiring the user to provide the information or having the LLM waste time asking for it,
you can use the tool role to provide the information directly.
Below is an example of how we're able to provide user allergies to the assistant by
simulating a function call to the User plugin.
Tip
Simulated function calls is particularly helpful for providing details about the
current user(s). Today's LLMs have been trained to be particularly sensitive to user
information. Even if you provide user details in a system message, the LLM may still
choose to ignore it. If you provide it via a user message, or tool message, the LLM
is more likely to use it.
When simulating tool results, you must always provide the id of the function call
that the result corresponds to. This is important for the AI to understand the
context of the result. Some LLMs, like OpenAI, will throw an error if the id is
missing or if the id does not correspond to a function call.
You must still, however, add the final messages to the chat history object. Below is an
example of how you can inspect the chat history object to see the function calls and
results.
using Microsoft.SemanticKernel.ChatCompletion;
ChatHistory chatHistory = [
new() {
Role = AuthorRole.User,
Content = "Please order me a pizza"
}
];
Next steps
Now that you know how to create and manage a chat history object, you can learn more
about function calling in the Function calling topic.
The most powerful feature of chat completion is the ability to call functions from the
model. This allows you to create a chat bot that can interact with your existing code,
making it possible to automate business processes, create code snippets, and more.
With Semantic Kernel, we simplify the process of using function calling by automatically
describing your functions and their parameters to the model and then handling the
back-and-forth communication between the model and your code.
When using function calling, however, it's good to understand what's actually
happening behind the scenes so that you can optimize your code and make the most of
this feature.
ノ Expand table
Step Description
1 Serialize functions All of the available functions (and its input parameters) in the kernel
are serialized using JSON schema.
2 Send the messages The serialized functions (and the current chat history) are sent to the
and functions to the model as part of the input.
model
3 Model processes the The model processes the input and generates a response. The
input response can either be a chat message or a function call
4 Handle the response If the response is a chat message, it is returned to the developer to
print the response to the screen. If the response is a function call,
however, Semantic Kernel extracts the function name and its
parameters.
5 Invoke the function The extracted function name and parameters are used to invoke the
function in the kernel.
6 Return the function The result of the function is then sent back to the model as part of the
result chat history. Steps 2-6 are then repeated until the model sends a
termination signal
The following diagram illustrates the process of function calling:
The following section will use a concrete example to illustrate how function calling
works in practice.
C#
[KernelFunction("add_pizza_to_cart")]
[Description("Add a pizza to the user's cart; returns the new item and
updated cart")]
public async Task<CartDelta> AddPizzaToCart(
PizzaSize size,
List<PizzaToppings> toppings,
int quantity = 1,
string specialInstructions = ""
)
{
Guid cartId = userContext.GetCartId();
return await pizzaService.AddPizzaToCart(
cartId: cartId,
size: size,
toppings: toppings,
quantity: quantity,
specialInstructions: specialInstructions);
}
[KernelFunction("remove_pizza_from_cart")]
public async Task<RemovePizzaResponse> RemovePizzaFromCart(int pizzaId)
{
Guid cartId = userContext.GetCartId();
return await pizzaService.RemovePizzaFromCart(cartId, pizzaId);
}
[KernelFunction("get_pizza_from_cart")]
[Description("Returns the specific details of a pizza in the user's
cart; use this instead of relying on previous messages since the cart may
have changed since then.")]
public async Task<Pizza> GetPizzaFromCart(int pizzaId)
{
Guid cartId = await userContext.GetCartIdAsync();
return await pizzaService.GetPizzaFromCart(cartId, pizzaId);
}
[KernelFunction("get_cart")]
[Description("Returns the user's current cart, including the total price
and items in the cart.")]
public async Task<Cart> GetCart()
{
Guid cartId = await userContext.GetCartIdAsync();
return await pizzaService.GetCart(cartId);
}
[KernelFunction("checkout")]
[Description("Checkouts the user's cart; this function will retrieve the
payment from the user and complete the order.")]
public async Task<CheckoutResponse> Checkout()
{
Guid cartId = await userContext.GetCartIdAsync();
Guid paymentId = await
paymentService.RequestPaymentFromUserAsync(cartId);
You would then add this plugin to the kernel like so:
C#
For the above plugin, the serialized functions would look like this:
JSON
[
{
"type": "function",
"function": {
"name": "OrderPizza-get_pizza_menu",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}
},
{
"type": "function",
"function": {
"name": "OrderPizza-add_pizza_to_cart",
"description": "Add a pizza to the user's cart; returns the new item
and updated cart",
"parameters": {
"type": "object",
"properties": {
"size": {
"type": "string",
"enum": ["Small", "Medium", "Large"]
},
"toppings": {
"type": "array",
"items": {
"type": "string",
"enum": ["Cheese", "Pepperoni", "Mushrooms"]
}
},
"quantity": {
"type": "integer",
"default": 1,
"description": "Quantity of pizzas"
},
"specialInstructions": {
"type": "string",
"default": "",
"description": "Special instructions for the pizza"
}
},
"required": ["size", "toppings"]
}
}
},
{
"type": "function",
"function": {
"name": "OrderPizza-remove_pizza_from_cart",
"parameters": {
"type": "object",
"properties": {
"pizzaId": {
"type": "integer"
}
},
"required": ["pizzaId"]
}
}
},
{
"type": "function",
"function": {
"name": "OrderPizza-get_pizza_from_cart",
"description": "Returns the specific details of a pizza in the user's
cart; use this instead of relying on previous messages since the cart may
have changed since then.",
"parameters": {
"type": "object",
"properties": {
"pizzaId": {
"type": "integer"
}
},
"required": ["pizzaId"]
}
}
},
{
"type": "function",
"function": {
"name": "OrderPizza-get_cart",
"description": "Returns the user's current cart, including the total
price and items in the cart.",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}
},
{
"type": "function",
"function": {
"name": "OrderPizza-checkout",
"description": "Checkouts the user's cart; this function will retrieve
the payment from the user and complete the order.",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}
}
]
There's a few things to note here which can impact both the performance and the
quality of the chat completion:
1. Verbosity of function schema – Serializing functions for the model to use doesn't
come for free. The more verbose the schema, the more tokens the model has to
process, which can slow down the response time and increase costs.
Tip
Keep your functions as simple as possible. In the above example, you'll notice
that not all functions have descriptions where the function name is self-
explanatory. This is intentional to reduce the number of tokens. The
parameters are also kept simple; anything the model shouldn't need to know
(like the cartId or paymentId ) are kept hidden. This information is instead
provided by internal services.
7 Note
The one thing you don't need to worry about is the complexity of the return
types. You'll notice that the return types are not serialized in the schema. This
is because the model doesn't need to know the return type to generate a
response. In the step 6, however, we'll see how overly verbose return types
can impact the quality of the chat completion.
2. Parameter types – With the schema, you can specify the type of each parameter.
This is important for the model to understand the expected input. In the above
example, the size parameter is an enum, and the toppings parameter is an array
of enums. This helps the model generate more accurate responses.
Tip
Avoid, where possible, using string as a parameter type. The model can't
infer the type of string, which can lead to ambiguous responses. Instead, use
enums or other types (e.g., int , float , and complex types) where possible.
3. Required parameters - You can also specify which parameters are required. This is
important for the model to understand which parameters are actually necessary
for the function to work. Later on in step 3, the model will use this information to
provide as minimal information as necessary to call the function.
Tip
Only mark parameters as required if they are actually required. This helps the
model call functions more quickly and accurately.
4. Function descriptions – Function descriptions are optional but can help the model
generate more accurate responses. In particular, descriptions can tell the model
what to expect from the response since the return type is not serialized in the
schema. If the model is using functions improperly, you can also add descriptions
to provide examples and guidance.
For example, in the get_pizza_from_cart function, the description tells the user to
use this function instead of relying on previous messages. This is important
because the cart may have changed since the last message.
Tip
Before adding a description, ask yourself if the model needs this information
to generate a response. If not, consider leaving it out to reduce verbosity. You
can always add descriptions later if the model is struggling to use the function
properly.
5. Plugin name – As you can see in the serialized functions, each function has a name
property. Semantic Kernel uses the plugin name to namespace the functions. This
is important because it allows you to have multiple plugins with functions of the
same name. For example, you may have plugins for multiple search services, each
with their own search function. By namespacing the functions, you can avoid
conflicts and make it easier for the model to understand which function to call.
Knowing this, you should choose a plugin name that is unique and descriptive. In
the above example, the plugin name is OrderPizza . This makes it clear that the
functions are related to ordering pizza.
Tip
In this scenario, we can imagine the user asking the assistant to add a pizza to their cart:
C#
C#
IChatCompletionService chatCompletion =
kernel.GetRequiredService<IChatCompletionService>();
7 Note
C#
Console.WriteLine(response);
chatHistory.AddAssistantMessage(response);
Since the model wants the user to respond next, a termination signal will be sent to
Semantic Kernel to stop automatic function calling until the user responds.
At this point, the user can respond with the size and toppings of the pizza they want to
order:
C#
Now that the model has the necessary information, it can now call the
add_pizza_to_cart function with the user's input. Behind the scenes, it adds a new
C#
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "OrderPizzaPlugin-add_pizza_to_cart",
"arguments": "{\n\"size\": \"Medium\",\n\"toppings\":
[\"Cheese\", \"Pepperoni\"]\n}"
}
}
]
Tip
It's good to remember that every argument you require must be generated by the
model. This means spending tokens to generate the response. Avoid arguments
that require many tokens (like a GUID). For example, notice that we use an int for
the pizzaId . Asking the model to send a one to two digit number is much easier
than asking for a GUID.
) Important
This step is what makes function calling so powerful. Previously, AI app developers
had to create separate processes to extract intent and slot fill functions. With
function calling, the model can decide when to call a function and what information
to provide.
With this information, Semantic Kernel can marshal the inputs into the appropriate types
and pass them to the add_pizza_to_cart function in the OrderPizzaPlugin . In this
example, the arguments originate as a JSON string but are deserialized by Semantic
Kernel into a PizzaSize enum and a List<PizzaToppings> .
7 Note
Marshaling the inputs into the correct types is one of the key benefits of using
Semantic Kernel. Everything from the model comes in as a JSON object, but
Semantic Kernel can automatically deserialize these objects into the correct types
for your functions.
After marshalling the inputs, Semantic Kernel can also add the function call to the chat
history:
C#
chatHistory.Add(
new() {
Role = AuthorRole.Assistant,
Items = [
new FunctionCallContent(
functionName: "add_pizza_to_cart",
pluginName: "OrderPizza",
id: "call_abc123",
arguments: new () { {"size", "Medium"}, {"toppings",
["Cheese", "Pepperoni"]} }
)
]
}
);
Not all functions will succeed, however. If the function fails, Semantic Kernel can handle
the error and provide a default response to the model. This allows the model to
understand what went wrong and generate a response to the user.
Tip
To ensure a model can self-correct, it's important to provide error messages that
clearly communicate what went wrong and how to fix it. This can help the model
retry the function call with the correct information.
7 Note
Behind the scenes, Semantic Kernel adds a new message to the chat history from the
tool role that looks like this:
C#
chatHistory.Add(
new() {
Role = AuthorRole.Tool,
Items = [
new FunctionResultContent(
functionName: "add_pizza_to_cart",
pluginName: "OrderPizza",
id: "0001",
result: "{ \"new_items\": [ { \"id\": 1, \"size\":
\"Medium\", \"toppings\": [\"Cheese\",\"Pepperoni\"] } ] }"
)
]
}
);
Notice that the result is a JSON string that the model then needs to process. As before,
the model will need to spend tokens consuming this information. This is why it's
important to keep the return types as simple as possible. In this case, the return only
includes the new items added to the cart, not the entire cart.
Tip
Be as succinct as possible with your returns. Where possible, only return the
information the model needs or summarize the information using another LLM
prompt before returning it.
For example, if a user wants to order multiple pizzas, the LLM can call the
add_pizza_to_cart function for each pizza at the same time. This can significantly
reduce the number of round trips to the LLM and speed up the ordering process.
Next steps
Now that you understand how function calling works, you can proceed to learn how to
configure various aspects of function calling that better correspond to your specific
scenarios by referring to the function choice behavior article
Function choice behaviors are bits of configuration that allows a developer to configure:
As of today, the function choice behaviors are represented by three static methods of
the FunctionChoiceBehavior class:
Auto: Allows the AI model to decide to choose from zero or more of the provided
function(s) for invocation.
Required: Forces the AI model to choose provided function(s).
None: Instructs the AI model not to choose any function(s).
2 Warning
7 Note
Function Advertising
Function advertising is the process of providing functions to AI models for further
calling and invocation. All three function choice behaviors accept a list of functions to
advertise as a functions parameter. By default, it is null, which means all functions from
plugins registered on the Kernel are provided to the AI model.
C#
using Microsoft.SemanticKernel;
If a list of functions is provided, only those functions are sent to the AI model:
C#
using Microsoft.SemanticKernel;
KernelFunction getWeatherForCity =
kernel.Plugins.GetFunction("WeatherForecastUtils", "GetWeatherForCity");
KernelFunction getCurrentTime = kernel.Plugins.GetFunction("DateTimeUtils",
"GetCurrentUtcDateTime");
An empty list of functions means no functions are provided to the AI model, which is
equivalent to disabling function calling.
C#
using Microsoft.SemanticKernel;
In this example, all the functions from the DateTimeUtils and WeatherForecastUtils
plugins will be provided to the AI model alongside the prompt. The model will first
choose GetCurrentTime function for invocation to obtain the current date and time, as
this information is needed as input for the GetWeatherForCity function. Next, it will
choose GetWeatherForCity function for invocation to get the weather forecast for the
city of Boston using the obtained date and time. With this information, the model will be
able to determine the likely color of the sky in Boston.
C#
using Microsoft.SemanticKernel;
The same example can be easily modeled in a YAML prompt template configuration:
C#
using Microsoft.SemanticKernel;
KernelFunction promptFunction =
KernelFunctionYaml.FromPromptYaml(promptTemplateConfig);
Console.WriteLine(await kernel.InvokeAsync(promptFunction));
7 Note
The behavior advertises functions in the first request to the AI model only and
stops sending them in subsequent requests to prevent an infinite loop where the
model keeps choosing the same functions for invocation repeatedly.
Here, we specify that the AI model must choose the GetWeatherForCity function for
invocation to obtain the weather forecast for the city of Boston, rather than guessing it
based on its own knowledge. The model will first choose the GetWeatherForCity
function for invocation to retrieve the weather forecast. With this information, the model
can then determine the likely color of the sky in Boston using the response from the call
to GetWeatherForCity .
C#
using Microsoft.SemanticKernel;
KernelFunction getWeatherForCity =
kernel.Plugins.GetFunction("WeatherForecastUtils", "GetWeatherForCity");
C#
using Microsoft.SemanticKernel;
KernelFunction promptFunction =
KernelFunctionYaml.FromPromptYaml(promptTemplateConfig);
Console.WriteLine(await kernel.InvokeAsync(promptFunction));
Alternatively, all functions registered in the kernel can be provided to the AI model as
required. However, only the ones chosen by the AI model as a result of the first request
will be invoked by the Semantic Kernel. The functions will not be sent to the AI model in
subsequent requests to prevent an infinite loop, as mentioned above.
C#
using Microsoft.SemanticKernel;
C#
using Microsoft.SemanticKernel;
KernelFunction getWeatherForCity =
kernel.Plugins.GetFunction("WeatherForecastUtils", "GetWeatherForCity");
PromptExecutionSettings settings = new() { FunctionChoiceBehavior =
FunctionChoiceBehavior.None() };
C#
using Microsoft.SemanticKernel;
KernelFunction promptFunction =
KernelFunctionYaml.FromPromptYaml(promptTemplateConfig);
Console.WriteLine(await kernel.InvokeAsync(promptFunction));
ノ Expand table
Function Invocation
Function invocation is the process whereby Sematic Kernel invokes functions chosen by
the AI model. For more details on function invocation see function invocation article.
Supported AI Connectors
As of today, the following AI connectors in Semantic Kernel support the function calling
model:
ノ Expand table
Anthropic Planned ❌
AzureOpenAI ✔️ ✔️
Gemini Planned ✔️
HuggingFace Planned ❌
Mistral Planned ✔️
OpenAI ✔️ ✔️
Function Invocation Modes
Article • 10/23/2024
When the AI model receives a prompt containing a list of functions, it may choose one
or more of them for invocation to complete the prompt. When a function is chosen by
the model, it needs be invoked by Semantic Kernel.
The function calling subsystem in Semantic Kernel has two modes of function
invocation: auto and manual.
Depending on the invocation mode, Semantic Kernel either does end-to-end function
invocation or gives the caller control over the function invocation process.
This example demonstrates how to use the auto function invocation in Semantic Kernel.
AI model decides which functions to call to complete the prompt and Semantic Kernel
does the rest and invokes them automatically.
C#
using Microsoft.SemanticKernel;
Some AI models support parallel function calling, where the model chooses multiple
functions for invocation. This can be useful in cases when invoking chosen functions
takes a long time. For example, the AI may choose to retrieve the latest news and the
current time simultaneously, rather than making a round trip per function.
Sequentially: The functions are invoked one after another. This is the default
behavior.
Concurrently: The functions are invoked at the same time. This can be enabled by
setting the FunctionChoiceBehaviorOptions.AllowConcurrentInvocation property to
true , as shown in the example below.
C#
using Microsoft.SemanticKernel;
// Enable concurrent invocation of functions to get the latest news and the
current time.
FunctionChoiceBehaviorOptions options = new() { AllowConcurrentInvocation =
true };
When manual function invocation is enabled, Semantic Kernel does not automatically
invoke the functions chosen by the AI model. Instead, it returns a list of chosen
functions to the caller, who can then decide which functions to invoke, invoke them
sequentially or in parallel, handle exceptions, and so on. The function invocation results
need to be added to the chat history and returned to the model, which reasons about
them and decides whether to choose additional functions or generate the final
response.
C#
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
IChatCompletionService chatCompletionService =
kernel.GetRequiredService<IChatCompletionService>();
while (true)
{
ChatMessageContent result = await
chatCompletionService.GetChatMessageContentAsync(chatHistory, settings,
kernel);
// Sequentially iterating over each chosen function, invoke it, and add
the result to the chat history.
foreach (FunctionCallContent functionCall in functionCalls)
{
try
{
// Invoking the function
FunctionResultContent resultContent = await
functionCall.InvokeAsync(kernel);
7 Note
Semantic Kernel provides a wide range of AI service integrations to help you build
powerful AI agents. Additionally, Semantic Kernel integrates with other Microsoft
services to provide additional functionality via plugins.
Out-of-the-box integrations
With the available AI connectors, developers can easily build AI agents with swappable
components. This allows you to experiment with different AI services to find the best
combination for your use case.
AI Services
ノ Expand table
Additional plugins
If you want to extend the functionality of your AI agent, you can use plugins to integrate
with other Microsoft services. Here are some of the plugins that are available for
Semantic Kernel:
ノ Expand table
Plugin C# Python Java Description
Logic Apps ✅ ✅ ✅ Build workflows within Logic Apps using its available
connectors and import them as plugins in Semantic
Kernel. Learn more.
Azure Container ✅ ✅ ❌ With dynamic sessions, you can recreate the Code
Apps Dynamic Interpreter experience from the Assistants API by
Sessions effortlessly spinning up Python containers where AI
agents can execute Python code. Learn more.
What are Filters?
Article • 11/05/2024
Filters enhance security by providing control and visibility over how and when functions
run. This is needed to instill responsible AI principles into your work so that you feel
confident your solution is enterprise ready.
For example, filters are leveraged to validate permissions before an approval flow
begins. The IFunctionInvocationFilter is run to check the permissions of the person
that’s looking to submit an approval. This means that only a select group of people will
be able to kick off the process.
A good example of filters is provided here in our detailed Semantic Kernel blog post
on Filters.
Prompt Render Filter - this filter is triggered before the prompt rendering
operation, enabling:
Viewing and modifying the prompt that will be sent to the AI (e.g., for RAG or
PII redaction )
Preventing prompt submission to the AI by overriding the function result (e.g.,
for Semantic Caching )
Auto Function Invocation Filter (experimental) - similar to the function invocation
filter, this filter operates within the scope of automatic function calling ,
providing additional context, including chat history, a list of all functions to be
executed, and iteration counters. It also allows termination of the auto function
calling process (e.g., if a desired result is obtained from the second of three
planned functions).
Each filter includes a context object that contains all relevant information about the
function execution or prompt rendering. Additionally, each filter has a next
delegate/callback to execute the next filter in the pipeline or the function itself, offering
control over function execution (e.g., in cases of malicious prompts or arguments).
Multiple filters of the same type can be registered, each with its own responsibility.
In a filter, calling the next delegate is essential to proceed to the next registered filter or
the original operation (whether function invocation or prompt rendering). Without
calling next , the operation will not be executed.
To use a filter, first define it, then add it to the Kernel object either through dependency
injection or the appropriate Kernel property. When using dependency injection, the
order of filters is not guaranteed, so with multiple filters, the execution order may be
unpredictable.
For cases where filter order is important, it is recommended to add filters directly to the
Kernel object using appropriate properties. This approach allows filters to be added,
C#
/// <summary>
/// Example of function invocation filter to perform logging before and
after function invocation.
/// </summary>
public sealed class LoggingFilter(ILogger logger) :
IFunctionInvocationFilter
{
public async Task OnFunctionInvocationAsync(FunctionInvocationContext
context, Func<FunctionInvocationContext, Task> next)
{
logger.LogInformation("FunctionInvoking - {PluginName}.
{FunctionName}", context.Function.PluginName, context.Function.Name);
await next(context);
logger.LogInformation("FunctionInvoked - {PluginName}.
{FunctionName}", context.Function.PluginName, context.Function.Name);
}
}
C#
builder.Services.AddSingleton<IFunctionInvocationFilter, LoggingFilter>();
C#
kernel.FunctionInvocationFilters.Add(new LoggingFilter(logger));
Code examples
Function invocation filter examples
C#
/// <summary>
/// Example of prompt render filter which overrides rendered prompt before
sending it to AI.
/// </summary>
public class SafePromptFilter : IPromptRenderFilter
{
public async Task OnPromptRenderAsync(PromptRenderContext context,
Func<PromptRenderContext, Task> next)
{
// Example: get function information
var functionName = context.Function.Name;
await next(context);
C#
builder.Services.AddSingleton<IPromptRenderFilter, SafePromptFilter>();
C#
kernel.PromptRenderFilters.Add(new SafePromptFilter());
Code examples
Prompt render filter examples
C#
/// <summary>
/// Example of auto function invocation filter which terminates function
calling process as soon as we have the desired result.
/// </summary>
public sealed class EarlyTerminationFilter : IAutoFunctionInvocationFilter
{
public async Task
OnAutoFunctionInvocationAsync(AutoFunctionInvocationContext context,
Func<AutoFunctionInvocationContext, Task> next)
{
// Call the function first.
await next(context);
C#
builder.Services.AddSingleton<IAutoFunctionInvocationFilter,
EarlyTerminationFilter>();
C#
kernel.AutoFunctionInvocationFilters.Add(new EarlyTerminationFilter());
Code examples
Auto function invocation filter examples
C#
/// <summary>Filter that can be used for both streaming and non-streaming
invocation modes at the same time.</summary>
public sealed class DualModeFilter : IFunctionInvocationFilter
{
public async Task OnFunctionInvocationAsync(FunctionInvocationContext
context, Func<FunctionInvocationContext, Task> next)
{
// Call next filter in pipeline or actual function.
await next(context);
C#
kernel.FunctionInvocationFilters.Add(new MyFilter());
IChatCompletionService chatCompletionService =
kernel.GetRequiredService<IChatCompletionService>();
More examples
PII detection and redaction with filters
Semantic Caching with filters
Content Safety with filters
Text summarization and translation quality check with filters
Observability in Semantic Kernel
Article • 09/24/2024
Observability is typically achieved through logging, metrics, and tracing. They are often
referred to as the three pillars of observability. You will also hear the term "telemetry"
used to describe the data collected by these three pillars. Unlike debugging,
observability provides an ongoing overview of the system's health and performance.
Logging: Semantic Kernel logs meaningful events and errors from the kernel,
kernel plugins and functions, as well as the AI connectors. Logs and events
) Important
Metrics: Semantic Kernel emits metrics from kernel functions and AI connectors.
You will be able to monitor metrics such as the kernel function execution time, the
token consumption of AI connectors, etc. Metrics
Tracing: Semantic Kernel supports distributed tracing. You can track activities
across different services and within Semantic Kernel.
Complete end-to-end transaction of a request
ノ Expand table
Telemetry Description
Log Logs are recorded throughout the Kernel. For more information on Logging in .Net,
please refer to this document. Sensitive data, such as kernel function arguments and
results, are logged at the trace level. Please refer to this table for more information
on log levels.
Activity Each kernel function execution and each call to an AI model are recorded as an
activity. All activities are generated by an activity source named
"Microsoft.SemanticKernel".
Metric Semantic Kernel captures the following metrics from kernel functions:
semantic_kernel.function.invocation.duration (Histogram) - function
execution time (in seconds)
semantic_kernel.function.streaming.duration (Histogram) - function
streaming execution time (in seconds)
semantic_kernel.function.invocation.token_usage.prompt (Histogram) -
number of prompt token usage (only for KernelFunctionFromPrompt )
semantic_kernel.function.invocation.token_usage.completion (Histogram) -
number of completion token usage (only for KernelFunctionFromPrompt )
7 Note
Next steps
Now that you have a basic understanding of observability in Semantic Kernel, you can
learn more about how to output telemetry data to the console or use APM tools to
visualize and analyze telemetry data.
Console
Application Insights
Aspire Dashboard
Inspection of telemetry data with the
console
Article • 09/24/2024
Although the console is not a recommended way to inspect telemetry data, it is a simple
and quick way to get started. This article shows you how to output telemetry data to the
console for inspection with a minimal Kernel setup.
Exporter
Exporters are responsible for sending telemetry data to a destination. Read more about
exporters here . In this example, we use the console exporter to output telemetry data
to the console.
Prerequisites
An Azure OpenAI chat completion deployment.
The latest .Net SDK for your operating system.
Setup
Console
Navigate to the newly created project directory after the command completes.
Console
Console
C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using OpenTelemetry;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
namespace TelemetryConsoleQuickstart
{
class Program
{
static async Task Main(string[] args)
{
// Telemetry setup code goes here
Console.WriteLine(answer);
}
}
}
Add telemetry
If you run the console app now, you should expect to see a sentence explaining why the
sky is blue. To observe the kernel via telemetry, replace the // Telemetry setup code
goes here comment with the following code:
C#
Next, we turn on diagnostics with sensitive data. This is an experimental feature that
allows you to enable diagnostics for the AI services in the Semantic Kernel. With this
turned on, you will see additional telemetry data such as the prompts sent to and the
responses received from the AI models, which are considered sensitive data. If you don't
want to include sensitive data in your telemetry, you can use another switch
Microsoft.SemanticKernel.Experimental.GenAI.EnableOTelDiagnostics to enable
diagnostics with non-sensitive data, such as the model name, the operation name, and
token usage, etc.
Then, we create a tracer provider builder and a meter provider builder. A provider is
responsible for processing telemetry data and piping it to exporters. We subscribe to
the Microsoft.SemanticKernel* source to receive telemetry data from the Semantic
Kernel namespaces. We add a console exporter to both the tracer provider and the
meter provider. The console exporter sends telemetry data to the console.
Finally, we create a logger factory and add OpenTelemetry as a logging provider that
sends log data to the console. We set the minimum log level to Information and include
formatted messages and scopes in the log output. The logger factory is then added to
the builder.
) Important
A provider should be a singleton and should be alive for the entire application
lifetime. The provider should be disposed of when the application is shutting down.
Run
Run the console application with the following command:
Console
dotnet run
Console
LogRecord.Timestamp: 2024-09-12T21:48:35.2295938Z
LogRecord.TraceId: 159d3f07664838f6abdad7af6a892cfa
LogRecord.SpanId: ac79a006da8a6215
LogRecord.TraceFlags: Recorded
LogRecord.CategoryName: Microsoft.SemanticKernel.KernelFunction
LogRecord.Severity: Info
LogRecord.SeverityText: Information
LogRecord.FormattedMessage: Function
InvokePromptAsync_290eb9bece084b00aea46b569174feae invoking.
LogRecord.Body: Function {FunctionName} invoking.
LogRecord.Attributes (Key:Value):
FunctionName: InvokePromptAsync_290eb9bece084b00aea46b569174feae
OriginalFormat (a.k.a Body): Function {FunctionName} invoking.
The log record itself: contains the timestamp and namespace at which the log
record was generated, the severity and body of the log record, and any attributes
associated with the log record.
The resource associated with the log record: contains information about the
service, instance, and SDK used to generate the log record.
Activities
7 Note
Activities in .Net are similar to spans in OpenTelemetry. They are used to represent
a unit of work in the application.
You should see multiple activities in the console output. They look similar to the
following:
Console
Activity.TraceId: 159d3f07664838f6abdad7af6a892cfa
Activity.SpanId: 8c7c79bc1036eab3
Activity.TraceFlags: Recorded
Activity.ParentSpanId: ac79a006da8a6215
Activity.ActivitySourceName: Microsoft.SemanticKernel.Diagnostics
Activity.DisplayName: chat.completions gpt-4o
Activity.Kind: Client
Activity.StartTime: 2024-09-12T21:48:35.5717463Z
Activity.Duration: 00:00:02.3992014
Activity.Tags:
gen_ai.operation.name: chat.completions
gen_ai.system: openai
gen_ai.request.model: gpt-4o
gen_ai.response.prompt_tokens: 16
gen_ai.response.completion_tokens: 29
gen_ai.response.finish_reason: Stop
gen_ai.response.id: chatcmpl-A6lxz14rKuQpQibmiCpzmye6z9rxC
Activity.Events:
gen_ai.content.prompt [9/12/2024 9:48:35 PM +00:00]
gen_ai.prompt: [{"role": "user", "content": "Why is the sky blue in
one sentence?"}]
gen_ai.content.completion [9/12/2024 9:48:37 PM +00:00]
gen_ai.completion: [{"role": "Assistant", "content": "The sky
appears blue because shorter blue wavelengths of sunlight are scattered in
all directions by the gases and particles in the Earth\u0027s atmosphere
more than other colors."}]
Resource associated with Activity:
service.name: TelemetryConsoleQuickstart
service.instance.id: a637dfc9-0e83-4435-9534-fb89902e64f8
telemetry.sdk.name: opentelemetry
telemetry.sdk.language: dotnet
telemetry.sdk.version: 1.9.0
The activity itself: contains the span ID and parent span ID that APM tools use to
build the traces, the duration of the activity, and any tags and events associated
with the activity.
The resource associated with the activity: contains information about the service,
instance, and SDK used to generate the activity.
) Important
The attributes to pay extra attention to are the ones that start with gen_ai . These
are the attributes specified in the GenAI Semantic Conventions .
Metrics
You should see multiple metric records in the console output. They look similar to the
following:
Console
Here you can see the name, the description, the unit, the time range, the type, the value
of the metric, and the meter that the metric belongs to.
7 Note
The above metric is a Counter metric. For a full list of metric types, see here.
Depending on the type of metric, the output may vary.
Next steps
Now that you have successfully output telemetry data to the console, you can learn
more about how to use APM tools to visualize and analyze telemetry data.
Application Insights
Aspire Dashboard
Inspection of telemetry data with
Application Insights
Article • 09/24/2024
In this example, we will learn how to export telemetry data to Application Insights, and
inspect the data in the Application Insights portal.
Exporter
Exporters are responsible for sending telemetry data to a destination. Read more about
exporters here . In this example, we use the Azure Monitor exporter to output
telemetry data to an Application Insights instance.
Prerequisites
An Azure OpenAI chat completion deployment.
An Application Insights instance. Follow the instructions here to create a resource if
you don't have one. Copy the connection string for later use.
The latest .Net SDK for your operating system.
Setup
Console
Navigate to the newly created project directory after the command completes.
Console
Console
C#
using Azure.Monitor.OpenTelemetry.Exporter;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using OpenTelemetry;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
namespace TelemetryApplicationInsightsQuickstart
{
class Program
{
static async Task Main(string[] args)
{
// Telemetry setup code goes here
Console.WriteLine(answer);
}
}
}
Add telemetry
If you run the console app now, you should expect to see a sentence explaining why the
sky is blue. To observe the kernel via telemetry, replace the // Telemetry setup code
goes here comment with the following code:
C#
Please refer to this article for more information on the telemetry setup code. The only
difference here is that we are using AddAzureMonitor[Trace|Metric|Log]Exporter to
export telemetry data to Application Insights.
Run
Run the console application with the following command:
Console
dotnet run
Transaction search
Navigate to the Transaction search tab to view the transactions that have been
recorded.
Hit refresh to see the latest transactions. When results appear, click on one of them to
see more details.
Toggle between the View all and View timeline button to see all traces and
dependencies of the transaction in different views.
) Important
Traces represent traditional log entries and OpenTelemetry span events . They
are not the same as distributed traces. Dependencies represent the calls to (internal
and external) components. Please refer to this article for more information on the
data model in Application Insights.
For this particular example, you should see two dependencies and multiple traces. The
first dependency represents a kernel function that is created from the prompt. The
second dependency represents the call to the Azure OpenAI chat completion model.
When you expand the chat.completion {your-deployment-name} dependency, you
should see the details of the call. A set of gen_ai attributes are attached to the
dependency, which provides additional context about the call.
GenAI Attributes
true , you will also see two traces that carry the sensitive data of the prompt and the
completion result.
Click on them and you will see the prompt and the completion result under the custom
properties section.
Log analytics
Transaction search is not the only way to inspect telemetry data. You can also use Log
analytics to query and analyze the data. Navigate to the Logs under Monitoring to start.
Below are some sample queries you can use for this example:
Kusto
// Retrieves the total number of completion and prompt tokens used for the
model if you run the application multiple times.
dependencies
| where name startswith "chat"
| project model = customDimensions["gen_ai.request.model"], completion_token
= toint(customDimensions["gen_ai.response.completion_tokens"]), prompt_token
= toint(customDimensions["gen_ai.response.prompt_tokens"])
| where model == "gpt-4o"
| project completion_token, prompt_token
| summarize total_completion_tokens = sum(completion_token),
total_prompt_tokens = sum(prompt_token)
Kusto
// Retrieves all the prompts and completions and their corresponding token
usage.
dependencies
| where name startswith "chat"
| project timestamp, operation_Id, name, completion_token =
customDimensions["gen_ai.response.completion_tokens"], prompt_token =
customDimensions["gen_ai.response.prompt_tokens"]
| join traces on operation_Id
| where message startswith "gen_ai"
|project timestamp, messages = customDimensions, token=iff(customDimensions
contains "gen_ai.prompt", prompt_token, completion_token)
Query Result
Next steps
Now that you have successfully output telemetry data to Application Insights, you can
explore more features of Semantic Kernel that can help you monitor and diagnose your
application:
Aspire Dashboard is part of the .NET Aspire offering. The dashboard allows developers
to monitor and inspect their distributed applications.
In this example, we will use the standalone mode and learn how to export telemetry
data to Aspire Dashboard, and inspect the data there.
Exporter
Exporters are responsible for sending telemetry data to a destination. Read more about
exporters here . In this example, we use the OpenTelemetry Protocol (OTLP) exporter
to send telemetry data to Aspire Dashboard.
Prerequisites
An Azure OpenAI chat completion deployment.
Docker
The latest .Net SDK for your operating system.
Setup
Console
Navigate to the newly created project directory after the command completes.
Console
C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using OpenTelemetry;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
namespace TelemetryAspireDashboardQuickstart
{
class Program
{
static async Task Main(string[] args)
{
// Telemetry setup code goes here
Add telemetry
If you run the console app now, you should expect to see a sentence explaining why the
sky is blue. To observe the kernel via telemetry, replace the // Telemetry setup code
goes here comment with the following code:
C#
Please refer to this article for more information on the telemetry setup code. The only
difference here is that we are using AddOtlpExporter to export telemetry data to Aspire
Dashboard.
Run
Run the console application with the following command:
Console
dotnet run
Tip
Traces
If this is your first time running the application after starting the dashboard, you should
see a one trace is the Traces tab. Click on the trace to view more details.
TracesOverview
In the trace details, you can see the span that represents the prompt function and the
span that represents the chat completion model. Click on the chat completion span to
see details about the request and response.
Tip
You can filter the attributes of the spans to find the one you are interested in.
TracesDetails
Logs
Head over to the Structured tab to view the logs emitted by the application. Please
refer to this guide on how to work with structured logs in the dashboard.
Next steps
Now that you have successfully output telemetry data to Aspire Dashboard, you can
explore more features of Semantic Kernel that can help you monitor and diagnose your
application:
7 Note
This article will use Aspire Dashboard for illustration. If you prefer to use other
tools, please refer to the documentation of the tool you are using on setup
instructions.
7 Note
Tip
You will hear the term "tools" and "tool calling" sometimes used interchangeably
with "functions" and "function calling".
Prerequisites
An Azure OpenAI chat completion deployment that supports function calling.
Docker
The latest .Net SDK for your operating system.
Setup
In a terminal, run the following command to create a new console application in C#:
Console
Navigate to the newly created project directory after the command completes.
Console
Console
C#
using System.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using OpenTelemetry;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
namespace TelemetryAutoFunctionCallingQuickstart
{
class BookingPlugin
{
[KernelFunction("FindAvailableRooms")]
[Description("Finds available conference rooms for today.")]
public async Task<List<string>> FindAvailableRoomsAsync()
{
// Simulate a remote call to a booking system.
await Task.Delay(1000);
return ["Room 101", "Room 201", "Room 301"];
}
[KernelFunction("BookRoom")]
[Description("Books a conference room.")]
public async Task<string> BookRoomAsync(string room)
{
// Simulate a remote call to a booking system.
await Task.Delay(1000);
return $"Room {room} booked.";
}
}
class Program
{
static async Task Main(string[] args)
{
// Endpoint to the Aspire Dashboard
var endpoint = "http://localhost:4317";
AppContext.SetSwitch("Microsoft.SemanticKernel.Experimental.GenAI.EnableOTel
DiagnosticsSensitive", true);
Console.WriteLine(answer);
}
}
}
In the code above, we first define a mock conference room booking plugin with two
functions: FindAvailableRoomsAsync and BookRoomAsync . We then create a simple
console application that registers the plugin to the kernel, and ask the kernel to
automatically call the functions when needed.
Run
Run the console application with the following command:
Console
dotnet run
Console
Find the trace for the application in the Traces tab. You should five spans in the trace:
TracesAdvancedScenarioDotNet
These 5 spans represent the internal operations of the kernel with auto function calling
enabled. It first invokes the model, which requests a function call. Then the kernel
automatically executes the function FindAvailableRoomsAsync and returns the result to
the model. The model then requests another function call to make a reservation, and the
kernel automatically executes the function BookRoomAsync and returns the result to the
model. Finally, the model returns a natural language response to the user.
And if you click on the last span, and look for the prompt in the gen_ai.content.prompt
event, you should see something similar to the following:
JSON
[
{ "role": "user", "content": "Reserve a conference room for me today." },
{
"role": "Assistant",
"content": null,
"tool_calls": [
{
"id": "call_NtKi0OgOllJj1StLkOmJU8cP",
"function": { "arguments": {}, "name": "FindAvailableRooms" },
"type": "function"
}
]
},
{
"role": "tool",
"content": "[\u0022Room 101\u0022,\u0022Room 201\u0022,\u0022Room
301\u0022]"
},
{
"role": "Assistant",
"content": null,
"tool_calls": [
{
"id": "call_mjQfnZXLbqp4Wb3F2xySds7q",
"function": { "arguments": { "room": "Room 101" }, "name":
"BookRoom" },
"type": "function"
}
]
},
{ "role": "tool", "content": "Room Room 101 booked." }
]
This is the chat history that gets built up as the model and the kernel interact with each
other. This is sent to the model in the last iteration to get a natural language response.
Error handling
If an error occurs during the execution of a function, the kernel will automatically catch
the error and return an error message to the model. The model can then use this error
message to provide a natural language response to the user.
C#
[KernelFunction("BookRoom")]
[Description("Books a conference room.")]
public async Task<string> BookRoomAsync(string room)
{
// Simulate a remote call to a booking system.
await Task.Delay(1000);
Run the application again and observe the trace in the dashboard. You should see the
span representing the kernel function call with an error:
TracesAdvancedScenarioErrorDotNet
7 Note
It is very likely that the model responses to the error may vary each time you run
the application, because the model is stochastic. You may see the model reserving
all three rooms at the same time, or reserving one the first time then reserving the
other two the second time, etc.
Observability in Semantic Kernel is constantly improving. You can find the latest updates
and new features in the GitHub repository .
What are Semantic Kernel Vector Store
connectors? (Preview)
Article • 10/31/2024
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that require
breaking changes may still occur in limited circumstances before release.
Tip
If you are looking for information about the legacy Memory Store connectors, refer to the
Memory Stores page.
Vector databases have many use cases across different domains and applications that involve
natural language processing (NLP), computer vision (CV), recommendation systems (RS), and
other areas that require semantic understanding and matching of data.
One use case for storing information in a vector database is to enable large language models
(LLMs) to generate more relevant and coherent responses. Large language models often face
challenges such as generating inaccurate or irrelevant information; lacking factual consistency or
common sense; repeating or contradicting themselves; being biased or offensive. To help
overcome these challenges, you can use a vector database to store information about different
topics, keywords, facts, opinions, and/or sources related to your desired domain or genre. The
vector database allows you to efficiently find the subset of information related to a specific
question or topic. You can then pass information from the vector database with your prompt to
your large language model to generate more accurate and relevant content.
For example, if you want to write a blog post about the latest trends in AI, you can use a vector
database to store the latest information about that topic and pass the information along with the
ask to a LLM in order to generate a blog post that leverages the latest information.
Semantic Kernel and .net provides an abstraction for interacting with Vector Stores and a list of
out-of-the-box connectors that implement these abstractions. Features include creating, listing
and deleting collections of records, and uploading, retrieving and deleting records. The
abstraction makes it easy to experiment with a free or locally hosted Vector Store and then switch
to a service when needing to scale up.
Microsoft.Extensions.VectorData.IVectorStoreRecordCollection<TKe
TRecord>
IVectorStoreRecordCollection<TKey, TRecord> represents a collection. This collection may or may
not exist, and the interface provides methods to check if the collection exists, create it or delete it.
The interface also provides methods to upsert, get and delete records. Finally, the interface
inherits from IVectorizedSearch<TRecord> providing vector search capabilities.
Microsoft.Extensions.VectorData.IVectorizedSearch<TRecord>
IVectorizedSearch<TRecord> contains a method for doing vector searches.
it possible to use IVectorizedSearch<TRecord> on its own in cases where only search is needed
and no record or collection management is needed.
IVectorizableTextSearch<TRecord>
IVectorizableTextSearch<TRecord> contains a method for doing vector searches where the vector
database has the ability to generate embeddings automatically. E.g. you can call this method with
a text string and the database will generate the embedding for you and search against a vector
field. This is not supported by all vector databases and is therefore only implemented by select
connectors.
is available in its own nuget package. For a list of known implementations, see the Out-of-the-box
connectors page.
.NET CLI
From version 1.23.0 of Semantic Kernel, the Vector Store abstractions have been removed
from Microsoft.SemanticKernel.Abstractions and are available in the new dedicated
Microsoft.Extensions.VectorData.Abstractions package.
When upgrading from 1.22.0 or earlier to 1.23.0 or later, you will need to add an additional
using Microsoft.Extensions.VectorData; clause in files where any of the Vector Store
This change has been made to support vector store providers when creating their own
implementations. A provider only has to reference the
Microsoft.Extensions.VectorData.Abstractions package. This reduces potential version
conflicts and allows Semantic Kernel to continue to evolve fast without impacting vector store
providers.
C#
using Microsoft.Extensions.VectorData;
[VectorStoreRecordData(IsFilterable = true)]
public string HotelName { get; set; }
[VectorStoreRecordData(IsFullTextSearchable = true)]
public string Description { get; set; }
[VectorStoreRecordVector(Dimensions: 4, DistanceFunction.CosineDistance,
IndexKind.Hnsw)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
[VectorStoreRecordData(IsFilterable = true)]
public string[] Tags { get; set; }
}
Tip
For more information on how to annotate your data model, refer to defining your data
model.
Tip
For an alternative to annotating your data model, refer to defining your schema with a
record definition.
In this example, we'll use Qdrant. You will therefore need to import the Qdrant nuget package.
.NET CLI
Since databases support many different types of keys and records, we allow you to specify the
type of the key and record for your collection using generics. In our case, the type of record will
be the Hotel class we already defined, and the type of key will be ulong , since the HotelId
property is a ulong and Qdrant only supports Guid or ulong keys.
C#
using Microsoft.SemanticKernel.Connectors.Qdrant;
using Qdrant.Client;
// Choose a collection from the database and specify the type of key and record
stored in it via Generic parameters.
var collection = vectorStore.GetCollection<ulong, Hotel>("skhotels");
Tip
For more information on what key and field types each Vector Store connector supports, refer
to the documentation for each connector.
Create the collection and add records
C#
// Upsert a record.
string descriptionText = "A place where everyone can be happy.";
ulong hotelId = 1;
// Create a record and generate a vector for the description using your chosen
embedding generation implementation.
// Just showing a placeholder embedding generation method here for brevity.
await collection.UpsertAsync(new Hotel
{
HotelId = hotelId,
HotelName = "Hotel Happy",
Description = descriptionText,
DescriptionEmbedding = await GenerateEmbeddingAsync(descriptionText),
Tags = new[] { "luxury", "pool" }
});
Tip
Do a vector search
C#
// Generate a vector for your search text, using your chosen embedding generation
implementation.
// Just showing a placeholder method here for brevity.
var searchVector = await GenerateEmbeddingAsync("I'm looking for a hotel where
customer happiness is the priority.");
// Do the search.
var searchResult = await collection.VectorizedSearchAsync(searchVector, new() { Top =
1 }).Results.ToListAsync()
Tip
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Vector Store abstractions in Semantic Kernel are based on three main components:
vector stores, collections and records. Records are contained by collections, and
collections are contained by vector stores.
Tip
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Overview
The Semantic Kernel Vector Store connectors use a model first approach to interacting
with databases.
All methods to upsert or get records use strongly typed model classes. The properties
on these classes are decorated with attributes that indicate the purpose of each
property.
Tip
For an alternative to using attributes, refer to defining your schema with a record
definition.
Tip
For an alternative to defining your own data model, refer to using Vector Store
abstractions without defining your own data model.
C#
using Microsoft.Extensions.VectorData;
[VectorStoreRecordData(IsFilterable = true)]
public string HotelName { get; set; }
[VectorStoreRecordData(IsFullTextSearchable = true)]
public string Description { get; set; }
[VectorStoreRecordVector(4, DistanceFunction.CosineDistance,
IndexKind.Hnsw)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
[VectorStoreRecordData(IsFilterable = true)]
public string[] Tags { get; set; }
}
Attributes
VectorStoreRecordKeyAttribute
Use this attribute to indicate that your property is the key of the record.
C#
[VectorStoreRecordKey]
public ulong HotelId { get; set; }
VectorStoreRecordKeyAttribute parameters
ノ Expand table
Tip
VectorStoreRecordDataAttribute
Use this attribute to indicate that your property contains general data that is not a key
or a vector.
C#
[VectorStoreRecordData(IsFilterable = true)]
public string HotelName { get; set; }
VectorStoreRecordDataAttribute parameters
ノ Expand table
Tip
VectorStoreRecordVectorAttribute
Use this attribute to indicate that your property contains a vector.
C#
[VectorStoreRecordVector(Dimensions: 4, DistanceFunction.CosineDistance,
IndexKind.Hnsw)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
VectorStoreRecordVectorAttribute parameters
ノ Expand table
Parameter Required Description
Dimensions Yes for The number of dimensions that the vector has. This is
collection typically required when creating a vector index for a
create, collection.
optional
otherwise
Common index kinds and distance function types are supplied as static values on the
Microsoft.SemanticKernel.Data.IndexKind and
implementations may also use their own index kinds and distance functions, where the
database supports unusual types.
Tip
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Overview
The Semantic Kernel Vector Store connectors use a model first approach to interacting
with databases and allows annotating data models with information that is needed for
creating indexes or mapping data to the database schema.
Another way of providing this information is via record definitions, that can be defined
and supplied separately to the data model. This can be useful in multiple scenarios:
There may be a case where a developer wants to use the same data model with
more than one configuration.
There may be a case where the developer wants to store data using a very
different schema to the model and wants to supply a custom mapper for
converting between the data model and storage schema.
There may be a case where a developer wants to use a built-in type, like a dict, or a
optimized format like a dataframe and still wants to leverage the vector store
functionality.
C#
using Microsoft.Extensions.VectorData;
When creating a definition you always have to provide a name and type for each
property in your schema, since this is required for index creation and data mapping.
C#
VectorStoreRecordKeyProperty
Use this class to indicate that your property is the key of the record.
C#
ノ Expand table
DataModelPropertyName Yes The name of the property on the data model. Used by
the built in mappers to automatically map between the
storage schema and data model and for creating indexes.
PropertyType Yes The type of the property on the data model. Used by the
built in mappers to automatically map between the
storage schema and data model and for creating indexes.
VectorStoreRecordDataProperty
Use this class to indicate that your property contains general data that is not a key or a
vector.
C#
ノ Expand table
DataModelPropertyName Yes The name of the property on the data model. Used by
the built in mappers to automatically map between the
storage schema and data model and for creating indexes.
PropertyType Yes The type of the property on the data model. Used by the
built in mappers to automatically map between the
storage schema and data model and for creating indexes.
Tip
For more information on which connectors support StoragePropertyName and
what alternatives are available, refer to the documentation for each connector.
VectorStoreRecordVectorProperty
Use this class to indicate that your property contains a vector.
C#
ノ Expand table
DataModelPropertyName Yes The name of the property on the data model. Used
by the built in mappers to automatically map
between the storage schema and data model and
for creating indexes.
PropertyType Yes The type of the property on the data model. Used by
the built in mappers to automatically map between
the storage schema and data model and for creating
indexes.
Dimensions Yes for The number of dimensions that the vector has. This
collection is typically required when creating a vector index for
create, a collection.
optional
otherwise
Tip
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Overview
The Semantic Kernel Vector Store connectors use a model first approach to interacting
with databases. This makes using the connectors easy and simple, since your data model
reflects the schema of your database records and to add any additional schema
information required, you can simply add attributes to your data model properties.
There are cases though where it is not desirable or possible to define your own data
model. E.g. let's say that you do not know at compile time what your database schema
looks like, and the schema is only provided via configuration. Creating a data model that
reflects the schema would be impossible in this case.
All other properties are divided into Data and Vector properties. Any property that is
not a vector or a key is considered a data property. Data and Vector property sets are
stored as string-keyed dictionaries of objects.
A record definition can be used to provide the schema information. Unlike a data model,
a record definition can be created from configuration at runtime, providing a solution
for when schema information is not known at compile time.
Tip
To see how to create a record definition, refer to defining your schema with a
record definition.
Example
To use the generic data model with a connector, simply specify it as your data model
when creating a collection, and simultaneously provide a record definition.
C#
C#
new
AzureAISearchVectorStoreRecordCollection<VectorStoreGenericDataModel<string>
>(
searchIndexClient,
"glossary",
new() { VectorStoreRecordDefinition = vectorStoreRecordDefinition });
Generating embeddings for Semantic
Kernel Vector Store connectors
Article • 10/16/2024
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Semantic Kernel supports generating embeddings using many popular AI services out of
the box.
C#
You can also use helpers to register them with a dependency injection container.
C#
Generating embeddings
To use the ITextEmbeddingGenerationService you created, just call the
GenerateEmbeddingAsync method on it.
C#
C#
Embedding dimensions
Vector databases typically require you to specify the number of dimensions that each
vector has when creating the collection. Different embedding models typically support
generating vectors with different dimension sizes. E.g. Open AI text-embedding-ada-002
generates vectors with 1536 dimensions. Some models also allow a developer to choose
the number of dimensions they want in the output vector, e.g. Google text-embedding-
004 produces vectors with 768 dimension by default, but allows a developer to choose
It is important to ensure that the vectors generated by the embedding model have the
same number of dimensions as the matching vector in the database.
If creating a collection using the Semantic Kernel Vector Store abstractions, you need to
specify the number of dimensions required for each vector property either via
annotations or via the record definition. Here are examples of both setting the number
of dimensions to 1536.
C#
[VectorStoreRecordVector(Dimensions: 1536)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
C#
Tip
For more information on how to annotate your data model, refer to defining your
data model.
Tip
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Semantic Kernel provides vector search capabilities as part of its Vector Store
abstractions. This supports filtering and many other options, which this article will
explain in more detail.
Vector Search
The VectorizedSearchAsync method allows searching using data that has already been
vectorized. This method takes a vector and an optional VectorSearchOptions class as
input. This method is available on the following interfaces:
1. IVectorizedSearch<TRecord>
2. IVectorStoreRecordCollection<TKey, TRecord>
Assuming you have a collection that already contains data, you can easily search it. Here
is an example using Qdrant.
C#
using Microsoft.SemanticKernel.Connectors.Qdrant;
using Microsoft.Extensions.VectorData;
using Qdrant.Client;
// Generate a vector for your search text, using your chosen embedding
generation implementation.
// Just showing a placeholder method here for brevity.
var searchVector = await GenerateEmbeddingAsync("I'm looking for a hotel
where customer happiness is the priority.");
Tip
vectors supported y each data store vary. See the documentation for each connector for
the list of supported vector types.
It is also important for the search vector type to match the target vector that is being
searched, e.g. if you have two vectors on the same record with different vector types,
make sure that the search vector you supply matches the type of the specific vector you
are targeting. See VectorPropertyName for how to pick a target vector if you have more
than one per record.
VectorPropertyName
The VectorPropertyName option can be used to specify the name of the vector property
to target during the search. If none is provided, the first vector found on the data model
or specified in the record definition will be used.
Note that when specifying the VectorPropertyName , use the name of the property as
defined on the data model or in the record definition. Use this property name even if
the property may be stored under a different name in the vector store. The storage
name may e.g. be different because of custom serialization settings.
C#
using Microsoft.Extensions.VectorData;
using Microsoft.Connectors.Memory.InMemory;
// Create the vector search options and indicate that we want to search the
FeatureListEmbedding property.
var vectorSearchOptions = new VectorSearchOptions
{
VectorPropertyName = nameof(Product.FeatureListEmbedding)
};
[VectorStoreRecordData]
public string Description { get; set; }
[VectorStoreRecordData]
public List<string> FeatureList { get; set; }
[VectorStoreRecordVector(1536)]
public ReadOnlyMemory<float> DescriptionEmbedding { get; set; }
[VectorStoreRecordVector(1536)]
public ReadOnlyMemory<float> FeatureListEmbedding { get; set; }
}
C#
// Create the vector search options and indicate that we want to skip the
first 40 results and then get the next 20.
var vectorSearchOptions = new VectorSearchOptions
{
Top = 20,
Skip = 40
};
IncludeVectors
The IncludeVectors option allows you to specify whether you wish to return vectors in
the search results. If false , the vector properties on the returned model will be left null.
Using false can significantly reduce the amount of data retrieved from the vector store
during search, making searches more efficient.
C#
// Create the vector search options and indicate that we want to include
vectors in the search results.
var vectorSearchOptions = new VectorSearchOptions
{
IncludeVectors = true
}
// This snippet assumes searchVector is already provided, having been
created using the embedding model of your choice.
var searchResult = await collection.VectorizedSearchAsync(searchVector,
vectorSearchOptions).Results.ToListAsync()
VectorSearchFilter
The VectorSearchFilter option can be used to provide a filter for filtering the records in
the chosen collection before applying the vector search.
Note that in order for fields to be used for filtering, many vector stores require those
fields to be indexed first. Some vector stores will allow filtering using any field, but may
optionally allow indexing to improve filtering performance.
If creating a collection via the Semantic Kernel vector store abstractions and you wish to
enable filtering on a field, set the IsFilterable property to true when defining your
data model or when creating your record definition.
Tip
To create a filter use the VectorSearchFilter class. You can combine multiple filter
clauses together in one VectorSearchFilter . All filter clauses are combined with and .
Note that when providing a property name when constructing the filter, use the name of
the property as defined on the data model or in the record definition. Use this property
name even if the property may be stored under a different name in the vector store. The
storage name may e.g. be different because of custom serialization settings.
C#
// Create the vector search options and set the filter on the options.
var vectorSearchOptions = new VectorSearchOptions
{
Filter = filter
};
[VectorStoreRecordData]
public string Term { get; set; }
[VectorStoreRecordData]
public string Definition { get; set; }
[VectorStoreRecordVector(1536)]
public ReadOnlyMemory<float> DefinitionEmbedding { get; set; }
}
Coming soon
More info coming soon.
Legacy Semantic Kernel Memory Stores
Article • 10/21/2024
Tip
We recommend using the Vector Store abstractions instead of the legacy Memory
Stores. For more information on how to use the Vector Store abstractions start
here.
Semantic Kernel provides a set of Memory Store abstractions where the primary
interface is Microsoft.SemanticKernel.Memory.IMemoryStore .
ノ Expand table
Supports No Yes
choosing your
preferred
vector search
index and
distance
function
Supports No Yes
multiple
vectors per
record
Supports No Yes
custom
schemas
Supports No Yes
metadata pre-
filtering for
vector search
Supports Yes No
vector search
on non-vector
databases by
downloading
the entire
dataset onto
the client and
doing a local
vector search
ノ Expand table
Service C# Python
Chroma C# Python
DuckDB C#
Milvus C# Python
Pinecone C# Python
Postgres C# Python
Qdrant C#
Redis C#
Sqlite C#
Weaviate C# Python
E.g. to access a collection created by the Azure AI Search Memory Store, you can use the
following Vector Store data model.
C#
using Microsoft.Extensions.VectorData;
class VectorStoreRecord
{
[VectorStoreRecordKey]
public string Id { get; set; }
[VectorStoreRecordData]
public string Description { get; set; }
[VectorStoreRecordData]
public string Text { get; set; }
[VectorStoreRecordData]
public bool IsReference { get; set; }
[VectorStoreRecordData]
public string ExternalSourceName { get; set; }
[VectorStoreRecordData]
public string AdditionalMetadata { get; set; }
[VectorStoreRecordVector(VectorSize)]
public ReadOnlyMemory<float> Embedding { get; set; }
}
Tip
For more detailed examples on how to use the Vector Store abstractions to access
collections created using a Memory Store, see here .
E.g. The Redis Memory store uses a schema with three fields:
string metadata
long timestamp
float[] embedding
All data other than the embedding or timestamp is stored as a serialized json string in
the Metadata field. This means that it is not possible to index the individual values and
filter on them. E.g. perhaps you may want to filter using the ExternalSourceName, but
this is not possible while it is inside a json string.
In this case, it may be better to migrate the data to a new collection with a flat schema.
There are two options here. You could create a new collection from your source data or
simply map and copy the data from the old to the new. The first option may be more
costly as you will need to regenerate the embeddings from the source data.
Tip
For an example using Redis showing how to copy data from a collection created
using the Memory Store abstractions to one created using the Vector Store
abstractions see here .
Semantic Kernel Vector Store code
samples (Preview)
Article • 11/05/2024
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
For a vector search sample demonstrating the same concept see the following samples.
Each of these samples are referencing the same common code, and just differ on the
type of vector store they create to use with the common code.
2 Warning
Not all vector databases support Skip functionality natively for vector searches, so
some connectors may have to fetch Skip + Top records and skip on the client side
to simulate this behavior.
Tip
For more information about using the generic data model, refer to using Vector
Store abstractions without defining your own data model.
1. Creating a data model that matches the storage schema that the Langchain
implemenation used.
2. Using a custom mapper to map between the storage schema and data model.
3. Using a record definition with special storage property names for fields.
In the following sample, we show how to use these approaches to construct Langchain
compatible Vector Store implementations.
For each vector store, there is a factory class that shows how to contruct the Langchain
compatible Vector Store. See e.g.
AzureAISearchFactory
PineconeFactory
QdrantFactory
RedisFactory
In this sample, we also demonstrate a technique for having a single unified data model
across different Vector Stores, where each Vector Store supports different key types and
may require different storage schemas.
We use a decorator class MappingVectorStoreRecordCollection that allows converting
data models and key types. E.g. Qdrant only supports Guid and ulong key types, and
Langchain uses the Guid key type when creating a collection. Azure AI Search, Pinecone
and Redis all support string keys. In the sample, we use the
MappingVectorStoreRecordCollection to expose the Qdrant Vector Store with a string
key containing a guid instead of the key being a Guid type. This allows us to easily use
all databases with one data model . Note that supplying string keys that do not
contain guids to the decorated Qdrant Vector Store will not work, since the underlying
database still requires Guid keys.
Out-of-the-box Vector Store connectors
(Preview)
Article • 10/29/2024
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
) Important
Some connectors are using SDKs that are not officially supported by Microsoft or
by the Database provider. The Officially supported SDK column lists which are using
officially supported SDKs and which are not.
ノ Expand table
Azure AI Search ✅ ✅ In ✅
Development
Cosmos DB ✅ In In ✅
MongoDB Development Development
Cosmos DB No ✅ In In ✅
SQL Development Development
In-Memory ✅ In In N/A
Development Development
MongoDB ✅ In In ✅
Development Development
Qdrant ✅ ✅ In ✅
Development
Redis ✅ ✅ In ✅
Development
SQLite ✅ In In ✅
Development Development
Weaviate ✅ In In N/A
Development Development
Using the Azure AI Search Vector Store
connector (Preview)
Article • 10/31/2024
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Overview
The Azure AI Search Vector Store connector can be used to access and manage data in
Azure AI Search. The connector has the following characteristics.
ノ Expand table
IsFullTextSearchable Yes
supported?
Limitations
Notable Azure AI Search connector functionality limitations.
ノ Expand table
Configuring full text search analyzers during Use the Azure AI Search Client SDK directly
collection creation is not supported. for collection creation
Getting started
Add the Azure AI Search Vector Store connector NuGet package to your project.
.NET CLI
You can add the vector store to the dependency injection container available on the
KernelBuilder or to the IServiceCollection dependency injection container using
C#
using Azure;
using Microsoft.SemanticKernel;
C#
using Azure;
using Microsoft.SemanticKernel;
Extension methods that take no parameters are also provided. These require an instance
of the Azure AI Search SearchIndexClient to be separately registered with the
dependency injection container.
C#
using Azure;
using Azure.Search.Documents.Indexes;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
C#
using Azure;
using Azure.Search.Documents.Indexes;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using Azure;
using Azure.Search.Documents.Indexes;
using Microsoft.SemanticKernel.Connectors.AzureAISearch;
C#
using Azure;
using Azure.Search.Documents.Indexes;
using Microsoft.SemanticKernel.Connectors.AzureAISearch;
Data mapping
The default mapper used by the Azure AI Search connector when mapping data from
the data model to storage is the one provided by the Azure AI Search SDK.
This mapper does a direct conversion of the list of properties on the data model to the
fields in Azure AI Search and uses System.Text.Json.JsonSerializer to convert to the
storage schema. This means that usage of the JsonPropertyNameAttribute is supported
if a different storage name to the data model property name is required.
C#
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Overview
The Azure CosmosDB MongoDB Vector Store connector can be used to access and
manage data in Azure CosmosDB MongoDB. The connector has the following
characteristics.
ノ Expand table
EuclideanDistance
IsFullTextSearchable supported? No
StoragePropertyName supported? No, use BsonElementAttribute instead. See here for more
info.
Getting started
Add the Azure CosmosDB MongoDB Vector Store connector NuGet package to your
project.
.NET CLI
You can add the vector store to the dependency injection container available on the
KernelBuilder or to the IServiceCollection dependency injection container using
C#
using Microsoft.SemanticKernel;
C#
using Microsoft.SemanticKernel;
C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using MongoDB.Driver;
C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using MongoDB.Driver;
You can construct an Azure CosmosDB MongoDB Vector Store instance directly.
C#
using Microsoft.SemanticKernel.Connectors.AzureCosmosDBMongoDB;
using MongoDB.Driver;
using Microsoft.SemanticKernel.Connectors.AzureCosmosDBMongoDB;
using MongoDB.Driver;
Data mapping
The Azure CosmosDB MognoDB Vector Store connector provides a default mapper
when mapping data from the data model to storage.
This mapper does a direct conversion of the list of properties on the data model to the
fields in Azure CosmosDB MongoDB and uses MongoDB.Bson.Serialization to convert to
the storage schema. This means that usage of the
MongoDB.Bson.Serialization.Attributes.BsonElement is supported if a different storage
name to the data model property name is required. The only exception is the key of the
record which is mapped to a database field named _id , since all CosmosDB MongoDB
records must use this name for ids.
The property name override is done by setting the BsonElement attribute on the data
model properties.
C#
using Microsoft.Extensions.VectorData;
[BsonElement("hotel_name")]
[VectorStoreRecordData(IsFilterable = true)]
public string HotelName { get; set; }
[BsonElement("hotel_description")]
[VectorStoreRecordData(IsFullTextSearchable = true)]
public string Description { get; set; }
[BsonElement("hotel_description_embedding")]
[VectorStoreRecordVector(4, DistanceFunction.CosineDistance,
IndexKind.Hnsw)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
}
Using the Azure CosmosDB NoSQL
Vector Store connector (Preview)
Article • 11/05/2024
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Overview
The Azure CosmosDB NoSQL Vector Store connector can be used to access and manage
data in Azure CosmosDB NoSQL. The connector has the following characteristics.
ノ Expand table
IsFullTextSearchable Yes
supported?
Limitations
When initializing CosmosClient manually, it is necessary to specify
CosmosClientOptions.UseSystemTextJsonSerializerWithOptions due to limitations in the
C#
Getting started
Add the Azure CosmosDB NoSQL Vector Store connector NuGet package to your
project.
.NET CLI
You can add the vector store to the dependency injection container available on the
KernelBuilder or to the IServiceCollection dependency injection container using
extension methods provided by Semantic Kernel.
C#
using Microsoft.SemanticKernel;
C#
using Microsoft.SemanticKernel;
Extension methods that take no parameters are also provided. These require an instance
of Microsoft.Azure.Cosmos.Database to be separately registered with the dependency
injection container.
C#
using Microsoft.Azure.Cosmos;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
return cosmosClient.GetDatabase(databaseName);
});
kernelBuilder.AddAzureCosmosDBNoSQLVectorStore();
C#
using Microsoft.Azure.Cosmos;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
return cosmosClient.GetDatabase(databaseName);
});
builder.Services.AddAzureCosmosDBNoSQLVectorStore();
You can construct an Azure CosmosDB NoSQL Vector Store instance directly.
C#
using Microsoft.Azure.Cosmos;
using Microsoft.SemanticKernel.Connectors.AzureCosmosDBNoSQL;
C#
using Microsoft.Azure.Cosmos;
using Microsoft.SemanticKernel.Connectors.AzureCosmosDBNoSQL;
var cosmosClient = new CosmosClient(connectionString, new
CosmosClientOptions()
{
// When initializing CosmosClient manually, setting this property is
required
// due to limitations in default serializer.
UseSystemTextJsonSerializerWithOptions = JsonSerializerOptions.Default,
});
Data mapping
The Azure CosmosDB NoSQL Vector Store connector provides a default mapper when
mapping from the data model to storage.
This mapper does a direct conversion of the list of properties on the data model to the
fields in Azure CosmosDB NoSQL and uses System.Text.Json.JsonSerializer to convert
to the storage schema. This means that usage of the JsonPropertyNameAttribute is
supported if a different storage name to the data model property name is required. The
only exception is the key of the record which is mapped to a database field named id ,
since all CosmosDB NoSQL records must use this name for ids.
C#
using System.Text.Json;
using Microsoft.Azure.Cosmos;
using Microsoft.SemanticKernel.Connectors.AzureCosmosDBNoSQL;
C#
using System.Text.Json.Serialization;
using Microsoft.Extensions.VectorData;
[VectorStoreRecordData(IsFilterable = true)]
public string HotelName { get; set; }
[VectorStoreRecordData(IsFullTextSearchable = true)]
public string Description { get; set; }
[JsonPropertyName("HOTEL_DESCRIPTION_EMBEDDING")]
[VectorStoreRecordVector(4, DistanceFunction.EuclideanDistance,
IndexKind.QuantizedFlat)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
}
JSON
{
"id": 1,
"HOTEL_NAME": "Hotel Happy",
"DESCRIPTION": "A place where everyone can be happy.",
"HOTEL_DESCRIPTION_EMBEDDING": [0.9, 0.1, 0.1, 0.1],
}
If the partition key property is not set (and the default key property is used), string
keys can be used for operations with database records. However, if a partition key
property is specified, it is recommended to use AzureCosmosDBNoSQLCompositeKey to
provide both the key and partition key values.
C#
C#
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Overview
The Elasticsearch Vector Store connector can be used to access and manage data in
Elasticsearch. The connector has the following characteristics.
ノ Expand table
Supported data property All types that are supported by System.Text.Json (etiher built-in
types or by using a custom converter)
IsFullTextSearchable Yes
supported?
Getting started
To run Elasticsearch locally for local development or testing run the start-local script
with one command:
Bash
Add the Elasticsearch Vector Store connector NuGet package to your project.
.NET CLI
You can add the vector store to the dependency injection container available on the
KernelBuilder or to the IServiceCollection dependency injection container using
C#
using Microsoft.SemanticKernel;
using Elastic.Clients.Elasticsearch;
C#
using Microsoft.SemanticKernel;
using Elastic.Clients.Elasticsearch;
Extension methods that take no parameters are also provided. These require an instance
of the Elastic.Clients.Elasticsearch.ElasticsearchClient class to be separately
registered with the dependency injection container.
C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using Elastic.Clients.Elasticsearch;
C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using Elastic.Clients.Elasticsearch;
C#
using Elastic.SemanticKernel.Connectors.Elasticsearch;
using Elastic.Clients.Elasticsearch;
C#
using Elastic.SemanticKernel.Connectors.Elasticsearch;
using Elastic.Clients.Elasticsearch;
Data mapping
The Elasticsearch connector will use System.Text.Json.JsonSerializer to do mapping.
Since Elasticsearch stores documents with a separate key/id and value, the mapper will
serialize all properties except for the key to a JSON object and use that as the value.
C#
using Elastic.SemanticKernel.Connectors.Elasticsearch;
using Elastic.Clients.Elasticsearch;
using Elastic.Clients.Elasticsearch.Serialization;
using Elastic.Transport;
C#
using Elastic.SemanticKernel.Connectors.Elasticsearch;
using Elastic.Clients.Elasticsearch;
Since a naming policy of snake case upper was chosen, here is an example of how this
data type will be set in Elasticsearch. Also note the use of JsonPropertyNameAttribute on
the Description property to further customize the storage naming.
C#
using System.Text.Json.Serialization;
using Microsoft.Extensions.VectorData;
[VectorStoreRecordData(IsFilterable = true)]
public string HotelName { get; set; }
[JsonPropertyName("HOTEL_DESCRIPTION")]
[VectorStoreRecordData(IsFullTextSearchable = true)]
public string Description { get; set; }
[VectorStoreRecordVector(Dimensions: 4,
DistanceFunction.CosineSimilarity, IndexKind.Hnsw)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
}
JSON
{
"_index" : "skhotelsjson",
"_id" : "h1",
"_source" : {
"HOTEL_NAME" : "Hotel Happy",
"HOTEL_DESCRIPTION" : "A place where everyone can be happy.",
"DESCRIPTION_EMBEDDING" : [
0.9,
0.1,
0.1,
0.1
]
}
}
Using the In-Memory connector
(Preview)
Article • 10/16/2024
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Overview
The In-Memory Vector Store connector is a Vector Store implementation provided by
Semantic Kernel that uses no external database and stores data in memory. This Vector
Store is useful for prototyping scenarios or where high-speed in-memory operations are
required.
ノ Expand table
IsFullTextSearchable Yes
Feature Area Support
supported?
StoragePropertyName No, since storage is in-memory and data reuse is therefore not
supported? possible, custom naming is not applicable.
Getting started
Add the Semantic Kernel Core nuget package to your project.
.NET CLI
You can add the vector store to the dependency injection container available on the
KernelBuilder or to the IServiceCollection dependency injection container using
C#
using Microsoft.SemanticKernel;
C#
using Microsoft.SemanticKernel;
C#
using Microsoft.SemanticKernel.Connectors.InMemory;
using Microsoft.SemanticKernel.Connectors.InMemory;
Overview
JDBC vector store is a Java-specific feature, available only for Java applications.
Using the MongoDB Vector Store
connector (Preview)
Article • 10/29/2024
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Overview
The MongoDB Vector Store connector can be used to access and manage data in
MongoDB. The connector has the following characteristics.
ノ Expand table
IsFullTextSearchable supported? No
StoragePropertyName supported? No, use BsonElementAttribute instead. See here for more
info.
Getting started
Add the MongoDB Vector Store connector NuGet package to your project.
.NET CLI
You can add the vector store to the IServiceCollection dependency injection container
using extension methods provided by Semantic Kernel.
C#
using Microsoft.SemanticKernel;
Extension methods that take no parameters are also provided. These require an instance
of MongoDB.Driver.IMongoDatabase to be separately registered with the dependency
injection container.
C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using MongoDB.Driver;
C#
using Microsoft.SemanticKernel.Connectors.MongoDB;
using MongoDB.Driver;
C#
using Microsoft.SemanticKernel.Connectors.MongoDB;
using MongoDB.Driver;
Data mapping
The MongoDB Vector Store connector provides a default mapper when mapping data
from the data model to storage.
This mapper does a direct conversion of the list of properties on the data model to the
fields in MongoDB and uses MongoDB.Bson.Serialization to convert to the storage
schema. This means that usage of the
MongoDB.Bson.Serialization.Attributes.BsonElement is supported if a different storage
name to the data model property name is required. The only exception is the key of the
record which is mapped to a database field named _id , since all MongoDB records
must use this name for ids.
The property name override is done by setting the BsonElement attribute on the data
model properties.
C#
using Microsoft.Extensions.VectorData;
[BsonElement("hotel_name")]
[VectorStoreRecordData(IsFilterable = true)]
public string HotelName { get; set; }
[BsonElement("hotel_description")]
[VectorStoreRecordData(IsFullTextSearchable = true)]
public string Description { get; set; }
[BsonElement("hotel_description_embedding")]
[VectorStoreRecordVector(4, DistanceFunction.CosineSimilarity)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
}
Using the Pinecone connector (Preview)
Article • 10/31/2024
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Overview
The Pinecone Vector Store connector can be used to access and manage data in
Pinecone. The connector has the following characteristics.
ノ Expand table
IsFullTextSearchable supported? No
Feature Area Support
Getting started
Add the Pinecone Vector Store connector NuGet package to your project.
.NET CLI
You can add the vector store to the dependency injection container available on the
KernelBuilder or to the IServiceCollection dependency injection container using
C#
using Microsoft.SemanticKernel;
C#
using Microsoft.SemanticKernel;
Extension methods that take no parameters are also provided. These require an instance
of the PineconeClient to be separately registered with the dependency injection
container.
C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using PineconeClient = Pinecone.PineconeClient;
C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using PineconeClient = Pinecone.PineconeClient;
C#
using Microsoft.SemanticKernel.Connectors.Pinecone;
using PineconeClient = Pinecone.PineconeClient;
C#
using Microsoft.SemanticKernel.Connectors.Pinecone;
using PineconeClient = Pinecone.PineconeClient;
Index Namespace
The Vector Store abstraction does not support a multi tiered record grouping
mechanism. Collections in the abstraction map to a Pinecone serverless index and no
second level exists in the abstraction. Pinecone does support a second level of grouping
called namespaces.
By default the Pinecone connector will pass null as the namespace for all operations.
However it is possible to pass a single namespace to the Pinecone collection when
constructing it and use this instead for all operations.
C#
using Microsoft.SemanticKernel.Connectors.Pinecone;
using PineconeClient = Pinecone.PineconeClient;
Data mapping
The Pinecone connector provides a default mapper when mapping data from the data
model to storage. Pinecone requires properties to be mapped into id, metadata and
values groupings. The default mapper uses the model annotations or record definition
to determine the type of each property and to do this mapping.
The data model property annotated as a key will be mapped to the Pinecone id
property.
The data model properties annotated as data will be mapped to the Pinecone
metadata object.
The data model property annotated as a vector will be mapped to the Pinecone
vector property.
Here is an example of a data model with StoragePropertyName set on its attributes and
how that will be represented in Pinecone.
C#
using Microsoft.Extensions.VectorData;
[VectorStoreRecordVector(Dimensions: 4, DistanceFunction.CosineDistance,
IndexKind.Hnsw)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
}
JSON
{
"id": "h1",
"values": [0.9, 0.1, 0.1, 0.1],
"metadata": { "hotel_name": "Hotel Happy", "hotel_description": "A place
where everyone can be happy." }
}
Using the Qdrant connector (Preview)
Article • 10/16/2024
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Overview
The Qdrant Vector Store connector can be used to access and manage data in Qdrant.
The connector has the following characteristics.
ノ Expand table
Collection maps to Qdrant collection with payload indices for filterable data
fields
Getting started
Add the Qdrant Vector Store connector NuGet package to your project.
.NET CLI
You can add the vector store to the dependency injection container available on the
KernelBuilder or to the IServiceCollection dependency injection container using
C#
using Microsoft.SemanticKernel;
C#
using Microsoft.SemanticKernel;
Extension methods that take no parameters are also provided. These require an instance
of the Qdrant.Client.QdrantClient class to be separately registered with the
dependency injection container.
C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using Qdrant.Client;
C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using Qdrant.Client;
C#
using Microsoft.SemanticKernel.Connectors.Qdrant;
using Qdrant.Client;
C#
using Microsoft.SemanticKernel.Connectors.Qdrant;
using Qdrant.Client;
Data mapping
The Qdrant connector provides a default mapper when mapping data from the data
model to storage. Qdrant requires properties to be mapped into id, payload and
vector(s) groupings. The default mapper uses the model annotations or record definition
to determine the type of each property and to do this mapping.
The data model property annotated as a key will be mapped to the Qdrant point
id.
The data model properties annotated as data will be mapped to the Qdrant point
payload object.
The data model properties annotated as vectors will be mapped to the Qdrant
point vector object.
The property name override is done by setting the StoragePropertyName option via the
data model attributes or record definition.
Here is an example of a data model with StoragePropertyName set on its attributes and
how that will be represented in Qdrant.
C#
using Microsoft.Extensions.VectorData;
[VectorStoreRecordVector(4, DistanceFunction.CosineDistance,
IndexKind.Hnsw, StoragePropertyName = "hotel_description_embedding")]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
}
JSON
{
"id": 1,
"payload": { "hotel_name": "Hotel Happy", "hotel_description": "A place
where everyone can be happy." },
"vector": {
"hotel_description_embedding": [0.9, 0.1, 0.1, 0.1],
}
}
C#
new Hotel
{
HotelId = 1,
HotelName = "Hotel Happy",
Description = "A place where everyone can be happy.",
DescriptionEmbedding = new float[4] { 0.9f, 0.1f, 0.1f, 0.1f }
};
JSON
{
"id": 1,
"payload": { "HotelName": "Hotel Happy", "Description": "A place where
everyone can be happy." },
"vector": [0.9, 0.1, 0.1, 0.1]
}
Named vectors
If using the named vectors mode, it means that each point in a collection may contain
more than one vector, and each will be named. Here is an example of how an object is
represented in Qdrant when using named vectors mode:
C#
new Hotel
{
HotelId = 1,
HotelName = "Hotel Happy",
Description = "A place where everyone can be happy.",
HotelNameEmbedding = new float[4] { 0.9f, 0.5f, 0.5f, 0.5f }
DescriptionEmbedding = new float[4] { 0.9f, 0.1f, 0.1f, 0.1f }
};
JSON
{
"id": 1,
"payload": { "HotelName": "Hotel Happy", "Description": "A place where
everyone can be happy." },
"vector": {
"HotelNameEmbedding": [0.9, 0.5, 0.5, 0.5],
"DescriptionEmbedding": [0.9, 0.1, 0.1, 0.1],
}
}
To enable named vectors mode, pass this as an option when constructing a Vector Store
or collection. The same options can also be passed to any of the provided dependency
injection container extension methods.
C#
using Microsoft.SemanticKernel.Connectors.Qdrant;
using Qdrant.Client;
2 Warning
Overview
The Redis Vector Store connector can be used to access and manage data in Redis. The
connector supports both Hashes and JSON modes and which mode you pick will
determine what other features are supported.
ノ Expand table
IsFullTextSearchable Yes
supported?
Getting started
Add the Redis Vector Store connector nuget package to your project.
.NET CLI
You can add the vector store to the dependency injection container available on the
KernelBuilder or to the to the IServiceCollection dependency injection container
C#
using Microsoft.SemanticKernel;
C#
using Microsoft.SemanticKernel;
Extension methods that take no parameters are also provided. These require an instance
of the Redis IDatabase to be separately registered with the dependency injection
container.
C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using StackExchange.Redis;
C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using StackExchange.Redis;
C#
using Microsoft.SemanticKernel.Connectors.Redis;
using StackExchange.Redis;
It is possible to construct a direct reference to a named collection. When doing so, you
have to choose between the JSON or Hashes instance depending on how you wish to
store data in Redis.
C#
using Microsoft.SemanticKernel.Connectors.Redis;
using StackExchange.Redis;
// Using Hashes.
var hashesCollection = new RedisHashSetVectorStoreRecordCollection<Hotel>(
ConnectionMultiplexer.Connect("localhost:6379").GetDatabase(),
"skhotelshashes");
C#
using Microsoft.SemanticKernel.Connectors.Redis;
using StackExchange.Redis;
// Using JSON.
var jsonCollection = new RedisJsonVectorStoreRecordCollection<Hotel>(
ConnectionMultiplexer.Connect("localhost:6379").GetDatabase(),
"skhotelsjson");
C#
using Microsoft.SemanticKernel.Connectors.Redis;
using StackExchange.Redis;
Index prefixes
Redis uses a system of key prefixing to associate a record with an index. When creating
an index you can specify one or more prefixes to use with that index. If you want to
associate a record with that index, you have to add the prefix to the key of that record.
E.g. If you create a index called skhotelsjson with a prefix of skhotelsjson: , when
setting a record with key h1 , the record key will need to be prefixed like this
skhotelsjson:h1 to be added to the index.
When creating a new collection using the Redis connector, the connector will create an
index in Redis with a prefix consisting of the collection name and a colon, like this
<collectionname>: . By default, the connector will also prefix all keys with the this prefix
If you didn't want to use a prefix consisting of the collection name and a colon, it is
possible to switch off the prefixing behavior and pass in the fully prefixed key to the
record operations.
C#
using Microsoft.SemanticKernel.Connectors.Redis;
using StackExchange.Redis;
await collection.GetAsync("myprefix_h1");
Data mapping
Redis supports two modes for storing data: JSON and Hashes. The Redis connector
supports both storage types, and mapping differs depending on the chosen storage
type.
separate key and value, the mapper will serialize all properties except for the key to a
JSON object and use that as the value.
C#
var jsonSerializerOptions = new JsonSerializerOptions { PropertyNamingPolicy
= JsonNamingPolicy.SnakeCaseUpper };
var collection = new RedisJsonVectorStoreRecordCollection<Hotel>(
ConnectionMultiplexer.Connect("localhost:6379").GetDatabase(),
"skhotelsjson",
new() { JsonSerializerOptions = jsonSerializerOptions });
Since a naming policy of snake case upper was chosen, here is an example of how this
data type will be set in Redis. Also note the use of JsonPropertyNameAttribute on the
Description property to further customize the storage naming.
C#
using System.Text.Json.Serialization;
using Microsoft.SemanticKernel.Data;
[VectorStoreRecordData(IsFilterable = true)]
public string HotelName { get; set; }
[JsonPropertyName("HOTEL_DESCRIPTION")]
[VectorStoreRecordData(IsFullTextSearchable = true)]
public string Description { get; set; }
[VectorStoreRecordVector(Dimensions: 4, IndexKind.Hnsw,
DistanceFunction.CosineDistance)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
}
redis
Property name overriding is done by setting the StoragePropertyName option via the
data model attributes or record definition.
Here is an example of a data model with StoragePropertyName set on its attributes and
how these are set in Redis.
C#
using Microsoft.SemanticKernel.Data;
[VectorStoreRecordVector(Dimensions: 4, IndexKind.Hnsw,
DistanceFunction.CosineDistance, StoragePropertyName =
"hotel_description_embedding")]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
}
redis
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Overview
The SQLite Vector Store connector can be used to access and manage data in SQLite.
The connector has the following characteristics.
ノ Expand table
IsFilterable supported? No
IsFullTextSearchable supported? No
Limitations
SQLite doesn't support vector search out-of-the-box. The SQLite extension should be
loaded first to enable vector search capability. The current implementation of the SQLite
connector is compatible with the sqlite-vec vector search extension.
In order to install the extension, use one of the releases with the specific extension
version of your choice. It's possible to get a pre-compiled version with the install.sh
script. This script will produce vec0.dll , which must be located in the same folder as the
running application. This will allow the application to call the
SqliteConnection.LoadExtension("vec0") method and load the vector search extension.
Getting started
Add the SQLite Vector Store connector NuGet package to your project.
.NET CLI
You can add the vector store to the IServiceCollection dependency injection container
using extension methods provided by Semantic Kernel.
using Microsoft.SemanticKernel;
Extension methods that take no parameters are also provided. These require an instance
of the Microsoft.Data.Sqlite.SqliteConnection class to be separately registered with
the dependency injection container.
In this case, the connection will be opened only if it wasn't opened before and the
extension method will assume that the vector search extension was already loaded for
the registered Microsoft.Data.Sqlite.SqliteConnection instance.
C#
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
connection.LoadExtension("vector-search-extension-name");
return connection;
});
builder.Services.AddSqliteVectorStore();
C#
using Microsoft.Data.Sqlite;
using Microsoft.SemanticKernel.Connectors.Sqlite;
connection.LoadExtension("vector-search-extension-name");
C#
using Microsoft.Data.Sqlite;
using Microsoft.SemanticKernel.Connectors.Sqlite;
connection.LoadExtension("vector-search-extension-name");
Data mapping
The SQLite Vector Store connector provides a default mapper when mapping from the
data model to storage. This mapper does a direct conversion of the list of properties on
the data model to the columns in SQLite.
It's also possible to override the default mapper behavior by providing a custom mapper
via the SqliteVectorStoreRecordCollectionOptions<TRecord>.DictionaryCustomMapper
property.
With the vector search extension, vectors are stored in virtual tables, separately from key
and data properties. By default, the virtual table with vectors will use the same name as
the table with key and data properties, but with a vec_ prefix. For example, if the
collection name in SqliteVectorStoreRecordCollection is skhotels , the name of the
virtual table with vectors will be vec_skhotels . It's possible to override the virtual table
name by using the SqliteVectorStoreOptions.VectorVirtualTableName or
SqliteVectorStoreRecordCollectionOptions<TRecord>.VectorVirtualTableName properties.
Here is an example of a data model with StoragePropertyName set on its attributes and
how that will be represented in SQLite query.
C#
using Microsoft.Extensions.VectorData;
[VectorStoreRecordData(StoragePropertyName = "hotel_name")]
public string? HotelName { get; set; }
[VectorStoreRecordData(StoragePropertyName = "hotel_description")]
public string? Description { get; set; }
[VectorStoreRecordVector(Dimensions: 4,
DistanceFunction.CosineDistance)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
}
2 Warning
The C# VolatileVectorStore is obsolete and has been replaced with a new package.
See InMemory Connector
Overview
The Volatile Vector Store connector is a Vector Store implementation provided by
Semantic Kernel that uses no external database and stores data in memory. This Vector
Store is useful for prototyping scenarios or where high-speed in-memory operations are
required.
ノ Expand table
IsFullTextSearchable Yes
supported?
StoragePropertyName No, since storage is volatile and data reuse is therefore not
supported? possible, custom naming is not useful and not supported.
Getting started
Add the Semantic Kernel Core nuget package to your project.
.NET CLI
You can add the vector store to the dependency injection container available on the
KernelBuilder or to the IServiceCollection dependency injection container using
C#
using Microsoft.SemanticKernel;
C#
using Microsoft.SemanticKernel;
C#
using Microsoft.SemanticKernel.Data;
C#
using Microsoft.SemanticKernel.Data;
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Overview
The Weaviate Vector Store connector can be used to access and manage data in
Weaviate. The connector has the following characteristics.
ノ Expand table
Dynamic
IsFullTextSearchable Yes
supported?
Limitations
Notable Weaviate connector functionality limitations.
ノ Expand table
Using the 'vector' property for single vector objects is Use of the 'vectors' property is
not supported supported instead.
Getting started
Add the Weaviate Vector Store connector NuGet package to your project.
.NET CLI
You can add the vector store to the dependency injection container available on the
KernelBuilder or to the IServiceCollection dependency injection container using
extension methods provided by Semantic Kernel. The Weaviate vector store uses an
HttpClient to communicate with the Weaviate service. There are two options for
providing the URL/endpoint for the Weaviate service. It can be provided via options or
by setting the base address of the HttpClient .
This first example shows how to set the service URL via options. Also note that these
methods will retrieve an HttpClient instance for making calls to the Weaviate service
from the dependency injection service provider.
C#
using Microsoft.SemanticKernel;
C#
using Microsoft.SemanticKernel;
Overloads where you can specify your own HttpClient are also provided. In this case it's
possible to set the service url via the HttpClient BaseAddress option.
C#
using System.Net.Http;
using Microsoft.SemanticKernel;
C#
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
C#
using System.Net.Http;
using Microsoft.SemanticKernel.Connectors.Weaviate;
C#
using System.Net.Http;
using Microsoft.SemanticKernel.Connectors.Weaviate;
If needed, it is possible to pass an Api Key, as an option, when using any of the above
mentioned mechanisms, e.g.
C#
using Microsoft.SemanticKernel;
Data mapping
The Weaviate Vector Store connector provides a default mapper when mapping from
the data model to storage. Weaviate requires properties to be mapped into id, payload
and vectors groupings. The default mapper uses the model annotations or record
definition to determine the type of each property and to do this mapping.
The data model property annotated as a key will be mapped to the Weaviate id
property.
The data model properties annotated as data will be mapped to the Weaviate
properties object.
The data model properties annotated as vectors will be mapped to the Weaviate
vectors object.
Here is an example of a data model with JsonPropertyNameAttribute set and how that
will be represented in Weaviate.
C#
using System.Text.Json.Serialization;
using Microsoft.Extensions.VectorData;
[VectorStoreRecordData(IsFilterable = true)]
public string HotelName { get; set; }
[VectorStoreRecordData(IsFullTextSearchable = true)]
public string Description { get; set; }
[JsonPropertyName("HOTEL_DESCRIPTION_EMBEDDING")]
[VectorStoreRecordVector(4, DistanceFunction.EuclideanDistance,
IndexKind.QuantizedFlat)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
}
JSON
{
"id": 1,
"properties": { "HotelName": "Hotel Happy", "Description": "A place
where everyone can be happy." },
"vectors": {
"HOTEL_DESCRIPTION_EMBEDDING": [0.9, 0.1, 0.1, 0.1],
}
}
How to ingest data into a Vector Store
using Semantic Kernel (Preview)
Article • 10/16/2024
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Prerequisites
For this sample you will need
Setup Redis
If you already have a Redis instance you can use that. If you prefer to test your project
locally you can easily start a Redis container using docker.
.NET CLI
Add a new file to the project called TextParagraph.cs and add the following model to it.
C#
using Microsoft.Extensions.VectorData;
namespace SKVectorIngest;
/// <summary>A uri that points at the original location of the document
containing the text.</summary>
[VectorStoreRecordData]
public required string DocumentUri { get; init; }
Note that we are passing the value 1536 to the VectorStoreRecordVectorAttribute . This
is the dimension size of the vector and has to match the size of vector that your chosen
embedding generator produces.
Tip
For more information on how to annotate your data model and what additional
options are available for each attribute, refer to definining your data model.
Add a new file to the project called DocumentReader.cs and add the following class to
read the paragraphs from a document.
C#
using System.Text;
using System.Xml;
using DocumentFormat.OpenXml.Packaging;
namespace SKVectorIngest;
xmlDoc.Load(wordDoc.MainDocumentPart.GetStream());
Add a new file called DataUploader.cs and add the following class to it.
C#
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Embeddings;
namespace SKVectorIngest;
Console.WriteLine();
}
}
}
Add the following code to your Program.cs file to create the container, register the
Redis vector store and register the embedding service. Make sure to replace the text
embedding generation settings with your own values.
C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using SKVectorIngest;
// Register Azure Open AI text embedding generation service and Redis vector
store.
var builder = Kernel.CreateBuilder()
.AddAzureOpenAITextEmbeddingGeneration(deploymentName, endpoint, apiKey)
.AddRedisVectorStore("localhost:6379");
C#
await dataUploader.GenerateEmbeddingsAndUpload(
"sk-documentation",
textParagraphs);
JSON
{
"DocumentUri" : "file:///c:/vector-store-data-ingestion-input.docx",
"ParagraphId" : "14CA7304",
"Text" : "Version 1.0+ support across C#, Python, and Java means it’s
reliable, committed to non breaking changes. Any existing chat-based APIs
are easily expanded to support additional modalities like voice and video.",
"TextEmbedding" : [...]
}
How to build a custom mapper for a
Vector Store connector (Preview)
Article • 10/16/2024
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
In this how to, we will show how you can replace the default mapper for a vector store
record collection with your own mapper.
We will use Qdrant to demonstrate this functionality, but the concepts will be similar for
other connectors.
Background
Each Vector Store connector includes a default mapper that can map from the provided
data model to the storage schema supported by the underlying store. Some stores allow
a lot of freedom with regards to how data is stored while other stores require a more
structured approach, e.g. where all vectors have to be added to a dictionary of vectors
and all non-vector fields to a dictionary of data fields. Therefore, mapping is an
important part of abstracting away the differences of each data store implementation.
In some cases, the developer may want to replace the default mapper if e.g.
1. they want to use a data model that differs from the storage schema.
2. they want to build a performance optimized mapper for their scenario.
3. the default mapper doesn't support a storage structure that the developer
requires.
All Vector Store connector implementations allow you to provide a custom mapper.
If you want to do custom mapping, and you want to use multiple connector types, you
will therefore need to implement a mapper for each connector type.
Also note that this model is complex, with seperate classes for vectors and additional
product info.
C#
Note that the definition here is different to the data model above. To store ProductInfo
we have a string property called ProductInfoJson , and the two vectors are defined at
the same level as the Id , Name and Description fields.
C#
using Microsoft.Extensions.VectorData;
) Important
For this scenario, it would not be possible to use attributes instead of a record
definition since the storage schema does not resemble the data model.
developer wants to use, and TStorageModel will be determined by the type of Vector
Store.
C#
using Microsoft.Extensions.VectorData;
using Qdrant.Client.Grpc;
// Add the data fields to the payload dictionary and serialize the
product info into a json string.
pointStruct.Payload.Add("Name", dataModel.Name);
pointStruct.Payload.Add("Description", dataModel.Description);
pointStruct.Payload.Add("ProductInfoJson",
JsonSerializer.Serialize(dataModel.ProductInfo));
return pointStruct;
}
return product;
}
}
C#
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.Qdrant;
using Qdrant.Client;
Here is an example of such a factory, which checks if CreateCollection was called with
the product definition and data type, and if so injects the custom mapper and switches
on named vectors mode.
C#
To use the collection factory, pass it to the Vector Store when constructing it, or when
registering it with the dependency injection container.
C#
C#
Now you can use the vector store as normal to get a collection.
C#
2 Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
This article provides guidance for anyone who wishes to build their own Vector Store
connector. This article can be used by database providers who wish to build and
maintain their own implementation, or for anyone who wishes to build and maintain an
unofficial connector for a database that lacks support.
If you wish to contribute your connector to the Semantic Kernel code base:
Overview
Vector Store connectors are implementations of the Vector Store abstraction . Some of
the decisions that were made when designing the Vector Store abstraction mean that a
Vector Store connector requires certain features to provide users with a good
experience.
A key design decision is that the Vector Store abstraction takes a strongly typed
approach to working with database records. This means that UpsertAsync takes a
strongly typed record as input, while GetAsync returns a strongly typed record. The
design uses C# generics to achieve the strong typing. This means that a connector has
to be able to map from this data model to the storage model used by the underlying
database. It also means that a connector may need to find out certain information about
the record properties in order to know how to map each of these properties. E.g. some
vector databases (such as Chroma, Qdrant and Weaviate) require vectors to be stored in
a specific structure and non-vectors in a different structure, or require record keys to be
stored in a specific field.
At the same time, the Vector Store abstraction also provides a generic data model that
allows a developer to work with a database without needing to create a custom data
model.
Requirements
In order to be considered a full implementation of the Vector Store abstractions, the
following set of requirements must be met.
Microsoft.Extensions.VectorData.IVectorStore
Microsoft.Extensions.VectorData.IVectorStoreRecordCollection<TKey, TRecord>
Microsoft.Extensions.VectorData.IVectorizedSearch<TRecord>
E.g.
MyDbVectorStore : IVectorStore
MyDbVectorStoreRecordCollection<TKey, TRecord> :
IVectorStoreRecordCollection<TKey, TRecord>
of the upserted records. This allows for the case where a database supports generating
keys automatically. In this case the keys on the record(s) passed to the upsert method
can be null, and the generated key(s) will be returned.
property name from the data model and not any customized name that the property
may be stored under in the database. E.g. let's say the user has a data model property
called TextEmbedding and they decorated the property with a
JsonPropertyNameAttribute that indicates that it should be serialized as text_embedding .
Assuming that the database is json based, it means that the property should be stored
in the database with the name text_embedding . When specifying the
VectorPropertyName option, the user should always provide TextEmbedding as the value.
This is to ensure that where different connectors are used with the same data model, the
user can always use the same property names, even though the storage name of the
property may be different.
1. Mapping between a data model and the underlying database's storage model
2. Creating a collection / index
3. Vector Search
Tip
Refer to Defining your data model for a detailed list of all attributes and settings
that need to be supported.
this information from the data model or try and validate that the data model matches
the definition in any way.
Tip
Refer to Defining your storage schema using a record definition for a detailed list
of all record definition settings that need to be supported.
index kinds and distance functions that are not available on the abovementioned static
classes additional custom strings may be accepted.
E.g. the goal is for a user to be able to specify a standard distance function, like
DotProductSimilarity for any connector that supports this distance function, without
C#
[VectorStoreRecordVector(1536, DistanceFunction.DotProductSimilarity]
public ReadOnlyMemory<float>? Embedding { get; set; }
4.2 A user can optionally choose whether each data property should be filterable or full
text searchable. In some databases, all properties may already be filterable or full text
searchable by default, however in many databases, special indexing is required to
achieve this. If special indexing is required this also means that adding this indexing will
most likely incur extra cost. The IsFilterable and IsFullTextSearchable settings allow
a user to control whether to enable this additional indexing per property.
The type of validation required will also depend on the type of mapper used by the user.
E.g. The user may have supplied a custom data model, a custom mapper and a
VectorStoreRecordDefinition . They may want the data model to differ significantly from
the storage schema, and the custom mapper would map between the two. In this case,
we want to avoid doing any checks on the data model, but focus on the
VectorStoreRecordDefinition only, to ensure the data types requested are allowed by
ノ Expand table
Generic No No No - Definition
required for
collection create
and mapper
6.1 Where the database has a storage format that supports its own mechanism for
specifying storage names, the connector should preferably use that mechanism.
6.2 Where the database does not use a storage format that supports its own mechanism
for specifying storage names, the connector must support the StoragePropertyName
settings from the data model attributes or the VectorStoreRecordDefinition .
7. Mapper support
Connectors should provide the ability to map between the user supplied data model
and the storage model that the database requires, but should also provide some
flexibility in how that mapping is done. Most connectors would typically need to support
the following three mappers.
7.1 All connectors should come with a built in mapper that can map between the user
supplied data model and the storage model required by the underlying database.
7.2 To allow users the ability to support data models that vary significantly from the
storage models of the underlying database, or to customize the mapping behavior
between the two, each connector must support custom mappers.
The IVectorStoreRecordCollection implementation should allow a user to provide a
custom mapper via options. E.g.
C#
TStorageModel> . TRecordDataModel should be the data model chosen by the user, while
7.3. All connectors should have a built in mapper that works with the
VectorStoreGenericDataModel . See Support GenericDataModel for more information.
8. Support GenericDataModel
While it is very useful for users to be able to define their own data model, in some cases
it may not be desirable, e.g. when the database schema is not known at coding time and
driven by configuration.
To support this scenario, connectors should have out of the box support for the generic
data model supplied by the abstraction package:
Microsoft.Extensions.VectorData.VectorStoreGenericDataModel<TKey> .
In practice this means that the connector must implement a special mapper to support
the generic data model. The connector should automatically use this mapper if the user
specified the generic data model as their data model and they did not provide their own
custom mapper.
There may be scenarios where the user wants to do something more complex, e.g. use a
data model that has complex properties, where sub properties of a property on the data
model are mapped to individual properties on the storage model. In this scenario the
user would need to supply both a custom mapper and a VectorStoreRecordDefinition .
The VectorStoreRecordDefinition is required to describe the database schema for
collection / index create scenarios, while the custom mapper is required to map
between the data and storage models.
To support this scenario, the connector must fulfil the following requirements:
provide additional configuration options on a per collection basis, that is specific to the
underlying database. E.g. Qdrant allows two modes, one where a single unnamed vector
is allowed per record, and another where zero or more named vectors are allowed per
record. The mode can be different for each collection.
C#
return recordCollection!;
}
}
11.1 For failures relating to service call or database failures the connector should throw:
Microsoft.Extensions.VectorData.VectorStoreOperationException
11.3 For cases where a certain setting or feature is not supported, e.g. an unsupported
index type, use: System.NotSupportedException .
12. Batching
The IVectorStoreRecordCollection interface includes batching overloads for Get, Upsert
and Delete. Not all underlying database clients may have the same level of support for
batching, so let's consider each option.
Firstly, if the database client doesn't support batching. In this case the connector should
simulate batching by executing all provided requests in parallel. Assume that the user
has broken up the requests into small enough batches already so that parallel requests
will succeed without throttling.
E.g. here is an exmaple where batching is simulated with requests happening in parallel.
C#
Secondly, if the database client does support batching, pass all requests directly to the
underlying client so that it may send the entire set in one request.
C#
...
}
Prompts play a crucial role in communicating and directing the behavior of Large
Language Models (LLMs) AI. They serve as inputs or queries that users can provide to
elicit specific responses from a model.
If you've already experimented with ChatGPT, you can see how the model's behavior
changes dramatically based on the inputs you provide. For example, the following
prompts produce very different outputs:
Prompt
Prompt
The first prompt produces a long report, while the second prompt produces a concise
response. If you were building a UI with limited space, the second prompt would be
more suitable for your needs. Further refined behavior can be achieved by adding even
more details to the prompt, but its possible to go too far and produce irrelevant
outputs. As a prompt engineer, you must find the right balance between specificity and
relevance.
When you work directly with LLM models, you can also use other controls to influence
the model's behavior. For example, you can use the temperature parameter to control
the randomness of the model's output. Other parameters like top-k, top-p, frequency
penalty, and presence penalty also influence the model's behavior.
Once you've become familiar with prompt engineering, you can also use Semantic
Kernel to apply your skills to real-world scenarios. By combining your prompts with
native functions and connectors, you can build powerful AI-powered applications.
Lastly, by deeply integrating with Visual Studio Code, Semantic Kernel also makes it easy
for you to integrate prompt engineering into your existing development processes.
The Semantic Kernel prompt template language is a simple way to define and compose
AI functions using plain text. You can use it to create natural language prompts,
generate responses, extract information, invoke other prompts or perform any other
task that can be expressed with text.
The language supports three basic features that allow you to 1) include variables, 2) call
external functions, and 3) pass parameters to functions.
You don't need to write any code or import any external libraries, just use the curly
braces {{...}} to embed expressions in your prompts. Semantic Kernel will parse your
template and execute the logic behind it. This way, you can easily integrate AI into your
apps with minimal effort and maximum flexibility.
Tip
Variables
To include a variable value in your prompt, use the {{$variableName}} syntax. For
example, if you have a variable called name that holds the user's name, you can write:
Spaces are ignored, so if you find it more readable, you can also write:
Function calls
To call an external function and embed the result in your prompt, use the
{{namespace.functionName}} syntax. For example, if you have a function called
weather.getForecast that returns the weather forecast for a given location, you can
write:
This will produce a sentence with the weather forecast for the default location stored in
the input variable. The input variable is set automatically by the kernel when invoking
a function. For instance, the code above is equivalent to:
Function parameters
To call an external function and pass a parameter to it, use the {{namespace.functionName
$varName}} and {{namespace.functionName "value"}} syntax. For example, if you want to
pass a different input to the weather forecast function, you can write:
This will produce two sentences with the weather forecast for two different locations,
using the city stored in the city variable and the "Schio" location value hardcoded in
the prompt template.
If you need to include the {{ and }} sequences in your prompts, which could trigger
special rendering logic, the best solution is to use string values enclosed in quotes, like
{{ "{{" }} and {{ "}}" }}
For example:
To avoid the need for special syntax, when working with a value that contains single
quotes, we recommend wrapping the value with double quotes. Similarly, when using a
value that contains double quotes, wrap the value with single quotes.
For example:
For those cases where the value contains both single and double quotes, you will need
escaping, using the special « \ » symbol.
When using double quotes around a value, use « \" » to include a double quote symbol
inside the value:
and similarly, when using single quotes, use « \' » to include a single quote inside the
value:
Note that for consistency, the sequences « \' » and « \" » do always render to « ' » and
« " », even when escaping might not be required.
For instance:
is equivalent to:
In case you may need to render a backslash in front of a quote, since « \ » is a special
char, you will need to escape it too, and use the special sequences « \\\' » and « \\\" ».
For example:
is rendered to:
Similarly to single and double quotes, the symbol « \ » doesn't always need to be
escaped. However, for consistency, it can be escaped even when not required.
For instance:
is equivalent to:
Lastly, backslashes have a special meaning only when used in front of « ' », « " » and
« \ ».
In all other cases, the backslash character has no impact and is rendered as is. For
example:
is rendered to:
nothing special about these sequences: \0 \n \t \r \foo
What is a Plugin?
Article • 06/24/2024
Plugins are a key component of Semantic Kernel. If you have already used plugins from
ChatGPT or Copilot extensions in Microsoft 365, you’re already familiar with them. With
plugins, you can encapsulate your existing APIs into a collection that can be used by an
AI. This allows you to give your AI the ability to perform actions that it wouldn’t be able
to do otherwise.
Behind the scenes, Semantic Kernel leverages function calling, a native feature of most
of the latest LLMs to allow LLMs, to perform planning and to invoke your APIs. With
function calling, LLMs can request (i.e., call) a particular function. Semantic Kernel then
marshals the request to the appropriate function in your codebase and returns the
results back to the LLM so the LLM can generate a final response.
Not all AI SDKs have an analogous concept to plugins (most just have functions or
tools). In enterprise scenarios, however, plugins are valuable because they encapsulate a
set of functionality that mirrors how enterprise developers already develop services and
APIs. Plugins also play nicely with dependency injection. Within a plugin's constructor,
you can inject services that are necessary to perform the work of the plugin (e.g.,
database connections, HTTP clients, etc.). This is difficult to accomplish with other SDKs
that lack plugins.
Anatomy of a plugin
At a high-level, a plugin is a group of functions that can be exposed to AI apps and
services. The functions within plugins can then be orchestrated by an AI application to
accomplish user requests. Within Semantic Kernel, you can invoke these functions
automatically with function calling.
7 Note
Just providing functions, however, is not enough to make a plugin. To power automatic
orchestration with function calling, plugins also need to provide details that semantically
describe how they behave. Everything from the function's input, output, and side effects
need to be described in a way that the AI can understand, otherwise, the AI will not
correctly call the function.
For example, the sample WriterPlugin plugin on the right has functions with semantic
descriptions that describe what each function does. An LLM can then use these
descriptions to choose the best functions to call to fulfill a user's ask.
In the picture on the right, an LLM would likely call the ShortPoem and StoryGen
functions to satisfy the users ask thanks to the provided semantic descriptions.
Importing different types of plugins
There are two primary ways of importing plugins into Semantic Kernel: using native
code or using an OpenAPI specification. The former allows you to author plugins in your
existing codebase that can leverage dependencies and services you already have. The
latter allows you to import plugins from an OpenAPI specification, which can be shared
across different programming languages and platforms.
Below we provide a simple example of importing and using a native plugin. To learn
more about how to import these different types of plugins, refer to the following
articles:
Tip
For example, with retrieval functions, you may want to use strategies to improve
performance (e.g., caching and using cheaper intermediate models for summarization).
Whereas with task automation functions, you'll likely want to implement human-in-the-
loop approval processes to ensure that tasks are completed correctly.
To learn more about the different types of plugin functions, refer to the following
articles:
Below we'll provide a high-level example of how to use a plugin within Semantic Kernel.
Refer to the links above for more detailed information on how to create and use plugins.
Below, we'll create a plugin that can retrieve the state of lights and alter its state.
Tip
Since most LLM have been trained with Python for function calling, its
recommended to use snake case for function names and property names even if
you're using the C# or Java SDK.
C#
using System.ComponentModel;
using Microsoft.SemanticKernel;
[KernelFunction("get_lights")]
[Description("Gets a list of lights and their current state")]
[return: Description("An array of lights")]
public async Task<List<LightModel>> GetLightsAsync()
{
return lights
}
[KernelFunction("get_state")]
[Description("Gets the state of a particular light")]
[return: Description("The state of the light")]
public async Task<LightModel?> GetStateAsync([Description("The ID of the
light")] int id)
{
// Get the state of the light with the specified ID
return lights.FirstOrDefault(light => light.Id == id);
}
[KernelFunction("change_state")]
[Description("Changes the state of the light")]
[return: Description("The updated state of the light; will return null if
the light does not exist")]
public async Task<LightModel?> ChangeStateAsync(int id, LightModel
LightModel)
{
var light = lights.FirstOrDefault(light => light.Id == id);
if (light == null)
{
return null;
}
return light;
}
}
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("is_on")]
public bool? IsOn { get; set; }
[JsonPropertyName("brightness")]
public byte? Brightness { get; set; }
[JsonPropertyName("hex")]
public string? Hex { get; set; }
}
Notice that we provide descriptions for the function, return value, and parameters. This
is important for the AI to understand what the function does and how to use it.
Tip
This example demonstrates the easiest way of adding a class as a plugin with the
AddFromType method. To learn about other ways of adding plugins, refer to the adding
C#
C#
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
// Enable planning
OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new()
{
ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};
With the above code, you should get a response that looks like the following:
ノ Expand table
Role Message
Tip
While you can invoke a plugin function directly, this is not advised because the AI
should be the one deciding which functions to call. If you need explicit control over
which functions are called, consider using standard methods in your codebase
instead of plugins.
Add native code as a plugin
Article • 06/24/2024
The easiest way to provide an AI agent with capabilities that are not natively supported
is to wrap native code into a plugin. This allows you to leverage your existing skills as an
app developer to extend the capabilities of your AI agents.
Behind the scenes, Semantic Kernel will then use the descriptions you provide, along
with reflection, to semantically describe the plugin to the AI agent. This allows the AI
agent to understand the capabilities of the plugin and how to interact with it.
The value of Semantic Kernel is that it can automatically generate most of this
information from the code itself. As a developer, this just means that you must provide
the semantic descriptions of the functions and parameters so the AI agent can
understand them. If you properly comment and annotate your code, however, you likely
already have this information on hand.
Below, we'll walk through the two different ways of providing your AI agent with native
code and how to provide this semantic information.
C#
public class LightsPlugin
{
private readonly List<LightModel> _lights;
[KernelFunction("get_lights")]
[Description("Gets a list of lights and their current state")]
[return: Description("An array of lights")]
public async Task<List<LightModel>> GetLightsAsync()
{
return _lights;
}
[KernelFunction("change_state")]
[Description("Changes the state of the light")]
[return: Description("The updated state of the light; will return null if
the light does not exist")]
public async Task<LightModel?> ChangeStateAsync(LightModel changeState)
{
// Find the light to change
var light = _lights.FirstOrDefault(l => l.Id == changeState.Id);
return light;
}
}
Tip
If your function has a complex object as an input variable, Semantic Kernel will also
generate a schema for that object and pass it to the AI agent. Similar to functions, you
should provide Description annotations for properties that are non-obvious to the AI.
Below is the definition for the LightState class and the Brightness enum.
C#
using System.Text.Json.Serialization;
[JsonPropertyName("name")]
public string? Name { get; set; }
[JsonPropertyName("is_on")]
public bool? IsOn { get; set; }
[JsonPropertyName("brightness")]
public enum? Brightness { get; set; }
[JsonPropertyName("color")]
[Description("The color of the light with a hex code (ensure you include
the # symbol)")]
public string? Color { get; set; }
}
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum Brightness
{
Low,
Medium,
High
}
7 Note
While this is a "fun" example, it does a good job showing just how complex a
plugin's parameters can be. In this single case, we have a complex object with four
different types of properties: an integer, string, boolean, and enum. Semantic
Kernel's value is that it can automatically generate the schema for this object and
pass it to the AI agent and marshal the parameters generated by the AI agent into
the correct object.
Once you're done authoring your plugin class, you can add it to the kernel using the
AddFromType<> or AddFromObject methods.
Tip
When creating a function, always ask yourself "how can I give the AI additional help
to use this function?" This can include using specific input types (avoid strings
where possible), providing descriptions, and examples.
The AddFromObject method allows you to add an instance of the plugin class directly to
the plugin collection in case you want to directly control how the plugin is constructed.
For example, the constructor of the LightsPlugin class requires the list of lights. In this
case, you can create an instance of the plugin class and add it to the plugin collection.
C#
kernel.Plugins.AddFromObject(new LightsPlugin(lights));
When using the AddFromType<> method, the kernel will automatically use dependency
injection to create an instance of the plugin class and add it to the plugin collection.
C#
[KernelFunction("get_lights")]
[Description("Gets a list of lights and their current state")]
[return: Description("An array of lights")]
public async Task<List<LightModel>> GetLightsAsync()
{
_logger.LogInformation("Getting lights");
return lightService.GetLights();
}
[KernelFunction("change_state")]
[Description("Changes the state of the light")]
[return: Description("The updated state of the light; will return null if
the light does not exist")]
public async Task<LightModel?> ChangeStateAsync(LightModel changeState)
{
_logger.LogInformation("Changing light state");
return lightService.ChangeState(changeState);
}
}
With Dependency Injection, you can add the required services and plugins to the kernel
builder before building the kernel.
C#
C#
kernel.Plugins.AddFromFunctions("time_plugin",
[
KernelFunctionFactory.CreateFromMethod(
method: () => DateTime.Now,
functionName: "get_time",
description: "Get the current time"
),
KernelFunctionFactory.CreateFromMethod(
method: (DateTime start, DateTime end) => (end -
start).TotalSeconds,
functionName: "diff_time",
description: "Get the difference between two times in seconds"
)
]);
Tip
C#
return pluginCollection;
});
Tip
C#
Next steps
Now that you know how to create a plugin, you can now learn how to use them with
your AI agent. Depending on the type of functions you've added to your plugins, there
are different patterns you should follow. For retrieval functions, refer to the using
retrieval functions article. For task automation functions, refer to the using task
automation functions article.
Often in an enterprise, you already have a set of APIs that perform real work. These
could be used by other automation services or power front-end applications that
humans interact with. In Semantic Kernel, you can add these exact same APIs as plugins
so your agents can also use them.
JSON
{
"openapi": "3.0.1",
"info": {
"title": "Light API",
"version": "v1"
},
"paths": {
"/Light": {
"get": {
"tags": [
"Light"
],
"summary": "Retrieves all lights in the system.",
"operationId": "get_all_lights",
"responses": {
"200": {
"description": "Returns a list of lights with their
current state",
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/LightStateModel"
}
}
}
}
}
}
},
"/Light/{id}": {
"post": {
"tags": [
"Light"
],
"summary": "Changes the state of a light.",
"operationId": "change_light_state",
"parameters": [
{
"name": "id",
"in": "path",
"description": "The ID of the light to change from the
get_all_lights tool.",
"required": true,
"style": "simple",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"description": "The new state of the light and change
parameters.",
"content": {
"application/json": {
"schema": {
"$ref":
"#/components/schemas/ChangeStateRequest"
}
}
}
},
"responses": {
"200": {
"description": "Returns the updated light state",
"content": {
"application/json": {
"schema": {
"$ref":
"#/components/schemas/LightStateModel"
}
}
}
},
"404": {
"description": "If the light is not found"
}
}
}
}
},
"components": {
"schemas": {
"ChangeStateRequest": {
"type": "object",
"properties": {
"isOn": {
"type": "boolean",
"description": "Specifies whether the light is turned
on or off.",
"nullable": true
},
"hexColor": {
"type": "string",
"description": "The hex color code for the light.",
"nullable": true
},
"brightness": {
"type": "integer",
"description": "The brightness level of the light.",
"format": "int32",
"nullable": true
},
"fadeDurationInMilliseconds": {
"type": "integer",
"description": "Duration for the light to fade to the
new state, in milliseconds.",
"format": "int32",
"nullable": true
},
"scheduledTime": {
"type": "string",
"description": "Use ScheduledTime to synchronize
lights. It's recommended that you asynchronously create tasks for each light
that's scheduled to avoid blocking the main thread.",
"format": "date-time",
"nullable": true
}
},
"additionalProperties": false,
"description": "Represents a request to change the state of
the light."
},
"LightStateModel": {
"type": "object",
"properties": {
"id": {
"type": "string",
"nullable": true
},
"name": {
"type": "string",
"nullable": true
},
"on": {
"type": "boolean",
"nullable": true
},
"brightness": {
"type": "integer",
"format": "int32",
"nullable": true
},
"hexColor": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
}
}
}
}
This specification provides everything needed by the AI to understand the API and how
to interact with it. The API includes two endpoints: one to get all lights and another to
change the state of a light. It also provides the following:
Since the AI agent can understand this specification, you can add it as a plugin to the
agent.
Tip
If you have existing OpenAPI specifications, you may need to make alterations to
make them easier for an AI to understand them. For example, you may need to
provide guidance in the descriptions. For more tips on how to make your OpenAPI
specifications AI-friendly, see Tips and tricks for adding OpenAPI plugins.
C#
await kernel.ImportPluginFromOpenApiAsync(
pluginName: "lights",
uri: new Uri("https://example.com/v1/swagger.json"),
executionParameters: new OpenApiFunctionExecutionParameters()
{
// Determines whether payload parameter names are augmented with
namespaces.
// Namespaces prevent naming conflicts by adding the parent parameter
name
// as a prefix, separated by dots
EnablePayloadNamespacing = true
}
);
Afterwards, you can use the plugin in your agent as if it were a native plugin.
ノ Expand table
Recommendation Description
Version control your API Instead of pointing to a live API specification, consider checking-in
specifications and versioning your Swagger file. This will allow your AI researchers
to test (and alter) the API specification used by the AI agent without
affecting the live API and vice versa.
Limit the number of Try to limit the number of endpoints in your API. Consolidate similar
endpoints functionalities into single endpoints with optional parameters to
reduce complexity.
Use descriptive names Ensure that the names of your endpoints and parameters are
for endpoints and descriptive and self-explanatory. This helps the AI understand their
parameters purpose without needing extensive explanations.
Use consistent naming Maintain consistent naming conventions throughout your API. This
conventions reduces confusion and helps the AI learn and predict the structure of
your API more easily.
Simplify your API Often, OpenAPI specifications are very detailed and include a lot of
specifications information that isn't necessary for the AI agent to help a user. The
simpler the API, the fewer tokens you need to spend to describe it,
and the fewer tokens the AI needs to send requests to it.
Avoid string parameters When possible, avoid using string parameters in your API. Instead,
use more specific types like integers, booleans, or enums. This will
help the AI understand the API better.
Provide examples in When humans use Swagger files, they typically are able to test the
descriptions API using the Swagger UI, which includes sample requests and
responses. Since the AI agent can't do this, consider providing
examples in the descriptions of the parameters.
Recommendation Description
Reference other Often, AIs will confuse similar endpoints. To help the AI differentiate
endpoints in descriptions between endpoints, consider referencing other endpoints in the
descriptions. For example, you could say "This endpoint is similar to
the get_all_lights endpoint, but it only returns a single light."
Provide helpful error While not within the OpenAPI specification, consider providing error
messages messages that help the AI self-correct. For example, if a user provides
an invalid ID, consider providing an error message that suggests the
AI agent get the correct ID from the get_all_lights endpoint.
Next steps
Now that you know how to create a plugin, you can now learn how to use them with
your AI agent. Depending on the type of functions you've added to your plugins, there
are different patterns you should follow. For retrieval functions, refer to the using
retrieval functions article. For task automation functions, refer to the using task
automation functions article.
Often in an enterprise, you already have a set of workflows that perform real work in
Logic Apps. These could be used by other automation services or power front-end
applications that humans interact with. In Semantic Kernel, you can add these exact
same workflows as plugins so your agents can also use them.
Take for example the Logic Apps workflows used by the Semantic Kernel team to answer
questions about new PRs. With the following workflows, an agent has everything it
needs to retrieve code changes, search for related files, and check failure logs.
Search files – to find code snippets that are relevant to a given problem
Get file – to retrieve the contents of a file in the GitHub repository
Get PR details – to retrieve the details of a PR (e.g., the PR title, description, and
author)
Get PR files – to retrieve the files that were changed in a PR
Get build and test failures – to retrieve the build and test failures for a given
GitHub action run
Get log file – to retrieve the log file for a given GitHub action run
Leveraging Logic Apps for Semantic Kernel plugins is also a great way to take advantage
of the over 1,400 connectors available in Logic Apps. This means you can easily connect
to a wide variety of services and systems without writing any code.
) Important
Today, you can only add standard Logic Apps (also known as single-tenant Logic
Apps) as plugins. Consumption Logic Apps are coming soon.
C#
await kernel.ImportPluginFromOpenApiAsync(
pluginName: "openapi_plugin",
uri: new Uri("https://example.azurewebsites.net/swagger.json"),
executionParameters: new OpenApiFunctionExecutionParameters()
{
// Determines whether payload parameter names are augmented with
namespaces.
// Namespaces prevent naming conflicts by adding the parent
parameter name
// as a prefix, separated by dots
EnablePayloadNamespacing = true
}
);
The below host.json file will create two unauthenticated endpoints. You can do this in
azure portal by going to kudu console and editing the host.json file located at
C:\home\site\wwwroot\host.json.
JSON
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle.Workflows",
"version": "[1.*, 2.0.0)"
},
"extensions": {
"http": {
"routePrefix": ""
},
"workflow": {
"MetadataEndpoints": {
"plugin": {
"enable": true,
"Authentication":{
"Type":"Anonymous"
}
},
"openapi": {
"enable": true,
"Authentication":{
"Type":"Anonymous"
}
}
},
"Settings": {
"Runtime.Triggers.RequestTriggerDefaultApiVersion": "2020-05-01-
preview"
}
}
}
}
For an in-depth walkthrough on setting up Easy Auth, refer to this tutorial titled Trigger
workflows in Standard logic apps with Easy Auth .
For those already familiar with Easy Auth (and already have an Entra client app you want
to use), this is the configuration you’ll want to post to Azure management.
Bash
#!/bin/bash
# Variables
subscription_id="[SUBSCRIPTION_ID]"
resource_group="[RESOURCE_GROUP]"
app_name="[APP_NAME]"
api_version="2022-03-01"
arm_token="[ARM_TOKEN]"
tenant_id="[TENANT_ID]"
aad_client_id="[AAD_CLIENT_ID]"
object_ids=("[OBJECT_ID_FOR_USER1]" "[OBJECT_ID_FOR_USER2]" "
[OBJECT_ID_FOR_APP1]")
# Request URL
url="https://management.azure.com/subscriptions/$subscription_id/resourceGro
ups/$resource_group/providers/Microsoft.Web/sites/$app_name/config/authsetti
ngsV2?api-version=$api_version"
# JSON payload
json_payload=$(cat <<EOF
{
"properties": {
"platform": {
"enabled": true,
"runtimeVersion": "~1"
},
"globalValidation": {
"requireAuthentication": true,
"unauthenticatedClientAction": "AllowAnonymous"
},
"identityProviders": {
"azureActiveDirectory": {
"enabled": true,
"registration": {
"openIdIssuer": "https://sts.windows.net/$tenant_id/",
"clientId": "$aad_client_id"
},
"validation": {
"jwtClaimChecks": {},
"allowedAudiences": [
"api://$aad_client_id"
],
"defaultAuthorizationPolicy": {
"allowedPrincipals": {
"identities": $object_ids_json
}
}
}
},
"facebook": {
"enabled": false,
"registration": {},
"login": {}
},
"gitHub": {
"enabled": false,
"registration": {},
"login": {}
},
"google": {
"enabled": false,
"registration": {},
"login": {},
"validation": {}
},
"twitter": {
"enabled": false,
"registration": {}
},
"legacyMicrosoftAccount": {
"enabled": false,
"registration": {},
"login": {},
"validation": {}
},
"apple": {
"enabled": false,
"registration": {},
"login": {}
}
}
}
}
EOF
)
When you create your plugin, you’ll want to provide a custom HTTP client that can
handle the authentication for your Logic App. This will allow you to use the plugin in
your AI agents without needing to worry about the authentication.
C#
// Add the plugin to the kernel with a custom HTTP client for authentication
kernel.Plugins.Add(await kernel.ImportPluginFromOpenApiAsync(
pluginName: "[NAME_OF_PLUGIN]",
uri: new Uri("https://[LOGIC_APP_NAME].azurewebsites.net/swagger.json"),
executionParameters: new OpenApiFunctionExecutionParameters()
{
HttpClient = new HttpClient()
{
DefaultRequestHeaders =
{
Authorization = new AuthenticationHeaderValue("Bearer",
authResult.AccessToken)
}
},
}
));
Next steps
Now that you know how to create a plugin, you can now learn how to use them with
your AI agent. Depending on the type of functions you've added to your plugins, there
are different patterns you should follow. For retrieval functions, refer to the using
retrieval functions article. For task automation functions, refer to the using task
automation functions article.
Often, your AI agents must retrieve data from external sources to generate grounded
responses. Without this additional context, your AI agents may hallucinate or provide
incorrect information. To address this, you can use plugins to retrieve data from external
sources.
When considering plugins for Retrieval Augmented Generation (RAG), you should ask
yourself two questions:
1. How will you (or your AI agent) "search" for the required data? Do you need
semantic search or classic search?
2. Do you already know the data the AI agent needs ahead of time (pre-fetched
data), or does the AI agent need to retrieve the data dynamically?
3. How will you keep your data secure and prevent oversharing of sensitive
information?
Semantic Search
Semantic search utilizes vector databases to understand and retrieve information based
on the meaning and context of the query rather than just matching keywords. This
method allows the search engine to grasp the nuances of language, such as synonyms,
related concepts, and the overall intent behind a query.
Semantic search excels in environments where user queries are complex, open-ended,
or require a deeper understanding of the content. For example, searching for "best
smartphones for photography" would yield results that consider the context of
photography features in smartphones, rather than just matching the words "best,"
"smartphones," and "photography."
When providing an LLM with a semantic search function, you typically only need to
define a function with a single search query. The LLM will then use this function to
retrieve the necessary information. Below is an example of a semantic search function
that uses Azure AI Search to find documents similar to a given query.
C#
using System.ComponentModel;
using System.Text.Json.Serialization;
using Azure;
using Azure.Search.Documents;
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Models;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Embeddings;
public AzureAISearchPlugin(ITextEmbeddingGenerationService
textEmbeddingGenerationService, SearchIndexClient indexClient)
{
_textEmbeddingGenerationService = textEmbeddingGenerationService;
_indexClient = indexClient;
}
[KernelFunction("Search")]
[Description("Search for a document similar to the given query.")]
public async Task<string> SearchAsync(string query)
{
// Convert string query to vector
ReadOnlyMemory<float> embedding = await
_textEmbeddingGenerationService.GenerateEmbeddingAsync(query);
return string.Empty;
}
[JsonPropertyName("vector")]
public ReadOnlyMemory<float> Vector { get; set; }
}
}
Classic Search
Classic search, also known as attribute-based or criteria-based search, relies on filtering
and matching exact terms or values within a dataset. It is particularly effective for
database queries, inventory searches, and any situation where filtering by specific
attributes is necessary.
For example, if a user wants to find all orders placed by a particular customer ID or
retrieve products within a specific price range and category, classic search provides
precise and reliable results. Classic search, however, is limited by its inability to
understand context or variations in language.
Tip
In most cases, your existing services already support classic search. Before
implementing a semantic search, consider whether your existing services can
provide the necessary context for your AI agents.
Take for example, a plugin that retrieves customer information from a CRM system using
classic search. Here, the AI simply needs to call the GetCustomerInfoAsync function with
a customer ID to retrieve the necessary information.
C#
using System.ComponentModel;
using Microsoft.SemanticKernel;
[KernelFunction("GetCustomerInfo")]
[Description("Retrieve customer information based on the given customer
ID.")]
public async Task<Customer> GetCustomerInfoAsync(string customerId)
{
return await _crmService.GetCustomerInfoAsync(customerId);
}
}
Achieving the same search functionality with semantic search would likely be impossible
or impractical due to the non-deterministic nature of semantic queries.
Below is an example of a plugin that combines semantic and classic search to retrieve
product information from an e-commerce database.
C#
using System.ComponentModel;
using Microsoft.SemanticKernel;
ノ Expand table
Role Message
🔴 Assistant Cloud Nine is made of memory foam and costs $1000. Best Sleep is
made of latex and costs $1200.
Pre-fetched data Retrieval
Static data retrieval involves fetching data from external sources and always providing it
to the AI agent. This is useful when the data is required for every request or when the
data is relatively stable and doesn't change frequently.
Take for example, an agent that always answers questions about the local weather.
Assuming you have a WeatherPlugin , you can pre-fetch weather data from a weather
API and provide it in the chat history. This allows the agent to generate responses about
the weather without wasting time requesting the data from the API.
C#
using System.Text.Json;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
ノ Expand table
Strategy Description
Use the user's Avoid creating service principals used by the AI agent to retrieve information
auth token for users. Doing so makes it difficult to verify that a user has access to the
retrieved information.
Avoid recreating Before creating a new search service with a vector DB, check if one already
search services exists for the service that has the required data. By reusing existing services,
you can avoid duplicating sensitive content, leverage existing access
controls, and use existing filtering mechanisms that only return data the user
has access to.
Store reference in Instead of duplicating sensitive content to vector DBs, you can store
vector DBs references to the actual data. For a user to access this information, their auth
instead of content token must first be used to retrieve the real data.
Next steps
Now that you now how to ground your AI agents with data from external sources, you
can now learn how to use AI agents to automate business processes. To learn more, see
using task automation functions.
Most AI agents today simply retrieve data and respond to user queries. AI agents,
however, can achieve much more by using plugins to automate tasks on behalf of users.
This allows users to delegate tasks to AI agents, freeing up time for more important
work.
Once AI Agents start performing actions, however, it's important to ensure that they are
acting in the best interest of the user. This is why we provide hooks / filters to allow you
to control what actions the AI agent can take.
In Semantic Kernel, you can use the function invocation filter. This filter is always called
whenever a function is invoked from an AI agent. To create a filter, you need to
implement the IFunctionInvocationFilter interface and then add it as a service to the
kernel.
C#
if (shouldProceed != "Y")
{
context.Result = new FunctionResult(context.Result, "The
order creation was not approved by the user");
return;
}
await next(context);
}
}
}
C#
Now, whenever the AI agent tries to create an order using the DynamicsPlugin , the user
will be prompted to approve the action.
Tip
Whenever a function is cancelled or fails, you should provide the AI agent with a
meaningful error message so it can respond appropriately. For example, if we didn't
let the AI agent know that the order creation was not approved, it would assume
that the order failed due to a technical issue and would try to create the order
again.
Next steps
Now that you've learned how to allow agents to automate tasks, you can learn how to
allow agents to automatically create plans to address user needs.
2 Warning
The Semantic Kernel Text Search functionality is preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Semantic Kernel provides capabilities that allow developers to integrate search when
calling a Large Language Model (LLM). This is important because LLM's are trained on
fixed data sets and may need access to additional data to accurately respond to a user
ask.
The process of providing additional context when prompting a LLM is called Retrieval-
Augmented Generation (RAG). RAG typically involves retrieving additional data that is
relevant to the current user ask and augmenting the prompt sent to the LLM with this
data. The LLM can use its training plus the additional context to provide a more accurate
response.
A simple example of when this becomes important is when the user's ask is related to
up-to-date information not included in the LLM's training data set. By performing an
appropriate text search and including the results with the user's ask, more accurate
responses will be achieved.
Semantic Kernel provides a set of Text Search capabilities that allow developers to
perform searches using Web Search or Vector Databases and easily add RAG to their
applications.
Tip
Tip
The following sample code uses the Semantic Kernel OpenAI connector and Web
plugins, install using the following commands:
C#
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Plugins.Web.Bing;
C#
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Plugins.Web.Google;
Tip
For more information on what types of search results can be retrieved, refer to the
documentation on Text Search Plugins.
1. Create a Kernel that has an OpenAI service registered. This will be used to call the
gpt-4o model with the prompt.
2. Create a text search instance.
3. Create a Search Plugin from the text search instance.
4. Create a prompt template that will invoke the Search Plugin with the query and
include search results in the prompt along with the original query.
5. Invoke the prompt and display the response.
The model will provide a response that is grounded in the latest information available
from a web search.
C#
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Plugins.Web.Bing;
// Build a text search plugin with Bing search and add to the kernel
var searchPlugin = textSearch.CreateWithSearch("SearchPlugin");
kernel.Plugins.Add(searchPlugin);
// Invoke prompt and use text search plugin to provide grounding information
var query = "What is the Semantic Kernel?";
var prompt = "{{SearchPlugin.Search $query}}. {{$query}}";
KernelArguments arguments = new() { { "query", query } };
Console.WriteLine(await kernel.InvokePromptAsync(prompt, arguments));
C#
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Plugins.Web.Google;
// Build a text search plugin with Google search and add to the kernel
var searchPlugin = textSearch.CreateWithSearch("SearchPlugin");
kernel.Plugins.Add(searchPlugin);
// Invoke prompt and use text search plugin to provide grounding information
var query = "What is the Semantic Kernel?";
var prompt = "{{SearchPlugin.Search $query}}. {{$query}}";
KernelArguments arguments = new() { { "query", query } };
Console.WriteLine(await kernel.InvokePromptAsync(prompt, arguments));
1. The response does not include citations showing the web pages that were used to
provide grounding context.
2. The response will include data from any web site, it would be better to limit this to
trusted sites.
3. Only a snippet of each web page is being used to provide grounding context to
the model, the snippet may not contain the data required to provide an accurate
response.
See the page which describes Text Search Plugins for solutions to these issues.
Next steps
Text Search Abstractions Text Search Plugins Text Search Function Calling
When dealing with text prompts or text content in chat history a common requirement
is to provide additional relevant information related to this text. This provides the AI
model with relevant context which helps it to provide more accurate responses. To meet
this requirement the Semantic Kernel provides a Text Search abstraction which allows
using text inputs from various sources, e.g. Web search engines, vector stores, etc., and
provide results in a few standardized formats.
7 Note
1. Search
2. GetSearchResults
3. GetTextSearchResults
Search
Performs a search for content related to the specified query and returns string values
representing the search results. Search can be used in the most basic use cases e.g.,
when augmenting a semantic-kernel format prompt template with search results.
Search always returns just a single string value per search result so is not suitable if
GetSearchResults
Performs a search for content related to the specified query and returns search results in
the format defined by the implementation. GetSearchResults returns the full search
result as defined by the underlying search service. This provides the most versatility at
the cost of tying your code to a specific search service implementation.
GetTextSearchResults
Performs a search for content related to the specified query and returns a normalized
data model representing the search results. This normalized data model includes a
string value and optionally a name and link. GetTextSearchResults allows your code to
be isolated from the a specific search service implementation, so the same prompt can
be used with multiple different search services.
Tip
The sample code below shows each of the text search methods in action.
C#
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Plugins.Web.Bing;
Next steps
Text Search Plugins Text Search Function Calling
Semantic Kernel uses Plugins to connect existing APIs with AI. These Plugins have
functions that can be used to add relevant data or examples to prompts, or to allow the
AI to perform actions automatically.
To integrate Text Search with Semantic Kernel, we need to turn it into a Plugin. Once we
have a Text Search plugin, we can use it to add relevant information to prompts or to
retrieve information as needed. Creating a plugin from Text Search is a simple process,
which we will explain below.
Tip
MyPlugin.Function will be invoked with the provided argument arg1 (which is resolved
from KernelArguments ). The return value from the function invocation is inserted into
the prompt. This technique can be used to inject relevant information into a prompt.
The sample below shows how to create a plugin named SearchPlugin from an instance
of BingTextSearch . Using CreateWithSearch creates a new plugin with a single Search
function that calls the underlying text search implementation. The SearchPlugin is
added to the Kernel which makes it available to be called during prompt rendering. The
prompt template includes a call to {{SearchPlugin.Search $query}} which will invoke
the SearchPlugin to retrieve results related to the current query. The results are then
inserted into the rendered prompt before it is sent to the AI model.
C#
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Plugins.Web.Bing;
// Create a kernel with OpenAI chat completion
IKernelBuilder kernelBuilder = Kernel.CreateBuilder();
kernelBuilder.AddOpenAIChatCompletion(
modelId: "gpt-4o",
apiKey: "<Your OpenAI API Key>");
Kernel kernel = kernelBuilder.Build();
// Build a text search plugin with Bing search and add to the kernel
var searchPlugin = textSearch.CreateWithSearch("SearchPlugin");
kernel.Plugins.Add(searchPlugin);
// Invoke prompt and use text search plugin to provide grounding information
var query = "What is the Semantic Kernel?";
var prompt = "{{SearchPlugin.Search $query}}. {{$query}}";
KernelArguments arguments = new() { { "query", query } };
Console.WriteLine(await kernel.InvokePromptAsync(prompt, arguments));
2. The prompt template uses Handlebars syntax. This allows the template to iterate
over the search results and render the name, value and link for each result.
3. The prompt includes an instruction to include citations, so the AI model will do the
work of adding citations to the response.
C#
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Plugins.Web.Bing;
// Build a text search plugin with Bing search and add to the kernel
var searchPlugin =
textSearch.CreateWithGetTextSearchResults("SearchPlugin");
kernel.Plugins.Add(searchPlugin);
// Invoke prompt and use text search plugin to provide grounding information
var query = "What is the Semantic Kernel?";
string promptTemplate = """
{{#with (SearchPlugin-GetTextSearchResults query)}}
{{#each this}}
Name: {{Name}}
Value: {{Value}}
Link: {{Link}}
-----------------
{{/each}}
{{/with}}
{{query}}
The sample below builds on the previous one to add filtering of the search results. A
TextSearchFilter with an equality clause is used to specify that only results from the
The site filter is Bing specific. For Google web search use siteSearch .
C#
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Plugins.Web.Bing;
// Build a text search plugin with Bing search and add to the kernel
var searchPlugin = KernelPluginFactory.CreateFromFunctions(
"SearchPlugin", "Search Microsoft Developer Blogs site only",
[textSearch.CreateGetTextSearchResults(searchOptions: searchOptions)]);
kernel.Plugins.Add(searchPlugin);
// Invoke prompt and use text search plugin to provide grounding information
var query = "What is the Semantic Kernel?";
string promptTemplate = """
{{#with (SearchPlugin-GetTextSearchResults query)}}
{{#each this}}
Name: {{Name}}
Value: {{Value}}
Link: {{Link}}
-----------------
{{/each}}
{{/with}}
{{query}}
Tip
Follow the link for more information on how to filter the answers that Bing
returns.
The next sample shows how you can perform more customization of the SearchPlugin
so that the filter value can be dynamic. The sample uses
KernelFunctionFromMethodOptions to specify the following for the SearchPlugin :
Parameters : The parameters include an additional optional parameter for the site
Customizing the search function is required if you want to provide multiple specialized
search functions. In prompts you can use the function names to make the template
more readable. If you use function calling then the model will use the function name
and description to select the best search function to invoke.
C#
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Plugins.Web.Bing;
// Build a text search plugin with Bing search and add to the kernel
var options = new KernelFunctionFromMethodOptions()
{
FunctionName = "GetSiteResults",
Description = "Perform a search for content related to the specified
query and optionally from the specified domain.",
Parameters =
[
new KernelParameterMetadata("query") { Description = "What to search
for", IsRequired = true },
new KernelParameterMetadata("top") { Description = "Number of
results", IsRequired = false, DefaultValue = 5 },
new KernelParameterMetadata("skip") { Description = "Number of
results to skip", IsRequired = false, DefaultValue = 0 },
new KernelParameterMetadata("site") { Description = "Only return
results from this domain", IsRequired = false },
],
ReturnParameter = new() { ParameterType =
typeof(KernelSearchResults<string>) },
};
var searchPlugin = KernelPluginFactory.CreateFromFunctions("SearchPlugin",
"Search specified site", [textSearch.CreateGetTextSearchResults(options)]);
kernel.Plugins.Add(searchPlugin);
// Invoke prompt and use text search plugin to provide grounding information
var query = "What is the Semantic Kernel?";
string promptTemplate = """
{{#with (SearchPlugin-GetSiteResults query)}}
{{#each this}}
Name: {{Name}}
Value: {{Value}}
Link: {{Link}}
-----------------
{{/each}}
{{/with}}
{{query}}
In the previous Retrieval-Augmented Generation (RAG) based samples the user ask has
been used as the search query when retrieving relevant information. The user ask could
be long and may span multiple topics or there may be multiple different search
implementations available which provide specialized results. For either of these
scenarios it can be useful to allow the AI model to extract the search query or queries
from the user ask and use function calling to retrieve the relevant information it needs.
Tip
Tip
C#
The sample below creates a SearchPlugin using Bing web search. This plugin will be
advertised to the AI model for use with automatic function calling, using the
FunctionChoiceBehavior in the prompt execution settings. When you run this sample
check the console output to see what the model used as the search query.
C#
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Plugins.Web.Bing;
// Build a text search plugin with Bing search and add to the kernel
var searchPlugin = textSearch.CreateWithSearch("SearchPlugin");
kernel.Plugins.Add(searchPlugin);
// Invoke prompt and use text search plugin to provide grounding information
OpenAIPromptExecutionSettings settings = new() { FunctionChoiceBehavior =
FunctionChoiceBehavior.Auto() };
KernelArguments arguments = new(settings);
Console.WriteLine(await kernel.InvokePromptAsync("What is the Semantic
Kernel?", arguments));
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Plugins.Web.Bing;
// Build a text search plugin with Bing search and add to the kernel
var searchPlugin =
textSearch.CreateWithGetTextSearchResults("SearchPlugin");
kernel.Plugins.Add(searchPlugin);
// Invoke prompt and use text search plugin to provide grounding information
OpenAIPromptExecutionSettings settings = new() { FunctionChoiceBehavior =
FunctionChoiceBehavior.Auto() };
KernelArguments arguments = new(settings);
Console.WriteLine(await kernel.InvokePromptAsync("What is the Semantic
Kernel? Include citations to the relevant information where it is referenced
in the response.", arguments));
C#
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Plugins.Web.Bing;
// Create a kernel with OpenAI chat completion
IKernelBuilder kernelBuilder = Kernel.CreateBuilder();
kernelBuilder.AddOpenAIChatCompletion(
modelId: "gpt-4o",
apiKey: "<Your OpenAI API Key>");
kernelBuilder.Services.AddSingleton<ITestOutputHelper>(output);
kernelBuilder.Services.AddSingleton<IFunctionInvocationFilter,
FunctionInvocationFilter>();
Kernel kernel = kernelBuilder.Build();
// Build a text search plugin with Bing search and add to the kernel
var filter = new TextSearchFilter().Equality("site",
"devblogs.microsoft.com");
var searchOptions = new TextSearchOptions() { Filter = filter };
var searchPlugin = KernelPluginFactory.CreateFromFunctions(
"SearchPlugin", "Search Microsoft Developer Blogs site only",
[textSearch.CreateGetTextSearchResults(searchOptions: searchOptions)]);
kernel.Plugins.Add(searchPlugin);
// Invoke prompt and use text search plugin to provide grounding information
OpenAIPromptExecutionSettings settings = new() { FunctionChoiceBehavior =
FunctionChoiceBehavior.Auto() };
KernelArguments arguments = new(settings);
Console.WriteLine(await kernel.InvokePromptAsync("What is the Semantic
Kernel? Include citations to the relevant information where it is referenced
in the response.", arguments));
Next steps
Text Search with Vector Stores
How to use Vector Stores with Semantic
Kernel Text Search
Article • 10/16/2024
All of the Vector Store connectors can be used for text search.
1. Use the Vector Store connector to retrieve the record collection you want to
search.
2. Wrap the record collection with VectorStoreTextSearch .
3. Convert to a plugin for use in RAG and/or function calling scenarios.
It's very likely that you will want to customize the plugin search function so that its
description reflects the type of data available in the record collection. For example if the
record collection contains information about hotels the plugin search function
description should mention this. This will allow you to register multiple plugins e.g., one
to search for hotels, another for restaurants and another for things to do.
The text search abstractions include a function to return a normalized search result i.e.,
an instance of TextSearchResult . This normalized search result contains a value and
optionally a name and link. The text search abstractions include a function to return a
string value e.g., one of the data model properties will be returned as the search result.
For text search to work correctly you need to provide a way to map from the Vector
Store data model to an instance of TextSearchResult . The next section describes the
two options you can use to perform this mapping.
Tip
The following sample shows an data model which has the text search result attributes
applied.
C#
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Data;
[VectorStoreRecordData]
[TextSearchResultValue]
public string Text { get; init; }
[VectorStoreRecordData]
[TextSearchResultLink]
public string Link { get; init; }
[VectorStoreRecordData(IsFilterable = true)]
public required string Tag { get; init; }
[VectorStoreRecordVector(1536)]
public ReadOnlyMemory<float> Embedding { get; init; }
}
The mapping from a Vector Store data model to a string or a TextSearchResult can
also be done by providing implementations of ITextSearchStringMapper and
ITextSearchResultMapper respectively.
You may decide to create custom mappers for the following scenarios:
1. Multiple properties from the data model need to be combined together e.g., if
multiple properties need to be combined to provide the value.
2. Additional logic is required to generate one of the properties e.g., if the link
property needs to be computed from the data model properties.
The following sample shows a data model and two example mapper implementations
that can be used with the data model.
C#
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Data;
[VectorStoreRecordData]
public required string Text { get; init; }
[VectorStoreRecordData]
public required string Link { get; init; }
[VectorStoreRecordData(IsFilterable = true)]
public required string Tag { get; init; }
[VectorStoreRecordVector(1536)]
public ReadOnlyMemory<float> Embedding { get; init; }
}
/// <summary>
/// String mapper which converts a DataModel to a string.
/// </summary>
protected sealed class DataModelTextSearchStringMapper :
ITextSearchStringMapper
{
/// <inheritdoc />
public string MapFromResultToString(object result)
{
if (result is DataModel dataModel)
{
return dataModel.Text;
}
throw new ArgumentException("Invalid result type.");
}
}
/// <summary>
/// Result mapper which converts a DataModel to a TextSearchResult.
/// </summary>
protected sealed class DataModelTextSearchResultMapper :
ITextSearchResultMapper
{
/// <inheritdoc />
public TextSearchResult MapFromResultToTextSearchResult(object result)
{
if (result is DataModel dataModel)
{
return new TextSearchResult(value: dataModel.Text) { Name =
dataModel.Key.ToString(), Link = dataModel.Link };
}
throw new ArgumentException("Invalid result type.");
}
}
C#
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Data;
// Create a text search instance using the vector store record collection.
var result = new VectorStoreTextSearch<DataModel>
(vectorStoreRecordCollection, textEmbeddingGeneration, stringMapper,
resultMapper);
Tip
Tip
A VectorStoreTextSearch can also be constructed from an instance of
IVectorizableTextSearch . In this case no ITextEmbeddingGenerationService is
needed.
C#
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
// Create a text search instance using the vector store record collection.
var textSearch = new VectorStoreTextSearch<DataModel>
(vectorStoreRecordCollection, textEmbeddingGeneration);
C#
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
// Create a text search instance using the vector store record collection.
var textSearch = new VectorStoreTextSearch<DataModel>
(vectorStoreRecordCollection, textEmbeddingGeneration);
// Build a text search plugin with vector store search and add to the kernel
var searchPlugin =
textSearch.CreateWithGetTextSearchResults("SearchPlugin");
kernel.Plugins.Add(searchPlugin);
// Invoke prompt and use text search plugin to provide grounding information
var query = "What is the Semantic Kernel?";
string promptTemplate = """
{{#with (SearchPlugin-GetTextSearchResults query)}}
{{#each this}}
Name: {{Name}}
Value: {{Value}}
Link: {{Link}}
-----------------
{{/each}}
{{/with}}
{{query}}
C#
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
// Create a text search instance using the vector store record collection.
var textSearch = new VectorStoreTextSearch<DataModel>
(vectorStoreRecordCollection, textEmbeddingGeneration);
// Build a text search plugin with vector store search and add to the kernel
var searchPlugin =
textSearch.CreateWithGetTextSearchResults("SearchPlugin");
kernel.Plugins.Add(searchPlugin);
// Invoke prompt and use text search plugin to provide grounding information
OpenAIPromptExecutionSettings settings = new() { FunctionChoiceBehavior =
FunctionChoiceBehavior.Auto() };
KernelArguments arguments = new(settings);
Console.WriteLine(await kernel.InvokePromptAsync("What is the Semantic
Kernel?", arguments));
1. Change the name of the search function to reflect what is in the associated record
collection e.g., you might want to name the function SearchForHotels if the record
collection contains hotel information.
2. Change the description of the function. An accurate function description helps the
AI model to select the best function to call. This is especially important if you are
adding multiple search functions.
3. Add an additional parameter to the search function. If the record collection contain
hotel information and one of the properties is the city name you could add a
property to the search function to specify the city. A filter will be automatically
added and it will filter search results by city.
Tip
The sample below uses the default implementation of search. You can opt to
provide your own implementation which calls the underlying Vector Store record
collection with additional options to fine tune your searches.
C#
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
// Create a text search instance using the vector store record collection.
var textSearch = new VectorStoreTextSearch<DataModel>
(vectorStoreRecordCollection, textEmbeddingGeneration);
// Build a text search plugin with vector store search and add to the kernel
var searchPlugin = textSearch.CreateWithGetTextSearchResults("SearchPlugin",
"Search a record collection", [textSearch.CreateSearch(options)]);
kernel.Plugins.Add(searchPlugin);
// Invoke prompt and use text search plugin to provide grounding information
OpenAIPromptExecutionSettings settings = new() { FunctionChoiceBehavior =
FunctionChoiceBehavior.Auto() };
KernelArguments arguments = new(settings);
Console.WriteLine(await kernel.InvokePromptAsync("What is the Semantic
Kernel?", arguments));
Out-of-the-box Text Search (Preview)
Article • 10/21/2024
2 Warning
The Semantic Kernel Text Search functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
ノ Expand table
2 Warning
The Semantic Kernel Text Search functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Overview
The Bing Text Search implementation uses the Bing Web Search API to retrieve search
results. You must provide your own Bing Search Api Key to use this component.
Limitations
ノ Expand table
Supported filter keys The responseFilter query parameter and advanced search keywords are
supported.
Tip
Follow this link for more information on how to filter the answers that Bing
returns. Follow this link for more information on using advanced search keywords
Getting started
The sample below shows how to create a BingTextSearch and use it to perform a text
search.
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Plugins.Web.Bing;
// Create an ITextSearch instance using Bing search
var textSearch = new BingTextSearch(apiKey: "<Your Bing API Key>");
Next steps
The following sections of the documentation show you how to:
Text Search Abstractions Text Search Plugins Text Search Function Calling
2 Warning
The Semantic Kernel Text Search functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Overview
The Google Text Search implementation uses Google Custom Search to retrieve
search results. You must provide your own Google Search Api Key and Search Engine Id
to use this component.
Limitations
ノ Expand table
Tip
Getting started
The sample below shows how to create a GoogleTextSearch and use it to perform a text
search.
C#
using Google.Apis.Http;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Plugins.Web.Google;
Next steps
The following sections of the documentation show you how to:
1. Create a plugin and use it for Retrieval Augmented Generation (RAG).
2. Use text search together with function calling.
3. Learn more about using vector stores for text search.
Text Search Abstractions Text Search Plugins Text Search Function Calling
2 Warning
The Semantic Kernel Text Search functionality is in preview, and improvements that
require breaking changes may still occur in limited circumstances before release.
Overview
The Vector Store Text Search implementation uses the Vector Store Connectors to
retrieve search results. This means you can use Vector Store Text Search with any Vector
Store which Semantic Kernel supports and any implementation of
Microsoft.Extensions.VectorData.Abstractions .
Limitations
See the limitations listed for the Vector Store connector you are using.
Getting started
The sample below shows how to use an in-memory vector store to create a
VectorStoreTextSearch and use it to perform a text search.
C#
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Connectors.InMemory;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Embeddings;
Next steps
The following sections of the documentation show you how to:
Text Search Abstractions Text Search Plugins Text Search Function Calling
Once you have multiple plugins, you then need a way for your AI agent to use them
together to solve a user’s need. This is where planning comes in.
Early on, Semantic Kernel introduced the concept of planners that used prompts to
request the AI to choose which functions to invoke. Since Semantic Kernel was
introduced, however, OpenAI introduced a native way for the model to invoke or “call” a
function: function calling. Other AI models like Gemini, Claude, and Mistral have since
adopted function calling as a core capability, making it a cross-model supported feature.
Because of these advancements, Semantic Kernel has evolved to use function calling as
the primary way to plan and execute tasks.
) Important
Function calling is only available in OpenAI models that are 0613 or newer. If you
use an older model (e.g., 0314), this functionality will return an error. We
recommend using the latest OpenAI models to take advantage of this feature.
ノ Expand table
Role Message
But what if the user doesn't know the ID of the light? Or what if the user wants to turn
on all the lights? This is where planning comes in. Today's LLM models are capable of
iteratively calling functions to solve a user's need. This is accomplished by creating a
feedback loop where the AI can call a function, check the result, and then decide what
to do next.
For example, a user may ask the AI to "toggle" a light bulb. The AI would first need to
check the state of the light bulb before deciding whether to turn it on or off.
ノ Expand table
Role Message
7 Note
In this example, you also saw parallel function calling. This is where the AI can call
multiple functions at the same time. This is a powerful feature that can help the AI
solve complex tasks more quickly. It was added to the OpenAI models in 1106.
In Semantic Kernel, we make it easy to use function calling by automating this loop for
you. This allows you to focus on building the plugins needed to solve your user's needs.
7 Note
Understanding how the function calling loop works is essential for building
performant and reliable AI agents. For an in-depth look at how the loop works, see
the function calling article.
using System.ComponentModel;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
var chatCompletionService =
kernel.GetRequiredService<IChatCompletionService>();
string? userInput;
do {
// Collect user input
Console.Write("User > ");
userInput = Console.ReadLine();
When you use automatic function calling, all of the steps in the automatic planning loop
are handled for you and added to the ChatHistory object. After the function calling
loop is complete, you can inspect the ChatHistory object to see all of the function calls
made and results provided by Semantic Kernel.
Before we deprecate these planners, we will provide guidance on how to migrate your
existing planners to function calling. If you have any questions about this process, please
reach out to us on the discussions board in the Semantic Kernel GitHub repository.
U Caution
If you are building a new AI agent, we recommend that you not use the Stepwise or
Handlebars planners. Instead, use function calling as it is more powerful and easier
to use.
Next steps
Now that you understand how planners work in Semantic Kernel, you can learn more
about how influence your AI agent so that they best plan and execute tasks on behalf of
your users.
2 Warning
The Semantic Kernel Agent Framework provides a platform within the Semantic Kernel
eco-system that allow for the creation of AI agents and the ability to incorporate
agentic patterns into any application based on the same patterns and features that exist
in the core Semantic Kernel framework.
What is an AI agent?
An AI agent is a software entity designed to perform tasks autonomously or semi-
autonomously by recieving input, processing information, and taking actions to achieve
specific goals.
Agents can send and receive messages, generating responses using a combination of
models, tools, human inputs, or other customizable components.
Collaboration: Multiple agents may "collaborate" on tasks. For example, one agent
might handle data collection while another analyzes it and yet another uses the
results to make decisions, creating a more sophisticated system with distributed
intelligence.
Process Orchestration: Agents can coordinate different tasks across systems, tools,
and APIs, helping to automate end-to-end processes like application deployments,
cloud orchestration, or even creative processes like writing and design.
Note: The core Semantic Kernel SDK is required in addition to any agent packages.
ノ Expand table
Package Description
Agent Architecture
An Overview of the Agent Architecture
(Experimental)
Article • 10/09/2024
2 Warning
This article covers key concepts in the architecture of the Agent Framework, including
foundational principles, design objectives, and strategic goals.
Goals
The Agent Framework was developed with the following key priorities in mind:
The Semantic Kernel framework serves as the core foundation for implementing
agent functionalities.
Multiple agents can collaborate within a single conversation, while integrating
human input.
An agent can engage in and manage multiple concurrent conversations
simultaneously.
Different types of agents can participate in the same conversation, each
contributing their unique capabilities.
Agent
The abstract Agent class serves as the core abstraction for all types of agents, providing
a foundational structure that can be extended to create more specialized agents. One
key subclass is Kernel Agent, which establishes a direct association with a Kernel object.
This relationship forms the basis for more specific agent implementations, such as the
Chat Completion Agent and the Open AI Assistant Agent, both of which leverage the
Kernel's capabilities to execute their respective functions.
Agent
KernelAgent
Agents can either be invoked directly to perform tasks or orchestrated within an Agent
Chat, where multiple agents may collaborate or interact dynamically with user inputs.
This flexible structure allows agents to adapt to various conversational or task-driven
scenarios, providing developers with robust tools for building intelligent, multi-agent
systems.
Deep Dive:
ChatCompletionAgent
OpenAIAssistantAgent
Agent Chat
The Agent Chat class serves as the foundational component that enables agents of any
type to engage in a specific conversation. This class provides the essential capabilities
for managing agent interactions within a chat environment. Building on this, the Agent
Group Chat class extends these capabilities by offering a stategy-based container, which
allows multiple agents to collaborate across numerous interactions within the same
conversation.
This structure facilitates more complex, multi-agent scenarios where different agents can
work together, share information, and dynamically respond to evolving conversations,
making it an ideal solution for advanced use cases such as customer support, multi-
faceted task management, or collaborative problem-solving environments.
Deep Dive:
AgentChat
Agent Channel
The Agent Channel class enables agents of various types to participate in an Agent Chat.
This functionality is completely hidden from users of the Agent Framework and only
needs to be considered by developers creating a custom Agent.
AgentChannel
The Kernel
At the heart of the Semantic Kernel ecosystem is the Kernel, which serves as the core
object that drives AI operations and interactions. To create any agent within this
framework, a Kernel instance is required as it provides the foundational context and
capabilities for the agent’s functionality. The Kernel acts as the engine for processing
instructions, managing state, and invoking the necessary AI services that power the
agent's behavior.
The Chat Completion Agent and Open AI Assistant Agent articles provide specific details
on how to create each type of agent. These resources offer step-by-step instructions
and highlight the key configurations needed to tailor the agents to different
conversational or task-based applications, demonstrating how the Kernel enables
dynamic and intelligent agent behaviors across diverse use cases.
Related API's:
IKernelBuilder
Kernel
KernelBuilderExtensions
KernelExtensions
Example:
How-To: Chat Completion Agent
Related API's:
KernelFunctionFactory
KernelFunction
KernelPluginFactory
KernelPlugin
Kernel.Plugins
Agent Messages
Agent messaging, including both input and response, is built upon the core content
types of the Semantic Kernel, providing a unified structure for communication. This
design choice simplifies the process of transitioning from traditional chat-completion
patterns to more advanced agent-driven patterns in your application development. By
leveraging familiar Semantic Kernel content types, developers can seamlessly integrate
agent capabilities into their applications without needing to overhaul existing systems.
This streamlining ensures that as you evolve from basic conversational AI to more
autonomous, task-oriented agents, the underlying framework remains consistent,
making development faster and more efficient.
Note: The Open AI Assistant Agent`_ introduced content types specific to its usage
for File References and Content Annotation:
Related API's:
ChatHistory
ChatMessageContent
KernelContent
StreamingKernelContent
FileReferenceContent
AnnotationContent
Templating
An agent's role is primarily shaped by the instructions it receives, which dictate its
behavior and actions. Similar to invoking a Kernel prompt, an agent's instructions can
include templated parameters—both values and functions—that are dynamically
substituted during execution. This enables flexible, context-aware responses, allowing
the agent to adjust its output based on real-time input.
Example:
Related API's:
PromptTemplateConfig
KernelFunctionYaml.FromPromptYaml
IPromptTemplateFactory
KernelPromptTemplateFactory
Handlebars
Prompty
Liquid
Chat Completion
The Chat Completion Agent is designed around any Semantic Kernel AI service, offering
a flexible and convenient persona encapsulation that can be seamlessly integrated into a
wide range of applications. This agent allows developers to easily bring conversational
AI capabilities into their systems without having to deal with complex implementation
details. It mirrors the features and patterns found in the underlying AI service, ensuring
that all functionalities—such as natural language processing, dialogue management,
and contextual understanding—are fully supported within the Chat Completion Agent,
making it a powerful tool for building conversational interfaces.
Related API's:
IChatCompletionService
Microsoft.SemanticKernel.Connectors.AzureOpenAI
Microsoft.SemanticKernel.Connectors.OpenAI
Microsoft.SemanticKernel.Connectors.Google
Microsoft.SemanticKernel.Connectors.HuggingFace
Microsoft.SemanticKernel.Connectors.MistralAI
Microsoft.SemanticKernel.Connectors.Onnx
2 Warning
ChatCompletionAgent
Microsoft.SemanticKernel.Agents
IChatCompletionService
Microsoft.SemanticKernel.ChatCompletion
A chat completion agent can leverage any of these AI services to generate responses,
whether directed to a user or another agent.
For .NET, some of AI services that support models with chat-completion include:
ノ Expand table
Gemini Microsoft.SemanticKernel.Connectors.Google
HuggingFace Microsoft.SemanticKernel.Connectors.HuggingFace
Model Semantic Kernel AI Service
Mistral Microsoft.SemanticKernel.Connectors.MistralAI
OpenAI Microsoft.SemanticKernel.Connectors.OpenAI
Onnx Microsoft.SemanticKernel.Connectors.Onnx
builder.AddAzureOpenAIChatCompletion(/*<...configuration parameters>*/);
AI Service Selection
No different from using Semantic Kernel AI services directly, a chat completion agent
support the specification of a service-selector. A service-selector indentifies which AI
service to target when the Kernel contains more than one.
Note: If multiple AI services are present and no service-selector is provided, the same
default logic is applied for the agent that you'd find when using an AI services
outside of the Agent Framework
ChatCompletionAgent agent =
new()
{
Name = "<agent name>",
Instructions = "<agent instructions>",
Kernel = kernel,
Arguments = // Specify the service-identifier via the
KernelArguments
new KernelArguments(
new OpenAIPromptExecutionSettings()
{
ServiceId = "service-2" // The target service-identifier.
});
};
// Define agent
ChatCompletionAgent agent = ...;
How-To:
2 Warning
OpenAIAssistantAgent
OpenAIAssistantDefinition
OpenAIClientProvider
What is an Assistant?
The OpenAI Assistant API is a specialized interface designed for more advanced and
interactive AI capabilities, enabling developers to create personalized and multi-step
task-oriented agents. Unlike the Chat Completion API, which focuses on simple
conversational exchanges, the Assistant API allows for dynamic, goal-driven interactions
with additional features like code-interpreter and file-search.
C#
OpenAIAssistantAgent agent =
await OpenAIAssistantAgent.CreateAsync(
OpenAIClientProvider.ForAzureOpenAI(/*<...service configuration>*/),
new OpenAIAssistantDefinition("<model name>")
{
Name = "<agent name>",
Instructions = "<agent instructions>",
},
new Kernel());
For .NET, the agent identifier is exposed as a string via the property defined by any
agent.
C#
OpenAIAssistantAgent agent =
await OpenAIAssistantAgent.RetrieveAsync(
OpenAIClientProvider.ForAzureOpenAI(/*<...service configuration>*/),
"<your agent id>",
new Kernel());
C#
// Define agent
OpenAIAssistantAgent agent = ...;
For .NET, the agent identifier is exposed as a string via the Agent.Id property defined
by any agent.
C#
How-To
For an end-to-end example for a Open AI Assistant Agent, see:
2 Warning
AgentChat
AgentGroupChat
Microsoft.SemanticKernel.Agents.Chat
One such subclass, Agent Group Chat, offers a concrete implementation of Agent Chat,
using a strategy-based approach to manage conversation dynamics.
C#
// Define agents
ChatCompletionAgent agent1 = ...;
OpenAIAssistantAgent agent2 = ...;
C#
// Define agents
ChatCompletionAgent agent1 = ...;
OpenAIAssistantAgent agent2 = ...;
Providing Input
Adding an input message to an Agent Chat follows the same pattern as whit a Chat
History object.
C#
C#
// Define an agent
ChatCompletionAgent agent = ...;
Agent responses are returned asynchronously as they are generated, allowing the
conversation to unfold in real-time.
Note: In following sections, Agent Selection and Chat Termination, will delve into the
Execution Settings in detail. The default Execution Settings employs sequential or
round-robin selection and limits agent participation to a single turn.
C#
// Define agents
ChatCompletionAgent agent1 = ...;
OpenAIAssistantAgent agent2 = ...;
// Invoke agents
await foreach (ChatMessageContent response in chat.InvokeAsync())
{
// Process agent response(s)...
}
Note: The most recent message is provided first (descending order: newest to
oldest).
C#
Since different agent types or configurations may maintain their own version of the
conversation history, agent specific history is also available by specifing an agent. (For
example: Open AI Assistant versus Chat Completion Agent.)
C#
The following sections, Agent Selection and Chat Termination, will delve into these
considerations in detail.
Agent Selection
In multi-turn invocation, agent selection is guided by a Selection Strategy. This strategy
is defined by a base class that can be extended to implement custom behaviors tailored
to specific needs. For convenience, two predefined concrete Selection Strategies are also
available, offering ready-to-use approaches for handling agent selection during
conversations.
If known, an initial agent may be specified to always take the first turn. A history reducer
may also be employed to limit token usage when using a strategy based on a Kernel
Function.
SelectionStrategy
SequentialSelectionStrategy
KernelFunctionSelectionStrategy
Microsoft.SemanticKernel.Agents.History
C#
ChatCompletionAgent reviewerAgent =
new()
{
Name = ReviewerName,
Instructions = "<reviewer instructions>",
Kernel = kernel
};
History:
{{$history}}
""",
safeParameterNames: "history");
Chat Termination
In multi-turn invocation, the Termination Strategy dictates when the final turn takes
place. This strategy ensures the conversation ends at the appropriate point.
This strategy is defined by a base class that can be extended to implement custom
behaviors tailored to specific needs. For convenience, serveral predefined concrete
Selection Strategies are also available, offering ready-to-use approaches for defining
termination criteria for an Agent Chat conversations.
TerminationStrategy
RegexTerminationStrategy
KernelFunctionSelectionStrategy
KernelFunctionTerminationStrategy
AggregatorTerminationStrategy
Microsoft.SemanticKernel.Agents.History
C#
ChatCompletionAgent reviewerAgent =
new()
{
Name = "Reviewer",
Instructions = "<reviewer instructions>",
Kernel = kernel
};
// Define a kernel function for the selection strategy
KernelFunction terminationFunction =
AgentGroupChat.CreatePromptFunctionForStrategy(
$$$"""
Determine if the reviewer has approved. If so, respond with a
single word: yes
History:
{{$history}}
""",
safeParameterNames: "history");
In the case of a multi-turn invocation that reaches the maximum turn limit, the system
will cease agent invocation but will not mark the instance as completed. This allows for
the possibility of extending the conversation without needing to reset the Completion
state.
C#
A full reset does not remove the agents that had joined the Agent Chat and leaves the
Agent Chat in a state where it can be reused. This allows for the continuation of
interactions with the same agents without needing to reinitialize them, making future
conversations more efficient.
C#
How-To
For an end-to-end example for using Agent Group Chat for Agent collaboration, see:
2 Warning
Related API's:
PromptTemplateConfig
KernelFunctionYaml.FromPromptYaml
IPromptTemplateFactory
KernelPromptTemplateFactory
Handlebars
Prompty
Liquid
C#
ChatCompletionAgent agent =
new()
{
Kernel = kernel,
Name = "StoryTeller",
Instructions = "Tell a story about {{$topic}} that is {{$length}}
sentences long.",
Arguments = new KernelArguments()
{
{ "topic", "Dog" },
{ "length", "3" },
}
};
C#
YAML Template
YAML
name: GenerateStory
template: |
Tell a story about {{$topic}} that is {{$length}} sentences long.
template_format: semantic-kernel
description: A function that generates a story about a topic.
input_variables:
- name: topic
description: The topic of the story.
is_required: true
- name: length
description: The number of sentences in the story.
is_required: true
Agent Initialization
C#
C#
ChatCompletionAgent agent =
new()
{
Kernel = kernel,
Name = "StoryTeller",
Instructions = "Tell a story about {{$topic}} that is {{$length}}
sentences long.",
Arguments = new KernelArguments()
{
{ "topic", "Dog" },
{ "length", "3" },
}
};
KernelArguments overrideArguments =
new()
{
{ "topic", "Cat" },
{ "length", "3" },
});
2 Warning
Once configured, an agent will choose when and how to call an available function, as it
would in any usage outside of the Agent Framework.
KernelFunctionFactory
KernelFunction
KernelPluginFactory
KernelPlugin
Kernel.Plugins
Plugins can be added to the Kernel either before or after the Agent is created. The
process of initializing Plugins follows the same patterns used for any Semantic Kernel
implementation, allowing for consistency and ease of use in managing AI capabilities.
Note: For a Chat Completion Agent, the function calling mode must be explicitly
enabled. Open AI Assistant agent is always based on automatic function calling.
C#
// Factory method to product an agent with a specific role.
// Could be incorporated into DI initialization.
ChatCompletionAgent CreateSpecificAgent(Kernel kernel, string credentials)
{
// Clone kernel instance to allow for agent specific plug-in definition
Kernel agentKernel = kernel.Clone();
C#
How-To
For an end-to-end example for using function calling, see:
2 Warning
Streaming References:
Open AI Streaming Guide
Open AI Chat Completion Streaming
Open AI Assistant Streaming
Azure OpenAI Service REST API
StreamingChatMessageContent
StreamingTextContent
StreamingFileReferenceContent
StreamingAnnotationContent
// Define agent
ChatCompletionAgent agent = ...;
// Define agents
ChatCompletionAgent agent1 = ...;
OpenAIAssistantAgent agent2 = ...;
// Invoke agents
string lastAgent = string.Empty;
await foreach (StreamingChatMessageContent response in
chat.InvokeStreamingAsync())
{
if (!lastAgent.Equals(response.AuthorName, StringComparison.Ordinal))
{
// Process begining of agent response
lastAgent = response.AuthorName;
}
2 Warning
Overview
In this sample, we will explore configuring a plugin to access GitHub API and provide
templatized instructions to a Chat Completion Agent to answer questions about a
GitHub repository. The approach will be broken down step-by-step to high-light the key
parts of the coding process. As part of the task, the agent will provide document
citations within the response.
Streaming will be used to deliver the agent's responses. This will provide real-time
updates as the task progresses.
Getting Started
Before proceeding with feature coding, make sure your development environment is
fully set up and configured.
Start by creating a Console project. Then, include the following package references to
ensure all required dependencies are available.
To add package dependencies from the command-line use the dotnet command:
PowerShell
The project file ( .csproj ) should contain the following PackageReference definitions:
XML
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="<stable>" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="
<stable>" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder"
Version="<stable>" />
<PackageReference
Include="Microsoft.Extensions.Configuration.UserSecrets" Version="<stable>"
/>
<PackageReference
Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="
<stable>" />
<PackageReference Include="Microsoft.SemanticKernel.Agents.Core"
Version="<latest>" />
<PackageReference
Include="Microsoft.SemanticKernel.Connectors.AzureOpenAI" Version="<latest>"
/>
</ItemGroup>
The Agent Framework is experimental and requires warning suppression. This may
addressed in as a property in the project file ( .csproj ):
XML
<PropertyGroup>
<NoWarn>$(NoWarn);CA2007;IDE1006;SKEXP0001;SKEXP0110;OPENAI001</NoWarn>
</PropertyGroup>
Configuration
This sample requires configuration setting in order to connect to remote services. You
will need to define settings for either Open AI or Azure Open AI and also for GitHub.
Note: For information on GitHub Personal Access Tokens, see: Managing your
personal access tokens .
PowerShell
# Open AI
dotnet user-secrets set "OpenAISettings:ApiKey" "<api-key>"
dotnet user-secrets set "OpenAISettings:ChatModel" "gpt-4o"
# Azure Open AI
dotnet user-secrets set "AzureOpenAISettings:ApiKey" "<api-key>" # Not
required if using token-credential
dotnet user-secrets set "AzureOpenAISettings:Endpoint" "<model-endpoint>"
dotnet user-secrets set "AzureOpenAISettings:ChatModelDeployment" "gpt-4o"
# GitHub
dotnet user-secrets set "GitHubSettings:BaseUrl" "https://api.github.com"
dotnet user-secrets set "GitHubSettings:Token" "<personal access token>"
The following class is used in all of the Agent examples. Be sure to include it in your
project to ensure proper functionality. This class serves as a foundational component for
the examples that follow.
c#
using System.Reflection;
using Microsoft.Extensions.Configuration;
namespace AgentsSample;
this.configRoot.GetRequiredSection(typeof(TSettings).Name).Get<TSettings>
()!;
public Settings()
{
this.configRoot =
new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddUserSecrets(Assembly.GetExecutingAssembly(), optional:
true)
.Build();
}
}
Coding
The coding process for this sample involves:
The full example code is provided in the Final section. Refer to that section for the
complete implementation.
Setup
Prior to creating a Chat Completion Agent, the configuration settings, plugins, and
Kernel must be initialized.
C#
Console.WriteLine("Initialize plugins...");
GitHubSettings githubSettings = settings.GetSettings<GitHubSettings>();
GitHubPlugin githubPlugin = new(githubSettings);
C#
Console.WriteLine("Creating kernel...");
IKernelBuilder builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
settings.AzureOpenAI.ChatModelDeployment,
settings.AzureOpenAI.Endpoint,
new AzureCliCredential());
builder.Plugins.AddFromObject(githubPlugin);
Agent Definition
Finally we are ready to instantiate a Chat Completion Agent with its Instructions,
associated Kernel, and the default Arguments and Execution Settings. In this case, we
desire to have the any plugin functions automatically executed.
C#
Console.WriteLine("Defining agent...");
ChatCompletionAgent agent =
new()
{
Name = "SampleAssistantAgent",
Instructions =
"""
You are an agent designed to query and retrieve information from
a single GitHub repository in a read-only manner.
You are also able to access the profile of the active user.
Console.WriteLine("Ready!");
C#
Now let's capture user input within the previous loop. In this case, empty input will be
ignored and the term EXIT will signal that the conversation is completed. Valid input will
be added to the Chat History as a User message.
C#
Console.WriteLine();
Console.Write("> ");
string input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input))
{
continue;
}
if (input.Trim().Equals("EXIT", StringComparison.OrdinalIgnoreCase))
{
isComplete = true;
break;
}
To generate a Agent response to user input, invoke the agent using Arguments to
provide the final template parameter that specifies the current date and time.
C#
Final
Bringing all the steps together, we have the final code for this example. The complete
implementation is provided below.
1. What is my username?
2. Describe the repo.
3. Describe the newest issue created in the repo.
4. List the top 10 issues closed within the last week.
5. How were these issues labeled?
6. List the 5 most recently opened issues with the "Agents" label
C#
using System;
using System.Threading.Tasks;
using Azure.Identity;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
using Plugins;
namespace AgentsSample;
Console.WriteLine("Initialize plugins...");
GitHubSettings githubSettings = settings.GetSettings<GitHubSettings>
();
GitHubPlugin githubPlugin = new(githubSettings);
Console.WriteLine("Creating kernel...");
IKernelBuilder builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
settings.AzureOpenAI.ChatModelDeployment,
settings.AzureOpenAI.Endpoint,
new AzureCliCredential());
builder.Plugins.AddFromObject(githubPlugin);
Console.WriteLine("Defining agent...");
ChatCompletionAgent agent =
new()
{
Name = "SampleAssistantAgent",
Instructions =
"""
You are an agent designed to query and retrieve
information from a single GitHub repository in a read-only manner.
You are also able to access the profile of the
active user.
Console.WriteLine();
} while (!isComplete);
}
}
2 Warning
Overview
In this sample, we will explore how to use the code-interpreter tool of an Open AI
Assistant Agent to complete data-analysis tasks. The approach will be broken down
step-by-step to high-light the key parts of the coding process. As part of the task, the
agent will generate both image and text responses. This will demonstrate the versatility
of this tool in performing quantitative analysis.
Streaming will be used to deliver the agent's responses. This will provide real-time
updates as the task progresses.
Getting Started
Before proceeding with feature coding, make sure your development environment is
fully set up and configured.
Start by creating a Console project. Then, include the following package references to
ensure all required dependencies are available.
To add package dependencies from the command-line use the dotnet command:
PowerShell
The project file ( .csproj ) should contain the following PackageReference definitions:
XML
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="<stable>" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="
<stable>" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder"
Version="<stable>" />
<PackageReference
Include="Microsoft.Extensions.Configuration.UserSecrets" Version="<stable>"
/>
<PackageReference
Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="
<stable>" />
<PackageReference Include="Microsoft.SemanticKernel" Version="<latest>"
/>
<PackageReference Include="Microsoft.SemanticKernel.Agents.OpenAI"
Version="<latest>" />
</ItemGroup>
The Agent Framework is experimental and requires warning suppression. This may
addressed in as a property in the project file ( .csproj ):
XML
<PropertyGroup>
<NoWarn>$(NoWarn);CA2007;IDE1006;SKEXP0001;SKEXP0110;OPENAI001</NoWarn>
</PropertyGroup>
XML
<ItemGroup>
<None Include="PopulationByAdmin1.csv">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="PopulationByCountry.csv">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
Configuration
This sample requires configuration setting in order to connect to remote services. You
will need to define settings for either Open AI or Azure Open AI.
PowerShell
# Open AI
dotnet user-secrets set "OpenAISettings:ApiKey" "<api-key>"
dotnet user-secrets set "OpenAISettings:ChatModel" "gpt-4o"
# Azure Open AI
dotnet user-secrets set "AzureOpenAISettings:ApiKey" "<api-key>" # Not
required if using token-credential
dotnet user-secrets set "AzureOpenAISettings:Endpoint" "<model-endpoint>"
dotnet user-secrets set "AzureOpenAISettings:ChatModelDeployment" "gpt-4o"
The following class is used in all of the Agent examples. Be sure to include it in your
project to ensure proper functionality. This class serves as a foundational component for
the examples that follow.
c#
using System.Reflection;
using Microsoft.Extensions.Configuration;
namespace AgentsSample;
this.configRoot.GetRequiredSection(typeof(TSettings).Name).Get<TSettings>
()!;
public Settings()
{
this.configRoot =
new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddUserSecrets(Assembly.GetExecutingAssembly(), optional:
true)
.Build();
}
}
Coding
The coding process for this sample involves:
The full example code is provided in the Final section. Refer to that section for the
complete implementation.
Setup
Prior to creating an Open AI Assistant Agent, ensure the configuration settings are
available and prepare the file resources.
Instantiate the Settings class referenced in the previous Configuration section. Use the
settings to also create an OpenAIClientProvider that will be used for the Agent
Definition as well as file-upload.
C#
Settings settings = new();
OpenAIClientProvider clientProvider =
OpenAIClientProvider.ForAzureOpenAI(new AzureCliCredential(), new
Uri(settings.AzureOpenAI.Endpoint));
Use the OpenAIClientProvider to access a FileClient and upload the two data-files
described in the previous Configuration section, preserving the File Reference for final
clean-up.
C#
Console.WriteLine("Uploading files...");
FileClient fileClient = clientProvider.Client.GetFileClient();
OpenAIFileInfo fileDataCountryDetail = await
fileClient.UploadFileAsync("PopulationByAdmin1.csv",
FileUploadPurpose.Assistants);
OpenAIFileInfo fileDataCountryList = await
fileClient.UploadFileAsync("PopulationByCountry.csv",
FileUploadPurpose.Assistants);
Agent Definition
We are now ready to instantiate an OpenAI Assistant Agent. The agent is configured with
its target model, Instructions, and the Code Interpreter tool enabled. Additionally, we
explicitly associate the two data files with the Code Interpreter tool.
C#
Console.WriteLine("Defining agent...");
OpenAIAssistantAgent agent =
await OpenAIAssistantAgent.CreateAsync(
clientProvider,
new
OpenAIAssistantDefinition(settings.AzureOpenAI.ChatModelDeployment)
{
Name = "SampleAssistantAgent",
Instructions =
"""
Analyze the available data to provide an answer to the
user's question.
Always format response using markdown.
Always include a numerical index that starts at 1 for any
lists or tables.
Always sort lists in ascending order.
""",
EnableCodeInterpreter = true,
CodeInterpreterFileIds = [fileDataCountryList.Id,
fileDataCountryDetail.Id],
},
new Kernel());
Let's also ensure the resources are removed at the end of execution to minimize
unnecessary charges.
C#
Console.WriteLine("Creating thread...");
string threadId = await agent.CreateThreadAsync();
Console.WriteLine("Ready!");
try
{
bool isComplete = false;
List<string> fileIds = [];
do
{
} while (!isComplete);
}
finally
{
Console.WriteLine();
Console.WriteLine("Cleaning-up...");
await Task.WhenAll(
[
agent.DeleteThreadAsync(threadId),
agent.DeleteAsync(),
fileClient.DeleteFileAsync(fileDataCountryList.Id),
fileClient.DeleteFileAsync(fileDataCountryDetail.Id),
]);
}
Now let's capture user input within the previous loop. In this case, empty input will be
ignored and the term EXIT will signal that the conversation is completed. Valid input will
be added to the Assistant Thread as a User message.
C#
Console.WriteLine();
Console.Write("> ");
string input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input))
{
continue;
}
if (input.Trim().Equals("EXIT", StringComparison.OrdinalIgnoreCase))
{
isComplete = true;
break;
}
Console.WriteLine();
Before invoking the Agent response, let's add some helper methods to download any
files that may be produced by the Agent.
Here we're place file content in the system defined temporary directory and then
launching the system defined viewer application.
C#
if (launchViewer)
{
Process.Start(
new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $"/C start {filePath}"
});
}
}
}
To generate an Agent response to user input, invoke the agent by specifying the
Assistant Thread. In this example, we choose a streamed response and capture any
generated File References for download and review at the end of the response cycle. It's
important to note that generated code is identified by the presence of a Metadata key in
the response message, distinguishing it from the conversational reply.
C#
// Display response.
Console.Write($"{response.Content}");
1. Compare the files to determine the number of countries do not have a state or
province defined compared to the total count
2. Create a table for countries with state or province defined. Include the count of
states or provinces and the total population
3. Provide a bar chart for countries whose names start with the same letter and sort
the x axis by highest count to lowest (include all countries)
C#
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Azure.Identity;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents.OpenAI;
using Microsoft.SemanticKernel.ChatCompletion;
using OpenAI.Files;
namespace AgentsSample;
OpenAIClientProvider clientProvider =
OpenAIClientProvider.ForAzureOpenAI(new AzureCliCredential(),
new Uri(settings.AzureOpenAI.Endpoint));
Console.WriteLine("Uploading files...");
FileClient fileClient = clientProvider.Client.GetFileClient();
OpenAIFileInfo fileDataCountryDetail = await
fileClient.UploadFileAsync("PopulationByAdmin1.csv",
FileUploadPurpose.Assistants);
OpenAIFileInfo fileDataCountryList = await
fileClient.UploadFileAsync("PopulationByCountry.csv",
FileUploadPurpose.Assistants);
Console.WriteLine("Defining agent...");
OpenAIAssistantAgent agent =
await OpenAIAssistantAgent.CreateAsync(
clientProvider,
new
OpenAIAssistantDefinition(settings.AzureOpenAI.ChatModelDeployment)
{
Name = "SampleAssistantAgent",
Instructions =
"""
Analyze the available data to provide an answer to
the user's question.
Always format response using markdown.
Always include a numerical index that starts at 1
for any lists or tables.
Always sort lists in ascending order.
""",
EnableCodeInterpreter = true,
CodeInterpreterFileIds = [fileDataCountryList.Id,
fileDataCountryDetail.Id],
},
new Kernel());
Console.WriteLine("Creating thread...");
string threadId = await agent.CreateThreadAsync();
Console.WriteLine("Ready!");
try
{
bool isComplete = false;
List<string> fileIds = [];
do
{
Console.WriteLine();
Console.Write("> ");
string input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input))
{
continue;
}
if (input.Trim().Equals("EXIT",
StringComparison.OrdinalIgnoreCase))
{
isComplete = true;
break;
}
Console.WriteLine();
// Display response.
Console.Write($"{response.Content}");
fileIds.AddRange(response.Items.OfType<StreamingFileReferenceContent>
().Select(item => item.FileId));
}
Console.WriteLine();
} while (!isComplete);
}
finally
{
Console.WriteLine();
Console.WriteLine("Cleaning-up...");
await Task.WhenAll(
[
agent.DeleteThreadAsync(threadId),
agent.DeleteAsync(),
fileClient.DeleteFileAsync(fileDataCountryList.Id),
fileClient.DeleteFileAsync(fileDataCountryDetail.Id),
]);
}
}
if (launchViewer)
{
Process.Start(
new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $"/C start {filePath}"
});
}
}
}
}
2 Warning
Overview
In this sample, we will explore how to use the file-search tool of an Open AI Assistant
Agent to complete comprehension tasks. The approach will be step-by-step, ensuring
clarity and precision throughout the process. As part of the task, the agent will provide
document citations within the response.
Streaming will be used to deliver the agent's responses. This will provide real-time
updates as the task progresses.
Getting Started
Before proceeding with feature coding, make sure your development environment is
fully set up and configured.
To add package dependencies from the command-line use the dotnet command:
PowerShell
XML
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="<stable>" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="
<stable>" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder"
Version="<stable>" />
<PackageReference
Include="Microsoft.Extensions.Configuration.UserSecrets" Version="<stable>"
/>
<PackageReference
Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="
<stable>" />
<PackageReference Include="Microsoft.SemanticKernel" Version="<latest>"
/>
<PackageReference Include="Microsoft.SemanticKernel.Agents.OpenAI"
Version="<latest>" />
</ItemGroup>
The Agent Framework is experimental and requires warning suppression. This may
addressed in as a property in the project file ( .csproj ):
XML
<PropertyGroup>
<NoWarn>$(NoWarn);CA2007;IDE1006;SKEXP0001;SKEXP0110;OPENAI001</NoWarn>
</PropertyGroup>
Kernel LearnResources Project . Add these files in your project folder and configure to
have them copied to the output directory:
XML
<ItemGroup>
<None Include="Grimms-The-King-of-the-Golden-Mountain.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Grimms-The-Water-of-Life.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Grimms-The-White-Snake.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
Configuration
This sample requires configuration setting in order to connect to remote services. You
will need to define settings for either Open AI or Azure Open AI.
PowerShell
# Open AI
dotnet user-secrets set "OpenAISettings:ApiKey" "<api-key>"
dotnet user-secrets set "OpenAISettings:ChatModel" "gpt-4o"
# Azure Open AI
dotnet user-secrets set "AzureOpenAISettings:ApiKey" "<api-key>" # Not
required if using token-credential
dotnet user-secrets set "AzureOpenAISettings:Endpoint" "https://lightspeed-
team-shared-openai-eastus.openai.azure.com/"
dotnet user-secrets set "AzureOpenAISettings:ChatModelDeployment" "gpt-4o"
The following class is used in all of the Agent examples. Be sure to include it in your
project to ensure proper functionality. This class serves as a foundational component for
the examples that follow.
c#
using System.Reflection;
using Microsoft.Extensions.Configuration;
namespace AgentsSample;
this.configRoot.GetRequiredSection(typeof(TSettings).Name).Get<TSettings>
()!;
public Settings()
{
this.configRoot =
new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddUserSecrets(Assembly.GetExecutingAssembly(), optional:
true)
.Build();
}
}
Coding
The coding process for this sample involves:
The full example code is provided in the Final section. Refer to that section for the
complete implementation.
Setup
Prior to creating an Open AI Assistant Agent, ensure the configuration settings are
available and prepare the file resources.
Instantiate the Settings class referenced in the previous Configuration section. Use the
settings to also create an OpenAIClientProvider that will be used for the Agent
Definition as well as file-upload and the creation of a VectorStore .
C#
Settings settings = new();
OpenAIClientProvider clientProvider =
OpenAIClientProvider.ForAzureOpenAI(
new AzureCliCredential(),
new Uri(settings.AzureOpenAI.Endpoint));
Now create an empty _Vector Store for use with the File Search tool:
C#
Console.WriteLine("Creating store...");
VectorStoreClient storeClient =
clientProvider.Client.GetVectorStoreClient();
VectorStore store = await storeClient.CreateVectorStoreAsync();
Let's declare the the three content-files described in the previous Configuration section:
C#
Now upload those files and add them to the Vector Store by using the previously created
FileClient and VectorStore client to upload each file and add it to the Vector Store,
C#
Console.WriteLine("Uploading files...");
FileClient fileClient = clientProvider.Client.GetFileClient();
foreach (string fileName in _fileNames)
{
OpenAIFileInfo fileInfo = await fileClient.UploadFileAsync(fileName,
FileUploadPurpose.Assistants);
await storeClient.AddFileToVectorStoreAsync(store.Id, fileInfo.Id);
fileReferences.Add(fileInfo.Id, fileInfo);
}
Agent Definition
We are now ready to instantiate an OpenAI Assistant Agent. The agent is configured with
its target model, Instructions, and the File Search tool enabled. Additionally, we explicitly
associate the Vector Store with the File Search tool.
C#
Console.WriteLine("Defining agent...");
OpenAIAssistantAgent agent =
await OpenAIAssistantAgent.CreateAsync(
clientProvider,
new
OpenAIAssistantDefinition(settings.AzureOpenAI.ChatModelDeployment)
{
Name = "SampleAssistantAgent",
Instructions =
"""
The document store contains the text of fictional stories.
Always analyze the document store to provide an answer to
the user's question.
Never rely on your knowledge of stories not included in the
document store.
Always format response using markdown.
""",
EnableFileSearch = true,
VectorStoreId = store.Id,
},
new Kernel());
Let's also ensure the resources are removed at the end of execution to minimize
unnecessary charges.
C#
Console.WriteLine("Creating thread...");
string threadId = await agent.CreateThreadAsync();
Console.WriteLine("Ready!");
try
{
bool isComplete = false;
do
{
// Processing occurrs here
} while (!isComplete);
}
finally
{
Console.WriteLine();
Console.WriteLine("Cleaning-up...");
await Task.WhenAll(
[
agent.DeleteThreadAsync(threadId),
agent.DeleteAsync(),
storeClient.DeleteVectorStoreAsync(store.Id),
..fileReferences.Select(fileReference =>
fileClient.DeleteFileAsync(fileReference.Key))
]);
}
Now let's capture user input within the previous loop. In this case, empty input will be
ignored and the term EXIT will signal that the conversation is completed. Valid nput will
be added to the Assistant Thread as a User message.
C#
Console.WriteLine();
Console.Write("> ");
string input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input))
{
continue;
}
if (input.Trim().Equals("EXIT", StringComparison.OrdinalIgnoreCase))
{
isComplete = true;
break;
}
Before invoking the Agent response, let's add a helper method to reformat the unicode
annotation brackets to ANSI brackets.
C#
private static string ReplaceUnicodeBrackets(this string content) =>
content?.Replace('【', '[').Replace('】', ']');
To generate an Agent response to user input, invoke the agent by specifying the
Assistant Thread. In this example, we choose a streamed response and capture any
associated Citation Annotations for display at the end of the response cycle. Note each
streamed chunk is being reformatted using the previous helper method.
C#
Console.WriteLine();
Final
Bringing all the steps together, we have the final code for this example. The complete
implementation is provided below.
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Azure.Identity;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents.OpenAI;
using Microsoft.SemanticKernel.ChatCompletion;
using OpenAI.Files;
using OpenAI.VectorStores;
namespace AgentsSample;
/// <summary>
/// The main entry point for the application.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous
operation.</returns>
public static async Task Main()
{
// Load configuration from environment variables or user secrets.
Settings settings = new();
OpenAIClientProvider clientProvider =
OpenAIClientProvider.ForAzureOpenAI(
new AzureCliCredential(),
new Uri(settings.AzureOpenAI.Endpoint));
Console.WriteLine("Creating store...");
VectorStoreClient storeClient =
clientProvider.Client.GetVectorStoreClient();
VectorStore store = await storeClient.CreateVectorStoreAsync();
Console.WriteLine("Uploading files...");
FileClient fileClient = clientProvider.Client.GetFileClient();
foreach (string fileName in _fileNames)
{
OpenAIFileInfo fileInfo = await
fileClient.UploadFileAsync(fileName, FileUploadPurpose.Assistants);
await storeClient.AddFileToVectorStoreAsync(store.Id,
fileInfo.Id);
fileReferences.Add(fileInfo.Id, fileInfo);
}
Console.WriteLine("Defining agent...");
OpenAIAssistantAgent agent =
await OpenAIAssistantAgent.CreateAsync(
clientProvider,
new
OpenAIAssistantDefinition(settings.AzureOpenAI.ChatModelDeployment)
{
Name = "SampleAssistantAgent",
Instructions =
"""
The document store contains the text of fictional
stories.
Always analyze the document store to provide an
answer to the user's question.
Never rely on your knowledge of stories not included
in the document store.
Always format response using markdown.
""",
EnableFileSearch = true,
VectorStoreId = store.Id,
},
new Kernel());
Console.WriteLine("Creating thread...");
string threadId = await agent.CreateThreadAsync();
Console.WriteLine("Ready!");
try
{
bool isComplete = false;
do
{
Console.WriteLine();
Console.Write("> ");
string input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input))
{
continue;
}
if (input.Trim().Equals("EXIT",
StringComparison.OrdinalIgnoreCase))
{
isComplete = true;
break;
}
footnotes.AddRange(chunk.Items.OfType<StreamingAnnotationContent>());
Console.WriteLine();
2 Warning
Overview
In this sample, we will explore how to use Agent Group Chat to coordinate collboration
of two different agents working to review and rewrite user provided content. Each agent
is assigned a distinct role:
The approach will be broken down step-by-step to high-light the key parts of the
coding process.
Getting Started
Before proceeding with feature coding, make sure your development environment is
fully set up and configured.
Start by creating a Console project. Then, include the following package references to
ensure all required dependencies are available.
To add package dependencies from the command-line use the dotnet command:
PowerShell
The project file ( .csproj ) should contain the following PackageReference definitions:
XML
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="<stable>" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="
<stable>" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder"
Version="<stable>" />
<PackageReference
Include="Microsoft.Extensions.Configuration.UserSecrets" Version="<stable>"
/>
<PackageReference
Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="
<stable>" />
<PackageReference Include="Microsoft.SemanticKernel.Agents.Core"
Version="<latest>" />
<PackageReference
Include="Microsoft.SemanticKernel.Connectors.AzureOpenAI" Version="<latest>"
/>
</ItemGroup>
The Agent Framework is experimental and requires warning suppression. This may
addressed in as a property in the project file ( .csproj ):
XML
<PropertyGroup>
<NoWarn>$(NoWarn);CA2007;IDE1006;SKEXP0001;SKEXP0110;OPENAI001</NoWarn>
</PropertyGroup>
Configuration
This sample requires configuration setting in order to connect to remote services. You
will need to define settings for either Open AI or Azure Open AI.
PowerShell
# Open AI
dotnet user-secrets set "OpenAISettings:ApiKey" "<api-key>"
dotnet user-secrets set "OpenAISettings:ChatModel" "gpt-4o"
# Azure Open AI
dotnet user-secrets set "AzureOpenAISettings:ApiKey" "<api-key>" # Not
required if using token-credential
dotnet user-secrets set "AzureOpenAISettings:Endpoint" "<model-endpoint>"
dotnet user-secrets set "AzureOpenAISettings:ChatModelDeployment" "gpt-4o"
The following class is used in all of the Agent examples. Be sure to include it in your
project to ensure proper functionality. This class serves as a foundational component for
the examples that follow.
c#
using System.Reflection;
using Microsoft.Extensions.Configuration;
namespace AgentsSample;
this.configRoot.GetRequiredSection(typeof(TSettings).Name).Get<TSettings>
()!;
public Settings()
{
this.configRoot =
new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddUserSecrets(Assembly.GetExecutingAssembly(), optional:
true)
.Build();
}
}
Coding
The coding process for this sample involves:
The full example code is provided in the Final section. Refer to that section for the
complete implementation.
Setup
Prior to creating any Chat Completion Agent, the configuration settings, plugins, and
Kernel must be initialized.
Instantiate the the Settings class referenced in the previous Configuration section.
C#
C#
builder.AddAzureOpenAIChatCompletion(
settings.AzureOpenAI.ChatModelDeployment,
settings.AzureOpenAI.Endpoint,
new AzureCliCredential());
Kernel kernel = builder.Build();
Let's also create a second Kernel instance via cloning and add a plug-in that will allow
the reivew to place updated content on the clip-board.
C#
C#
clipProcess.StandardInput.Write(content);
clipProcess.StandardInput.Close();
}
}
Agent Definition
Let's declare the agent names as const so they might be referenced in Agent Group
Chat strategies:
C#
Defining the Reviewer agent uses the pattern explored in How-To: Chat Completion
Agent.
Here the Reviewer is given the role of responding to user input, providing direction to
the Writer agent, and verifying result of the Writer agent.
C#
ChatCompletionAgent agentReviewer =
new()
{
Name = ReviewerName,
Instructions =
"""
Your responsiblity is to review and identify how to improve user
provided content.
If the user has providing input or direction for content already
provided, specify how to address this input.
Never directly perform the correction or provide example.
Once the content has been updated in a subsequent response, you
will review the content again until satisfactory.
Always copy satisfactory content to the clipboard using
available tools and inform user.
RULES:
- Only identify suggestions that are specific and actionable.
- Verify previous suggestions have been addressed.
- Never repeat previous suggestions.
""",
Kernel = toolKernel,
Arguments =
new KernelArguments(
new AzureOpenAIPromptExecutionSettings()
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
})
};
The Writer agent is is similiar, but doesn't require the specification of Execution Settings
since it isn't configured with a plug-in.
Here the Writer is given a single-purpose task, follow direction and rewrite the content.
C#
ChatCompletionAgent agentWriter =
new()
{
Name = WriterName,
Instructions =
"""
Your sole responsiblity is to rewrite content according to
review suggestions.
Chat Definition
Defining the Agent Group Chat requires considering the strategies for selecting the
Agent turn and determining when to exit the Chat loop. For both of these
considerations, we will define a Kernel Prompt Function.
C#
KernelFunction selectionFunction =
AgentGroupChat.CreatePromptFunctionForStrategy(
$$$"""
Examine the provided RESPONSE and choose the next participant.
State only the name of the chosen participant without explanation.
Never choose the participant named in the RESPONSE.
RESPONSE:
{{$lastmessage}}
""",
safeParameterNames: "lastmessage");
The second will evaluate when to exit the Chat loop:
C#
KernelFunction terminationFunction =
AgentGroupChat.CreatePromptFunctionForStrategy(
$$$"""
Examine the RESPONSE and determine whether the content has been
deemed satisfactory.
If content is satisfactory, respond with a single word without
explanation: {{{TerminationToken}}}.
If specific suggestions are being provided, it is not satisfactory.
If no correction is suggested, it is satisfactory.
RESPONSE:
{{$lastmessage}}
""",
safeParameterNames: "lastmessage");
Both of these Strategies will only require knowledge of the most recent Chat message.
This will reduce token usage and help improve performance:
C#
Finally we are ready to bring everything together in our Agent Group Chat definition.
Notice that each strategy is responsible for parsing the KernelFunction result.
C#
AgentGroupChat chat =
new(agentReviewer, agentWriter)
{
ExecutionSettings = new AgentGroupChatSettings
{
SelectionStrategy =
new KernelFunctionSelectionStrategy(selectionFunction,
kernel)
{
// Always start with the editor agent.
InitialAgent = agentReviewer,
// Save tokens by only including the final response
HistoryReducer = historyReducer,
// The prompt variable name for the history argument.
HistoryVariableName = "lastmessage",
// Returns the entire result value as a string.
ResultParser = (result) => result.GetValue<string>() ??
agentReviewer.Name
},
TerminationStrategy =
new KernelFunctionTerminationStrategy(terminationFunction,
kernel)
{
// Only evaluate for editor's response
Agents = [agentReviewer],
// Save tokens by only including the final response
HistoryReducer = historyReducer,
// The prompt variable name for the history argument.
HistoryVariableName = "lastmessage",
// Limit total number of turns
MaximumIterations = 12,
// Customer result parser to determine if the response
is "yes"
ResultParser = (result) => result.GetValue<string>
()?.Contains(TerminationToken, StringComparison.OrdinalIgnoreCase) ?? false
}
}
};
Console.WriteLine("Ready!");
Note: Unlike the other examples, no external history or thread is managed. Agent
Group Chat manages the conversation history internally.
C#
Now let's capture user input within the previous loop. In this case:
C#
Console.WriteLine();
Console.Write("> ");
string input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input))
{
continue;
}
input = input.Trim();
if (input.Equals("EXIT", StringComparison.OrdinalIgnoreCase))
{
isComplete = true;
break;
}
if (input.Equals("RESET", StringComparison.OrdinalIgnoreCase))
{
await chat.ResetAsync();
Console.WriteLine("[Converation has been reset]");
continue;
}
To initate the Agent collaboration in response to user input and display the Agent
responses, invoke the Agent Group Chat; however, first be sure to reset the Completion
state from any prior invocation.
Note: Service failures are being caught and displayed to avoid crashing the
conversation loop.
C#
chat.IsComplete = false;
try
{
await foreach (ChatMessageContent response in chat.InvokeAsync())
{
Console.WriteLine();
Console.WriteLine($"{response.AuthorName.ToUpperInvariant()}:
{Environment.NewLine}{response.Content}");
}
}
catch (HttpOperationException exception)
{
Console.WriteLine(exception.Message);
if (exception.InnerException != null)
{
Console.WriteLine(exception.InnerException.Message);
if (exception.InnerException.Data.Count > 0)
{
Console.WriteLine(JsonSerializer.Serialize(exception.InnerException.Data,
new JsonSerializerOptions() { WriteIndented = true }));
}
}
}
Final
Bringing all the steps together, we have the final code for this example. The complete
implementation is provided below.
C#
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using Azure.Identity;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.Chat;
using Microsoft.SemanticKernel.Agents.History;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
namespace AgentsSample;
Console.WriteLine("Creating kernel...");
IKernelBuilder builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
settings.AzureOpenAI.ChatModelDeployment,
settings.AzureOpenAI.Endpoint,
new AzureCliCredential());
Console.WriteLine("Defining agents...");
ChatCompletionAgent agentReviewer =
new()
{
Name = ReviewerName,
Instructions =
"""
Your responsiblity is to review and identify how to
improve user provided content.
If the user has providing input or direction for content
already provided, specify how to address this input.
Never directly perform the correction or provide
example.
Once the content has been updated in a subsequent
response, you will review the content again until satisfactory.
Always copy satisfactory content to the clipboard using
available tools and inform user.
RULES:
- Only identify suggestions that are specific and
actionable.
- Verify previous suggestions have been addressed.
- Never repeat previous suggestions.
""",
Kernel = toolKernel,
Arguments = new KernelArguments(new
AzureOpenAIPromptExecutionSettings() { FunctionChoiceBehavior =
FunctionChoiceBehavior.Auto() })
};
ChatCompletionAgent agentWriter =
new()
{
Name = WriterName,
Instructions =
"""
Your sole responsiblity is to rewrite content according
to review suggestions.
RESPONSE:
{{$lastmessage}}
""",
safeParameterNames: "lastmessage");
KernelFunction terminationFunction =
AgentGroupChat.CreatePromptFunctionForStrategy(
$$$"""
Examine the RESPONSE and determine whether the content has
been deemed satisfactory.
If content is satisfactory, respond with a single word
without explanation: {{{TerminationToken}}}.
If specific suggestions are being provided, it is not
satisfactory.
If no correction is suggested, it is satisfactory.
RESPONSE:
{{$lastmessage}}
""",
safeParameterNames: "lastmessage");
AgentGroupChat chat =
new(agentReviewer, agentWriter)
{
ExecutionSettings = new AgentGroupChatSettings
{
SelectionStrategy =
new
KernelFunctionSelectionStrategy(selectionFunction, kernel)
{
// Always start with the editor agent.
InitialAgent = agentReviewer,
// Save tokens by only including the final
response
HistoryReducer = historyReducer,
// The prompt variable name for the history
argument.
HistoryVariableName = "lastmessage",
// Returns the entire result value as a string.
ResultParser = (result) =>
result.GetValue<string>() ?? agentReviewer.Name
},
TerminationStrategy =
new
KernelFunctionTerminationStrategy(terminationFunction, kernel)
{
// Only evaluate for editor's response
Agents = [agentReviewer],
// Save tokens by only including the final
response
HistoryReducer = historyReducer,
// The prompt variable name for the history
argument.
HistoryVariableName = "lastmessage",
// Limit total number of turns
MaximumIterations = 12,
// Customer result parser to determine if the
response is "yes"
ResultParser = (result) =>
result.GetValue<string>()?.Contains(TerminationToken,
StringComparison.OrdinalIgnoreCase) ?? false
}
}
};
Console.WriteLine("Ready!");
chat.AddChatMessage(new ChatMessageContent(AuthorRole.User,
input));
chat.IsComplete = false;
try
{
await foreach (ChatMessageContent response in
chat.InvokeAsync())
{
Console.WriteLine();
Console.WriteLine($"
{response.AuthorName.ToUpperInvariant()}:{Environment.NewLine}
{response.Content}");
}
}
catch (HttpOperationException exception)
{
Console.WriteLine(exception.Message);
if (exception.InnerException != null)
{
Console.WriteLine(exception.InnerException.Message);
if (exception.InnerException.Data.Count > 0)
{
Console.WriteLine(JsonSerializer.Serialize(exception.InnerException.Data,
new JsonSerializerOptions() { WriteIndented = true }));
}
}
}
} while (!isComplete);
}
clipProcess.StandardInput.Write(content);
clipProcess.StandardInput.Close();
}
}
}
Overview of the Process Framework
Article • 11/08/2024
7 Note
Key Features
Leverage Semantic Kernel: Steps can utilize one or multiple Kernel Functions,
enabling you to tap into all aspects of Semantic Kernel within your processes.
Reusability & Flexibility: Steps and processes can be reused across different
applications, promoting modularity and scalability.
Event-Driven Architecture: Utilize events and metadata to trigger actions and
transitions between process steps effectively.
Full Control and Auditability: Maintain control of processes in a defined and
repeatable manner, complete with audit capabilities through Open Telemetry.
Core Concepts
Process: A collection of steps arranged to achieve a specific business goal for
customers.
Step: An activity within a process that has defined inputs and outputs, contributing
to a larger goal.
Pattern: The specific sequence type that dictates how steps are executed for the
process to be fully completed.
1. Account Opening: This process includes multiple steps such as credit pulls and
ratings, fraud detection, creating customer accounts in core systems, and sending
welcome information to the customer, including their customer ID.
2. Food Delivery: Ordering food for delivery is a familiar process. From receiving the
order via phone, website, or app, to preparing each food item, ensuring quality
control, driver assignment, and final delivery, there are many steps in this process
that can be streamlined.
3. Support Ticket: We have all submitted support tickets—whether for new services,
IT support, or other needs. This process can involve multiple subprocesses based
on business and customer requirements, ultimately aiming for satisfaction by
addressing customer needs effectively.
Getting Started
Are you ready to harness the power of the Process Framework?
Begin your journey by exploring our .NET samples and Python Python samples on
GitHub.
By diving into the Process Framework, developers can transform traditional workflows
into intelligent, adaptive systems. Start building with the tools at your disposal and
redefine what's possible with AI-driven business processes.
Core Components of the Process
Framework
Article • 09/28/2024
The Process Framework is built upon a modular architecture that enables developers to
construct sophisticated workflows through its core components. Understanding these
components is essential for effectively leveraging the framework.
Process
A Process serves as the overarching container that orchestrates the execution of Steps. It
defines the flow and routing of data between Steps, ensuring that process goals are
achieved efficiently. Processes handle inputs and outputs, providing flexibility and
scalability across various workflows.
Process Features
Stateful: Supports querying information such as tracking status and percent
completion, as well as the ability to pause and resume.
Reusable: A Process can be invoked within other processes, promoting modularity
and reusability.
Event Driven: Employs event-based flow with listeners to route data to Steps and
other Processes.
Scalable: Utilizes well-established runtimes for global scalability and rollouts.
Cloud Event Integrated: Incorporates industry-standard eventing for triggering a
Process or Step.
Creating A Process
To create a new Process, add the Process Package to your project and define a name for
your process.
Step
Steps are the fundamental building blocks within a Process. Each Step corresponds to a
discrete unit of work and encapsulates one or more Kernel Functions. Steps can be
created independently of their use in specific Processes, enhancing their reusability.
They emit events based on the work performed, which can trigger subsequent Steps.
Step Features
Stateful: Facilitates tracking information such as status and defined tags.
Reusable: Steps can be employed across multiple Processes.
Dynamic: Steps can be created dynamically by a Process as needed, depending on
the required pattern.
Flexible: Offers different types of Steps for developers by leveraging Kernel
Functions, including Code-only, API calls, AI Agents, and Human-in-the-loop.
Auditable: Telemetry is enabled across both Steps and Processes.
Defining a Step
To create a Step, define a public class to name the Step and add it to the
KernelStepBase. Within your class, you can incorporate one or multiple Kernel Functions.
Step Events
Steps have several events available, including:
Pattern
Patterns standardize common process flows, simplifying the implementation of
frequently used operations. They promote a consistent approach to solving recurring
problems across various implementations, enhancing both maintainability and
readability.
Pattern Types
Fan In: The input for the next Step is supported by multiple outputs from previous
Steps.
Fan Out: The output of previous Steps is directed into multiple Steps further down
the Process.
Cycle: Steps continue to loop until completion based on input and output.
Map Reduce: Outputs from a Step are consolidated into a smaller amount and
directed to the next Step's input.
Setting up a Pattern
Once your class is created for your Step and registered within the Process, you can
define the events that should be sent downstream to other Steps or set conditions for
Steps to be restarted based on the output from your Step.
Deployment of the Process Framework
Article • 11/08/2024
Deploying workflows built with the Process Framework can be done seamlessly across
local development environments and cloud runtimes. This flexibility enables developers
to choose the best approach tailored to their specific use cases.
Local Development
The Process Framework provides an in-process runtime that allows developers to run
processes directly on their local machines or servers without requiring complex setups
or additional infrastructure. This runtime supports both memory and file-based
persistence, ideal for rapid development and debugging. You can quickly test processes
with immediate feedback, accelerating the development cycle and enhancing efficiency.
Cloud Runtimes
For scenarios requiring scalability and distributed processing, the Process Framework
supports cloud runtimes such as Orleans and Dapr . These options empower
developers to deploy processes in a distributed manner, facilitating high availability and
load balancing across multiple instances. By leveraging these cloud runtimes,
organizations can streamline their operations and manage substantial workloads with
ease.
Using either runtime, developers can scale applications according to demand, ensuring
that processes run smoothly and efficiently, regardless of workload.
With the flexibility to choose between local testing environments and robust cloud
platforms, the Process Framework is designed to meet diverse deployment needs. This
enables developers to concentrate on building innovative AI-powered processes without
the burden of infrastructure complexities.
7 Note
Dapr will be supported first with the Process Framework, followed by Orleans in an
upcoming release of the Process Framework.
Best Practices for the Process
Framework
Article • 09/28/2024
Utilizing the Process Framework effectively can significantly enhance your workflow
automation. Here are some best practices to help you optimize your implementation
and avoid common pitfalls.
An organized structure not only simplifies navigation within the project but also
enhances code reusability and facilitates collaboration among team members.
Common Pitfalls
To ensure smooth implementation and operation of the Process Framework, be mindful
of these common pitfalls to avoid:
Ignoring Event Handling: Events are vital for smooth communication between
Steps. Ensure that you handle all potential events and errors within the process to
prevent unexpected behavior or crashes.
By following these best practices, you can maximize the effectiveness of the Process
Framework, enabling more robust and manageable workflows. Keeping organization,
simplicity, and performance in mind will lead to a smoother development experience
and higher-quality applications.
Support for Semantic Kernel
Article • 10/10/2024
👋 Welcome! There are a variety of ways to get supported in the Semantic Kernel (SK)
world.
ノ Expand table
Read the docs This learning site is the home of the latest information for
developers
Visit the repo Our open-source GitHub repository is availble for perusal and
suggestions
Connect with the Semantic Visit our GitHub Discussions to get supported quickly with our
Kernel Team CoC actively enforced
Office Hours We will be hosting regular office hours; the calendar invites and
cadence are located here: Community.MD
Next step
Run the samples
Contributing to Semantic Kernel
Article • 09/24/2024
You can contribute to Semantic Kernel by submitting issues, starting discussions, and
submitting pull requests (PRs). Contributing code is greatly appreciated, but simply filing
issues for problems you encounter is also a great way to contribute since it helps us
focus our efforts.
Reporting issues
New issues for the SDK can be reported in our list of issues , but before you file a new
issue, please search the list of issues to make sure it does not already exist. If you have
issues with the Semantic Kernel documentation (this site), please file an issue in the
Semantic Kernel documentation repository .
If you do find an existing issue for what you wanted to report, please include your own
feedback in the discussion. We also highly recommend up-voting (👍 reaction) the
original post, as this helps us prioritize popular issues in our backlog.
Good bug reports make it easier for maintainers to verify and root cause the underlying
problem. The better a bug report, the faster the problem can be resolved. Ideally, a bug
report should contain the following information:
Create issue
Submitting feedback
If you have general feedback on Semantic Kernel or ideas on how to make it better,
please share it on our discussions board . Before starting a new discussion, please
search the list of discussions to make sure it does not already exist.
We recommend using the ideas category if you have a specific idea you would like to
share and the Q&A category if you have a question about Semantic Kernel.
You can also start discussions (and share any feedback you've created) in the Discord
community by joining the Semantic Kernel Discord server .
Start a discussion
If you think others would benefit from a feature, we also encourage you to ask others to
up-vote the issue. This helps us prioritize issues that are impacting the most users. You
can ask colleagues, friends, or the community on Discord to up-vote an issue by
sharing the link to the issue or discussion.
2. Create a personal fork of the repository on GitHub (if you don't already have one).
3. In your fork, create a branch off of main ( git checkout -b mybranch ).
8. Wait for feedback or approval of your changes from the code maintainers.
9. When area owners have signed off, and all checks are green, your PR will be
merged.
Do's:
Do follow the standard .NET coding style and Python code style
Do give priority to the current style of the project or file you're changing if it
diverges from the general guidelines.
Do include tests when adding new features. When fixing bugs, start with adding a
test that highlights how the current behavior is broken.
Do keep the discussions focused. When a new or related topic comes up it's often
better to create new issue than to side track the discussion.
Do clearly state on an issue that you are going to take on implementing it.
Do blog and/or tweet about your contributions!
Don'ts:
Don't surprise the team with big pull requests. We want to support contributors, so
we recommend filing an issue and starting a discussion so we can agree on a
direction before you invest a large amount of time.
Don't commit code that you didn't write. If you find code that you think is a good
fit to add to Semantic Kernel, file an issue and start a discussion before
proceeding.
Don't submit PRs that alter licensing related files or headers. If you believe there's
a problem with them, file an issue and we'll be happy to discuss it.
Don't make new APIs without filing an issue and discussing with the team first.
Adding new public surface area to a library is a big deal and we want to make sure
we get it right.
Breaking Changes
Contributions must maintain API signature and behavioral compatibility. If you want to
make a change that will break existing code, please file an issue to discuss your idea or
change if you believe that a breaking change is warranted. Otherwise, contributions that
include breaking changes will be rejected.
If the CI build fails for any reason, the PR issue will be updated with a link that can be
used to determine the cause of the failure so that it can be addressed.
Contributing to documentation
We also accept contributions to the Semantic Kernel documentation repository .
Running your own Hackathon
Article • 09/24/2024
With these materials you can run your own Semantic Kernel Hackathon, a hands-on
event where you can learn and create AI solutions using Semantic Kernel tools and
resources.
By participating and running a Semantic Kernel hackathon, you will have the opportunity
to:
Explore the features and capabilities of Semantic Kernel and how it can help you
solve problems with AI
Work in teams to brainstorm and develop your own AI plugins or apps using
Semantic Kernel SDK and services
Present your results and get feedback from other participants
Have fun!
Once you have unzipped the file, you will find the following resources:
Here is an approximate agenda and structure for each phase but feel free to modify this
based on your team:
ノ Expand table
Day 1
30 Overview of Semantic The facilitator will guide you through a live presentation
Kernel that will give you an overview of AI and why it is
important for solving problems in today's world. You will
also see demos of how Semantic Kernel can be used for
different scenarios.
5 Choose your Track Review slides in the deck for the specific track you’ll pick
for the hackathon.
120 Brainstorming The facilitator will help you form teams based on your
interests or skill levels. You will then brainstorm ideas for
your own AI plugins or apps using design thinking
techniques.
360+ Development/Hack You will use Semantic Kernel SDKs tools, and resources
to develop, test, and deploy your projects. This could be
Length Phase Description
(Minutes)
Day 2
20 What did you learn? Review what you’ve learned so far in Day 1 of the
Hackathon.
120 Hack You will use Semantic Kernel SDKs tools, and resources
to develop, test, and deploy your projects. This could be
for the rest of the day or over multiple days based on
the time available and problem to be solved.
120 Demo Each team will present their results using a PowerPoint
template provided. You will have about 15 minutes per
team to showcase your project, demonstrate how it
works, and explain how it solves a problem with AI. You
will also receive feedback from other participants.
If you want to continue developing your AI plugins or projects after the hackathon, you
can find more resources and support for Semantic Kernel.
Thank you for your engagement and creativity during the hackathon. We look forward
to seeing what you create next with Semantic Kernel!
Glossary for Semantic Kernel
Article • 06/24/2024
ノ Expand table
Term/Word Defintion
Agent An agent is an artificial intelligence that can answer questions and automate
processes for users. There's a wide spectrum of agents that can be built, ranging
from simple chat bots to fully automated AI assistants. With Semantic Kernel, we
provide you with the tools to build increasingly more sophisticated agents that
don't require you to be an AI expert.
API Application Programming Interface. A set of rules and specifications that allow
software components to communicate and exchange data.
Autonomous Agents that can respond to stimuli with minimal human intervention.
Kernel Similar to operating system, the kernel is responsible for managing resources that
are necessary to run "code" in an AI application. This includes managing the AI
models, services, and plugins that are necessary for both native code and AI
services to run together. Because the kernel has all the services and plugins
necessary to run both native code and AI services, it is used by nearly every
component within the Semantic Kernel SDK. This means that if you run any
prompt or code in Semantic Kernel, it will always go through a kernel.
LLM Large Language Models are Artificial Intelligence tools that can summarize, read
or generate text in the form of sentences similar to how a humans talk and write.
LLMs can be incorporate into various products at Microsoft to unearth richer user
value.
Memory Memories are a powerful way to provide broader context for your ask.
Historically, we've always called upon memory as a core component for how
computers work: think the RAM in your laptop. For with just a CPU that can
crunch numbers, the computer isn't that useful unless it knows what numbers you
care about. Memories are what make computation relevant to the task at hand.
Term/Word Defintion
Plugins To generate this plan, the copilot would first need the capabilities necessary to
perform these steps. This is where plugins come in. Plugins allow you to give your
agent skills via code. For example, you could create a plugin that sends emails,
retrieves information from a database, asks for help, or even saves and retrieves
memories from previous conversations.
Planners To use a plugin (and to wire them up with other steps), the copilot would need to
first generate a plan. This is where planners come in. Planners are special prompts
that allow an agent to generate a plan to complete a task. The simplest planners
are just a single prompt that helps the agent use function calling to complete a
task.
Prompts Prompts play a crucial role in communicating and directing the behavior of Large
Language Models (LLMs) AI. They serve as inputs or queries that users can
provide to elicit specific responses from a model.
Prompt Because of the amount of control that exists, prompt engineering is a critical skill
Engineering for anyone working with LLM AI models. It's also a skill that's in high demand as
more organizations adopt LLM AI models to automate tasks and improve
productivity. A good prompt engineer can help organizations get the most out of
their LLM AI models by designing prompts that produce the desired outputs.
RAG Retrieval Augmented Generation - a term that refers to the process of retrieving
additional data to provide as context to an LLM to use when generating a
response (completion) to a user’s question (prompt).
7 Note
This guide is intended to help you upgrade from a pre-v1 version of the .NET Semantic Kernel SDK
to v1+. The pre-v1 version used as a reference for this document was the 0.26.231009 version which
was the last version before the first beta release where the majority of the changes started to
happen.
Package Changes
As a result of many packages being redefined, removed and renamed, also considering that we did a
good cleanup and namespace simplification many of our old packages needed to be renamed,
deprecated and removed. The table below shows the changes in our packages.
All packages that start with Microsoft.SemanticKernel were truncated with a .. prefix for brevity.
ノ Expand table
..Connectors.AI.OpenAI ..Connectors.OpenAI v1
-- ..Connectors.Postgres alpha
Core
-- ..Experimental.Agents alpha
-- ..Experimental.Orchestration.Flow v1
C#
// Before
var retryConfig = new BasicRetryConfig
{
MaxRetryCount = 3,
UseExponentialBackoff = true,
};
retryConfig.RetryableStatusCodes.Add(HttpStatusCode.Unauthorized);
var kernel = new KernelBuilder().WithRetryBasic(retryConfig).Build();
C#
// After
builder.Services.ConfigureHttpClientDefaults(c =>
{
// Use a standard resiliency policy, augmented to retry on 401 Unauthorized for
this example
c.AddStandardResilienceHandler().Configure(o =>
{
o.Retry.ShouldHandle = args =>
ValueTask.FromResult(args.Outcome.Result?.StatusCode is HttpStatusCode.Unauthorized);
});
});
Package Removal and Changes Needed
Ensure that if you use any of the packages below you match the latest version that V1 uses:
ノ Expand table
Microsoft.Extensions.Configuration 8.0.0
Microsoft.Extensions.Configuration.Binder 8.0.0
Microsoft.Extensions.Configuration.EnvironmentVariables 8.0.0
Microsoft.Extensions.Configuration.Json 8.0.0
Microsoft.Extensions.Configuration.UserSecrets 8.0.0
Microsoft.Extensions.DependencyInjection 8.0.0
Microsoft.Extensions.DependencyInjection.Abstractions 8.0.0
Microsoft.Extensions.Http 8.0.0
Microsoft.Extensions.Http.Resilience 8.0.0
Microsoft.Extensions.Logging 8.0.0
Microsoft.Extensions.Logging.Abstractions 8.0.0
Microsoft.Extensions.Logging.Console 8.0.0
ノ Expand table
Skill Plugin
ノ Expand table
ContextVariables KernelArguments
ContextVariables.Set KernelArguments.Add
IImageGenerationService ITextToImageService
ITextCompletionService ITextGenerationService
Kernel.CreateSemanticFunction Kernel.CreateFunctionFromPrompt
Kernel.ImportFunctions Kernel.ImportPluginFrom____
Kernel.ImportSemanticFunctionsFromDirectory Kernel.ImportPluginFromPromptDirectory
Kernel.RunAsync Kernel.InvokeAsync
NativeFunction MethodFunction
OpenAIRequestSettings OpenAIPromptExecutionSettings
RequestSettings PromptExecutionSettings
SKException KernelException
SKFunction KernelFunction
SKFunctionMetadata KernelFunctionAttribute
SKJsonSchema KernelJsonSchema
SKParameterMetadata KernelParameterMetadata
SKPluginCollection KernelPluginCollection
SKReturnParameterMetadata KernelReturnParameterMetadata
SemanticFunction PromptFunction
Namespace Simplifications
The old namespaces before had a deep hierarchy matching 1:1 the directory names in the projects.
This is a common practice but did mean that consumers of the Semantic Kernel packages had to add
a lot of different using 's in their code. We decided to reduce the number of namespaces in the
Semantic Kernel packages so the majority of the functionality is in the main
Microsoft.SemanticKernel namespace. See below for more details.
ノ Expand table
Microsoft.SemanticKernel.Orchestration Microsoft.SemanticKernel
Microsoft.SemanticKernel.Connectors.AI.* Microsoft.SemanticKernel.Connectors.*
Microsoft.SemanticKernel.SemanticFunctions Microsoft.SemanticKernel
Microsoft.SemanticKernel.Events Microsoft.SemanticKernel
Microsoft.SemanticKernel.AI.* Microsoft.SemanticKernel.*
Microsoft.SemanticKernel.Connectors.AI.OpenAI.* Microsoft.SemanticKernel.Connectors.OpenAI
Microsoft.SemanticKernel.Connectors.AI.HuggingFace.* Microsoft.SemanticKernel.Connectors.HuggingFace
Kernel
The code to create and use a Kernel instance has been simplified. The IKernel interface has been
eliminated as developers should not need to create their own Kernel implementation. The Kernel
class represents a collection of services and plugins. The current Kernel instance is available
everywhere which is consistent with the design philosophy behind the Semantic Kernel.
C#
// Before
var textFunctions = kernel.ImportFunctions(new StaticTextPlugin(), "text");
// After
var textFunctions = kernel.ImportPluginFromObject(new StaticTextPlugin(), "text");
C#
// Before
KernelResult result = kernel.RunAsync(textFunctions["Uppercase"], "Hello World!");
// After
FunctionResult result = kernel.InvokeAsync(textFunctions["Uppercase"], new() {
["input"] = "Hello World!" });
Kernel.InvokeAsync only targets one function per call as first parameter. Pipelining is not
❌ Not supported
C#
C#
C#
return finalResult.GetValue<string>();
}
}
instead. The function now is the first argument and the input argument needs to be provided
as a KernelArguments instance.
C#
// Before
var result = await kernel.RunAsync("I missed the F1 final race", excuseFunction);
// After
var result = await kernel.InvokeAsync(excuseFunction, new() { ["input"] = "I
missed the F1 final race" });
Kernel.ImportPluginFromPromptDirectory .
Context Variables
ContextVariables was redefined as KernelArguments and is now a dictionary, where the key is the
name of the argument and the value is the value of the argument. Methods like Set and Get were
removed and the common dictionary Add or the indexer [] to set and get values should be used
instead.
C#
// Before
var variables = new ContextVariables("Today is: ");
variables.Set("day", DateTimeOffset.Now.ToString("dddd", CultureInfo.CurrentCulture));
// After
var arguments = new KernelArguments() {
["input"] = "Today is: ",
["day"] = DateTimeOffset.Now.ToString("dddd", CultureInfo.CurrentCulture)
};
Kernel Builder
Many changes were made to our KernelBuilder to make it more intuitive and easier to use, as well as
to make it simpler and more aligned with the .NET builders approach.
Creating a KernelBuilder can now be only created using the Kernel.CreateBuilder() method.
This change make it simpler and easier to use the KernelBuilder in any code-base ensureing
one main way of using the builder instead of multiple ways that adds complexity and
maintenance overhead.
C#
// Before
IKernel kernel = new KernelBuilder().Build();
// After
var builder = Kernel.CreateBuilder().Build();
WithAIService<ITextCompletion>
C#
Previously the KernelBuilder had a method WithAIService<T> that was removed and a new
ServiceCollection Services property is exposed to allow the developer to add services to the
C#
builder.Services.AddSingleton<ITextGenerationService>()
Kernel Result
As the Kernel became just a container for the plugins and now executes just one function there was
not more need to have a KernelResult entity and all function invocations from Kernel now return a
FunctionResult .
SKContext
After a lot of discussions and feedback internally and from the community, to simplify the API and
make it more intuitive, the SKContext concept was dilluted in different entities: KernelArguments for
function inputs and FunctionResult for function outputs.
With the important decision to make Kernel a required argument of a function calling, the
SKContext was removed and the KernelArguments and FunctionResult were introduced.
KernelArguments is a dictionary that holds the input arguments for the function invocation that were
FunctionResult is the output of the Kernel.InvokeAsync method and holds the result of the function
Plugins Immutability
Plugins are created by default as immutable by our out-of-the-box DefaultKernelPlugin
implementation, which means that they cannot be modified or changed after creation.
Also attempting to import the plugins that share the same name in the kernel will give you a key
violation exception.
The addition of the KernelPlugin abstraction allows dynamic implementations that may support
mutability and we provided an example on how to implement a mutable plugin in the Example 69 .
For mode details one the list of current released experimental features check here .
Each property name in the execution_settings once matched to the service_id will be used
to configure the service/model execution settings. i.e.:
C#
Before
JSON
{
"schema": 1,
"description": "Given a text input, continue it with additional text.",
"type": "completion",
"completion": {
"max_tokens": 4000,
"temperature": 0.3,
"top_p": 0.5,
"presence_penalty": 0.0,
"frequency_penalty": 0.0
},
"input": {
"parameters": [
{
"name": "input",
"description": "The text to continue.",
"defaultValue": ""
}
]
}
}
After
JSON
{
"schema": 1,
"description": "Given a text input, continue it with additional text.",
"execution_settings": {
"default": {
"max_tokens": 4000,
"temperature": 0.3,
"top_p": 0.5,
"presence_penalty": 0.0,
"frequency_penalty": 0.0
},
"service1": {
"model_id": "gpt-4",
"max_tokens": 200,
"temperature": 0.2,
"top_p": 0.0,
"presence_penalty": 0.0,
"frequency_penalty": 0.0,
"stop_sequences": ["Human", "AI"]
},
"service2": {
"model_id": "gpt-3.5_turbo",
"max_tokens": 256,
"temperature": 0.3,
"top_p": 0.0,
"presence_penalty": 0.0,
"frequency_penalty": 0.0,
"stop_sequences": ["Human", "AI"]
}
},
"input_variables": [
{
"name": "input",
"description": "The text to continue.",
"default": ""
}
]
}
OpenAI Connector Migration Guide
Article • 09/24/2024
Coming as part of the new 1.18 version of Semantic Kernel we migrated our OpenAI and
AzureOpenAI services to use the new OpenAI SDK v2.0 and Azure OpenAI SDK v2.0 SDKs.
As those changes were major breaking changes when implementing ours we looked
forward to break as minimal as possible the dev experience.
This guide prepares you for the migration that you may need to do to use our new
OpenAI Connector is a complete rewrite of the existing OpenAI Connector and is
designed to be more efficient, reliable, and scalable. This manual will guide you through
the migration process and help you understand the changes that have been made to
the OpenAI Connector.
Those changes are needed for anyone using OpenAI or AzureOpenAI connectors with
Semantic Kernel version 1.18.0-rc or above.
) Important
diff
Before
- using Microsoft.SemanticKernel.Connectors.OpenAI;
After
+ using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
1.1 AzureOpenAIClient
When using Azure with OpenAI, before where you were using OpenAIClient you will
need to update your code to use the new AzureOpenAIClient type.
1.2 Services
All services below now belong to the Microsoft.SemanticKernel.Connectors.AzureOpenAI
namespace.
AzureOpenAIAudioToTextService
AzureOpenAIChatCompletionService
AzureOpenAITextEmbeddingGenerationService
AzureOpenAITextToAudioService
AzureOpenAITextToImageService
If you were using OpenAI's gpt-3.5-turbo-instruct legacy model with any of the
OpenAITextGenerationService or AzureOpenAITextGenerationService you will need to
7 Note
tags: results_per_prompt
We have the two only specific cases where we attempted to auto-correct the endpoint.
diff
- http://any-host-and-port/v1/chat/completions
+ http://any-host-and-port/v1
2. If you provided a custom endpoint without any path. We won't be adding the v1/
as the first path. Now the v1 path needs to provided as part of your endpoint.
diff
- http://any-host-and-port/
+ http://any-host-and-port/v1
6. SemanticKernel MetaPackage
To be retrocompatible with the new OpenAI and AzureOpenAI Connectors, our
Microsoft.SemanticKernel meta package changed its dependency to use the new
7.1 OpenAIChatMessageContent
The Tools property type has changed from
IReadOnlyList<ChatCompletionsToolCall> to IReadOnlyList<ChatToolCall> .
7.2 OpenAIStreamingChatMessageContent
The FinishReason property type has changed from CompletionsFinishReason to
FinishReason .
The ToolCallUpdate property has been renamed to ToolCallUpdates and its type
has changed from StreamingToolCallUpdate? to
IReadOnlyList<StreamingToolCallUpdate>? .
The AuthorName property is not initialized because it's not provided by the
underlying library anymore.
semantic_kernel.connectors.openai.tokens.prompt
semantic_kernel.connectors.openai.tokens.completion
semantic_kernel.connectors.openai.tokens.total
C#
After
C#
Some of the keys in the content metadata dictionary have changed and removed.
Changed: Created -> CreatedAt
Changed: LogProbabilityInfo -> ContentTokenLogProbabilities
Changed: PromptFilterResults -> ContentFilterResultForPrompt
Changed: ContentFilterResultsForPrompt -> ContentFilterResultForResponse
Removed: FinishDetails
Removed: Index
Removed: Enhancements
The Token usage naming convention from OpenAI changed from Completion , Prompt
tokens to Output and Input respectively. You will need to update your code to use the
new naming.
diff
Before
- var usage = FunctionResult.Metadata?["Usage"] as CompletionsUsage;
- var completionTokesn = usage?.CompletionTokens;
- var promptTokens = usage?.PromptTokens;
After
+ var usage = FunctionResult.Metadata?["Usage"] as ChatTokenUsage;
+ var promptTokens = usage?.InputTokens;
+ var completionTokens = completionTokens: usage?.OutputTokens;
10.8 OpenAIClient
The OpenAIClient type previously was a Azure specific namespace type but now it is an
OpenAI SDK namespace type, you will need to update your code to use the new
OpenAIClient type.
When using Azure, you will need to update your code to use the new AzureOpenAIClient
type.
10.9 OpenAIClientOptions
The OpenAIClientOptions type previously was a Azure specific namespace type but now
it is an OpenAI SDK namespace type, you will need to update your code to use the new
AzureOpenAIClientOptions type if you are using the new AzureOpenAIClient with any of
diff
Semantic Kernel is gradually transitioning from the current function calling capabilities,
represented by the ToolCallBehavior class, to the new enhanced capabilities,
represented by the FunctionChoiceBehavior class. The new capability is service-agnostic
and is not tied to any specific AI service, unlike the current model. Therefore, it resides in
Semantic Kernel abstractions and will be used by all AI connectors working with
function-calling capable AI models.
This guide is intended to help you to migrate your code to the new function calling
capabilities.
Migrate
ToolCallBehavior.AutoInvokeKernelFunctions
behavior
The ToolCallBehavior.AutoInvokeKernelFunctions behavior is equivalent to the
FunctionChoiceBehavior.Auto behavior in the new model.
C#
// Before
var executionSettings = new OpenAIPromptExecutionSettings { ToolCallBehavior
= ToolCallBehavior.AutoInvokeKernelFunctions };
// After
var executionSettings = new OpenAIPromptExecutionSettings {
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() };
Migrate
ToolCallBehavior.EnableKernelFunctions
behavior
The ToolCallBehavior.EnableKernelFunctions behavior is equivalent to the
FunctionChoiceBehavior.Auto behavior with disabled auto invocation.
C#
// Before
var executionSettings = new OpenAIPromptExecutionSettings { ToolCallBehavior
= ToolCallBehavior.EnableKernelFunctions };
// After
var executionSettings = new OpenAIPromptExecutionSettings {
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(autoInvoke: false) };
Migrate ToolCallBehavior.EnableFunctions
behavior
The ToolCallBehavior.EnableFunctions behavior is equivalent to the
FunctionChoiceBehavior.Auto behavior that configured with list of functions with
C#
// Before
var executionSettings = new OpenAIPromptExecutionSettings() {
ToolCallBehavior = ToolCallBehavior.EnableFunctions(functions:
[function.Metadata.ToOpenAIFunction()]) };
// After
var executionSettings = new OpenAIPromptExecutionSettings {
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(functions: [function],
autoInvoke: false) };
Migrate ToolCallBehavior.RequireFunction
behavior
The ToolCallBehavior.RequireFunction behavior is equivalent to the
FunctionChoiceBehavior.Required behavior that configured with list of functions with
C#
// Before
var executionSettings = new OpenAIPromptExecutionSettings() {
ToolCallBehavior = ToolCallBehavior.RequireFunction(functions:
[function.Metadata.ToOpenAIFunction()]) };
// After
var executionSettings = new OpenAIPromptExecutionSettings {
FunctionChoiceBehavior = FunctionChoiceBehavior.Required(functions:
[function], autoInvoke: false) };
Both ways are supported at the moment by the current and new models. However, we
strongly recommend using the connector-agnostic approach to access function calls, as
it is more flexible and allows your code to work with any AI connector that supports the
new function-calling model. Moreover, considering that the current model will be
deprecated soon, now is a good time to migrate your code to the new model to avoid
breaking changes in the future.
So, if you use Manual Function Invocation with the connector-specific function call
classes like in this code snippet:
C#
using System.Text.Json;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using OpenAI.Chat;
C#
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
while (functionCalls.Length != 0)
{
// Adding function call from AI model to chat history
chatHistory.Add(messageContent);
chatHistory.Add(result.ToChatMessage());
}
The code snippets above demonstrate how to migrate your code that uses the OpenAI
AI connector. A similar migration process can be applied to the Gemini and Mistral AI
connectors when they are updated to support the new function calling model.
Next steps
Now after you have migrated your code to the new function calling model, you can
proceed to learn how to configure various aspects of the model that might better
correspond to your specific scenarios by referring to the function choice behaviors
article
Plan generation
Following code shows how to generate a new plan with Auto Function Calling by using
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() . After sending a request to AI
model, the plan will be located in ChatHistory object where a message with Assistant
role will contain a list of functions (steps) to call.
Old approach:
C#
New approach:
C#
IChatCompletionService chatCompletionService =
kernel.GetRequiredService<IChatCompletionService>();
ChatHistory chatHistory = [];
chatHistory.AddUserMessage("Check current UTC time and return current
weather in Boston city.");
await chatCompletionService.GetChatMessageContentAsync(chatHistory,
executionSettings, kernel);
only result is needed without plan steps. In this case, Kernel object can be used to pass
a goal to InvokePromptAsync method. The result of plan execution will be located in
FunctionResult object.
Old approach:
C#
New approach:
C#
Old approach:
C#
New approach:
C#
IChatCompletionService chatCompletionService =
kernel.GetRequiredService<IChatCompletionService>();
The code snippets above demonstrate how to migrate your code that uses Stepwise
Planner to use Auto Function Calling. Learn more about Function Calling with chat
completion.
Migrating from Memory Stores to
Vector Stores
Article • 11/11/2024
Semantic Kernel provides two distinct abstractions for interacting with vector stores.
The Vector Store abstractions provide more functionality than what the Memory Store
abstractions provide, e.g. being able to define your own schema, supporting multiple
vectors per record (database permitting), supporting more vector types than
ReadOnlyMemory<float> , etc. We recommend using the Vector Store abstractions instead
Tip
For a more detailed comparison of the Memory Store and Vector Store abstractions
see here.
Microsoft takes the security of our software products and services seriously, which
includes all source code repositories managed through our GitHub organizations, which
include Microsoft , Azure , DotNet , AspNet , Xamarin , and our GitHub
organizations .
If you believe you have found a security vulnerability in any Microsoft-owned repository
that meets Microsoft's definition of a security vulnerability , please report it to us as
described below.
Instead, please report them to the Microsoft Security Response Center (MSRC) at
https://msrc.microsoft.com/create-report .
You should receive a response within 24 hours. If for some reason you do not, please
follow up via email to ensure we received your original message. Additional information
can be found at microsoft.com/msrc .
Please include the requested information listed below (as much as you can provide) to
help us better understand the nature and scope of the possible issue:
Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
Full paths of source file(s) related to the manifestation of the issue
The location of the affected source code (tag/branch/commit or direct URL)
Any special configuration required to reproduce the issue
Step-by-step instructions to reproduce the issue
Proof-of-concept or exploit code (if possible)
Impact of the issue, including how an attacker might exploit the issue
Preferred Languages
We prefer all communications to be in English.
Policy
Microsoft follows the principle of Coordinated Vulnerability Disclosure .
Microsoft.SemanticKernel Namespace
Reference
Classes
ノ Expand table
FunctionChoiceBehavior Represents the base class for different function choice behaviors.
These behaviors define the way functions are chosen by AI model
and various aspects of their invocation by AI connectors.
KernelException Represents the base exception from which all Semantic Kernel
exceptions derive.
Interfaces
ノ Expand table
IPromptTemplate Represents a factory for prompt templates for one or more prompt
Factory template formats.
This is the main entry point for Semantic Kernel. It provides the ability to run functions
and manage filters, plugins, and AI services.
Constructor
Python
Parameters
ノ Expand table
Name Description
plugins The plugins to be used by the kernel, will be rewritten to a dict with
plugin name as key
Default value: None
services The services to be used by the kernel, will be rewritten to a dict with
service_id as key
Name Description
**kwargs Additional fields to be passed to the Kernel model, these are limited to
Required* retry_mechanism and function_invoking_handlers and
function_invoked_handlers, the best way to add
function_invoking_handlers and function_invoked_handlers is to use the
add_function_invoking_handler and add_function_invoked_handler
methods.
Keyword-Only Parameters
ノ Expand table
Name Description
retry_mechanism
Required*
function_invocation_filters
Required*
prompt_rendering_filters
Required*
auto_function_invocation_filters
Required*
Methods
ノ Expand table
add_embedding_to_object Gather all fields to embed, batch the embedding generation and
store.
This will execute the functions in the order they are provided, if a list
of functions is provided. When multiple functions are provided only
the last one is streamed, the rest is executed as a pipeline.
add_embedding_to_object
Gather all fields to embed, batch the embedding generation and store.
Python
Parameters
ノ Expand table
Name Description
inputs
Required*
field_to_embed
Required*
field_to_store
Required*
execution_settings
Required*
invoke
Execute a function and return the FunctionResult.
Python
async invoke(function: KernelFunction | None = None, arguments:
KernelArguments | None = None, function_name: str | None = None,
plugin_name: str | None = None, metadata: dict[str, Any] = {}, **kwargs:
Any) -> FunctionResult | None
Parameters
ノ Expand table
Name Description
function <xref:semantic_kernel.kernel.KernelFunction>
The function or functions to execute, this value has precedence when
supplying both this and using function_name and plugin_name, if this is
none, function_name and plugin_name are used and cannot be None.
Default value: None
arguments <xref:semantic_kernel.kernel.KernelArguments>
The arguments to pass to the function(s), optional
Default value: None
Exceptions
ノ Expand table
Type Description
invoke_function_call
Processes the provided FunctionCallContent and updates the chat history.
Python
Parameters
ノ Expand table
Name Description
function_call
Required*
chat_history
Required*
invoke_prompt
Invoke a function from the provided prompt.
Python
Parameters
ノ Expand table
Name Description
prompt str
Required* The prompt to use
function_name str
The name of the function, optional
Default value: None
plugin_name str
The name of the plugin, optional
Default value: None
Returns
ノ Expand table
Type Description
invoke_prompt_stream
Invoke a function from the provided prompt and stream the results.
Python
Parameters
ノ Expand table
Name Description
prompt str
Required* The prompt to use
function_name str
The name of the function, optional
Default value: None
plugin_name str
The name of the plugin, optional
Default value: None
return_function_results bool
If True, the function results are yielded as a list[FunctionResult]
Default value: False
Returns
ノ Expand table
Type Description
invoke_stream
Execute one or more stream functions.
This will execute the functions in the order they are provided, if a list of functions is
provided. When multiple functions are provided only the last one is streamed, the
rest is executed as a pipeline.
Python
Parameters
ノ Expand table
Name Description
function <xref:semantic_kernel.kernel.KernelFunction>
The function to execute, this value has precedence when
supplying both this and using function_name and
plugin_name, if this is none, function_name and plugin_name
are used and cannot be None.
Default value: None
return_function_results bool
If True, the function results are yielded as a list[FunctionResult]
Default value: False
Attributes
model_computed_fields
A dictionary of computed field names and their corresponding ComputedFieldInfo
objects.
Python
model_config
Configuration for the model, should be a dictionary conforming to [ConfigDict]
[pydantic.config.ConfigDict].
Python
model_fields
Metadata about the fields defined on the model, mapping of field names to
[FieldInfo][pydantic.fields.FieldInfo] objects.
Python
function_invocation_filters
Filters applied during function invocation, from KernelFilterExtension.
Python
function_invocation_filters: list[tuple[int,
Callable[[FILTER_CONTEXT_TYPE, Callable[[FILTER_CONTEXT_TYPE], None]],
None]]]
prompt_rendering_filters
Filters applied during prompt rendering, from KernelFilterExtension.
Python
auto_function_invocation_filters
Filters applied during auto function invocation, from KernelFilterExtension.
Python
auto_function_invocation_filters: list[tuple[int,
Callable[[FILTER_CONTEXT_TYPE, Callable[[FILTER_CONTEXT_TYPE], None]],
None]]]
plugins
A dict with the plugins registered with the Kernel, from KernelFunctionExtension.
Python
plugins: dict[str, KernelPlugin]
services
A dict with the services registered with the Kernel, from KernelServicesExtension.
Python
ai_service_selector
The AI service selector to be used by the kernel, from KernelServicesExtension.
Python
ai_service_selector: AIServiceSelector
retry_mechanism
The retry mechanism to be used by the kernel, from KernelReliabilityExtension.
Python
retry_mechanism: RetryMechanismBase
com.microsoft.semantickernel
Reference
Package: com.microsoft.semantickernel
Maven Artifact: com.microsoft.semantic-kernel:semantickernel-api:1.4.0
Classes
ノ Expand table
Feedback
Was this page helpful? Yes No