AgentCircuits 0.2.1

dotnet add package AgentCircuits --version 0.2.1
                    
NuGet\Install-Package AgentCircuits -Version 0.2.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AgentCircuits" Version="0.2.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AgentCircuits" Version="0.2.1" />
                    
Directory.Packages.props
<PackageReference Include="AgentCircuits" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add AgentCircuits --version 0.2.1
                    
#r "nuget: AgentCircuits, 0.2.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package AgentCircuits@0.2.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=AgentCircuits&version=0.2.1
                    
Install as a Cake Addin
#tool nuget:?package=AgentCircuits&version=0.2.1
                    
Install as a Cake Tool

AgentCircuits

A high-performance C# SDK for building AI agent systems

Simple by Default. Powerful When Needed.

Build everything from one-shot AI queries to complex multi-agent orchestrations—all in idiomatic C# with minimal overhead.

// Get started in one line
var result = await Agent.Query("Create a C# function that validates email addresses");

Build Status NuGet License: MIT


Why AgentCircuits?

Feature AgentCircuits Others
In-Process Performance Run 100+ agents in a single process Most require separate processes/containers
Progressive API Start with 1 line, scale to advanced Often all-or-nothing complexity
Turn-by-Turn Control StepAsync() for approvals, debugging, multi-day workflows Black-box loops with no control between turns
Context Builders Custom formats (XML, Compact) with 30-50% token savings Fixed message format, no optimization
Real-Time Streaming Built-in streaming with 83% faster perceived latency Often requires custom SSE handling
Multimodal Input Native image support with all vision models Often limited or provider-specific
Native Multi-Agent Built-in sub-agents and orchestration Usually requires custom coordination
Web Management Portal Built-in dashboard, session viewer, playground UI Usually requires custom development
A2A Protocol Standard agent-to-agent communication (Python, Java, JS interop) Often no cross-framework integration
C# Idiomatic Feels like native .NET with attributes, async/await, records Python-first with C# as afterthought
MCP Integration First-class Model Context Protocol support Limited or no MCP support
Agent Hooks Cross-cutting concerns (auth, logging, cost tracking) Requires custom middleware

Quick Start

Installation

# Core framework (required)
dotnet add package AgentCircuits.Core

# LLM Providers (choose one or more)
dotnet add package AgentCircuits.Providers.Anthropic  # For Claude models
dotnet add package AgentCircuits.Providers.OpenAI     # For GPT models
dotnet add package AgentCircuits.Providers.Gemini     # For Google Gemini models
dotnet add package AgentCircuits.Providers.Ollama     # For local models (Llama, Mistral, etc.)
dotnet add package AgentCircuits.Providers.Bedrock    # For AWS Bedrock (Claude, Titan, etc.)

# Multi-Agent Orchestration (optional)
dotnet add package AgentCircuits.Host              # For AgentBus, pub-sub, and coordination

# Agent-to-Agent Communication (optional)
dotnet add package AgentCircuits.A2A                  # For A2A protocol integration (call/expose agents)

# Web Management Portal (optional)
dotnet add package AgentCircuits.Portal               # Web-based dashboard and management UI

One-Shot Query

The simplest way to use AgentCircuits—get an answer in one line:

using AgentCircuits;
using AgentCircuits.Providers.Anthropic;

var result = await Agent.Query(
    "Analyze this code for bugs",
    model: Anthropic.LanguageModel("claude-sonnet-4-5"),
    tools: [BuiltInTools.Read, BuiltInTools.Grep]
);

Console.WriteLine(result);

Interactive Conversations

For multi-turn interactions with context retention:

await using var agent = new Agent
{
    SystemPrompt = "You are a senior software architect",
    Tools = BuiltInTools.Safe,  // Read, Write, Edit, Grep, etc.
    Model = "claude-sonnet-4-5",
    WorkingDirectory = "./my-project"
};

// First turn
await agent.SendAsync("Review the authentication module");

await foreach (var evt in agent.ReceiveAsync())
{
    if (evt is TextEvent text)
        Console.Write(text.Content);

    if (evt is ToolUseEvent tool)
        Console.WriteLine($"\n[Using: {tool.ToolName}]");
}

// Follow-up turn - agent remembers context
await agent.SendAsync("Now fix the security issues you found");

await foreach (var evt in agent.ReceiveAsync())
{
    if (evt is TextEvent text)
        Console.Write(text.Content);
}

Key Features:

  • 🧠 Full conversation history
  • 🔄 Real-time streaming with events
  • 🛠️ Built-in tools (file I/O, grep, etc. - excludes Bash for safety)
  • 💾 Session persistence

Turn-by-Turn Control (StepAsync)

For advanced scenarios requiring control between turns—human approvals, multi-day workflows, or custom retry logic—use StepAsync() for step-by-step execution:

When to Use Which Pattern

Use ReceiveAsync() for:

  • Chatbots, Q&A, code analysis
  • Tasks that can run to completion
  • Simple, straightforward workflows

Use StepAsync() for:

  • Approval workflows (deployments, deletions)
  • Multi-day async operations
  • Custom retry logic
  • Debugging and observability

Approval Workflow Example

Stop before dangerous operations and require human confirmation:

using AgentCircuits.Events;

var agent = new Agent
{
    SystemPrompt = "You are a deployment assistant",
    Model = "claude-sonnet-4-5",
    Tools = [BuiltInTools.Bash, deployProdTool]
};

await agent.SendAsync("Deploy version 2.1.0 to production");

while (true)
{
    var turn = await agent.StepAsync();

    // Check for dangerous operations BEFORE they execute
    if (turn.ToolCalls.Any(t => t.ToolName == "deploy_prod"))
    {
        Console.Write("Approve deployment? (y/n): ");
        if (Console.ReadLine() != "y")
        {
            await agent.SendAsync("Deployment denied by operator");
            continue;
        }
    }

    foreach (var evt in turn.Events)
        if (evt is TextEvent text)
            Console.WriteLine(text.Content);

    if (turn.FinishReason == FinishReason.Stop || !turn.RequiresNextTurn)
        break;
}

Multi-Day Workflow Example

Exit and resume when waiting for external approvals:

// Tool returns operation ID immediately with Interrupt flag
[Tool("request_approval")]
public async Task<ToolResult> RequestApproval(string approver, string question)
{
    var operationId = $"req_{Guid.NewGuid():N}";

    // Store in database, send notification
    await _db.SavePendingOperationAsync(operationId, approver, question);
    await _notificationService.NotifyAsync(approver, question);

    // Return with Interrupt flag - signals agent to exit
    return new ToolResult
    {
        IsSuccess = true,
        Interrupt = true,  // Exit agent loop
        Content = operationId,
        Metadata = new() { ["operation_id"] = operationId, ["status"] = "pending" }
    };
}

// Orchestration with StepAsync
await agent.SendAsync("I need database access");

while (true)
{
    var turn = await agent.StepAsync();

    // Check for operations that need external approval
    var interruptedOps = turn.Events
        .OfType<ToolResultEvent>()
        .Where(e => e.Metadata?.ContainsKey("operation_id") == true);

    if (interruptedOps.Any())
    {
        // Save session and exit - don't block for hours/days
        await _sessionService.SaveAsync(agent.Session);
        Console.WriteLine("Approval requested. Exiting...");
        break;
    }

    if (turn.FinishReason == FinishReason.Stop || !turn.RequiresNextTurn)
        break;
}

// Hours or days later: Resume when approval arrives
var session = await _sessionService.GetSessionAsync(sessionId);
var resumedAgent = new Agent { Session = session, Model = "claude-sonnet-4-5" };

await resumedAgent.SendAsync($"Approval granted for operation {operationId}");
await foreach (var evt in resumedAgent.ReceiveAsync())
{
    // Agent continues from where it left off
}

Key Benefits:

  • 🛡️ Safety: Inspect and approve dangerous operations before execution
  • ⏸️ Async Operations: Exit and resume without blocking
  • 🔍 Observability: Full visibility into each turn's tool calls
  • 🔄 Retry Control: Custom logic for failed operations
  • 📊 Debugging: Step through agent execution turn by turn

