Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dotnet/agent-framework-dotnet.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
<Folder Name="/Samples/02-agents/AgentSkills/">
<File Path="samples/02-agents/AgentSkills/README.md" />
<Project Path="samples/02-agents/AgentSkills/Agent_Step01_FileBasedSkills/Agent_Step01_FileBasedSkills.csproj" />
<Project Path="samples/02-agents/AgentSkills/Agent_Step02_CodeDefinedSkills/Agent_Step02_CodeDefinedSkills.csproj" />
</Folder>
<Folder Name="/Samples/02-agents/AGUI/Step05_StateManagement/">
<Project Path="samples/02-agents/AGUI/Step05_StateManagement/Client/Client.csproj" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>

<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<NoWarn>$(NoWarn);MAAI001</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" />
<PackageReference Include="Azure.Identity" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) Microsoft. All rights reserved.

// This sample demonstrates how to define Agent Skills entirely in code using AgentInlineSkill.
// No SKILL.md files are needed — skills, resources, and scripts are all defined programmatically.
//
// Three approaches are shown using a unit-converter skill:
// 1. Static resources — inline content provided via AddResource
// 2. Dynamic resources — computed at runtime via a factory delegate
// 3. Code scripts — executable delegates the agent can invoke directly

using System.Text.Json;
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using OpenAI.Responses;

// --- Configuration ---
string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";

// --- Build the code-defined skill ---
var unitConverterSkill = new AgentInlineSkill(
name: "unit-converter",
description: "Convert between common units using a multiplication factor. Use when asked to convert miles, kilometers, pounds, or kilograms.",
instructions: """
Use this skill when the user asks to convert between units.

1. Review the conversion-table resource to find the factor for the requested conversion.
2. Check the conversion-policy resource for rounding and formatting rules.
3. Use the convert script, passing the value and factor from the table.
""")
// 1. Static Resource: conversion tables
.AddResource(
"conversion-table",
"""
# Conversion Tables

Formula: **result = value × factor**

| From | To | Factor |
|-------------|-------------|----------|
| miles | kilometers | 1.60934 |
| kilometers | miles | 0.621371 |
| pounds | kilograms | 0.453592 |
| kilograms | pounds | 2.20462 |
""")
// 2. Dynamic Resource: conversion policy (computed at runtime)
.AddResource("conversion-policy", () =>
{
const int Precision = 4;
return $"""
# Conversion Policy

**Decimal places:** {Precision}
**Format:** Always show both the original and converted values with units
**Generated at:** {DateTime.UtcNow:O}
""";
})
// 3. Code Script: convert
.AddScript("convert", (double value, double factor) =>
{
double result = Math.Round(value * factor, 4);
return JsonSerializer.Serialize(new { value, factor, result });
});

// --- Skills Provider ---
var skillsProvider = new AgentSkillsProvider(unitConverterSkill);

// --- Agent Setup ---
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
.GetResponsesClient()
.AsAIAgent(new ChatClientAgentOptions
{
Name = "UnitConverterAgent",
ChatOptions = new()
{
Instructions = "You are a helpful assistant that can convert units.",
},
AIContextProviders = [skillsProvider],
},
model: deploymentName);

// --- Example: Unit conversion ---
Console.WriteLine("Converting units with code-defined skills");
Console.WriteLine(new string('-', 60));

AgentResponse response = await agent.RunAsync(
"How many kilometers is a marathon (26.2 miles)? And how many pounds is 75 kilograms?");

Console.WriteLine($"Agent: {response.Text}");
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Code-Defined Agent Skills Sample

This sample demonstrates how to define **Agent Skills entirely in code** using `AgentInlineSkill`.

## What it demonstrates

- Creating skills programmatically with `AgentInlineSkill` — no SKILL.md files needed
- **Static resources** via `AddResource` with inline content
- **Dynamic resources** via `AddResource` with a factory delegate (computed at runtime)
- **Code scripts** via `AddScript` with a delegate handler
- Using the `AgentSkillsProvider` constructor with inline skills

