Creating Your Agent
Overview#
aevatar Agents are powerful, stateful entities that can interact, process events, and manage their own lifecycle. This guide will walk you through the process of creating a new Agent.
Prerequisites#
Agent Types#
Aevatar supports two main types of Agents:
Dependency package #
dotnet add package Aevatar.Core:
https://www.myget.org/F/aelf-project-dev/api/v3/index.json
dotnet add package Aevatar.EventSourcing.Core:
https://www.myget.org/F/aelf-project-dev/api/v3/index.json
dotnet add package Aevatar.Core.Abstractions:
https://www.myget.org/F/aelf-project-dev/api/v3/index.json
Creating Agent Steps#
Define State#
Your agent’s persistent state must inherit from StateBase and be annotated properly with [GenerateSerializer] and [Id(...)]. For example:
using System;
using System.Collections.Generic;
using Aevatar.Core;
using Aevatar.Core.Abstractions;
using Aevatar.EventSourcing.Core;
using Orleans.Serialization;
[GenerateSerializer]
public class TwitterGAgentState : StateBase
{
[Id(0)] public Guid Id { get; set; } = Guid.NewGuid();
[Id(1)] public string UserId { get; set; }
[Id(2)] public string Token { get; set; }
[Id(3)] public string TokenSecret { get; set; }
[Id(4)] public Dictionary<string, string> RepliedTweets { get; set; } = new();
[Id(5)] public string UserName { get; set; }
// Add other relevant fields as needed
}
Note:
Define Event Sourcing Event(s)#
Event Sourcing events (which are appended to the event log) must inherit from StateLogEventBase. They should also use the Orleans [GenerateSerializer] and [Id(...)] attributes:
using Aevatar.EventSourcing.Core;
using Orleans.Serialization;
[GenerateSerializer]
public class TweetGEvent :StateLogEventBase<TweetGEvent>
{
[Id(0)] public string Text { get; set; }
// You can add more properties as needed, e.g., TweetId, Timestamp, etc.
}
Note:
Define External Message Event(s)#
Events that come from "outside" (i.e., external messages or commands) inherit from EventBase. This is what your agent will handle with [EventHandler].
using Aevatar.Core.Abstractions;
using Orleans.Serialization;
[GenerateSerializer]
public class CreateTweetGEvent : EventBase
{
[Id(0)] public string Text { get; set; }
}
Note:
Create the Agent (Grain)#
Your agent implements a corresponding Grain interface and inherits from GAgentBase<TState, TEvent>. You must specify:
For example, define an interface:
using Orleans;
public interface ITwitterGAgent : IGrainWithStringKey
{
// Any custom methods you'd like to expose, e.g.:
// Task HandleExternalEventAsync(CreateTweetGEvent @event);
}
Then implement the agent:
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Aevatar.Core;
using Aevatar.Core.Abstractions;
using Aevatar.EventSourcing.Core;
using Aevatar.GAgents;
using Orleans;
using Orleans.Providers;
[StorageProvider(ProviderName = "PubSubStore")]
[LogConsistencyProvider(ProviderName = "LogStorage")]
public class TwitterGAgent : GAgentBase<TwitterGAgentState, TweetGEvent>, ITwitterGAgent
{
private readonly ILogger<TwitterGAgent> _logger;
public TwitterGAgent(ILogger<TwitterGAgent> logger) : base(logger)
{
_logger = logger;
}
/// <summary>
/// Handle external event for creating a Tweet.
/// </summary>
[EventHandler]
public async Task HandleEventAsync(CreateTweetGEvent @event)
{
_logger.LogDebug("Received CreateTweetGEvent. Text: {Text}", @event.Text);
if (string.IsNullOrWhiteSpace(@event.Text))
{
_logger.LogWarning("CreateTweetGEvent received with empty text. Skipping...");
return;
}
if (string.IsNullOrWhiteSpace(State.UserId))
{
_logger.LogWarning("No UserId found in state. Skipping tweet creation...");
return;
}
// 1. Raise an internal event for event sourcing (if needed)
await RaiseEventAsync(new TweetGEvent
{
Text = @event.Text
});
// 2. Optionally, publish to the global stream so that other GAgents or services can react
await PublishAsync(new SocialGEvent
{
Content = @event.Text
// Fill in other fields if needed
});
}
/// <summary>
/// Optional override: When a TweetGEvent is applied, update the state if needed.
/// This is called when the event is raised via RaiseEventAsync.
/// </summary>
protected override Task ApplyEventAsync(TweetGEvent @event)
{
_logger.LogInformation("Applying TweetGEvent to state. Text: {Text}", @event.Text);
// Example: record last tweeted content in the agent's state
// State.LastTweetText = @event.Text;
// Then save changes to the state if necessary (GAgentBase handles saving, but you can do additional logic).
return Task.CompletedTask;
}
}
Key Notes:#
- RaiseEventAsync() appends an event to the event log (i.e., TweetGEvent).
- PublishAsync() sends a message to an Orleans stream that other grains or external consumers can subscribe to.
- This attribute on the method indicates that this method handles external events/commands (like CreateTweetGEvent).
- The ApplyEventAsync() method (or any relevant method in your base class) is where your internal state is updated once the event is appended to the log.
- GAgentBase typically manages saving and loading your TState from the configured storage provider.
- The event-sourcing provider replays the log of TEvent events to rebuild the current state.
- Use different log levels (Debug, Information, Warning, Error) for clarity.
Creating AI Agent Steps#
1: Define AI Agent State#
Create a custom state class that inherits from StateBase or AIGAgentStateBase:
[GenerateSerializer]
public class SampleAIAgentState : StateBase
{
// Define your agent's state properties
[Id(0)] public Guid Id { get; set; } = Guid.NewGuid();
[Id(1)] public string Name { get; set; }
[Id(2)] public int Age { get; set; }
}
[GenerateSerializer]
public class SampleAIAgentStateLogEvent : StateLogEventBase<SampleAIAgentStateLogEvent>
{
// Add any additional properties for the state log event here
}
[GenerateSerializer]
public class SampleAIAgentConfigurationEvent : ConfigurationEventBase
{
// Add any additional properties for the configuration event here
}
2: Create AI Agent Class#
Inherit from GAgentBase or AIGAgentBase:
public class SampleAIAgent : GAgentBase<SampleAIAgentState, SampleAIAgentStateLogEvent, EventBase, SampleAIAgentConfigurationEvent>
{
public SampleAIAgent(ILogger<SampleAIAgent> logger) : base(logger)
{
}
// Define agent-specific methods and event handlers
[EventHandler]
public async Task DoSomethingAsync()
{
// Agent logic here
Logger.LogInformation($"Agent {State.Name} is doing something");
}
public override async Task PerformConfigAsync(SampleAIAgentConfigurationEvent configurationEvent)
{
// Your agent-specific configuration logic goes here.
// For example, if you need to set some internal values based on the configuration event:
// State.SomeProperty = configurationEvent.SomeValue;
// Example placeholder implementation:
// await Task.Delay(10);
}
}
3: Event Handling#
Implement event handling methods:
[EventHandler]
public async Task HandleSomeEventAsync(SomeEventType eventData)
{
// Process the event
// Optionally modify state or raise new events
}
Either mark the [EventHandler] attribute, or use HandleEventAsync as method name.
4: Registration and Lifecycle#
Agents can be registered and managed using these methods:
Best Practices#
Advanced Features#
Dependency Injection#
Ensure your agent is registered in the dependency injection container:
services.AddTransient<MyAgent>();
services.AddSingleton<IBrainFactory, DefaultBrainFactory>();
Example: AI Agent with LLM#
public class AIAssistantAgent : AIGAgentBase<MyAgentState, StateLogEventBase<MyAgentState>>
{
public AIAssistantAgent(ILogger<AIAssistantAgent> logger) : base(logger)
{
}
public async Task<string> GenerateResponseAsync(string prompt)
{
// Use the initialized brain to generate a response
return await _brain.GenerateResponseAsync(prompt);
}
}
Troubleshooting#
Conclusion#
Creating an Agent in Aevatar is a powerful way to build modular, event-driven, and stateful components in your application.
References#
Edited on: 19 February 2025 07:32:30 GMT+0