Implementation Notes:

  • ReceiveAsync() internally calls StepAsync() in a loop (single execution path)
  • Both methods share the same underlying turn execution logic
  • Zero overhead for simple cases, full control when needed
  • See ControlFlowDemo for complete examples

Real-Time Streaming

Enable streaming for character-by-character responses—83% faster perceived latency (0.5s vs 3-5s to first content):

await using var agent = new Agent
{
    Model = Anthropic.LanguageModel("claude-3-5-sonnet-20241022"),
    UseStreaming = true,  // Enable streaming
    SystemPrompt = "You are a helpful coding assistant"
};

await agent.SendAsync("Explain async/await in C#");

await foreach (var evt in agent.ReceiveAsync())
{
    switch (evt)
    {
        case TextEvent { Partial: true } text:
            // Real-time partial content - display immediately
            Console.Write(text.Content);
            break;

        case TextEvent { Partial: false }:
            // Final complete text - skip (already displayed)
            Console.WriteLine();
            break;

        case ResultEvent result:
            Console.WriteLine($"\nCost: ${result.Usage?.TotalCostUsd:F4}");
            break;
    }
}

Streaming Benefits:

  • Instant Feedback: See response as it's generated
  • 🎯 Better UX: 83% faster time-to-first-content
  • 🔧 Works with Tools: Text streams, tools execute normally
  • 💾 Session Friendly: Only final content persisted (no duplicate partial events)

Multimodal Input

Send images to vision-capable models (Claude, GPT-4o, Gemini) for analysis, description, or comparison:

using AgentCircuits.Providers;

// Load an image
var image = await ImageHelper.FromFileAsync("screenshot.png");

// Send with a prompt
await agent.SendAsync(Message.UserImage(
    imageData: image.Data,
    mediaType: image.MediaType,
    caption: "What UI issues do you see in this screenshot?"
));

await foreach (var evt in agent.ReceiveAsync())
{
    if (evt is TextEvent text)
        Console.WriteLine(text.Content);
}

Multi-Image Messages:

// Compare before/after screenshots
var before = await ImageHelper.FromFileAsync("before.png");
var after = await ImageHelper.FromFileAsync("after.png");

await agent.SendAsync(Message.UserBlocks(
    new TextContent { Text = "Compare these screenshots:" },
    new ImageContent { Data = before.Data, MediaType = before.MediaType },
    new TextContent { Text = "\nAfter:" },
    new ImageContent { Data = after.Data, MediaType = after.MediaType }
));

Supported Formats:

  • Image Types: JPEG, PNG, GIF, WebP
  • Providers: All support vision (Anthropic, Google, OpenAI)
  • Size Limits: 10-20MB per image depending on provider
  • Use Cases: Screenshot analysis, diagram extraction, image description, visual comparison

See MultimodalDemo for complete examples including UI/UX analysis and visual diff tools.


Auto-Compaction

AgentCircuits automatically manages context limits to prevent overflow—enabled by default with zero configuration:

await using var agent = new Agent
{
    Model = "claude-sonnet-4-5",
    AutoCompaction = true,  // Default: true (on by default)
    SystemPrompt = "You are a coding assistant"
};

// Agent automatically compacts context at 90% of limit
// No manual intervention needed - continues seamlessly
await agent.SendAsync("Read all files in this large project and analyze them");

How It Works:

  1. Automatic Detection: Monitors context usage before each LLM call
  2. Smart Triggering: Activates at 90% of model's context window (e.g., 180K tokens for Claude)
  3. Intelligent Summarization: Uses a separate "compactor" agent to create a concise summary
  4. Seamless Continuation: Replaces history with summary, conversation continues without interruption
  5. Tool Result Trimming: Automatically truncates large tool outputs to 500 chars before compaction

Key Features:

  • 🤖 On by Default: Zero configuration required
  • 📊 Provider-Agnostic: Works with any LLM (Claude, GPT, Ollama, etc.)
  • 🎯 Model-Aware: Fetches context limits from models.dev API with 24h caching
  • 🛡️ Fail-Safe: If compaction fails, preserves original session and continues
  • 💰 Cost Tracking: Token usage accumulates across compactions for accurate billing

Configure Compaction Behavior:

var agent = new Agent
{
    AutoCompaction = false,  // Turn off for full history retention

    // Or customize thresholds
    ContextWindow = new ContextWindowConfig
    {
        CompactionThreshold = 0.85,        // Trigger at 85% instead of 90%
        OutputBufferTokens = 3000,         // Reserve more space for output
        TokensPerToolDefinition = 200      // Adjust tool definition estimates
    }
};

System Events:

await foreach (var evt in agent.ReceiveAsync())
{
    if (evt is SystemEvent sys && sys.Subtype == "compaction")
    {
        Console.WriteLine($"[SYSTEM] {sys.Data["message"]}");
        // Output: "Context usage approaching limit (90%). Compacting conversation history..."
        // Output: "Compaction complete. Continuing..."
    }
}

Context Builders (Token Optimization)

Customize how session events are converted to LLM messages for 30-50% token savings, model-specific optimization, or domain-specific formats:

await using var agent = new Agent
{
    Model = "claude-sonnet-4-5",
    ContextBuilder = ContextBuilders.Compact(),  // 30-50% token reduction
    SystemPrompt = "You are a helpful assistant"
};

await agent.SendAsync("Analyze this project");
// Events converted to compact format: "U: message" instead of full Message objects

Built-In Context Builders

1. Compact (Token Optimization)

var agent = new Agent
{
    ContextBuilder = ContextBuilders.Compact(maxEventLength: 150)
};

// Output format (30-50% savings):
// U: user message
// A: assistant response
// T: tool_name(arg1=val1)
// R✓: tool result

2. XML (Claude-Optimized)

var agent = new Agent
{
    ContextBuilder = ContextBuilders.Xml(includeTimestamps: true)
};

// Output format (optimized for Claude):
// <conversation>
//   
//   <user>message</user>
//   <tool_use name="read">...</tool_use>
//   <tool_result success="true">result</tool_result>
//   <assistant>response</assistant>
// </conversation>

3. SlackStyle (Chat Platforms)

var agent = new Agent
{
    ContextBuilder = ContextBuilders.SlackStyle(
        channelName: "support",
        userName: "customer",
        botName: "supportbot"
    )
};

// Output format:
// [Slack Thread #support]
// @customer (15:30): help me
// @supportbot (15:30): happy to help

4. Custom Implementation

public class MedicalContextBuilder : IContextBuilder
{
    public List<Message> BuildMessages(
        IReadOnlyList<Event> events,
        string? systemPrompt,
        IEnumerable<ITool> tools,
        Message? firstTurnOverride = null)
    {
        // Custom formatting logic for medical records...
        return formattedMessages;
    }
}

var agent = new Agent { ContextBuilder = new MedicalContextBuilder() };

5. Inline Functions

var agent = new Agent
{
    ContextBuilder = ContextBuilders.FromFunc(events =>
    {
        var summary = string.Join("\n", events
            .OfType<TextEvent>()
            .Select(e => $"{e.Author}: {e.Content}"));

        return new List<Message>
        {
            Message.User(summary)
        };
    })
};

Key Features:

  • 💰 Token Savings: Compact format reduces costs by 30-50%
  • 🎯 Model-Specific: XML optimized for Claude (trained on XML)
  • 🔧 Extensible: Implement IContextBuilder for custom formats
  • ↔️ Backward Compatible: Null ContextBuilder = default behavior
  • 📊 4 Built-In Formats: Compact, XML, SlackStyle, JsonStructured

See ContextBuildersDemo for examples.


Token Monitoring & Performance Metrics

AgentCircuits provides real-time visibility into context window usage and performance metrics:

Context Window Monitoring

Track token usage and get proactive warnings before hitting context limits:

using AgentCircuits.Sessions;

