Skip to content

Agents API

The Strategos.Agents package provides integration with Microsoft.Extensions.AI for LLM-powered workflow steps.

IAgentStep<TState>

Base interface for LLM-powered workflow steps. Extends IWorkflowStep<TState> with agent-specific context.

Methods

MethodParametersReturnsDescription
ExecuteAsyncTState state, AgentStepContext context, CancellationToken ctTask<StepResult<TState>>Executes the agent step

Example

csharp
public class AnalyzeDocumentStep : IAgentStep<DocumentState>
{
    private readonly IChatClient _chatClient;

    public AnalyzeDocumentStep(IChatClient chatClient)
    {
        _chatClient = chatClient;
    }

    public async Task<StepResult<DocumentState>> ExecuteAsync(
        DocumentState state,
        AgentStepContext context,
        CancellationToken ct)
    {
        var response = await _chatClient.GetResponseAsync(
            $"Analyze this document: {state.Content}",
            ct);

        return state
            .With(s => s.Analysis, response)
            .AsResult();
    }
}

AgentStepContext

Extended execution context for agent steps. Inherits all properties from StepContext.

Properties

PropertyTypeDescription
WorkflowIdGuidWorkflow instance identifier
CorrelationIdstringCorrelation ID for tracing
TimestampDateTimeOffsetWhen the step execution started
PhasestringCurrent workflow phase name
StepNamestringCurrent step name
MetadataIReadOnlyDictionary<string, object>Additional context data
ConversationThreadIConversationThread?Conversation history access
StreamingCallbackIStreamingCallback?Real-time token streaming
BudgetStatusBudgetStatusCurrent resource budget

Example

csharp
public async Task<StepResult<ChatState>> ExecuteAsync(
    ChatState state,
    AgentStepContext context,
    CancellationToken ct)
{
    // Access conversation history
    var history = context.ConversationThread?.GetMessages();

    // Check budget before expensive operation
    if (context.BudgetStatus.Level == ScarcityLevel.Critical)
    {
        return state.With(s => s.Response, "Budget exhausted").AsResult();
    }

    // Stream response tokens
    await foreach (var chunk in _chatClient.GetStreamingResponseAsync(...))
    {
        context.StreamingCallback?.OnToken(chunk.Text);
    }

    // ...
}

IConversationalState

Interface for workflow state that includes conversation history.

Properties

PropertyTypeDescription
MessagesIReadOnlyList<ChatMessage>Conversation history

Example

csharp
[WorkflowState]
public record ChatState : IConversationalState
{
    public string Query { get; init; }
    public string Response { get; init; }

    [Append]
    public List<ChatMessage> Messages { get; init; } = new();

    IReadOnlyList<ChatMessage> IConversationalState.Messages => Messages;
}

IConversationThread

Interface for accessing and managing conversation history.

Methods

MethodParametersReturnsDescription
GetMessages-IReadOnlyList<ChatMessage>Gets full conversation history
GetRecentMessagesint countIReadOnlyList<ChatMessage>Gets N most recent messages
AddMessageChatMessage messagevoidAppends message to history

IStreamingCallback

Callback interface for real-time token streaming.

Methods

MethodParametersReturnsDescription
OnTokenstring tokenvoidCalled for each streamed token
OnComplete-voidCalled when streaming completes
OnErrorException errorvoidCalled on streaming error

Example

csharp
public class StreamingStep : IAgentStep<ChatState>
{
    private readonly IChatClient _chatClient;

    public async Task<StepResult<ChatState>> ExecuteAsync(
        ChatState state,
        AgentStepContext context,
        CancellationToken ct)
    {
        var fullResponse = new StringBuilder();

        await foreach (var chunk in _chatClient.GetStreamingResponseAsync(
            state.Query, ct))
        {
            fullResponse.Append(chunk.Text);
            context.StreamingCallback?.OnToken(chunk.Text);
        }

        context.StreamingCallback?.OnComplete();

        return state
            .With(s => s.Response, fullResponse.ToString())
            .AsResult();
    }
}

ChatMessage

Represents a single message in a conversation.

Properties

PropertyTypeDescription
RoleChatRoleMessage role (User, Assistant, System)
ContentstringMessage content
TimestampDateTimeOffsetWhen message was created
MetadataDictionary<string, object>Additional message data

ChatRole

Enumeration of message roles.

ValueDescription
SystemSystem/instruction message
UserUser input message
AssistantLLM response message
ToolTool/function result message

IChatClient Integration

The package integrates with Microsoft.Extensions.AI.IChatClient.

Supported Providers

ProviderPackageRegistration
OpenAIMicrosoft.Extensions.AI.OpenAInew OpenAIChatClient(model, apiKey)
Azure OpenAIMicrosoft.Extensions.AI.AzureOpenAInew AzureOpenAIChatClient(endpoint, key)
OllamaOllamaChatClientnew OllamaChatClient(model)

Example Registration

csharp
// OpenAI
services.AddSingleton<IChatClient>(
    new OpenAIChatClient("gpt-4o", apiKey));

// Azure OpenAI
services.AddSingleton<IChatClient>(
    new AzureOpenAIChatClient(
        new Uri("https://your-resource.openai.azure.com"),
        new AzureKeyCredential(apiKey),
        "gpt-4o"));

// Ollama (local)
services.AddSingleton<IChatClient>(
    new OllamaChatClient("llama2"));

Agent Step Patterns

Simple Chat Step

csharp
public class SimpleChatStep : IAgentStep<ChatState>
{
    private readonly IChatClient _chatClient;

    public async Task<StepResult<ChatState>> ExecuteAsync(
        ChatState state,
        AgentStepContext context,
        CancellationToken ct)
    {
        var response = await _chatClient.GetResponseAsync(state.Query, ct);
        return state.With(s => s.Response, response).AsResult();
    }
}

Step with History

csharp
public class ConversationalStep : IAgentStep<ChatState>
{
    private readonly IChatClient _chatClient;

    public async Task<StepResult<ChatState>> ExecuteAsync(
        ChatState state,
        AgentStepContext context,
        CancellationToken ct)
    {
        var messages = state.Messages
            .Select(m => new ChatMessage(m.Role, m.Content))
            .Append(new ChatMessage(ChatRole.User, state.Query))
            .ToList();

        var response = await _chatClient.GetResponseAsync(messages, ct);

        return state
            .With(s => s.Response, response)
            .With(s => s.Messages, state.Messages
                .Append(new ChatMessage { Role = ChatRole.User, Content = state.Query })
                .Append(new ChatMessage { Role = ChatRole.Assistant, Content = response })
                .ToList())
            .AsResult();
    }
}

Step with Streaming

csharp
public class StreamingChatStep : IAgentStep<ChatState>
{
    private readonly IChatClient _chatClient;

    public async Task<StepResult<ChatState>> ExecuteAsync(
        ChatState state,
        AgentStepContext context,
        CancellationToken ct)
    {
        var response = new StringBuilder();

        await foreach (var chunk in _chatClient.GetStreamingResponseAsync(
            state.Query, ct))
        {
            response.Append(chunk.Text);
            context.StreamingCallback?.OnToken(chunk.Text);
        }

        context.StreamingCallback?.OnComplete();

        return state.With(s => s.Response, response.ToString()).AsResult();
    }
}

Released under the MIT License.