Skip to content

Agents

FabrCore 2.0 · Release and package availability

These guides track the current 2.0 source. Stable 2.0.0 publication is pending; package commands show the release target. Until it is published, follow the source quick start or use a matching available prerelease set. Release migration · Runtime modes

Agents

Agents are the core building block of FabrCore. Each agent is a distributed actor backed by an Orleans grain, with its own LLM context, tools, and persistent state. You build agents by extending FabrCoreAgentProxy.

Long-running work

Compose an existing proxy with CreateFabrCoreHarnessAgent or AsFabrCoreHarnessAgent for model-managed todos, iteration loops, durable snapshots, and delegation. Harness guide

Bounded context

The 2.0.0 compaction ladder bounds tool results, history, projections, and whole-run budgets. Compaction guide

FabrCoreAgentProxy

FabrCoreAgentProxy is a wrapper around the Microsoft Agent Framework's AIAgent that enables building distributed, actor-based AI agents.

Protected Members

MemberTypeDescription
configAgentConfigurationConfiguration passed during creation
fabrcoreAgentHostIFabrCoreAgentHostHost interface for messaging, timers, persistence
serviceProviderIServiceProviderDI service provider
loggerILoggerPre-configured logger instance
configurationIConfigurationApplication configuration

Basic Agent

MyAgent.cs
using System.ComponentModel;
using FabrCore.Core;
using FabrCore.Sdk;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

[AgentAlias("my-agent")]
[Description("A helpful assistant")]
[FabrCoreCapabilities("Answers questions using configured tools")]
[FabrCoreNote("Configure tools and model access before use")]
public class MyAgent : FabrCoreAgentProxy
{
    private AIAgent? _agent;
    private AgentSession? _session;

    public MyAgent(
        AgentConfiguration config,
        IServiceProvider serviceProvider,
        IFabrCoreAgentHost fabrcoreAgentHost)
        : base(config, serviceProvider, fabrcoreAgentHost) { }

    public override async Task OnInitialize()
    {
        // Resolve plugins, standalone tools, and MCP tools from config
        var tools = await ResolveConfiguredToolsAsync();

        var result = await CreateChatClientAgent(
            "default",
            threadId: config.Handle ?? fabrcoreAgentHost.GetHandle(),
            tools: tools);
        _agent = result.Agent;
        _session = result.Session;
    }

    public override async Task<AgentMessage> OnMessage(AgentMessage message)
    {
        var response = message.Response();
        var chatMessage = new ChatMessage(ChatRole.User, message.Message);

        await foreach (var update in _agent!.RunStreamingAsync(
            chatMessage, _session!))
        {
            response.Message += update.Text;
        }

        return response;
    }

}

Helper Methods

GetChatClient

Returns a raw IChatClient for direct LLM access. Use with custom agent frameworks or direct completions.

CreateChatClientAgent

Returns a fully configured ChatClientAgent with automatic message persistence via FabrCoreChatHistoryProvider.

CreateChatClientAgent — With Persistence
var created = await CreateChatClientAgent(
    "default",
    threadId: config.Handle,
    tools: [AIFunctionFactory.Create(plugin.MyTool)]
);
agent = created.Agent;
thread = created.Session;

Lifecycle

MethodWhen CalledPurpose
OnInitialize()Agent creation / grain activationSet up LLM clients, tools, and state
OnMessage(AgentMessage)Request/response message receivedProcess user messages and return responses
OnEvent(EventMessage)Fire-and-forget event receivedHandle event notifications from other agents
GetHealth(HealthDetailLevel)Health check requestedReturn custom health metrics

AgentSession Patterns

PatternDescriptionUse Case
One thread per agentCreate in OnInitialize, reuseSingle continuous conversation
Thread per userStore in dictionary by user IDMulti-user agents
Independent callsUse a client without a persistent message-store providerA new session alone does not disable a configured durable history store

AgentAlias

Use the [AgentAlias] attribute to register your agent with one or more aliases for routing:

Multiple Aliases
[AgentAlias("CustomerSupport")]
[AgentAlias("support")]
public class CustomerSupportAgent : FabrCoreAgentProxy
{
    // ...
}
Alias Resolution

Multiple aliases can be applied to the same class. Whitespace is trimmed. The alias is used when creating agents via the REST API, the SDK API client, or host services.

Registry Attributes

FabrCore provides metadata attributes that enrich the discovery registry with information about what your agents can do. This metadata is returned by the /fabrcoreapi/discovery endpoint and helps other agents and users decide whether to interact with a given agent.

AttributeMultiplicityPurpose
[FabrCoreCapabilities("...")]One per classDescribes what the agent can do — its core responsibilities and features
[FabrCoreHidden]One per classHides the agent from the discovery endpoint (still usable, just not listed)
[FabrCoreNote("...")]Multiple allowedUsage instructions, prerequisites, or guidance on when not to use this agent

FabrCoreCapabilities

Use [FabrCoreCapabilities] to describe the agent's core functionality. This is surfaced in the discovery response as the capabilities field:

C# — FabrCoreCapabilities Attribute
[AgentAlias("job-agent")]
[Description("Manufacturing job management agent")]
[FabrCoreCapabilities("Manages manufacturing jobs — lookup, status tracking, priority changes, and ship date queries.")]
public class JobAgent : FabrCoreAgentProxy
{
    // ...
}

FabrCoreNote

Use [FabrCoreNote] to add usage guidance, prerequisites, or warnings. Multiple notes are allowed and appear as an array in the discovery response:

C# — FabrCoreNote Attributes
[AgentAlias("job-agent")]
[FabrCoreNote("Requires a job number in context before most tools will work.")]
[FabrCoreNote("Do not use for quoting — use the quotes-agent instead.")]
public class JobAgent : FabrCoreAgentProxy
{
    // ...
}