var agent = new Agent
{
    Model = "claude-sonnet-4-5",
    SystemPrompt = "You are a helpful assistant",
    Tools = BuiltInTools.Safe
};

await agent.SendAsync("Analyze this project");

// Monitor context usage during execution
var stats = await agent.GetContextStatsAsync();

Console.WriteLine($"Context Usage: {stats.UsagePercentage:F1}%");
Console.WriteLine($"Tokens: {stats.TotalTokens:N0} / {stats.MaxContextTokens:N0}");
Console.WriteLine($"Remaining: {stats.RemainingTokens:N0}");
Console.WriteLine($"Action: {stats.RecommendedAction}");

// Token distribution breakdown
var dist = stats.Distribution;
Console.WriteLine($"System Prompt: {dist.SystemPromptTokens:N0}");
Console.WriteLine($"Tool Definitions: {dist.ToolDefinitionTokens:N0}");
Console.WriteLine($"User Messages: {dist.UserMessageTokens:N0}");
Console.WriteLine($"Assistant: {dist.AssistantTokens:N0}");
Console.WriteLine($"Tool Results: {dist.ToolResultTokens:N0} (typically 50-80%)");

Recommended Actions:

  • None (<60%): All good
  • Monitor (60-80%): Watch usage
  • Compact (80-95%): Consider compaction
  • Critical (>95%): Near limit

Per-Turn Performance Metrics

Get detailed performance data for every LLM call:

await agent.SendAsync("Review the authentication code");

await foreach (var evt in agent.ReceiveAsync())
{
    if (evt is TurnMetricsEvent metrics)
    {
        Console.WriteLine($"\n=== Turn {metrics.TurnNumber} Metrics ===");
        Console.WriteLine($"Duration: {metrics.DurationMs}ms");
        Console.WriteLine($"Tokens: {metrics.Usage.InputTokens} in, {metrics.Usage.OutputTokens} out");
        Console.WriteLine($"Cost: ${metrics.Usage.TotalCostUsd:F4}");

        var perf = metrics.Performance;
        Console.WriteLine($"Throughput: {perf.TokensPerSecond:F1} tokens/sec");
        Console.WriteLine($"TTFT: {perf.TimeToFirstTokenMs}ms");
        Console.WriteLine($"Tools: {perf.ToolCallsSuccessful}/{perf.ToolCallsAttempted}");

        // Per-tool statistics
        if (perf.ToolCallsByName != null)
        {
            foreach (var (tool, stats) in perf.ToolCallsByName)
            {
                Console.WriteLine($"  {tool}: {stats.SuccessCount}/{stats.CallCount} " +
                                $"({stats.AverageExecutionMs:F0}ms avg)");
            }
        }
    }

    if (evt is ResultEvent result && result.Performance != null)
    {
        // Aggregate metrics across all turns
        var aggPerf = result.Performance;
        Console.WriteLine($"\n=== Session Summary ===");
        Console.WriteLine($"Total Tokens/sec: {aggPerf.TokensPerSecond:F1}");
        Console.WriteLine($"Total Tools: {aggPerf.ToolCallsSuccessful}/{aggPerf.ToolCallsAttempted}");
        Console.WriteLine($"Avg Tool Time: {aggPerf.AverageToolExecutionMs:F0}ms");
    }
}

Metrics Captured:

  • 🚀 Throughput: Tokens per second (total, input, output)
  • ⏱️ Latency: Time to first token (streaming only)
  • 🔧 Tool Stats: Success/failure rates, execution times
  • 💰 Cost: Per-turn and cumulative
  • 📊 Per-Tool Breakdown: Individual tool performance

Use Cases:

  • Monitor production agent performance
  • Identify slow tools or model degradation
  • Track token throughput across conversations
  • Detect runaway agents with anomaly detection
  • Optimize tool execution strategies

Custom Tools

Create domain-specific tools with simple attributes:

public class DatabaseTools
{
    private readonly IDbConnection _db;

    public DatabaseTools(IDbConnection db) => _db = db;

    [Tool("query_users", "Query the user database")]
    public async Task<ToolResult> QueryUsers(
        [ToolParam("SQL WHERE clause")] string where,
        [ToolParam("Maximum rows")] int limit,
        IToolContext context)
    {
        if (limit > 1000)
            return ToolResult.Error("Limit cannot exceed 1000");

        var sql = $"SELECT * FROM users WHERE {where} LIMIT {limit}";
        var users = await _db.QueryAsync(sql);

        return ToolResult.Success(JsonSerializer.Serialize(users));
    }
}

// Use with agent
var dbTools = new DatabaseTools(dbConnection);

var agent = new Agent
{
    SystemPrompt = "You are a data analyst assistant",
    Tools = Tool.FromInstance(dbTools),  // Auto-discovers [Tool] methods
    Model = "claude-sonnet-4-5"
};

Features:

  • 🏷️ Attribute-based tool definition
  • 💉 Dependency injection via instance methods
  • 🔄 Automatic JSON schema generation
  • 🛡️ Built-in validation and error handling

Multi-Agent Systems

Orchestrate specialized sub-agents for complex workflows:

// Define specialized agents
var securityAgent = new Agent
{
    Name = "security_scanner",
    Description = "Scans code for security vulnerabilities",
    SystemPrompt = "You are a security expert. Look for SQL injection, XSS, auth issues...",
    Tools = [BuiltInTools.Read, BuiltInTools.Grep],
    Model = "claude-sonnet-4-5"
};

var performanceAgent = new Agent
{
    Name = "performance_analyzer",
    Description = "Analyzes performance bottlenecks",
    SystemPrompt = "You are a performance expert. Find N+1 queries, memory leaks...",
    Tools = [BuiltInTools.Read, BuiltInTools.Bash],
    Model = "claude-sonnet-4-5"
};

// Orchestrator delegates to specialists
var orchestrator = new Agent
{
    SystemPrompt = """
        Coordinate code analysis by delegating to specialists:
        1. Use security_scanner for security review
        2. Use performance_analyzer for performance analysis
        3. Synthesize findings into actionable report
        """,
    SubAgents = [securityAgent, performanceAgent],
    Tools = [BuiltInTools.Task, BuiltInTools.Read],  // Task tool enables delegation
    Model = "claude-sonnet-4-5"
};

await orchestrator.SendAsync("Analyze the API endpoints");

await foreach (var evt in orchestrator.ReceiveAsync())
{
    if (evt is ToolUseEvent { ToolName: "Task" } task)
    {
        var subAgent = task.Arguments["subagent_type"];
        Console.WriteLine($"→ Delegating to: {subAgent}");
    }

    if (evt is TextEvent text)
        Console.WriteLine(text.Content);
}

Patterns Supported:

  • 🎯 Orchestrator: Root agent delegates to specialists
  • 🔄 Pipeline: Sequential processing through agents
  • Parallel: Concurrent agent execution
  • 🌳 Hierarchical: Multi-level agent trees

AgentBus (Multi-Agent Orchestration)

For production multi-agent systems, AgentBus provides Kafka-style pub-sub messaging and coordination:

using AgentCircuits.Host;
using AgentCircuits.Host.Patterns.MultiAgent;

// Setup AgentBus
var sessionService = new InMemorySessionService();
var agentRepo = new FileBasedAgentConfigRepository("./agents");
var bus = new AgentBus(sessionService, agentRepo);

// Register agents
await bus.RegisterAgentAsync("researcher", new AgentConfig
{
    Id = "researcher",
    ModelId = "claude-3-5-sonnet-20241022",
    SystemPrompt = "You research topics thoroughly",
    ToolNames = ["websearch", "webfetch"]
});

await bus.RegisterAgentAsync("writer", new AgentConfig
{
    Id = "writer",
    ModelId = "claude-3-5-sonnet-20241022",
    SystemPrompt = "You write clear, concise articles",
    ToolNames = ["write", "edit"]
});

// Execute agents in pipeline
var result = await bus.SequentialAsync(
    agentIds: new List<string> { "researcher", "writer" },
    initialMessage: "Write an article about async patterns in C#"
);

