Message Types and Session History

Welcome back! In the previous lessons, you learned how to send a simple query to DeepSeek's language model and explored various model parameters to customize the AI's responses. Now, we will delve into the concept of message types and the importance of maintaining session history.

These elements are crucial for creating dynamic and context-aware interactions with the AI, allowing your personal tutor to engage in more meaningful educational conversations.

Understanding Message Types

Before we dive into building and managing session history, it's important to understand the concept of message types and how a tutoring session history is structured. In a tutor-student interaction, messages are typically categorized by roles: "system", "user", and "assistant". While we'll explore system prompts more thoroughly in a later lesson, remember that these primary roles help define the flow of dialogue and ensure the AI understands who is speaking at any given time.

DeepSeek expects the session history to be formatted as a sequence of messages, where each message has a role and content. When working with the async-openai crate, we can represent this using a vector of ChatCompletionRequestMessage structs.

Here's an example of what a simple tutoring session might look like using Rust data structures

let session: Vec<ChatCompletionRequestMessage> = vec![
    ChatCompletionRequestUserMessageArgs::default()
        .content("Can you explain the theory of relativity?")
        .build()
        .unwrap()
        .into(),
    ChatCompletionRequestAssistantMessageArgs::default()
        .content("Einstein's theory of relativity consists of two parts: special relativity and general relativity...")
        .build()
        .unwrap()
        .into(),
    ChatCompletionRequestUserMessageArgs::default()
        .content("What practical applications does it have?")
        .build()
        .unwrap()
        .into(),
    ChatCompletionRequestAssistantMessageArgs::default()
        .content("The theory of relativity has several practical applications including GPS systems, particle accelerators, and understanding astronomical phenomena.")
        .build()
        .unwrap()
        .into(),
];

In this example, the session history consists of alternating messages between the user (the student) and the assistant (the AI tutor). Each message is stored with its respective role, providing context for the AI to generate appropriate educational responses.

Understanding this structure is key to effectively managing tutoring sessions and ensuring that the AI can provide coherent and contextually relevant explanations.

Creating a Function to Handle Tutoring Sessions

To manage tutoring sessions effectively, we will create a function called send_query. This function will send queries to the AI and receive explanations, allowing us to handle multiple interactions seamlessly.

Here’s how the function can be structured:

// Function to send the full session history and get the assistant’s reply
async fn send_query(
    client: &Client<OpenAIConfig>,
    session: &[ChatCompletionRequestMessage],
) -> Result<String> {
    // Create a chat completion request with the session history
    let request = CreateChatCompletionRequestArgs::default()
        .model("deepseek-ai/DeepSeek-V3")
        .messages(session.to_vec())
        .build()?;

    // Send the request
    let response = client.chat().create(request).await?;

    // Extract the assistant’s reply from the response
    let reply = response.choices[0]
        .message
        .content
        .as_deref()
        .unwrap_or_default()
        .trim();

    Ok(reply.to_string())
}

In this function, we use the create method to send a vector of messages to the AI. The messages parameter contains the session history, which provides context for the AI's response. The function returns the AI's explanation, which is extracted from the API result and stripped of any leading or trailing whitespace.

Building and Managing Session History

Maintaining a session history is crucial for providing context to the AI tutor. This allows the AI to generate explanations that are relevant to the ongoing educational dialogue.

If we don't maintain session history, the AI will lack the context of previous interactions, leading to responses that may not be coherent or relevant to the current conversation: that is, it would treat each query as an isolated request, which could result in repetitive or disconnected explanations.

Moreover, using a vector of ChatCompletionRequestMessage structs allows us to include both the role and content in a structured way, compared to a simple vector of strings, which lacks role attribution and conversational context. This struct format ensures that each message clearly indicates who is speaking and what is being said, making it easier for the AI to interpret and maintain the flow of dialogue.

Let's see how we can build and manage session history in Rust:

let mut session = Vec::new();

