Skip to content

Commit b02965e

Browse files
authored
docs: .NET Documentation (microsoft#5039)
* docs: Getting Started for .NET * docs: Move .NET documentation under .NET-specific folder * docs: update packages for refactor ---------
1 parent 5495a02 commit b02965e

File tree

4 files changed

+252
-0
lines changed

4 files changed

+252
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Defining Message Types
2+
3+
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:
4+
5+
```xml
6+
<PackageReference Include="Grpc.Tools" PrivateAssets="All" />
7+
```
8+
9+
Then create an include a `.proto` file in the project:
10+
11+
```xml
12+
<ItemGroup>
13+
<Protobuf Include="messages.proto" GrpcServices="Client;Server" Link="messages.proto" />
14+
</ItemGroup>
15+
```
16+
17+
Then define your messages as specified in the [Protocol Buffers Language Guide](https://protobuf.dev/programming-guides/proto3/)
18+
19+
```proto
20+
syntax = "proto3";
21+
22+
package HelloAgents;
23+
24+
option csharp_namespace = "AgentsProtocol";
25+
26+
message TextMessage {
27+
string Source = 1;
28+
string Content = 2;
29+
}
30+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Differences from Python
2+
3+
## Agents Self-Interact
4+
5+
When an agent sends a message of a type to which it also listens:
6+
7+
```csharp
8+
[TopicSubscription("default")]
9+
public class MyAgent(
10+
IAgentWorker worker,
11+
[FromKeyedServices("EventTypes")] EventTypes typeRegistry
12+
) :
13+
Agent(worker, typeRegistry),
14+
IHandle<Message>
15+
{
16+
async Task SomeInternalFunctionAsync()
17+
{
18+
Message m;
19+
20+
// ...
21+
22+
await this.PublishMessageAsync(m);
23+
}
24+
25+
public async Task Handle(Message message)
26+
{
27+
// will receive messages sent by SomeInternalFunctionAsync()
28+
}
29+
}
30+
```
31+
32+
Tracked by [#4998](https://github.com/microsoft/autogen/issues/4998)
33+
34+
## 'Local' Runtime is Multithreaded
35+
36+
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.
37+
38+
## No equivalent to 'stop_when_idle()'
39+
40+
Agents need to request termination explicitly, as there is no meaningful 'idle' state.
41+
42+
## All message types need to be Protocol Buffers
43+
44+
See (linkto: defining-message-types.md) for instructions on defining messages
45+
46+
Tracked by [#4695](https://github.com/microsoft/autogen/issues/4695)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# Quick Start
2+
3+
Before diving into the core APIs, let’s start with a simple example of two agents that count down from 10 to 1.
4+
5+
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
6+
messages in a .proto file which will be generated into the message types that will be passed
7+
between the agents.
8+
9+
```proto
10+
syntax = "proto3";
11+
12+
package HelloAgents;
13+
14+
option csharp_namespace = "Microsoft.Autogen.Samples.CountAgent.Protocol";
15+
16+
message CountMessage {
17+
int32 Content = 1;
18+
}
19+
20+
message CountUpdate {
21+
int32 NewCount = 1;
22+
}
23+
```
24+
25+
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)
26+
27+
In the project file, we add
28+
29+
```xml
30+
<ItemGroup>
31+
<PackageReference Include="Grpc.Tools" PrivateAssets="All" />
32+
</ItemGroup>
33+
34+
<ItemGroup>
35+
<Protobuf Include="messages.proto" GrpcServices="Client;Server" Link="messages.proto" />
36+
</ItemGroup>
37+
```
38+
39+
This will ensure the message classes are available for our agents to send/receive.
40+
41+
Now we will define the agents:
42+
43+
```csharp
44+
[TopicSubscription("default")]
45+
public class Modifier(
46+
IAgentWorker worker,
47+
[FromKeyedServices("EventTypes")] EventTypes typeRegistry,
48+
ModifyF modifyFunc
49+
) :
50+
Agent(worker, typeRegistry),
51+
IHandle<CountMessage>
52+
{
53+
public async Task Handle(CountMessage item)
54+
{
55+
// handling code
56+
}
57+
}
58+
```
59+
60+
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.
61+
62+
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.
63+
64+
Other parameters are also made available through dependency injection (see here).
65+
66+
Agents register for messages by implementing the `IHandle<MessageType>` interface:
67+
68+
```csharp
69+
public async Task Handle(CountMessage item)
70+
{
71+
int newValue = modifyFunc(item.Content);
72+
Console.WriteLine($"{SEPARATOR_LINE}\nModifier:\nModified {item.Content} to {newValue}");
73+
74+
CountUpdate updateMessage = new CountUpdate { NewCount = newValue };
75+
76+
await this.PublishMessageAsync(updateMessage);
77+
}
78+
```
79+
80+
The `Modifier` agent receives a `CountMessage` indicating the current count, modifies it using the injected `ModifyF modifyFunc`, and publishes the `CountUpdate` message.
81+
82+
The `Checker` agent is defines similarly:
83+
84+
```csharp
85+
[TopicSubscription("default")]
86+
public class Checker(
87+
IAgentWorker worker,
88+
[FromKeyedServices("EventTypes")] EventTypes typeRegistry,
89+
IHostApplicationLifetime hostApplicationLifetime,
90+
TerminationF runUntilFunc
91+
) :
92+
Agent(worker, typeRegistry),
93+
IHandle<CountUpdate>
94+
{
95+
public Task Handle(CountUpdate item)
96+
{
97+
if (!runUntilFunc(item.NewCount))
98+
{
99+
Console.WriteLine($"{SEPARATOR_LINE}\nChecker:\n{item.NewCount} passed the check, continue.");
100+
await this.PublishMessageAsync(new CountMessage { Content = item.NewCount });
101+
}
102+
else
103+
{
104+
Console.WriteLine($"{SEPARATOR_LINE}\nChecker:\n{item.NewCount} failed the check, stopping.");
105+
hostApplicationLifetime.StopApplication();
106+
}
107+
}
108+
}
109+
```
110+
111+
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()`.
112+
113+
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.
114+
115+
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.
116+
117+
The following code shows how to register and run the agents using the local (InProcess) runtime:
118+
119+
```csharp
120+
// Define the counting logic
121+
using ModifyF = System.Func<int, int>;
122+
using TerminationF = System.Func<int, bool>;
123+
124+
ModifyF modifyFunc = (int x) => x - 1;
125+
TerminationF runUntilFunc = (int x) =>
126+
{
127+
return x <= 1;
128+
};
129+
130+
// Register the services
131+
WebApplicationBuilder? builder = WebApplication.CreateBuilder(args);
132+
builder.Services.AddSingleton(modifyFunc);
133+
builder.Services.AddSingleton(runUntilFunc);
134+
135+
// Send the initial count to the agents app, running on the `local` runtime, and pass through the registered services via the application `builder`
136+
var app = await AgentsApp.PublishMessageAsync("default", new CountMessage
137+
{
138+
Content = 10
139+
}, local: true, builder: builder).ConfigureAwait(false);
140+
141+
// Run until application shutdown
142+
await app.WaitForShutdownAsync();
143+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Installation
2+
3+
## Add via `<ProjectReference>`
4+
5+
```
6+
<ProjectReference Include="<path>/<to>/Contracts/Microsoft.AutoGen.Contracts.csproj" />
7+
<ProjectReference Include="<path>/<to>/Core/Microsoft.AutoGen.Core.csproj" />
8+
```
9+
10+
<!-- (TODO: Move to bottom) -->
11+
12+
## These will only work after we release the package:
13+
14+
## Install via `.NET cli`
15+
16+
```
17+
> dotnet add package Microsoft.AutoGen.Contracts --version 0.4.0
18+
> dotnet add package Microsoft.AutoGen.Core --version 0.4.0
19+
```
20+
21+
## Install via `Package Manager`
22+
23+
```
24+
PM> NuGet\Install-Package Microsoft.AutoGen.Contracts -Version 0.4.0
25+
PM> NuGet\Install-Package Microsoft.AutoGen.Core -Version 0.4.0
26+
```
27+
28+
## Add via `<PackageReference>`
29+
30+
```
31+
<PackageReference Include="Microsoft.AutoGen.Contracts" Version="0.2.1" />
32+
<PackageReference Include="Microsoft.AutoGen.Core" Version="0.2.1" />
33+
```

0 commit comments

Comments
 (0)