Managing Multiple Tutoring Sessions with DeepSeek in C#

Welcome to the next step in your journey of creating a personal tutor with DeepSeek! In the previous lessons, you learned how to send queries to DeepSeek's language model, explored model parameters, maintained tutoring session history, and personalized AI behavior with system prompts. Now, we will focus on managing multiple tutoring sessions. This is crucial for applications where you need to handle several educational interactions simultaneously, such as a tutoring platform serving multiple students. By the end of this lesson, you will be able to create and manage multiple tutoring sessions using DeepSeek's API in C#, setting the stage for more complex educational interactions.

Creating Unique Tutoring Sessions

In a tutoring application, each educational interaction should be treated as a separate session. To achieve this, we use unique identifiers for each tutoring session. This ensures that queries and explanations are correctly associated with their respective sessions. In C#, we use Guid.NewGuid().ToString() to generate a unique identifier for each tutoring session. When a new tutoring session is created, a unique sessionId is generated, and an empty history is initialized.

We store tutoring history in a dictionary called tutoringSessions, where each key is a unique sessionId and the value is a list of messages representing the conversation history. When a new session is created, we also add a system prompt to the session's history to define the tutor's behavior.

using System;
using System.Collections.Generic;

// Define a message structure
public class Message
{
    required public string Role { get; set; }
    required public string Content { get; set; }
}

// Store all active tutoring sessions
private static Dictionary<string, List<Message>> tutoringSessions = new Dictionary<string, List<Message>>();

// Define a common system prompt for all sessions
private static Message systemPrompt = new Message
{
    Role = "system",
    Content = "You are a knowledgeable and patient tutor, ready to assist with various academic subjects."
};

// Create a new tutoring session with a unique identifier
public static string CreateSession()
{
    string sessionId = Guid.NewGuid().ToString(); // Create unique session identifier
    tutoringSessions[sessionId] = new List<Message>();
    tutoringSessions[sessionId].Add(systemPrompt); // Add system prompt to tutoring history
    return sessionId;
}

This approach ensures that each tutoring session is uniquely identified and maintains its own conversation history, allowing for distinct and coherent educational interactions.

Sending Queries and Receiving Explanations

Once a tutoring session is established, you can send queries and receive explanations from the DeepSeek model. It's important to maintain the context by sending the full tutoring history to the model. However, keep in mind that language models have a context window, which limits the amount of conversation history they can process at once. If the conversation becomes too large, you should trim the history by removing the oldest messages and passing only the most recent ones.

Below is an updated asynchronous method for sending a query and receiving an explanation, matching the approach used in the practice section. The method takes a sessionId and a userQuery as inputs, adds the query to the tutoring history, and requests an explanation from the AI. The explanation is then processed and added to the tutoring history, ensuring continuity in the educational interaction.

using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

// Method to send a query and receive an explanation
public static async Task<string> SendQuery(string sessionId, string userQuery)
{
    if (!tutoringSessions.ContainsKey(sessionId))
    {
        throw new ArgumentException("Tutoring session not found!");
    }

    // Add student's query to history
    tutoringSessions[sessionId].Add(new Message { Role = "user", Content = userQuery });

    // Prepare the request payload
    var payload = new
    {
        model = "deepseek-ai/DeepSeek-V3",
        messages = tutoringSessions[sessionId].ConvertAll(m => new Dictionary<string, string>
        {
            { "role", m.Role },
            { "content", m.Content }
        })
    };

    var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");
    var baseUri = Environment.GetEnvironmentVariable("OPENAI_BASE_URL");
    if (string.IsNullOrEmpty(apiKey))
    {
        throw new Exception("Set your OPENAI_API_KEY.");
    }
    if (string.IsNullOrEmpty(baseUri))
    {
        throw new Exception("Set your OPENAI_BASE_URL (e.g. https://api.openai.com).");
    }

    // Ensure there's no trailing slash, then append the chat-completions path:
    var endpoint = baseUri.TrimEnd('/') + "/v1/chat/completions";

    using var client = new HttpClient();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);

    var jsonPayload = JsonSerializer.Serialize(payload);
    using var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
    var response = await client.PostAsync(endpoint, content);
    var body = await response.Content.ReadAsStringAsync();

    if (!response.IsSuccessStatusCode)
    {
        throw new Exception($"Error ({response.StatusCode}): {body}");
    }

    // Parse out the assistant's reply
    using var doc = JsonDocument.Parse(body);
    var reply = doc.RootElement
                   .GetProperty("choices")[0]
                   .GetProperty("message")
                   .GetProperty("content")
                   .GetString();

    string assistantResponse = reply?.Trim() ?? "";

    // Add AI's explanation to history
    tutoringSessions[sessionId].Add(new Message { Role = "assistant", Content = assistantResponse });

    // Return AI's explanation
    return assistantResponse;
}

This method ensures that each query and response is properly tracked within the session's history, maintaining the context for ongoing educational interactions.

Handling Multiple Tutoring Sessions

Managing multiple tutoring sessions simultaneously is a crucial feature for advanced educational applications. By using unique identifiers, you can create and interact with different tutoring sessions independently, ensuring that each educational interaction remains distinct and contextually accurate. Below, we demonstrate this by initiating a first session and sending queries to it.

// Example usage in an async Main method or similar context
string sessionId1 = CreateSession();
string response1_1 = await SendQuery(sessionId1, "Can you explain the concept of limits in calculus?");
Console.WriteLine("Session 1, Query 1: " + response1_1);
string response1_2 = await SendQuery(sessionId1, "How does the epsilon-delta definition work?");
Console.WriteLine("Session 1, Query 2: " + response1_2);

Output for the first tutoring session (example):

Session 1, Query 1: In calculus, a limit is the value that a function approaches as the input approaches a certain value. Formally, we write lim(x→a) f(x) = L, meaning that as x gets closer to a, f(x) gets closer to L.
Session 1, Query 2: The epsilon-delta definition formalizes the concept of limits. It states that lim(x→a) f(x) = L if for every ε > 0, there exists a δ > 0 such that if 0 < |x - a| < δ, then |f(x) - L| < ε.

Now, let's create a second tutoring session and interact with it.

string sessionId2 = CreateSession();
string response2_1 = await SendQuery(sessionId2, "What is the significance of chemical bonds in molecules?");
Console.WriteLine("Session 2, Query 1: " + response2_1);
string response2_2 = await SendQuery(sessionId2, "Can you explain covalent bonding?");
Console.WriteLine("Session 2, Query 2: " + response2_2);

Output for the second tutoring session (example):

Session 2, Query 1: Chemical bonds are forces of attraction that hold atoms together in molecules. They are crucial for determining a molecule's structure, properties, and reactivity.
Session 2, Query 2: Covalent bonding occurs when atoms share pairs of electrons to achieve a more stable electron configuration. In this type of bond, the shared electrons orbit around both atomic nuclei.

This approach not only maintains the integrity of each tutoring session but also enhances scalability, making it ideal for applications like online tutoring platforms where multiple educational interactions occur simultaneously. By keeping tutoring sessions separate, you can provide personalized educational support to each student.

Summary and Preparation for Practice

In this lesson, you learned how to manage multiple tutoring sessions using DeepSeek's API in C#. We covered creating unique tutoring sessions, maintaining educational interaction history, and handling multiple sessions simultaneously. These skills are essential for building scalable educational applications that can support numerous students at once. As you move on to the practice exercises, I encourage you to apply what you've learned by creating and managing tutoring sessions independently. This hands-on practice will reinforce your understanding and prepare you for more advanced educational AI development. Keep up the great work, and enjoy the journey of creating your personal tutor with DeepSeek!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal