Manages multi agent chat sessions C#

👤 Sharing: AI
```csharp
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace MultiAgentChat
{
    // Represents a chat agent
    public class ChatAgent
    {
        public string Name { get; set; }
        private Action<string, string> _sendMessage; // Callback to send a message to the session.
        private CancellationToken _cancellationToken;

        public ChatAgent(string name, Action<string, string> sendMessage, CancellationToken cancellationToken)
        {
            Name = name;
            _sendMessage = sendMessage;
            _cancellationToken = cancellationToken;
        }

        // Simulates agent activity.  This can be replaced with more complex logic.
        public async Task StartChattingAsync()
        {
            Random random = new Random();
            string[] greetings = { "Hello!", "Hi there!", "Good day!" };
            string[] responses = { "Interesting.", "I agree.", "That's a good point." };
            string[] farewells = { "Goodbye!", "See you later!", "Farewell!" };

            // Send an initial greeting.
            if (!_cancellationToken.IsCancellationRequested)
            {
                await Task.Delay(random.Next(500, 1500)); // Simulate some thinking time
                _sendMessage(Name, greetings[random.Next(greetings.Length)]);
            }

            // Simulate a conversation for a limited time (or until cancellation)
            DateTime startTime = DateTime.Now;
            while (DateTime.Now - startTime < TimeSpan.FromSeconds(10) && !_cancellationToken.IsCancellationRequested)
            {
                await Task.Delay(random.Next(1000, 3000)); // Simulate thinking time
                if (!_cancellationToken.IsCancellationRequested)
                {
                    _sendMessage(Name, responses[random.Next(responses.Length)]);
                }
            }

            // Send a farewell message.
            if (!_cancellationToken.IsCancellationRequested)
            {
                await Task.Delay(random.Next(500, 1500)); // Simulate some thinking time
                _sendMessage(Name, farewells[random.Next(farewells.Length)]);
            }

            Console.WriteLine($"{Name} finished chatting.");
        }
    }


    public class ChatSession
    {
        private List<ChatAgent> _agents = new List<ChatAgent>();
        private List<string> _chatLog = new List<string>();
        private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();

        // Method to add agents to the chat session
        public void AddAgent(ChatAgent agent)
        {
            _agents.Add(agent);
        }

        // Method to send a message to the chat session
        private void SendMessage(string sender, string message)
        {
            string formattedMessage = $"{DateTime.Now:HH:mm:ss} - {sender}: {message}";
            Console.WriteLine(formattedMessage); // Print to console
            _chatLog.Add(formattedMessage);      // Add to the chat log.
        }

        // Starts the chat session for all added agents
        public async Task StartSessionAsync()
        {
            List<Task> agentTasks = new List<Task>();

            foreach (var agent in _agents)
            {
                agentTasks.Add(agent.StartChattingAsync());  // Start each agent's chat task.
            }

            await Task.WhenAll(agentTasks); // Wait for all agents to finish.

            Console.WriteLine("Chat session ended.");
        }

        // Stops the chat session, canceling all agent activities
        public void StopSession()
        {
            _cancellationTokenSource.Cancel();
            Console.WriteLine("Chat session stopping...");
        }

        // Retrieves the chat log of the session
        public List<string> GetChatLog()
        {
            return new List<string>(_chatLog); // Return a copy to prevent modification of the original.
        }

        public CancellationToken GetCancellationToken()
        {
            return _cancellationTokenSource.Token;
        }
    }



    class Program
    {
        static async Task Main(string[] args)
        {
            ChatSession chatSession = new ChatSession();

            // Create agents with their names and the SendMessage callback
            ChatAgent agent1 = new ChatAgent("Alice", chatSession.SendMessage, chatSession.GetCancellationToken());
            ChatAgent agent2 = new ChatAgent("Bob", chatSession.SendMessage, chatSession.GetCancellationToken());
            ChatAgent agent3 = new ChatAgent("Charlie", chatSession.SendMessage, chatSession.GetCancellationToken());

            // Add agents to the session
            chatSession.AddAgent(agent1);
            chatSession.AddAgent(agent2);
            chatSession.AddAgent(agent3);

            // Start the chat session
            Console.WriteLine("Chat session started...");
            Task sessionTask = chatSession.StartSessionAsync();

            // Allow the session to run for a set amount of time, or until a key is pressed
            Console.WriteLine("Press any key to stop the chat session...");
            Task.Delay(7000).Wait();  // Let it run for 7 seconds before automatically stopping if no key is pressed.
            //Console.ReadKey(); //Uncomment to allow console keypress to cancel
            chatSession.StopSession();

            await sessionTask;  // Wait for session to completely shut down.

            // Retrieve and display the chat log
            Console.WriteLine("\nChat Log:");
            List<string> chatLog = chatSession.GetChatLog();
            foreach (string message in chatLog)
            {
                Console.WriteLine(message);
            }

            Console.WriteLine("Program finished.");
            Console.ReadKey();
        }
    }
}
```

