Skip to content

Commit b4c853e

Browse files
authored
.NET: Add a ModeProvider for managing agent modes (#5247)
* Add a ModeProvider for managing agent modes * Fix typo * Fix typo * Fix typo * Address PR comments
1 parent 673f3d9 commit b4c853e

4 files changed

Lines changed: 513 additions & 1 deletion

File tree

dotnet/src/Microsoft.Agents.AI/AgentJsonUtilities.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,17 @@ private static JsonSerializerOptions CreateDefaultOptions()
7070
[JsonSerializable(typeof(TextSearchProvider.TextSearchProviderState))]
7171
[JsonSerializable(typeof(ChatHistoryMemoryProvider.State))]
7272

73-
// Harness types
73+
// TodoProvider types
7474
[JsonSerializable(typeof(TodoState))]
7575
[JsonSerializable(typeof(TodoItem))]
7676
[JsonSerializable(typeof(TodoItemInput))]
7777
[JsonSerializable(typeof(List<int>), TypeInfoPropertyName = "IntList")]
7878
[JsonSerializable(typeof(List<TodoItem>), TypeInfoPropertyName = "TodoItemList")]
7979
[JsonSerializable(typeof(List<TodoItemInput>), TypeInfoPropertyName = "TodoItemInputList")]
8080

81+
// AgentModeProvider types
82+
[JsonSerializable(typeof(AgentModeState))]
83+
8184
[ExcludeFromCodeCoverage]
8285
internal sealed partial class JsonContext : JsonSerializerContext;
8386
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Diagnostics.CodeAnalysis;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.Extensions.AI;
9+
using Microsoft.Shared.DiagnosticIds;
10+
11+
namespace Microsoft.Agents.AI;
12+
13+
/// <summary>
14+
/// An <see cref="AIContextProvider"/> that tracks the agent's operating mode (e.g., "plan" or "execute")
15+
/// in the session state and provides tools for querying and switching modes.
16+
/// </summary>
17+
/// <remarks>
18+
/// <para>
19+
/// The <see cref="AgentModeProvider"/> enables agents to operate in distinct modes during long-running
20+
/// complex tasks. The current mode is persisted in the session's <see cref="AgentSessionStateBag"/>
21+
/// and is included in the instructions provided to the agent on each invocation.
22+
/// </para>
23+
/// <para>
24+
/// This provider exposes the following tools to the agent:
25+
/// <list type="bullet">
26+
/// <item><description><c>SetMode</c> — Switch the agent's operating mode.</description></item>
27+
/// <item><description><c>GetMode</c> — Retrieve the agent's current operating mode.</description></item>
28+
/// </list>
29+
/// </para>
30+
/// <para>
31+
/// Public helper methods <see cref="GetMode"/> and <see cref="SetMode"/> allow external code
32+
/// to programmatically read and change the mode.
33+
/// </para>
34+
/// </remarks>
35+
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
36+
public sealed class AgentModeProvider : AIContextProvider
37+
{
38+
/// <summary>
39+
/// The "plan" mode, indicating the agent is planning work.
40+
/// </summary>
41+
public const string PlanMode = "plan";
42+
43+
/// <summary>
44+
/// The "execute" mode, indicating the agent is executing work.
45+
/// </summary>
46+
public const string ExecuteMode = "execute";
47+
48+
private readonly ProviderSessionState<AgentModeState> _sessionState;
49+
private IReadOnlyList<string>? _stateKeys;
50+
51+
/// <summary>
52+
/// Initializes a new instance of the <see cref="AgentModeProvider"/> class.
53+
/// </summary>
54+
public AgentModeProvider()
55+
{
56+
this._sessionState = new ProviderSessionState<AgentModeState>(
57+
_ => new AgentModeState(),
58+
this.GetType().Name,
59+
AgentJsonUtilities.DefaultOptions);
60+
}
61+
62+
/// <inheritdoc />
63+
public override IReadOnlyList<string> StateKeys => this._stateKeys ??= [this._sessionState.StateKey];
64+
65+
/// <summary>
66+
/// Gets the current operating mode from the session state.
67+
/// </summary>
68+
/// <param name="session">The agent session to read the mode from.</param>
69+
/// <returns>The current mode string.</returns>
70+
public string GetMode(AgentSession? session)
71+
{
72+
return this._sessionState.GetOrInitializeState(session).CurrentMode;
73+
}
74+
75+
/// <summary>
76+
/// Sets the operating mode in the session state.
77+
/// </summary>
78+
/// <param name="session">The agent session to update the mode in.</param>
79+
/// <param name="mode">The new mode to set.</param>
80+
public void SetMode(AgentSession? session, string mode)
81+
{
82+
if (mode != PlanMode && mode != ExecuteMode)
83+
{
84+
throw new ArgumentException($"Invalid mode: {mode}. Supported modes are \"{PlanMode}\" and \"{ExecuteMode}\".", nameof(mode));
85+
}
86+
87+
AgentModeState state = this._sessionState.GetOrInitializeState(session);
88+
state.CurrentMode = mode;
89+
this._sessionState.SaveState(session, state);
90+
}
91+
92+
/// <inheritdoc />
93+
protected override ValueTask<AIContext> ProvideAIContextAsync(InvokingContext context, CancellationToken cancellationToken = default)
94+
{
95+
AgentModeState state = this._sessionState.GetOrInitializeState(context.Session);
96+
97+
string instructions = $"""
98+
You are currently operating in "{state.CurrentMode}" mode.
99+
Available modes:
100+
- "plan": Use this mode when analyzing requirements, breaking down tasks, and creating plans.
101+
- "execute": Use this mode when implementing changes, writing code, and carrying out planned work.
102+
Use the SetMode tool to switch between modes as your work progresses. Only use SetMode if the user explicitly instructs you to change modes.
103+
Use the GetMode tool to check your current operating mode.
104+
""";
105+
106+
return new ValueTask<AIContext>(new AIContext
107+
{
108+
Instructions = instructions,
109+
Tools = this.CreateTools(state, context.Session),
110+
});
111+
}
112+
113+
private AITool[] CreateTools(AgentModeState state, AgentSession? session)
114+
{
115+
var serializerOptions = AgentJsonUtilities.DefaultOptions;
116+
117+
return
118+
[
119+
AIFunctionFactory.Create(
120+
(string mode) =>
121+
{
122+
if (mode != PlanMode && mode != ExecuteMode)
123+
{
124+
throw new ArgumentException($"Invalid mode: {mode}. Supported modes are \"{PlanMode}\" and \"{ExecuteMode}\".", nameof(mode));
125+
}
126+
127+
state.CurrentMode = mode;
128+
this._sessionState.SaveState(session, state);
129+
return $"Mode changed to \"{mode}\".";
130+
},
131+
new AIFunctionFactoryOptions
132+
{
133+
Name = "SetMode",
134+
Description = "Switch the agent's operating mode. Supported modes: \"plan\" and \"execute\".",
135+
SerializerOptions = serializerOptions,
136+
}),
137+
138+
AIFunctionFactory.Create(
139+
() => state.CurrentMode,
140+
new AIFunctionFactoryOptions
141+
{
142+
Name = "GetMode",
143+
Description = "Get the agent's current operating mode.",
144+
SerializerOptions = serializerOptions,
145+
}),
146+
];
147+
}
148+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using System.Diagnostics.CodeAnalysis;
4+
using System.Text.Json.Serialization;
5+
using Microsoft.Shared.DiagnosticIds;
6+
7+
namespace Microsoft.Agents.AI;
8+
9+
/// <summary>
10+
/// Represents the state of the agent's operating mode, stored in the session's <see cref="AgentSessionStateBag"/>.
11+
/// </summary>
12+
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
13+
internal sealed class AgentModeState
14+
{
15+
/// <summary>
16+
/// Gets or sets the current operating mode of the agent.
17+
/// </summary>
18+
[JsonPropertyName("currentMode")]
19+
public string CurrentMode { get; set; } = AgentModeProvider.PlanMode;
20+
}

0 commit comments

Comments
 (0)