FabrCoreHidden

Use [FabrCoreHidden] to exclude an agent from the discovery endpoint. The agent remains fully functional — it just won't appear in the registry listing:

C# — FabrCoreHidden Attribute
[AgentAlias("internal-worker")]
[FabrCoreHidden]
public class InternalWorkerAgent : FabrCoreAgentProxy
{
    // Hidden from /fabrcoreapi/discovery but still fully operational
}
Recommended Practice

These attributes are optional but strongly recommended for any agent that will be discoverable by other agents or surfaced in a registry UI. Combine [Description] (from System.ComponentModel) for a short summary with [FabrCoreCapabilities] for detailed functionality.

AIContextProvider

Microsoft's AIContextProvider enables dynamic context injection during agent execution. Wire providers directly to each ChatClientAgent for per-agent context control.

Wiring AIContextProvider
var userInfoMemory = new UserInfoMemory(chatClient);

var created = await CreateChatClientAgent(
    "default",
    threadId: config.Handle,
    tools: [AIFunctionFactory.Create(plugin.Echo)],
    configureOptions: options =>
    {
        options.AIContextProviders = [.. options.AIContextProviders ?? [], userInfoMemory];
    }
);
agent = created.Agent;
thread = created.Session;

AIContext Properties

PropertyTypeDescription
Instructionsstring?Additional instructions injected into the system prompt
MessagesIList<ChatMessage>?Messages added to the conversation context
ToolsIList<AITool>?Dynamic tools available for this invocation
Common Patterns

Use AIContextProvider for RAG (retrieval augmented generation), dynamic tool injection based on user permissions, and user memory extraction. See the Persistence guide for combining with custom state.

Health Monitoring

Override GetHealth(HealthDetailLevel) for custom health metrics:

Custom Health Check
public override async Task<ProxyHealthStatus> GetHealth(HealthDetailLevel detailLevel)
{
    var health = await base.GetHealth(detailLevel);
    return health with { CustomMetrics = new Dictionary<string, string>
        { ["application_status"] = "ready" } };
}

Health Detail Levels

LevelFields Returned
BasicHandle, State, Timestamp, IsConfigured
Detailed+ AgentType, Uptime, MessagesProcessed, TimerCount, ReminderCount
Full+ ProxyHealth, ActiveStreams, Diagnostics

Agent Discovery

FabrCore provides two scopes for querying agents, each with a different purpose:

APIScopeUse Case
IPrincipalGrain.GetTrackedAgents()Per-principalAgents tracked for a specific principal handle
GET /fabrcoreapi/diagnostics/agentsGlobalAll agents in the cluster (administrative view)
GET /fabrcoreapi/diagnostics/agents/statisticsGlobalAggregate counts by type and status
Intentional Separation

Agents created through the Host API are routed through PrincipalGrain so they can be tracked for the owning principal. Use GetTrackedAgents() for principal-scoped queries and the diagnostics API for administrative views.

Delegation Patterns

Agents that orchestrate other agents can use the health checking and messaging primitives to discover and delegate work:

Harness agents can also create private internal specialists for bounded subtasks. These specialists are owned by the host session rather than exposed as durable public team members, which is useful for focused research, review, and transformation work. For reusable visible teams, define a Surface squad.

Health-Based Agent Discovery
private async Task<List<AgentHealthStatus>> GetHealthyAgentsAsync(
    IEnumerable<string> handles)
{
    var results = new List<AgentHealthStatus>();
    foreach (var handle in handles)
    {
        var health = await fabrcoreAgentHost.GetAgentHealth(
            handle, HealthDetailLevel.Detailed);
        if (health.State == HealthState.Healthy && health.IsConfigured)
            results.Add(health);
    }
    return results;
}

// Use SendAndReceiveMessage for synchronous delegation
var result = await fabrcoreAgentHost.SendAndReceiveMessage(new AgentMessage
{
    ToHandle = targetHandle,
    FromHandle = fabrcoreAgentHost.GetHandle(),
    Kind = MessageKind.Request,
    Message = userMessage
});

Actor Model Constraints

FabrCore is built on Orleans, which follows the actor model. This provides strong guarantees but imposes design constraints:

  • Grain isolation: Each agent's state is isolated. No shared mutable state between agents.
  • Single-threaded execution: Grains process one message at a time (with [AlwaysInterleave] exceptions).
  • Location transparency: Grains may run on different silos in a cluster. All communication is via messaging.
Do Not Share Tools Across Agents

Calling tools on one agent from another agent bypasses the message queue, violates single-threaded guarantees, and introduces concurrency hazards. Use SendAndReceiveMessage() for inter-agent communication — its latency depends on routing, load and the work performed. If two agents need the same tool, configure the same plugin on both.

Operator management and isolated diagnostics

FabrCore 2.0 exposes configuration, health, custom state and thread management through the administration API. Restart preserves configuration and stored state; reset clears threads/custom state; eviction permanently deletes runtime and persisted state. Revision checks protect edits, and active normal/admin turns block disruptive actions.

Ask about this agent runs a read-only internal diagnostic agent over copied history and captured observations. It can overlap normal processing while keeping its own busy flag, model usage, timeout and reply path. It never calls normal OnMessage or business/MCP tools. Applications expose read-only data through GetAdminDiagnosticSnapshotAsync(CancellationToken). Diagnostic transcripts live in operator-owned sessions outside normal threads; default in-memory storage cannot resume after process restart.

Access connected services

FabrCoreAgentProxy exposes Connections.GetHttpClientAsync and GetAccessTokenAsync to trusted agent code. Ownership and exact agent grants are checked by the host. Keep raw tokens out of model tools. Read the integration guide.