// Final output
var article = result.Responses["writer"];

Basic Usage

Simple scenarios for getting started with AgentBus:

1. Talk to an agent (new session):

// Publish message and wait for response
var result = await bus.PublishTextAsync(
    agentId: "support-bot",
    sessionId: null,  // null = create new session
    text: "Help me reset my password",
    options: new PublishOptions { WaitForResponse = true }
);

Console.WriteLine($"Session: {result.SessionId}");
Console.WriteLine($"Response: {result.Response?.AssistantMessage}");

2. Continue conversation (existing session):

// Use existing sessionId to continue
var followUp = await bus.PublishTextAsync(
    agentId: "support-bot",
    sessionId: result.SessionId,  // same session
    text: "What if I forgot my username too?",
    options: new PublishOptions { WaitForResponse = true }
);

// Agent remembers previous context

3. Use different agent in same session:

// Switch to security analyst in same conversation
var securityReview = await bus.PublishTextAsync(
    agentId: "security-analyst",  // different agent
    sessionId: result.SessionId,   // same session
    text: "Review this password reset request for security issues"
);

// New agent sees full conversation history

4. Retrieve session history:

// Get full session with all messages
var session = await bus.GetSessionAsync(result.SessionId);

foreach (var msg in session.Messages)
{
    Console.WriteLine($"[{msg.Role}]: {msg.Content}");
}

5. Fire-and-forget (async):

// Don't wait for response, agent runs in background
await bus.PublishTextAsync(
    agentId: "logger-agent",
    sessionId: logSessionId,
    text: "Log this event",
    options: new PublishOptions { WaitForResponse = false }
);
// Returns immediately

Declarative Agents (Configuration as Code)

Define agents in JSON files for version control and easy deployment:

agents/support-bot.json:

{
  "id": "support-bot",
  "name": "Support Assistant",
  "modelId": "claude-3-5-sonnet-20241022",
  "toolNames": ["read", "write"],
  "systemPrompt": "You are a helpful IT support assistant",
  "maxIterations": 50
}
// Agents auto-loaded from *.json files
var repo = new FileBasedAgentConfigRepository("./agents");
var bus = new AgentBus(sessionService, repo);

// Use any agent
await bus.PublishTextAsync("support-bot", null, "Help me reset my password");

Real-Time Subscriptions

Subscribe to sessions for real-time updates:

var sessionId = await bus.CreateSessionAsync();

// Logger subscriber
await bus.SubscribeAsync(sessionId, async (msg) =>
{
    logger.LogInformation($"[{msg.Role}]: {msg.Content}");
});

// Analytics subscriber
await bus.SubscribeAsync(sessionId, async (msg) =>
{
    await metrics.RecordMessage(msg);
});

// Publish - all subscribers notified
await bus.PublishTextAsync("assistant", sessionId, "Hello!");

Async Operations

Handle long-running operations with external completion:

var asyncOps = new AsyncOperationService(bus);

// Create operation
var op = await asyncOps.CreateAsync(
    sessionId: sessionId,
    agentId: "approval-agent",
    type: "approval",
    metadata: JsonSerializer.SerializeToElement(new { question = "Deploy to production?" }),
    timeout: TimeSpan.FromMinutes(5)
);

// External system completes (e.g., webhook)
await asyncOps.CompleteAsync(op.Id, JsonSerializer.SerializeToElement(new { approved = true }));
// Agent automatically triggered with result

Multi-Agent Coordination Patterns

Broadcast (Parallel):

// Execute multiple agents in parallel
var result = await bus.BroadcastAsync(
    agentIds: new List<string> { "security-analyst", "performance-analyst", "ux-analyst" },
    message: "Review the authentication module"
);

Coordinate (Map-Reduce):

// Workers + coordinator
var result = await bus.CoordinateAsync(
    workerAgentIds: new List<string> { "frontend-analyst", "backend-analyst" },
    coordinatorAgentId: "tech-lead",
    workerMessage: "Review your component",
    coordinatorPrompt: "Synthesize into action plan"
);

AgentBus Features:

  • 📬 Pub-Sub Messaging: Sessions as topics, real-time subscriptions
  • 🎭 Agent Registry: Centralized configuration management
  • 📝 Declarative Agents: Load from JSON (version control!)
  • Async Operations: Long-running tasks with callbacks
  • 🔀 Coordination Patterns: Broadcast, Sequential, Coordinate
  • 💾 Pluggable Storage: In-memory, file-based, or custom repositories

Runtime Architecture

AgentBus builds on Core primitives to provide production orchestration:

                    ┌──────────────────────────────────────┐
                    │      YOUR APPLICATION                │
                    │  bus.PublishTextAsync(...)           │
                    │  bus.SubscribeAsync(...)             │
                    │  bus.BroadcastAsync(...)             │
                    └──────────────┬───────────────────────┘
                                   │
          ╔════════════════════════▼═══════════════════════╗
          ║                                                ║
          ║         🚌 AgentBus (Orchestration Layer)      ║
          ║    Kafka-style coordination for agents         ║
          ║                                                ║
          ╚══╦═════════╦═════════╦═════════╦══════════╦═══╝
             ║         ║         ║         ║          ║
    ┌────────╨──┐ ┌────╨────┐ ┌─╨─────┐ ┌─╨──────┐ ┌─╨────────┐
    │  Agent    │ │ Message │ │Session│ │Execute │ │ Patterns │
    │  Registry │ │ Router  │ │Manager│ │ Engine │ │          │
    ├───────────┤ ├─────────┤ ├───────┤ ├────────┤ ├──────────┤
    │• Load cfg │ │• Pub/Sub│ │• Store│ │• Invoke│ │Sequential│
    │• Create   │ │• Route  │ │• Topic│ │• Save  │ │Broadcast │
    │• Factory  │ │• Subs   │ │access │ │• Notify│ │Coordinate│
    └─────┬─────┘ └─────────┘ └───┬───┘ └────┬───┘ └──────────┘
          │                       │          │
          ▼                       ▼          │
    ┌─────────────────┐    ┌──────────────┐ │
    │IAgentConfig     │    │ISession      │ │
    │Repository       │    │Service       │ │
    ├─────────────────┤    ├──────────────┤ │
    │• In-Memory      │    │• In-Memory   │ │
    │• File (JSON)    │    │• JSON File   │ │
    │• Custom         │    │• Custom      │ │
    └─────────────────┘    └──────────────┘ │
                                            │
          ┌─────────────────────────────────┘
          │
          ▼
    ┌──────────────────────────────────────────────┐
    │     AgentCircuits.Core (Foundation)         │
    │  ┌──────┐  ┌────────┐  ┌────────┐  ┌─────┐ │
    │  │Agent │  │Session │  │Message │  │Tools│ │
    │  └──────┘  └────────┘  └────────┘  └─────┘ │
    └──────────────────────────────────────────────┘

    ┌────────────────────────────────────────────┐
    │  Additional Runtime Components             │
    ├────────────────────────────────────────────┤
    │  AsyncOperationService                     │
    │  • CreateAsync()                           │
    │  • CompleteAsync()                         │
    │  • WaitForCompletionAsync()               │
    │  • Event-driven agent resumption           │
    │                                            │
    │  Multi-Agent Patterns (Extensions)         │
    │  • bus.SequentialAsync() - Pipeline        │
    │  • bus.BroadcastAsync() - Parallel         │
    │  • bus.CoordinateAsync() - Map-Reduce      │
    │                                            │
    │  Future: A2A Bridge                        │
    │  • ExposeAgentAsync() - Export local       │
    │  • RegisterRemoteAgentAsync() - Import     │
    │  • Transparent remote agent routing        │
    └────────────────────────────────────────────┘

Key Design Principles:

  • 🎯 Builds on Core: Uses Agent, Session, Message primitives unchanged
  • 🔌 Dependency Inversion: Delegates to pluggable repositories (config, session)
  • 📬 Pub-Sub Pattern: Sessions as append-only topics, agents as consumers
  • 🏭 Dynamic Creation: Creates Core.Agent instances on-demand from configs
  • 🔀 Coordination Layer: Orchestrates multi-agent workflows (Sequential, Broadcast, Coordinate)
  • 💾 Separation of Concerns: AgentBus coordinates, repositories store, Core executes