// Start a session with an initial query
session.push(
    ChatCompletionRequestUserMessageArgs::default()
        .content("What was the most notable achievement of Albert Einstein?")
        .build()
        .unwrap()
        .into(),
);

// Get first response
let answer = send_query(&client, &session).await?;
println!("Answer: {}", answer);

While in the example above we manually push messages to the session vector, this process can also be handled by a dedicated function to streamline session management. We will explore how to implement such a function in the practice section.

After sending the initial query, the AI responds with information about Einstein's achievements, showcasing its ability to provide educational content:

Answer: Albert Einstein's most notable achievement is generally considered to be his theory of relativity, particularly the general theory of relativity published in 1915...

In this example, we start a tutoring session with an initial query from the user. The send_query function is used to get the AI's explanation, which is then printed.

// Add the response to session history
session.push(
    ChatCompletionRequestAssistantMessageArgs::default()
        .content(answer.as_str())
        .build()
        .unwrap()
        .into(),
);

// Add a follow-up query
session.push(
    ChatCompletionRequestUserMessageArgs::default()
        .content("Can you provide three more examples?")
        .build()
        .unwrap()
        .into(),
);

// Get explanation with tutoring context
let follow_up_reply = send_query(&client, &session).await?;
println!("Follow-up: {}", follow_up_reply);

With the session history maintained, the AI provides a contextually relevant follow-up explanation, listing additional achievements of Einstein:

Follow-up: Here are three more notable achievements of Albert Einstein:

1. Photoelectric Effect: In 1905, Einstein explained the photoelectric effect, demonstrating that light consists of particles called photons...

2. Einstein's Theory of Brownian Motion: Also in 1905, Einstein provided mathematical evidence for the existence of atoms...

3. Mass-Energy Equivalence: Einstein formulated the equation E=mc²...

By maintaining this history, we provide context for subsequent interactions, allowing the AI to generate more coherent and relevant educational explanations.

Visualizing the Session History

To better understand how the tutoring session has evolved, we can print the entire session history using a loop that formats and displays each message by its role and content.

println!("\n--- Current Session History ---");
for message in &session {
    match message {
        ChatCompletionRequestMessage::User(msg) => {
            if let ChatCompletionRequestUserMessageContent::Text(text) = &msg.content {
                println!("User: {}", text);
            }
        }
        ChatCompletionRequestMessage::Assistant(msg) => {
            if let Some(ChatCompletionRequestAssistantMessageContent::Text(text)) = &msg.content {
                println!("Assistant: {}", text);
            }
        }
        ChatCompletionRequestMessage::System(msg) => {
            if let ChatCompletionRequestSystemMessageContent::Text(text) = &msg.content {
                println!("System: {}", text);
            }
        }
        _ => {}
    }
}
println!("-------------------------------\n");

This will output the complete dialogue, showing both student queries and tutor explanations:

User: What was the most notable achievement of Albert Einstein?
Assistant: Albert Einstein's most notable achievement is generally considered to be his theory of relativity...
User: Can you provide three more examples?
Assistant: Here are three more notable achievements of Albert Einstein: 1. Photoelectric Effect... 2. Einstein's Theory of Brownian Motion... 3. Mass-Energy Equivalence...

Having access to the session history allows you to track the flow of educational dialogue and ensure that the AI tutor's explanations remain contextually relevant and build upon previous discussions.

Summary and Preparation for Practice

In this lesson, you learned about query types and the importance of maintaining session history in tutoring interactions. We explored how to set up your environment, initialize the DeepSeek client, and create a function to handle tutoring sessions. You also saw how to build and manage session history, enabling the AI to generate contextually relevant educational explanations.

As you move on to the practice exercises, I encourage you to experiment with different tutoring scenarios and observe how the AI's explanations change based on the context provided. This hands-on practice will reinforce what you've learned and prepare you for the next unit, where we'll continue to build on these concepts. 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