AgentCircuits 0.2.1
dotnet add package AgentCircuits --version 0.2.1
NuGet\Install-Package AgentCircuits -Version 0.2.1
<PackageReference Include="AgentCircuits" Version="0.2.1" />
<PackageVersion Include="AgentCircuits" Version="0.2.1" />
<PackageReference Include="AgentCircuits" />
paket add AgentCircuits --version 0.2.1
#r "nuget: AgentCircuits, 0.2.1"
#:package AgentCircuits@0.2.1
#addin nuget:?package=AgentCircuits&version=0.2.1
#tool nuget:?package=AgentCircuits&version=0.2.1
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");
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 callsStepAsync()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:
- Automatic Detection: Monitors context usage before each LLM call
- Smart Triggering: Activates at 90% of model's context window (e.g., 180K tokens for Claude)
- Intelligent Summarization: Uses a separate "compactor" agent to create a concise summary
- Seamless Continuation: Replaces history with summary, conversation continues without interruption
- 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
IContextBuilderfor 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 goodMonitor(60-80%): Watch usageCompact(80-95%): Consider compactionCritical(>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:
- 📚 AgentBus Developer Guide - Complete patterns and usage
- 🔧 Runtime API Reference - Full API documentation
- 🎯 AgentBus Visualization - Detailed architecture diagrams
- 🎯 Examples: AgentBusBasicsDemo, AsyncOperationsDemo, DeclarativeAgentsDemo, RealtimeSubscriptionDemo
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:
- 📚 A2A Protocol Specification
- 🔧 A2A Integration Guide
- 🎯 Portal A2A dashboard and management UI
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:
- Code Review Bot - One-shot
Agent.Query()with read-only tools - Interactive Chat - Multi-turn conversation with event streaming
- Multi-Agent System - SubAgent orchestration with Task tool
- Custom Tools -
[Tool]attribute andIToolContextusage - Session Workflows - Fork, Rewind, and Checkpoint operations
- Memory Learning - Cross-session knowledge persistence
- Hooks Patterns - Audit logging and cost tracking
- Iteration Control - Custom stop logic and composition
- Cost Optimization - Smart model routing (Haiku vs Sonnet)
- 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
MemoryToolHandlerworks 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:
- 📚 Complete Examples - Multi-agent, custom tools, hooks
- 🎯 Deep Agents Guide - Long-horizon tasks
- 🔧 Custom Tools Guide - Build your own tools
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.UseStreamingwith 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
TurnMetricsEventwith 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
- 💬 GitHub Discussions - Ask questions
- 🐛 Issue Tracker - Report bugs
Built with ❤️ for the .NET community
| Product | Versions 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. |
-
net9.0
- ModelContextProtocol (>= 0.4.0-preview.1)
- ReverseMarkdown (>= 4.6.0)
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.
Core agent framework with streaming, multimodal input, auto-compaction, token monitoring, multi-agent orchestration, MCP integration, A2A protocol support, and web management portal.