See Also:


Web Management Portal

AgentCircuits includes a complete web-based management portal for monitoring, configuring, and interacting with agents:

Features

Dashboard:

  • Real-time system metrics (throughput, tokens/s, messages/s)
  • Performance analytics (time to first token, step duration)
  • Top active users and agents
  • Recent sessions overview

Agent Management:

  • Create, edit, and delete agent configurations
  • Configure system prompts, tools, and model settings
  • Enable/disable agents
  • Agent card preview and discovery

Session Viewer:

  • Browse all agent sessions
  • View full conversation history with events
  • Inspect tool calls and results
  • Token usage and cost tracking per session
  • Export sessions for debugging

Interactive Playground:

  • Chat with any configured agent
  • Real-time streaming responses
  • Tool execution visibility
  • Multi-turn conversations

Provider Management:

  • View available LLM providers (Anthropic, OpenAI, Gemini, Ollama, Bedrock)
  • Configure API keys and endpoints
  • Test provider connectivity

MCP Server Management:

  • Configure MCP server connections
  • Enable/disable MCP tools
  • Test MCP server connectivity

Async Operations Monitoring:

  • View pending, completed, and expired operations
  • Monitor operation status and results
  • Manual completion/cancellation

Getting Started

# Run the portal (from src/AgentCircuits.Portal)
cd src/AgentCircuits.Portal
dotnet run

# Navigate to http://localhost:5000

