Building the Tutor Service Layer

In the previous lesson, we explored the SessionManager class, which plays a crucial role in managing tutoring session data within our application. Now, we will take the next step in our journey by building the Tutor Service Layer. This layer is essential for integrating the DeepSeek language model with tutoring sessions, allowing us to process student queries and generate tailored explanations. By the end of this lesson, you will understand how to set up the TutorService class, create tutoring sessions, and process academic questions using DeepSeek models via HTTP requests.

The service layer acts as a bridge between the model layer, where data is managed, and the AI model, which generates educational responses. It is responsible for orchestrating the flow of data and ensuring that student interactions are handled effectively. Let's dive into the details of setting up this important component.

Setting Up the TutorService Class

The TutorService class is the heart of our service layer. It is responsible for managing tutoring sessions and interacting with the DeepSeek model to generate educational responses. To begin, we need to set up the class and its components.

First, we include the necessary namespaces, such as System, System.IO, System.Net.Http, and any project-specific namespaces for session management. We also use Guid.NewGuid() to generate unique session IDs. Here’s how the class is initialized in C#:

using System;
using System.IO;
using System.Net.Http;
using PersonalTutor.Models;

namespace PersonalTutor.Services
{
    public class TutorService
    {
        private SessionManager sessionManager;
        private HttpClient httpClient;
        private string systemPrompt;

        public TutorService()
        {
            sessionManager = new SessionManager();
            httpClient = new HttpClient();
            systemPrompt = LoadSystemPrompt("data/system_prompt.txt");
        }

        // ... other methods will be added here
    }
}

In this setup, we instantiate SessionManager to manage tutoring data, initialize an HttpClient for making HTTP requests to the DeepSeek API, and load the systemPrompt using the LoadSystemPrompt method, which we'll discuss next.

Processing Student Queries

The ProcessQuery method is where the educational magic happens. It processes student questions, interacts with the DeepSeek model to generate tutoring explanations, and updates the session history. Below, we outline the steps involved in this process, followed by the corresponding code implementation:

  1. Retrieve the session using GetSession, and throw an exception if the session is not found.
  2. Add the student's query to the session history.
  3. Send the conversation, including the system prompt and all previous exchanges, to the DeepSeek model via an HTTP request to generate a response.
  4. Add the tutor's explanation to the session history and return it to the student.
  5. Handle any errors with the HTTP client gracefully.
public async Task<string> ProcessQuery(string studentId, string sessionId, string query)
{
    var session = sessionManager.GetSession(studentId, sessionId);
    if (session == null)
    {
        throw new ArgumentException("Session not found");
    }

    // Add student query
    sessionManager.AddMessage(studentId, sessionId, "user", query);

    try
    {
        // Retrieve the conversation using the SessionManager
        var conversation = sessionManager.GetConversation(studentId, sessionId)
            .Select(msg => new { role = msg.Role, content = msg.Content })
            .ToArray();

        // Prepare API key and endpoint
        var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");
        var baseUri = Environment.GetEnvironmentVariable("OPENAI_BASE_URL");
        if (string.IsNullOrEmpty(apiKey))
        {
            throw new InvalidOperationException("OPENAI_API_KEY environment variable is not set.");
        }
        if (string.IsNullOrEmpty(baseUri))
        {
            throw new InvalidOperationException("OPENAI_BASE_URL environment variable is not set.");
        }
        var endpoint = baseUri.TrimEnd('/') + "/v1/chat/completions";

        // Prepare payload
        var payload = new
        {
            model = "deepseek-ai/DeepSeek-V3",
            messages = conversation,
            temperature = 0.7,
            max_tokens = 500
        };
        var jsonPayload = JsonSerializer.Serialize(payload);

        // Prepare HTTP request
        using var request = new HttpRequestMessage(HttpMethod.Post, endpoint);
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
        request.Content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");

        // Send request
        var response = await httpClient.SendAsync(request);
        var body = await response.Content.ReadAsStringAsync();

        if (!response.IsSuccessStatusCode)
        {
            throw new InvalidOperationException($"Error from DeepSeek API ({response.StatusCode}): {body}");
        }

        using var doc = JsonDocument.Parse(body);
        var aiMessage = doc.RootElement
                           .GetProperty("choices")[0]
                           .GetProperty("message")
                           .GetProperty("content")
                           .GetString()
                           ?.Trim() ?? "";

        // Add AI response to session history
        sessionManager.AddMessage(studentId, sessionId, "assistant", aiMessage);

        return aiMessage;
    }
    catch (Exception e)
    {
        throw new InvalidOperationException($"Error getting AI response: {e.Message}");
    }
}