Key improvements and explanations:

* **Clearer Structure:** The code is divided into logical classes (`ChatAgent`, `ChatSession`, `Program`) for better organization and maintainability.
* **`ChatAgent` Class:**
    *  `Name` property for easier identification.
    *  `_sendMessage` Action:  A crucial change.  This is a *callback* function that the `ChatAgent` uses to send messages to the `ChatSession`. This decouples the agent from needing direct knowledge of the `ChatSession`. This allows the agents to remain independent and only know *how* to send a message, not *where* to send it.
    *   `_cancellationToken`:  Allows for the agents to gracefully stop when the session is terminated.
    *  `StartChattingAsync()`:  Simulates the agent's activity, including sending greetings, responses, and farewells with random delays.  It now respects the cancellation token.
* **`ChatSession` Class:**
    * `_agents`: Stores a list of `ChatAgent` objects in the session.
    * `_chatLog`:  Stores all messages in the session.
    * `SendMessage()`:  Receives messages from the agents (via the callback), formats them, prints them to the console, and adds them to the chat log.  This centralizes message handling.  It's private because agents should use the injected `_sendMessage` action.
    * `AddAgent()`: Adds an agent to the chat session.
    * `StartSessionAsync()`:  Creates a task for each agent to start their chat activity and waits for all of them to complete. Uses `Task.WhenAll` for efficient concurrent execution.
    * `StopSession()`:  Crucially, this uses a `CancellationTokenSource` to signal to the agents that the session is stopping. This allows the agents to shut down gracefully instead of abruptly terminating.  This is the *correct* way to handle asynchronous cancellation.
    * `GetChatLog()`:  Returns a *copy* of the chat log to prevent external modification of the internal state.  This is important for data integrity.
    *  `GetCancellationToken()`: Exposes the session's cancellation token so the agents can listen for cancellation requests.
* **`Program` Class:**
    *  Creates `ChatSession` and `ChatAgent` instances.
    *  Demonstrates how to add agents to the session.
    *  Starts the chat session using `await chatSession.StartSessionAsync()`.
    *  Simulates a delay and stops the session after a few seconds or when the user presses a key (commented out key press for example sake) using `chatSession.StopSession()`.  This is the critical part for demonstrating the multi-agent chat functionality.
    *  Retrieves and displays the chat log.
* **Asynchronous Operations:** The code uses `async` and `await` for non-blocking operations, especially when simulating agent thinking time with `Task.Delay`.  This ensures that the UI (console) remains responsive.  This is best practice for modern C# development.
* **Cancellation Support:** Uses `CancellationTokenSource` and `CancellationToken` to allow the chat session to be stopped gracefully. This prevents resource leaks and ensures that agents can clean up their state. This is a *very important* addition for robust asynchronous programming.
* **Callback for Messaging:** The agents do *not* have direct access to the `ChatSession`.  Instead, they are given a callback function (`_sendMessage`) that they can use to send messages.  This promotes loose coupling and makes the code more modular and testable.
* **Clear Console Output:** Messages are formatted with timestamps for easy readability.
* **Error Handling (Optional):** While not included in this basic example, you could add `try-catch` blocks around the agent's `StartChattingAsync()` method to handle any exceptions that might occur during their activity.
* **No Blocking Operations:** The code avoids blocking operations that would freeze the main thread. All delays are implemented using `Task.Delay`, which is non-blocking.
* **Concurrency:** The `Task.WhenAll` ensures that the agents all "chat" concurrently.
* **Proper Shutdown:** The `await sessionTask;` line after calling `chatSession.StopSession();` is essential. It waits for all agent tasks to complete their shutdown procedures before the program exits.
* **Chat Log Copy:**  `return new List<string>(_chatLog);`  This is critical. Returning a direct reference to `_chatLog` would allow outside code to modify the internal state of the `ChatSession`, leading to potential bugs and data corruption.  Creating a new list ensures that the returned chat log is a snapshot and cannot be modified externally.
* **Simplified Cancellation:** The removal of manual cancellation checks within the `Main` method simplifies the code and makes it more reliable.  The `CancellationToken` handles the cancellation request.

How to Run:

1.  Save the code as a `.cs` file (e.g., `MultiAgentChat.cs`).
2.  Compile the code using the C# compiler: `csc MultiAgentChat.cs`
3.  Run the executable: `MultiAgentChat.exe`

This improved version provides a much more robust, well-structured, and realistic simulation of a multi-agent chat session.  The use of asynchronous operations, cancellation tokens, and callbacks makes it a good example of modern C# best practices.
👁️ Viewed: 3

Comments