The portal uses file-based storage by default:

  • Agents: data/agents/*.json
  • Sessions: data/sessions/*.json
  • Providers: data/providers/*.json
  • MCP Servers: data/mcp-servers/*.json
  • Async Operations: data/async-operations/*.json

Portal Architecture

// Portal uses Runtime repositories for data persistence
services.AddSingleton<IAgentConfigRepository, FileBasedAgentConfigRepository>();
services.AddSingleton<ISessionService, JsonFileSessionService>();
services.AddSingleton<IProviderRepository, FileBasedProviderRepository>();
services.AddSingleton<IMcpServerRepository, FileBasedMcpServerRepository>();
services.AddSingleton<IAsyncOperationRepository, FileBasedAsyncOperationRepository>();

// AgentBus for agent orchestration
services.AddSingleton<IAgentBus, AgentBus>();

Technology Stack:

  • Backend: ASP.NET Core 9.0 Minimal APIs
  • Frontend: Vanilla JavaScript (no framework dependencies)
  • Storage: JSON file-based repositories (production-ready)
  • Real-time: Server-Sent Events for streaming

Key Features:

  • 🎨 Modern UI: Clean, responsive design with dark mode
  • 📊 Real-time Updates: Streaming responses and metrics
  • 🔍 Full Observability: Session viewer with complete event history
  • 🎮 Interactive: Playground for testing agents
  • 📁 File-based Storage: Simple, debuggable, version-control friendly
  • 🔌 No Dependencies: Pure JavaScript, no build step required

A2A Protocol (Agent-to-Agent Communication)

AgentCircuits implements the Agent2Agent (A2A) Protocol, an open standard for cross-platform agent communication:

What is A2A?

A2A enables agents from different frameworks and languages to collaborate:

  • Call remote agents: Python, Java, JavaScript agents from C#
  • Expose AgentCircuits agents: Make your agents callable by external systems
  • Standard protocol: JSON-RPC 2.0 over HTTP(S) with agent card discovery
  • Enterprise-ready: OAuth, API keys, bearer token authentication

A2A vs MCP

Key Difference:

  • MCP (Model Context Protocol): Agents → Tools/Resources (databases, APIs, calculators)
  • A2A Protocol: Agents ↔ Agents (multi-turn collaboration, reasoning, negotiation)

Both protocols are complementary: A2A agents use MCP to access their tools.

Call Remote Agents

using AgentCircuits.A2A;
using AgentCircuits.A2A.Tools;

// Register remote agent in Portal or via repository
var remoteAgentConfig = new RemoteAgentConfig
{
    Id = "python-researcher",
    Name = "Python Research Agent",
    BaseUrl = "https://research.company.com",
    Authentication = new AuthenticationConfig
    {
        Type = "Bearer",
        Token = "your-api-key"
    }
};

// Call remote agent from your agent
var agent = new Agent
{
    SystemPrompt = "You coordinate research tasks",
    Tools = [new CallRemoteAgentTool(remoteAgentRepository), BuiltInTools.Write],
    Model = "claude-sonnet-4-5"
};

await agent.SendAsync("Use python-researcher to analyze the latest ML papers");
// Agent automatically calls remote Python agent via A2A protocol

Expose Agent Circuit Agents

// Configure agent for A2A exposure in AgentConfig
var agentConfig = new AgentConfig
{
    Id = "code-reviewer",
    Name = "Code Review Agent",
    SystemPrompt = "You review code for bugs and best practices",
    ToolNames = ["read", "grep"],
    ModelId = "claude-sonnet-4-5",

    // A2A exposure settings
    A2A = new A2AExposureSettings
    {
        Exposed = true,
        Skills = ["code_review", "security_analysis", "performance_optimization"],
        RequiresAuth = true
    }
};

// Agent is now discoverable via:
// GET https://your-domain.com/a2a/agents/code-reviewer/.well-known/agent-card.json

// And callable via:
// POST https://your-domain.com/a2a/agents/code-reviewer (JSON-RPC 2.0)

Agent Card Discovery

A2A uses agent cards for discovery (similar to OpenAPI specs):

{
  "name": "code-reviewer",
  "description": "Reviews code for bugs and best practices",
  "url": "https://your-domain.com/a2a/agents/code-reviewer",
  "skills": ["code_review", "security_analysis", "performance_optimization"],
  "authentication": {
    "type": "bearer"
  }
}

Portal Integration

The Portal provides full A2A management:

Remote Agents Page:

  • Add/edit/delete remote agents
  • Test connectivity via agent card discovery
  • Status indicators (online/offline)

Exposed Agents Page:

  • Toggle A2A exposure per agent
  • View generated agent cards
  • Copy discovery URLs
  • Configure authentication

Dashboard:

  • A2A activity metrics
  • Remote agent usage statistics

Architecture

┌─────────────────────────────────────────────────────────────┐
│                    AgentCircuits A2A Integration                 │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌───────────────┐              ┌──────────────────┐       │
│  │ A2A Client    │◄────────────►│ Remote Agents    │       │
│  │ (Outbound)    │              │ (Python/Java/JS) │       │
│  ├───────────────┤              └──────────────────┘       │
│  │• AgentCircuits │                                          │
│  │  Client       │              ┌──────────────────┐       │
│  │• Agent Card   │              │ A2A Server       │       │
│  │  Cache        │◄────────────►│ (Inbound)        │       │
│  │• CallRemote   │              ├──────────────────┤       │
│  │  AgentTool    │              │• Discovery       │       │
│  └───────────────┘              │  Endpoints       │       │
│                                 │• Message Handler │       │
│                                 │• Task Tracking   │       │
│                                 └──────────────────┘       │
│                                                             │
│  Official A2A .NET SDK (v0.3.1-preview)                    │
│  • JSON-RPC 2.0 protocol                                   │
│  • Agent card generation                                   │
│  • Authentication                                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Key Components:

  • 📦 AgentCircuits.A2A: Client and server implementations
  • 🔧 CallRemoteAgentTool: Built-in tool for calling remote agents
  • 📋 IRemoteAgentRepository: Manage remote agent configurations
  • 🎴 Agent Cards: JSON-based agent capability discovery
  • 🔐 Authentication: Bearer tokens, API keys (OAuth/mTLS in v2)

See Also:


Agent Hooks

Add cross-cutting concerns like security, logging, and cost tracking. Hooks are agent-level, allowing multi-tenant scenarios with different policies per agent:

// Create agent with hooks
var agent = new Agent
{
    Model = "claude-sonnet-4-5",

    // Security: Block dangerous operations
    BeforeToolUse = async ctx =>
    {
        if (ctx.Tool.Name == "Bash" && ctx.Arguments["command"].ToString().Contains("rm -rf"))
            return ToolResult.Denied("Dangerous command blocked");
        return null; // Allow
    },

    // Auto-backup: Save files before modifications
    AfterToolUse = async ctx =>
    {
        if (ctx.Tool.Name is "Write" or "Edit" && ctx.Result?.IsSuccess == true)
        {
            var filePath = ctx.Arguments["file_path"].ToString();
            await CreateBackupAsync(filePath);
        }
        return null;
    },

    // Cost tracking: Monitor LLM spend
    AfterModel = async ctx =>
    {
        if (ctx.Response?.Usage?.TotalCostUsd is decimal cost)
        {
            totalCost += cost;
            Console.WriteLine($"[Cost: ${cost:F4} | Total: ${totalCost:F2}]");
        }
        return null;
    }
};

Fluent Hook Builder for common patterns:

// Composable hook configuration
agent
    .BlockTool("bash", "Security policy prohibits shell access")
    .AllowOnlyTools("read", "write", "grep")
    .OnBeforeToolUse(SecurityHooks.ValidateFilePaths);

Built-in Hook Patterns:

  • 🔒 SecurityHooks - Validate and block operations
  • 💰 CostTrackingHooks - Track API spending
  • 📝 AuditLoggingHooks - Log all tool uses
  • 💾 FileBackupHooks - Auto-backup before edits
  • ⏱️ RateLimitingHooks - Throttle API calls
  • 📊 UsageStatisticsHooks - Collect metrics

Logging Configuration

AgentCircuits uses Microsoft.Extensions.Logging for structured, high-performance logging throughout the SDK. Configure log levels via appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "AgentCircuits": "Information",
      "AgentCircuits.Internal.TurnExecutor": "Debug",
      "AgentCircuits.Internal.ToolExecutor": "Debug",
      "AgentCircuits.Host": "Information"
    }
  }
}

Log Categories:

Category Event ID Range Description
AgentCircuits 1000-1099 Agent lifecycle (start, complete, cancel)
AgentCircuits.Internal.TurnExecutor 1100-1199 Turn execution details
AgentCircuits.Internal.ToolExecutor 1200-1299 Tool execution (start, complete, not found)
AgentCircuits.Sessions 1300-1399 Session/context events, compaction
AgentCircuits.Internal.McpManager 1400-1499 MCP server initialisation
AgentCircuits.Hooks 1500-1599 Hook invocations
AgentCircuits.Host 2000-2199 AgentBus and AsyncOperationService

Recommended Log Levels:

  • Production: Information - Agent lifecycle, operation results
  • Debugging: Debug - Turn/tool execution details
  • Troubleshooting: Trace - Full context, parameters, payloads

Key Events:

[Information] Agent execution started. SessionId=abc, Agent=reviewer, Model=claude-sonnet-4-5
[Debug] Tool execution started. Tool=read, SessionId=abc
[Debug] Tool execution completed. Tool=read, Success=True, DurationMs=45
[Information] Agent execution completed. SessionId=abc, TotalTurns=3, TotalTokens=1250, DurationMs=4500

MCP Integration

Connect to Model Context Protocol servers for extended capabilities:

using AgentCircuits.Mcp;

var agent = new Agent
{
    Model = "claude-sonnet-4-5",
    Tools = BuiltInTools.Safe,  // Built-in tools
    McpServers = new()
    {
        ["filesystem"] = new McpServerConfig
        {
            Type = McpTransportType.Stdio,
            Command = "npx",
            Args = ["-y", "@modelcontextprotocol/server-filesystem", "/my/project"]
        },
        ["github"] = new McpServerConfig
        {
            Type = McpTransportType.Http,
            Url = "https://api.githubcopilot.com/mcp/"
        }
    }
};

// MCP tools are automatically loaded and available alongside built-in tools
await agent.SendAsync("Read the README.md file");

MCP Support:

  • ✅ Stdio, HTTP, and SSE transports
  • ✅ Thin adapter over ModelContextProtocol.Core
  • ✅ MCP tools work as regular ITool instances
  • ✅ Compose MCP + built-in + custom tools

Advanced Features

Iteration Control

The agent naturally stops when the LLM signals completion, with MaxIterations as a safety limit:

// Default behavior - natural stopping
var agent1 = new Agent
{
    MaxIterations = 50  // Default, safety limit
};

// Strict limit for simple tasks
var agent2 = new Agent
{
    MaxIterations = 10
};

// Higher limit for complex research
var agent3 = new Agent
{
    MaxIterations = 200
};

// Long-form content generation
var agent4 = new Agent
{
    ContinueOnMaxTokens = true,
    MaxIterations = 100
};

The agent automatically stops when the LLM returns FinishReason.Stop with no tools used.

Session Management

Persist and resume conversations with powerful session helpers:

// Save session for later
var sessionId = agent.SessionId;

// Resume in new process/request
var session = await sessionService.GetSessionAsync(sessionId);
var resumedAgent = new Agent
{
    Session = session,
    Model = "claude-sonnet-4-5"
};

await resumedAgent.SendAsync("Continue from where we left off");

// Fork a session to try different approaches
var experimentalSession = SessionHelpers.Fork(session);

// Rewind by removing last 10 events
var rewindedSession = SessionHelpers.Rewind(session, 10);

// Resume at a specific event
var checkpointSession = SessionHelpers.ResumeAt(session, eventIndex: 42);

// Remove failed tool uses for retry logic
var cleanedSession = SessionHelpers.RemoveFailedTools(session);

// Create checkpoints
var checkpointId = await SessionHelpers.CreateCheckpoint(session,
    sessionService,
    name: "before_risky_operation");

Session Storage Options:

// In-memory (development)
var sessionService = ISessionService.InMemory();

// JSON file (production-ready persistence)
var sessionService = ISessionService.JsonFile("./sessions");

Multi-Provider Support

Use different LLMs for different tasks:

using AgentCircuits.Providers.Anthropic;
using AgentCircuits.Providers.OpenAI;
using AgentCircuits.Providers.Gemini;
using AgentCircuits.Providers.Ollama;
using AgentCircuits.Providers.Bedrock;

// Classify task complexity with fast model
var classification = await Agent.Query(
    $"Is this complex? {userQuery}",
    model: Anthropic.LanguageModel("claude-haiku-4"),
    systemPrompt: "Classify as 'simple' or 'complex'. Respond with one word only."
);

// Route to appropriate model based on complexity
var model = classification.Contains("complex")
    ? OpenAI.LanguageModel("gpt-4o")           // Complex: expensive model
    : Gemini.LanguageModel("gemini-2.0-flash-exp");  // Simple: fast & cheap

var result = await Agent.Query(userQuery, model: model);

// For AWS infrastructure, use Bedrock
var bedrockResult = await Agent.Query(
    userQuery,
    model: Bedrock.LanguageModel("amazon.nova-lite-v1:0")  // AWS Bedrock
);

// For local/privacy-sensitive tasks, use Ollama
var localResult = await Agent.Query(
    userQuery,
    model: Ollama.LanguageModel("llama3")  // Localhost:11434 by default
);

Architecture

AgentCircuits is built around the Agent as the central orchestrator. All capabilities—tools, providers, sessions, hooks, and observability—connect through the agent runtime:

                            ┌──────────────────────────┐
                            │    YOUR APPLICATION      │
                            │   Agent.Query(...)       │
                            │   agent.SendAsync(...)   │
                            │   agent.ReceiveAsync()   │
                            │   agent.GetContextStats()│
                            └────────────┬─────────────┘
                                         │
                    ╔════════════════════▼════════════════════╗
                    ║                                         ║
                    ║            🤖 AGENT CORE                ║
                    ║    (Conversation Engine & Runtime)      ║
                    ║                                         ║
                    ╚══╦═════╦═════╦═════╦═════╦═════╦═════╦═╝
                       ║     ║     ║     ║     ║     ║     ║
        ┌──────────────╨┐ ┌──╨────────┐ ║     ║     ║     ║
        │ LLM PROVIDERS │ │   TOOLS    │ ║     ║     ║     ║
        ├───────────────┤ ├────────────┤ ║     ║     ║     ║
        │ • Anthropic   │ │ Built-in:  │ ║     ║     ║     ║
        │ • OpenAI      │ │  Read      │ ║     ║     ║     ║
        │ • Gemini      │ │  Write     │ ║     ║     ║     ║
        │ • Ollama      │ │  Edit      │ ║     ║     ║     ║
        │               │ │  Bash      │ ║     ║     ║     ║
        │ ModelRegistry │ │  Grep/Glob │ ║     ║     ║     ║
        │ (context      │ │  Task      │ ║     ║     ║     ║
        │  limits)      │ │  Memory    │ ║     ║     ║     ║
        └───────────────┘ │  WebFetch  │ ║     ║     ║     ║
                          │  WebSearch │ ║     ║     ║     ║
                          │            │ ║     ║     ║     ║
                          │            │ ║     ║     ║     ║
                          │ Custom:    │ ║     ║     ║     ║
                          │  [Tool]    │ ║     ║     ║     ║
                          │  MCP Srvs  │ ║     ║     ║     ║
                          └────────────┘ ║     ║     ║     ║
                                         ║     ║     ║     ║
                ┌────────────────────────╨┐ ┌──╨─────────┐ ║
                │       SESSIONS         │ │   EVENTS   │ ║
                ├────────────────────────┤ ├────────────┤ ║
                │ Storage:               │ │ TextEvent  │ ║
                │  • InMemory            │ │ ToolUse    │ ║
                │  • JSON File           │ │ ToolResult │ ║
                │                        │ │ SystemEvt  │ ║
                │                        │ │ TurnMetric │ ║
                │ Helpers:               │ │            │ ║
                │  • Fork (experiment)   │ │IAsyncEnum  │ ║
                │  • Rewind (undo)       │ │ streaming  │ ║
                │  • Checkpoint (save)   │ └────────────┘ ║
                │  • RemoveFailedTools   │                ║
                └────────────────────────┘                ║
                                                          ║
        ┌─────────────────────────────────────────────────╨┐
        │                  OBSERVABILITY                    │
        ├───────────────────────────────────────────────────┤
        │ Context Monitoring:          Performance Metrics: │
        │  • agent.GetContextStats()    • TurnMetricsEvent  │
        │  • Token usage & limits       • Throughput (t/s)  │
        │  • Distribution breakdown     • TTFT (streaming)  │
        │  • Recommended actions        • Tool statistics   │
        │                                                    │
        │ Auto-Compaction: Triggers @ 90% of context limit  │
        └────────────────────────────────────────────────────┘

   ┌──────────────────╨──┐ ┌────────────────╨───┐ ┌──────────────────╨──┐
   │   AGENT-LEVEL HOOKS │ │   SUB-AGENTS       │ │   DEEP AGENTS       │
   │  (Per-Agent Policy) │ │  (Multi-Agent)     │ │ (Long-Horizon)      │
   ├─────────────────────┤ ├────────────────────┤ ├─────────────────────┤
   │ • BeforeModel       │ │ • Orchestrator     │ │ • Memory Tool       │
   │ • AfterModel        │ │ • Specialist       │ │   (cross-session    │
   │ • BeforeToolUse     │ │ • Task delegation  │ │    learning)        │
   │ • AfterToolUse      │ │ • Event aggregate  │ │ • TodoWrite         │
   │ • OnSessionStart    │ │ • Session isolate  │ │   (planning)        │
   │ • OnAgentStop       │ └────────────────────┘ │ • High MaxIter      │
   │                     │                        │   (200+)            │
   │ Fluent Builder:     │                        │ • Auto-compaction   │
   │  BlockTool()        │                        │   (context mgmt)    │
   │  AllowOnlyTools()   │                        │ • Multi-day tasks   │
   │  OnBeforeToolUse()  │                        │ • Research agents   │
   └─────────────────────┘                        └─────────────────────┘

Key Design Principles:

  • 🎯 Agent-Centric: Everything flows through the Agent runtime—one unified interface
  • 🏗️ In-Process: Run 100+ agents in a single process with minimal overhead
  • 🔌 Provider-Agnostic: Swap LLMs (Claude, GPT, Llama) without code changes
  • 📦 Zero Dependencies: Core framework has no external dependencies
  • 🧩 Composable: Tools, hooks, sessions, and agents compose naturally
  • 🔍 Observable: Built-in token monitoring, metrics, and auto-compaction
  • 🧪 Testable: MockLanguageModel and TestToolContext for deterministic tests

Examples

AgentCircuits comes with comprehensive example projects demonstrating all major features.

SdkShowcase - Complete Feature Tour

Interactive demonstrations of all SDK capabilities:

cd examples/SdkShowcase
dotnet run

10 Comprehensive Demos:

  1. Code Review Bot - One-shot Agent.Query() with read-only tools
  2. Interactive Chat - Multi-turn conversation with event streaming
  3. Multi-Agent System - SubAgent orchestration with Task tool
  4. Custom Tools - [Tool] attribute and IToolContext usage
  5. Session Workflows - Fork, Rewind, and Checkpoint operations
  6. Memory Learning - Cross-session knowledge persistence
  7. Hooks Patterns - Audit logging and cost tracking
  8. Iteration Control - Custom stop logic and composition
  9. Cost Optimization - Smart model routing (Haiku vs Sonnet)
  10. Multimodal Input - Vision models with images (Claude, GPT-4o, Gemini)

Each demo is self-contained and heavily commented for learning.


Feature-Specific Demos

AutoCompactionDemo - Automatic Context Management

cd examples/AutoCompactionDemo
dotnet run
  • Demonstrates auto-compaction at 90% context threshold
  • Shows SystemEvent notifications during compaction
  • Compares enabled vs disabled behavior

ContextBuildersDemo - Token Optimization

cd examples/ContextBuildersDemo
dotnet run
  • Compact format (30-50% token savings)
  • XML format (Claude-optimized)
  • SlackStyle format (chat platforms)
  • Custom IContextBuilder implementation

ControlFlowDemo - Turn-by-Turn Execution

cd examples/ControlFlowDemo
dotnet run
  • Approval workflows with StepAsync()
  • Multi-day operations with interrupts
  • Custom stop conditions
  • Observability and debugging

MultimodalDemo - Vision & Image Analysis

cd examples/MultimodalDemo
dotnet run
  • Screenshot analysis and UI/UX feedback
  • Visual diff tool (before/after comparison)
  • Image description and extraction
  • Works with Claude, GPT-4o, Gemini vision models

ToolsDemo - Custom Tool Creation

cd examples/ToolsDemo
dotnet run
  • Tool.FromType<T>() for attribute-based tools
  • Tool builder pattern for dynamic tools
  • Tool composition patterns
  • Built-in tools overview

WebSearchDemo - Research Assistant

cd examples/WebSearchDemo
dotnet run
  • Web search with DuckDuckGo (no API key needed)
  • Multi-turn research conversations
  • Fact-checking with sources
  • Works with local models (Ollama + Qwen)

MemoryDemo - Cross-Conversation Learning

Demonstrates the Memory Tool's unique capability to learn and apply knowledge across separate sessions:

cd examples/MemoryDemo
dotnet run

3 Essential Demos:

1. Cross-Conversation Learning

  • Session 1: Agent reviews code, discovers a race condition, stores pattern in /memories/patterns/concurrency.md
  • Session 2: New agent instance reads the pattern and applies it to different code
  • Zero shared state between sessions—knowledge persists through memory files

2. Multi-Model Compatibility

  • Same MemoryToolHandler works with Anthropic, OpenAI, Google, local models
  • Anthropic: Uses type: "memory_20250818" for automatic memory checking (native support)
  • Others: Explicit prompting in system prompt
  • Provider-agnostic memory architecture—write once, use everywhere

3. Memory Organization

  • Production-ready memory structure: patterns/, stats/, preferences/, knowledge/, decisions/
  • Realistic examples with proper categorization
  • Scalable knowledge management patterns
  • Multi-tenant isolation guidance

Key Insights:

  • No vector databases or embeddings needed
  • Simple file-based storage with full control
  • Works with any LLM provider
  • Debuggable—just open the files

SessionHelpersDemo - Advanced Session Operations

Shows session manipulation patterns:

cd examples/SessionHelpersDemo
dotnet run
  • Fork sessions for experimentation
  • Rewind to retry different approaches
  • Create checkpoints before risky operations
  • Clean up failed tool uses for retries

Provider Demos

Basic usage examples for each LLM provider:

  • AnthropicDemo - Claude models via Anthropic API
  • BedrockDemo - AWS Bedrock (Nova, Claude, Titan)
  • GeminiDemo - Google Gemini models
  • OllamaDemo - Local models (Llama, Mistral, Qwen)

Production Patterns

Code Review Assistant with Memory:

using AgentCircuits;
using AgentCircuits.Tools;
using AgentCircuits.Tools.BuiltIn;

var memoryHandler = new MemoryToolHandler("./code_review_memory");
var memoryTool = new MemoryTool(memoryHandler);

var reviewer = new Agent
{
    SystemPrompt = """
        You are a code reviewer that learns from experience.
        Check your memory for similar bugs you've seen before.
        Store new patterns when you find interesting issues.
        """,
    Tools = [BuiltInTools.Read, memoryTool],
    Model = "claude-sonnet-4-5"
};

await reviewer.SendAsync("Review AuthController.cs");
// Agent checks /memories/patterns/ for known bugs, applies learned patterns

Deep Research Agent (Long-Horizon Tasks):

var researcher = new Agent
{
    SystemPrompt = """
        You conduct multi-day research projects.
        1. Create a plan with TodoWrite
        2. Research each phase thoroughly
        3. Save findings to files
        4. Synthesize final report
        """,
    Tools = [
        BuiltInTools.TodoWrite,
        BuiltInTools.Read,
        BuiltInTools.Write,
        BuiltInTools.Bash
    ],
    MaxIterations = 200,  // Long-running research tasks
    Model = "claude-sonnet-4-5"
};

See Full Documentation:


Project Status

Current Version: Beta (approaching v1.0) Completion: ~98% of planned features (SDK + Portal + A2A Protocol) Test Coverage: 1150+ tests passing (656 core + 61 Anthropic + 23 OpenAI + 23 Ollama + 38 Gemini + 52 Bedrock + 51 Runtime + 23 integration + 223+ additional) Stability: Production-ready core with comprehensive observability, web portal, and A2A protocol integration

Core Features (Complete)

  • Agent Framework: Query, Send/Receive patterns with natural conversation flow
  • Turn-by-Turn Control: StepAsync() for approval workflows, debugging, multi-day operations
  • Context Builders: Custom message formatting (XML, Compact, SlackStyle, JSON) with 30-50% token savings
  • Real-Time Streaming: Agent.UseStreaming with partial events for character-by-character display
  • Multimodal Input: Native image support for all vision models (Claude, GPT-4o, Gemini)
  • Auto-Compaction: Automatic context management at 90% threshold (provider-agnostic)
  • Token Monitoring: Real-time tracking with agent.GetContextStats(), distribution breakdown
  • Performance Metrics: Per-turn TurnMetricsEvent with throughput (tok/s), TTFT, tool statistics
  • Session Management: In-memory and JSON file persistence with Fork/Rewind/Checkpoint helpers
  • Multi-Agent System: SubAgents with natural delegation and event aggregation
  • Agent Hooks: BeforeModel, AfterModel, BeforeToolUse, AfterToolUse, OnSessionStart, OnAgentStop
  • Stop Conditions: Natural stopping, max iterations, tool-triggered interrupts

Built-In Tools

  • File Operations: Read, Write, Edit, Glob, Grep
  • Shell Integration: Bash, BashOutput, KillShell
  • Agent Orchestration: Task (SubAgents), TodoWrite
  • Memory Tool: Cross-conversation learning with markdown file storage
  • Web Tools: WebFetch (single URL fetching), WebSearch (DuckDuckGo default, pluggable providers)

LLM Providers (Complete)

  • Anthropic: Claude 3.5 Sonnet, Opus, Haiku with streaming and tool calling
  • OpenAI: GPT-4o, GPT-4, GPT-3.5 with streaming, tool calling, Azure OpenAI support
  • Gemini: Gemini 2.0 Flash, 2.5 Flash, 2.5 Pro with streaming and tool calling
  • Ollama: Llama 3, Mistral, Mixtral, Phi with local deployment support
  • Bedrock: Amazon Nova, Claude (via AWS), Titan, Llama, Cohere with streaming and tool calling
  • Auto-Registration: Reflection-based provider discovery via LlmProviders.GetModel()

Advanced Features

  • Custom Tools: Attribute-based tool creation with [Tool] and [ToolParam]
  • MCP Integration: Load tools from MCP servers (stdio, SSE) with whitelist/blacklist
  • Hook Patterns: Pre-built hooks for audit logging, cost tracking, rate limiting, security
  • Cost Tracking: Accumulates tokens and costs across turns (cache-aware)
  • Web Management Portal: Complete web UI for agent management, session viewer, playground, metrics
  • A2A Protocol: Agent-to-agent communication (call Python/Java/JS agents, expose Agent Circuit agents)
  • Runtime Repositories: File-based storage for agents, sessions, providers, MCP servers, async operations

Remaining Work

Critical (production-blocking):

  • 🔴 Tool Result Size Management - WebFetchTool and other tools can return unlimited content, poisoning sessions

Medium Priority:

  • 🟡 Advanced Context Management - Smart pruning, progressive compaction, memory-assisted strategies
  • 🟡 Provider Caching - Cache breakpoints for 10x cost reduction (Anthropic/OpenAI/Bedrock)

Low Priority:

  • 🟢 OpenTelemetry - Distributed tracing and metrics export
  • 🟢 NotebookEdit Tool - Jupyter notebook cell editing
  • 🟢 MCP Advanced Capabilities - Resources, prompts, rich content, sampling
  • 🟢 Documentation - Testing strategy, serialization spec, error handling spec, performance benchmarks

Contributing

We welcome contributions! Please see our Contributing Guide for details.


License

MIT License - see LICENSE for details.


Support


Built with ❤️ for the .NET community

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (9)

Showing the top 5 NuGet packages that depend on AgentCircuits:

Package Downloads
AgentCircuits.Portal

Web-based management portal for AgentCircuits. Provides dashboard, agent configuration UI, session viewer, and interactive playground.

AgentCircuits.Providers.Anthropic

Anthropic Claude provider for AgentCircuits agent framework. Supports Claude 3.5 Sonnet, Opus, and Haiku with streaming, tool calling, and prompt caching.

AgentCircuits.Providers.Gemini

Google Gemini provider for AgentCircuits agent framework. Supports Gemini 2.0 Flash, 2.5 Flash, and 2.5 Pro with streaming and tool calling.

AgentCircuits.Providers.Bedrock

AWS Bedrock provider for AgentCircuits agent framework. Enterprise-grade AWS integration with IAM credentials for Claude and other Bedrock models.

AgentCircuits.Providers.Ollama

Ollama provider for AgentCircuits agent framework. Run Llama 3, Mistral, Mixtral, and other open-source models locally with streaming and tool calling.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.2.1 232 11/28/2025
0.2.0 224 11/28/2025
0.1.3 382 11/25/2025
0.1.2 133 11/25/2025
0.1.0 433 11/24/2025

Core agent framework with streaming, multimodal input, auto-compaction, token monitoring, multi-agent orchestration, MCP integration, A2A protocol support, and web management portal.