In the context of a personal tutor, we configure our DeepSeek model with specific parameters to optimize its educational performance. The temperature is set to 0.7, which balances accuracy and creativity in the tutor's explanations, ensuring they are both informative and engaging. The max_tokens is set to 500, allowing the model to provide detailed educational content without overwhelming the student, thus maintaining an effective learning experience.

Example: Simulating Multiple Tutoring Sessions

Let's see the TutorService in action by simulating multiple tutoring sessions for the same student. We'll create a simple program to initialize two tutoring sessions and process a student's academic query in each session.

using System;
using System.Threading.Tasks;
using PersonalTutor.Services;

namespace PersonalTutor
{
    class Program
    {
        static async Task Main(string[] args)
        {
            // Initialize the tutor service
            var tutorService = new TutorService();

            // Simulate a student ID
            string studentId = "student123";

            // Create a new tutoring session
            string sessionId = tutorService.CreateSession(studentId);
            Console.WriteLine($"Tutoring session created with ID: {sessionId}");

            // Simulate sending a query
            string studentQuery = "In what year Rome burned down?";
            Console.WriteLine($"Student Query: {studentQuery}");

            try
            {
                string aiResponse = await tutorService.ProcessQuery(studentId, sessionId, studentQuery);
                Console.WriteLine($"AI Response: {aiResponse}");
            }
            catch (Exception e)
            {
                Console.WriteLine($"Error: {e.Message}");
            }

            // Create a second tutoring session for the same student
            string secondSessionId = tutorService.CreateSession(studentId);
            Console.WriteLine($"Second tutoring session created with ID: {secondSessionId}");

            // Simulate sending a query in the second session
            string secondStudentQuery = "What's the capital of Burkina Faso?";
            Console.WriteLine($"Student Query in second session: {secondStudentQuery}");

            try
            {
                string secondAiResponse = await tutorService.ProcessQuery(studentId, secondSessionId, secondStudentQuery);
                Console.WriteLine($"AI Response for second session: {secondAiResponse}");
            }
            catch (Exception e)
            {
                Console.WriteLine($"Error: {e.Message}");
            }
        }
    }
}

In this example, we initialize the TutorService, simulate a student ID, and create two separate tutoring sessions for the same student, printing the session IDs. We then simulate sending a question in each session and print the AI's response, demonstrating the flow from student query to tutoring explanation and showcasing the functionality of the TutorService. Each session operates independently, ensuring that the context of one session does not affect the other.

Tutoring session created with ID: 01a17870-8a4f-4b6f-a3ce-f04e1136d597
Student Query: In what year Rome burned down?
AI Response: The city of Rome experienced a major fire in 64 AD, commonly known as the Great Fire of Rome. The fire began on July 18 and lasted for about six days, destroying much of the city. The event is historically significant and is often associated with the reign of Emperor Nero.

Second tutoring session created with ID: 7b2e1c2d-2e4a-4b8e-9c2a-1e2f3d4c5b6a
Student Query in second session: What's the capital of Burkina Faso?
AI Response for second session: The capital of Burkina Faso is Ouagadougou.

This output illustrates a successful tutoring interaction where two new sessions are created for the same student, and the AI responds to each question with a relevant answer. The tutor's responses demonstrate the system's ability to provide independent, structured, and educational content for each session, showcasing how the DeepSeek model can be effectively used for personalized academic support.

Summary and Next Steps

In this lesson, we explored the TutorService class and its role in integrating the DeepSeek language model with tutoring sessions. We learned how to set up the class, load the system prompt, create sessions, and process student queries. The service layer is a vital component of our personal tutor application, ensuring that student interactions are handled effectively and that educational content is delivered in a clear and engaging manner.

As you move on to the practice exercises, take the opportunity to experiment with the TutorService functionality. This hands-on practice will reinforce the concepts covered in this lesson and prepare you for the next steps in our course. Keep up the great work, and I look forward to seeing your progress!

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