## Skills Included

### unit-converter (code-defined)

Converts between common units using multiplication factors. Defined entirely in C# code:

- `conversion-table` — Static resource with factor table
- `conversion-policy` — Dynamic resource with formatting rules (generated at runtime)
- `convert` — Script that performs `value × factor` conversion

## Running the Sample

### Prerequisites

- .NET 10.0 SDK
- Azure OpenAI endpoint with a deployed model

### Setup

```bash
export AZURE_OPENAI_ENDPOINT="https://your-endpoint.openai.azure.com/"
export AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini"
```

### Run

```bash
dotnet run
```

### Expected Output

```
Converting units with code-defined skills
------------------------------------------------------------
Agent: Here are your conversions:

1. **26.2 miles → 42.16 km** (a marathon distance)
2. **75 kg → 165.35 lbs**
```
19 changes: 18 additions & 1 deletion dotnet/samples/02-agents/AgentSkills/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
# AgentSkills Samples

Samples demonstrating Agent Skills capabilities.
Samples demonstrating Agent Skills capabilities. Each sample shows a different way to define and use skills.

| Sample | Description |
|--------|-------------|
| [Agent_Step01_FileBasedSkills](Agent_Step01_FileBasedSkills/) | Define skills as `SKILL.md` files on disk with reference documents. Uses a unit-converter skill. |
| [Agent_Step02_CodeDefinedSkills](Agent_Step02_CodeDefinedSkills/) | Define skills entirely in C# code using `AgentInlineSkill`, with static/dynamic resources and scripts. |

## Key Concepts

### File-Based vs Code-Defined Skills

| Aspect | File-Based | Code-Defined |
|--------|-----------|--------------|
| Definition | `SKILL.md` files on disk | `AgentInlineSkill` instances in C# |
| Resources | All files in skill directory (filtered by extension) | `AddResource` (static value or delegate-backed) |
| Scripts | Supported via script executor delegate | `AddScript` delegates |
| Discovery | Automatic from directory path | Explicit via constructor |
| Dynamic content | No (static files only) | Yes (factory delegates) |
| Reusability | Copy skill directory | Inline or shared instances |

For single-source scenarios, use the `AgentSkillsProvider` constructors directly. To combine multiple skill types, use the `AgentSkillsProviderBuilder`.

35 changes: 35 additions & 0 deletions dotnet/src/Microsoft.Agents.AI/Skills/AgentInMemorySkillsSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;

namespace Microsoft.Agents.AI;

/// <summary>
/// A skill source that holds <see cref="AgentSkill"/> instances in memory.
/// </summary>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
internal sealed class AgentInMemorySkillsSource : AgentSkillsSource
{
private readonly List<AgentSkill> _skills;

/// <summary>
/// Initializes a new instance of the <see cref="AgentInMemorySkillsSource"/> class.
/// </summary>
/// <param name="skills">The skills to include in this source.</param>
public AgentInMemorySkillsSource(IEnumerable<AgentSkill> skills)
{
this._skills = Throw.IfNull(skills).ToList();
}
Comment thread
SergeyMenshykh marked this conversation as resolved.

/// <inheritdoc/>
public override Task<IList<AgentSkill>> GetSkillsAsync(CancellationToken cancellationToken = default)
{
return Task.FromResult<IList<AgentSkill>>(this._skills);
Comment thread
SergeyMenshykh marked this conversation as resolved.
}
}
5 changes: 4 additions & 1 deletion dotnet/src/Microsoft.Agents.AI/Skills/AgentSkill.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ namespace Microsoft.Agents.AI;
/// <remarks>
/// <para>
/// A skill represents a domain-specific capability with instructions, resources, and scripts.
/// Concrete implementations include <see cref="AgentFileSkill"/> (filesystem-backed).
/// Concrete implementations include <see cref="AgentFileSkill"/> (filesystem-backed)
/// and <see cref="AgentInlineSkill"/> (code-defined).
/// </para>
/// <para>
/// Skill metadata follows the <see href="https://agentskills.io/specification">Agent Skills specification</see>.
Expand All @@ -35,6 +36,8 @@ public abstract class AgentSkill
/// </summary>
/// <remarks>
/// For file-based skills this is the raw SKILL.md file content.
/// For code-defined skills this is a synthesized XML document
/// containing name, description, and body (instructions, resources, scripts).
/// </remarks>
public abstract string Content { get; }

