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 slice of message structs, where each struct represents a message with two fields: Role and Content. Here's an example of what a simple tutoring session might look like in Go:

// Define a struct for a chat message
type Message struct {
    Role    string
    Content string
}

// Example session history
session := []Message{
    {Role: "user", Content: "Can you explain the theory of relativity?"},
    {Role: "assistant", Content: "Einstein's theory of relativity consists of two parts: special relativity and general relativity..."},
    {Role: "user", Content: "What practical applications does it have?"},
    {Role: "assistant", Content: "The theory of relativity has several practical applications including GPS systems, particle accelerators, and understanding astronomical phenomena."},
}

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 SendQuery. This function will send the session history to the AI and receive explanations, allowing us to handle multiple interactions seamlessly. Here is the updated function and full example in Go:

package main

import (
    "context"
    "fmt"
    "log"
    "os"
    "strings" // Import the strings package for capitalization
    "github.com/openai/openai-go"
    "github.com/openai/openai-go/option"
)

// Message struct for session history
type Message struct {
    Role    string
    Content string
}

// SendQuery sends the session history to DeepSeek and returns the AI's reply.
func SendQuery(client *openai.Client, session []Message) (string, error) {
    var messages []openai.ChatCompletionMessageParamUnion
    for _, m := range session {
        switch m.Role {
        case "user":
            messages = append(messages, openai.UserMessage(m.Content))
        case "assistant":
            messages = append(messages, openai.AssistantMessage(m.Content))
        case "system":
            messages = append(messages, openai.SystemMessage(m.Content))
        }
    }

    chatCompletion, err := client.Chat.Completions.New(
        context.Background(),
        openai.ChatCompletionNewParams{
            Messages: messages,
            Model:    "deepseek-ai/DeepSeek-V3",
        },
    )
    if err != nil {
        return "", err
    }
    if len(chatCompletion.Choices) == 0 {
        return "", nil
    }
    return chatCompletion.Choices[0].Message.Content, nil
}

// Helper function to capitalize the role
func capitalize(s string) string {
    if len(s) == 0 {
        return s
    }
    return strings.ToUpper(string(s[0])) + s[1:]
}

func main() {
    // Read OpenAI credentials from environment variables
    apiKey := os.Getenv("OPENAI_API_KEY")
    baseURL := os.Getenv("OPENAI_BASE_URL")

    if apiKey == "" {
        log.Fatal("Please set the OPENAI_API_KEY environment variable.")
    }


    client := openai.NewClient(
        option.WithAPIKey(apiKey),
        option.WithBaseURL(baseURL),
    )

    // Start a session with an initial query
    session := []Message{
        {Role: "user", Content: "What was the most notable achievement of Albert Einstein?"},
    }

    // Get first response
    // Pass the address of client using &
    reply, err := SendQuery(&client, session)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Answer:", reply)

    // Add the response to session history
    session = append(session, Message{Role: "assistant", Content: reply})

    // Add a follow-up query
    session = append(session, Message{Role: "user", Content: "Can you provide three more examples?"})

    // Get explanation with tutoring context
    // Pass the address of client using &
    followUpReply, err := SendQuery(&client, session)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Follow-up:", followUpReply)

    // Add the follow-up response to session history
    session = append(session, Message{Role: "assistant", Content: followUpReply})

    fmt.Println("\n--- Full Session History ---")
    // Print the entire session history
    for _, message := range session {
        fmt.Printf("%s: %s\n", capitalize(message.Role), message.Content)
    }
}
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. It would treat each query as an isolated request, which could result in repetitive or disconnected explanations. Using a structured slice of message structs, as opposed to just storing responses in a list, offers several advantages. The struct format allows us to clearly define the role of each message (e.g., user or assistant) and its content, ensuring that the AI can accurately interpret who is speaking and maintain the flow of dialogue. It also makes it easier to manage and update the session history, as each message is self-contained with its role and content, providing a clear and organized way to track the conversation.

Let's look at the main function above to see how this works. We start a session with an initial query from the user:

    // Start a session with an initial query
    session := []Message{
        {Role: "user", Content: "What was the most notable achievement of Albert Einstein?"},
    }

    // Get first response
    reply, err := SendQuery(&client, session)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Answer:", reply)

After sending this initial query using SendQuery, the AI responds with information about Einstein's achievements, showcasing its ability to provide educational content. For example:

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...

Next, we add the AI's response to the session history and then add a follow-up query from the user:

    // Add the response to session history
    session = append(session, Message{Role: "assistant", Content: reply})

    // Add a follow-up query
    session = append(session, Message{Role: "user", Content: "Can you provide three more examples?"})

We then call SendQuery again with the updated session slice. With the session history maintained, the AI provides a contextually relevant follow-up explanation, listing additional achievements of Einstein. For example:

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 Go for loop:

fmt.Println("\n--- Full Session History ---")
// Print the entire session history
for _, message := range session {
    fmt.Printf("%s: %s\n", capitalize(message.Role), message.Content)
}

// Helper function to capitalize the role
func capitalize(s string) string {
    if len(s) == 0 {
        return s
    }
    return strings.ToUpper(string(s[0])) + s[1:]
}

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

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