diff --git a/docs/dotnet/user-guide/core-user-guide/defining-message-types.md b/docs/dotnet/user-guide/core-user-guide/defining-message-types.md new file mode 100644 index 000000000000..9d7a130574b3 --- /dev/null +++ b/docs/dotnet/user-guide/core-user-guide/defining-message-types.md @@ -0,0 +1,30 @@ +# Defining Message Types + +Messages are currently required to be Protocol Buffers. To define them, it is necessary to include the Protocol Buffers compiler, through the `Grpc.Tools` package. In your `.csproj` file, add/edit: + +```xml + +``` + +Then create an include a `.proto` file in the project: + +```xml + + + +``` + +Then define your messages as specified in the [Protocol Buffers Language Guide](https://protobuf.dev/programming-guides/proto3/) + +```proto +syntax = "proto3"; + +package HelloAgents; + +option csharp_namespace = "AgentsProtocol"; + +message TextMessage { + string Source = 1; + string Content = 2; +} +``` \ No newline at end of file diff --git a/docs/dotnet/user-guide/core-user-guide/differences-python.md b/docs/dotnet/user-guide/core-user-guide/differences-python.md new file mode 100644 index 000000000000..d5e43b9e32e9 --- /dev/null +++ b/docs/dotnet/user-guide/core-user-guide/differences-python.md @@ -0,0 +1,46 @@ +# Differences from Python + +## Agents Self-Interact + +When an agent sends a message of a type to which it also listens: + +```csharp +[TopicSubscription("default")] +public class MyAgent( + IAgentWorker worker, + [FromKeyedServices("EventTypes")] EventTypes typeRegistry + ) : + Agent(worker, typeRegistry), + IHandle +{ + async Task SomeInternalFunctionAsync() + { + Message m; + + // ... + + await this.PublishMessageAsync(m); + } + + public async Task Handle(Message message) + { + // will receive messages sent by SomeInternalFunctionAsync() + } +} +``` + +Tracked by [#4998](https://github.com/microsoft/autogen/issues/4998) + +## 'Local' Runtime is Multithreaded + +Unlike the `single_threaded_runtime`, the InProcess (`local: true`) runtime for .NET is multi-threaded, so messages will process in arbitrary order across agents. This means that an agent may process messages sent after the termination request has been made unless checking for termination using the `IHostApplicationLifecycle` service. + +## No equivalent to 'stop_when_idle()' + +Agents need to request termination explicitly, as there is no meaningful 'idle' state. + +## All message types need to be Protocol Buffers + +See (linkto: defining-message-types.md) for instructions on defining messages + +Tracked by [#4695](https://github.com/microsoft/autogen/issues/4695) \ No newline at end of file diff --git a/docs/dotnet/user-guide/core-user-guide/getting-started.md b/docs/dotnet/user-guide/core-user-guide/getting-started.md new file mode 100644 index 000000000000..9e91b14d1cb3 --- /dev/null +++ b/docs/dotnet/user-guide/core-user-guide/getting-started.md @@ -0,0 +1,143 @@ +# Quick Start + +Before diving into the core APIs, let’s start with a simple example of two agents that count down from 10 to 1. + +We first define the agent classes and their respective procedures for handling messages. We create two agent classes: `Modifier` and `Checker`. The `Modifier` agent modifies a number that is given and the `Check` agent checks the value against a condition. We also define a pair of +messages in a .proto file which will be generated into the message types that will be passed +between the agents. + +```proto +syntax = "proto3"; + +package HelloAgents; + +option csharp_namespace = "Microsoft.Autogen.Samples.CountAgent.Protocol"; + +message CountMessage { + int32 Content = 1; +} + +message CountUpdate { + int32 NewCount = 1; +} +``` + +We create two messages to ensure we have tick-tock behaviour between the agents; if we used a single type, then both agents would receive the other agents' message as well as self-sent messages. (Note: this is a behaviour difference from Python; Issue#4998) + +In the project file, we add + +```xml + + + + + + + +``` + +This will ensure the message classes are available for our agents to send/receive. + +Now we will define the agents: + +```csharp +[TopicSubscription("default")] +public class Modifier( + IAgentWorker worker, + [FromKeyedServices("EventTypes")] EventTypes typeRegistry, + ModifyF modifyFunc + ) : + Agent(worker, typeRegistry), + IHandle +{ + public async Task Handle(CountMessage item) + { + // handling code + } +} +``` + +The `TopicSubscription` attribute defines the set of topics the agents will listen to. Topics (see here) are useful for separaating different logical chains of agent communications. + +The first two parameters to the constructor, `IAgentWorker` and `EventTypes` are automatically made available through dependency injection to the workers. (We do not allow direct construction of workers in Autogen.Core: see here for FAQ), and need to be passed on to the base class. + +Other parameters are also made available through dependency injection (see here). + +Agents register for messages by implementing the `IHandle` interface: + +```csharp + public async Task Handle(CountMessage item) + { + int newValue = modifyFunc(item.Content); + Console.WriteLine($"{SEPARATOR_LINE}\nModifier:\nModified {item.Content} to {newValue}"); + + CountUpdate updateMessage = new CountUpdate { NewCount = newValue }; + + await this.PublishMessageAsync(updateMessage); + } +``` + +The `Modifier` agent receives a `CountMessage` indicating the current count, modifies it using the injected `ModifyF modifyFunc`, and publishes the `CountUpdate` message. + +The `Checker` agent is defines similarly: + +```csharp +[TopicSubscription("default")] +public class Checker( + IAgentWorker worker, + [FromKeyedServices("EventTypes")] EventTypes typeRegistry, + IHostApplicationLifetime hostApplicationLifetime, + TerminationF runUntilFunc + ) : + Agent(worker, typeRegistry), + IHandle +{ + public Task Handle(CountUpdate item) + { + if (!runUntilFunc(item.NewCount)) + { + Console.WriteLine($"{SEPARATOR_LINE}\nChecker:\n{item.NewCount} passed the check, continue."); + await this.PublishMessageAsync(new CountMessage { Content = item.NewCount }); + } + else + { + Console.WriteLine($"{SEPARATOR_LINE}\nChecker:\n{item.NewCount} failed the check, stopping."); + hostApplicationLifetime.StopApplication(); + } + } +} +``` + +The `Checker` continues the count when `runUntilFunc` has not triggered by publishing a new `CountMessage` with the updated count; if termination is desired, it will request it by calling `hostApplicationLifetime.StopApplication()`. + +You might have already noticed, the agents’ logic, whether it is using model or code executor, is completely decoupled from how messages are delivered. This is the core idea: the framework provides a communication infrastructure, and the agents are responsible for their own logic. We call the communication infrastructure an Agent Runtime. + +Agent runtime is a key concept of this framework. Besides delivering messages, it also manages agents’ lifecycle. So the creation of agents are handled by the runtime. + +The following code shows how to register and run the agents using the local (InProcess) runtime: + +```csharp +// Define the counting logic +using ModifyF = System.Func; +using TerminationF = System.Func; + +ModifyF modifyFunc = (int x) => x - 1; +TerminationF runUntilFunc = (int x) => +{ + return x <= 1; +}; + +// Register the services +WebApplicationBuilder? builder = WebApplication.CreateBuilder(args); +builder.Services.AddSingleton(modifyFunc); +builder.Services.AddSingleton(runUntilFunc); + +// Send the initial count to the agents app, running on the `local` runtime, and pass through the registered services via the application `builder` +var app = await AgentsApp.PublishMessageAsync("default", new CountMessage +{ + Content = 10 +}, local: true, builder: builder).ConfigureAwait(false); + +// Run until application shutdown +await app.WaitForShutdownAsync(); +``` \ No newline at end of file diff --git a/docs/dotnet/user-guide/core-user-guide/installation.md b/docs/dotnet/user-guide/core-user-guide/installation.md new file mode 100644 index 000000000000..270bcd659db6 --- /dev/null +++ b/docs/dotnet/user-guide/core-user-guide/installation.md @@ -0,0 +1,33 @@ +# Installation + +## Add via `` + +``` + + +``` + + + +## These will only work after we release the package: + +## Install via `.NET cli` + +``` +> dotnet add package Microsoft.AutoGen.Contracts --version 0.4.0 +> dotnet add package Microsoft.AutoGen.Core --version 0.4.0 +``` + +## Install via `Package Manager` + +``` +PM> NuGet\Install-Package Microsoft.AutoGen.Contracts -Version 0.4.0 +PM> NuGet\Install-Package Microsoft.AutoGen.Core -Version 0.4.0 +``` + +## Add via `` + +``` + + +```