Expand Down
32 changes: 32 additions & 0 deletions dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,38 @@ public AgentSkillsProvider(
{
}

/// <summary>
/// Initializes a new instance of the <see cref="AgentSkillsProvider"/> class
/// with one or more inline (code-defined) skills.
/// Duplicate skill names are automatically deduplicated (first occurrence wins).
/// </summary>
/// <param name="skills">The inline skills to include.</param>
public AgentSkillsProvider(params AgentInlineSkill[] skills)
: this(skills as IEnumerable<AgentInlineSkill>)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="AgentSkillsProvider"/> class
/// with inline (code-defined) skills.
/// Duplicate skill names are automatically deduplicated (first occurrence wins).
/// </summary>
/// <param name="skills">The inline skills to include.</param>
/// <param name="options">Optional provider configuration.</param>
/// <param name="loggerFactory">Optional logger factory.</param>
public AgentSkillsProvider(
IEnumerable<AgentInlineSkill> skills,
AgentSkillsProviderOptions? options = null,
ILoggerFactory? loggerFactory = null)
: this(
new DeduplicatingAgentSkillsSource(
new AgentInMemorySkillsSource(Throw.IfNull(skills)),
loggerFactory),
options,
loggerFactory)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="AgentSkillsProvider"/> class
/// from a custom <see cref="AgentSkillsSource"/>. Unlike other constructors, this one does not
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ namespace Microsoft.Agents.AI;
/// Fluent builder for constructing an <see cref="AgentSkillsProvider"/> backed by a composite source.
/// </summary>
/// <remarks>
/// <para>
/// Use this builder to combine multiple skill sources into a single provider:
/// </para>
/// <code>
/// var provider = new AgentSkillsProviderBuilder()
/// .UseFileSkills("/path/to/skills")
/// .UseSkills(myInlineSkill1, myInlineSkill2)
/// .Build();
/// </code>
/// </remarks>
Expand Down Expand Up @@ -65,6 +69,40 @@ public AgentSkillsProviderBuilder UseFileSkills(IEnumerable<string> skillPaths,
return this;
}

/// <summary>
/// Adds a single skill.
/// </summary>
/// <param name="skill">The skill to add.</param>
/// <returns>This builder instance for chaining.</returns>
public AgentSkillsProviderBuilder UseSkill(AgentSkill skill)
{
return this.UseSkills(skill);
}

/// <summary>
/// Adds one or more skills.
/// </summary>
/// <param name="skills">The skills to add.</param>
/// <returns>This builder instance for chaining.</returns>
public AgentSkillsProviderBuilder UseSkills(params AgentSkill[] skills)
{
var source = new AgentInMemorySkillsSource(skills);
this._sourceFactories.Add((_, _) => source);
return this;
}

/// <summary>
/// Adds skills from the specified collection.
/// </summary>
/// <param name="skills">The skills to add.</param>
/// <returns>This builder instance for chaining.</returns>
public AgentSkillsProviderBuilder UseSkills(IEnumerable<AgentSkill> skills)
{
var source = new AgentInMemorySkillsSource(skills);
this._sourceFactories.Add((_, _) => source);
return this;
}

/// <summary>
/// Adds a custom skill source.
/// </summary>
Expand Down
Loading
Loading