Build an AI Study Buddy with Gemini

Build a Python CLI tool that uses Google Gemini to summarize notes and quiz you.

Introduction

30 Second Summary

Studying for exams usually means rereading the same notes until your eyes glaze over. What if you had a personal tutor that could summarize your notes, quiz you on them, and build you a study schedule?

In this project, you will build an AI-powered study assistant using Python and Google Gemini. You'll create a command-line tool with four modes that transform your notes into summaries, quizzes, explanations, and study plans.

What You'll Build

A Python CLI application that connects to the Google Gemini API and uses different system instructions to power four distinct study modes.

By the end of this project, you'll have:

  • A working Python CLI tool with four AI-powered study modes: summarize, quiz, explain, and study plan.
  • Hands-on experience writing system instructions that shape how an LLM responds to different tasks.
  • A multi-step quiz feature that generates questions, collects your answers, and grades them in a single session.
  • Secret Mission: Add a Flashcard Generator mode that creates spaced-repetition flashcards from your notes.

Are there any prerequisites?

You should be comfortable with Python basics like functions, loops, and conditionals. You also need a Google account to get your free Gemini API key.

Before We Start

Before We Start...

Set Up Your Project and API Key

Your Study Buddy will talk to Google Gemini every time it summarizes notes, generates a quiz, or builds a study plan. For that to work, you need two things: the Python SDK that knows how to talk to Gemini, and an API key that proves you have permission to use it.

In this step, you'll create a project folder, install the Gemini SDK, and grab a free API key from Google AI Studio.

In this step, get ready to:
  • Create a project folder with a Python virtual environment.
  • Install the google-genai SDK.
  • Get a free Gemini API key and verify the connection.
Create your project and install the SDK

Every Python project benefits from its own virtual environment. It keeps installed packages isolated so they don't conflict with other projects on your machine.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal (macOS) or PowerShell (Windows) and press Enter to open it.
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir study-buddy
cd study-buddy
  • Confirm the folder was created by running:
ls

The folder should be empty for now. That's expected.

  • Create a Python virtual environment by running this command:

macOS

python3 -m venv venv

Windows

python -m venv venv
  • Activate the virtual environment by running:

macOS

source venv/bin/activate

Windows

venv\Scripts\activate

You should see (venv) appear at the start of your terminal prompt. This confirms the virtual environment is active.

Don't see (venv) in your prompt?

Make sure you ran the activate command, not the create command again.

On Windows, if you see a permissions error, run Set-ExecutionPolicy -Scope CurrentUser RemoteSigned first, then try the activate command again.

Help me activate my Python virtual environment.

  • Install the Google Gemini SDK by running:
pip install google-genai

Pip downloads the package and its dependencies. This takes a few seconds.

  • Confirm the install succeeded by running:
pip show google-genai

You should see package details including the name google-genai and a version number.

Seeing 'command not found' for pip?

Make sure your virtual environment is active. You should see (venv) in your terminal prompt. If it's missing, run the activate command from the previous step again.

Help me fix pip not being found in my virtual environment.

Get your Gemini API key

Google AI Studio is the dashboard where you manage your access to Gemini models. You'll create a free API key here that lets your Python code send requests to Gemini.

  • Open your browser and go to aistudio.google.com.
  • Sign in with your Google account.
  • Click Get API Key in the left sidebar.
  • Click Create API Key.
  • Copy the API key that appears on screen.

What is an API key?

An API key is a unique string that identifies your application to Google's servers. Think of it like a password for your code.

Keep it private and never share it publicly. Gemini's free tier gives you 1,500 requests per day, which is more than enough for this project.

Save your key somewhere safe

Paste your API key into a text file or notes app for now. You'll need it in the next substep. If you lose it, you can always create a new one in Google AI Studio.

Set the API key as an environment variable

Your code needs the API key to authenticate with Gemini. Instead of pasting the key directly into your code, you'll store it as an environment variable.

  • Back in your terminal, set the environment variable by running this command. Replace your-api-key-here with the API key you just copied:

macOS

export GEMINI_API_KEY="your-api-key-here"

Windows

$env:GEMINI_API_KEY="your-api-key-here"

Why use an environment variable?

Environment variables store sensitive values outside your code files. If you ever share your code or push it to GitHub, the key stays on your machine instead of leaking into the repository.

This variable is temporary

The export command only sets the variable for your current terminal session. If you close the terminal and reopen it later, you'll need to run this command again before using the Study Buddy.

  • Verify everything works by running this test command:

macOS

python3 -c "from google import genai; import os; client = genai.Client(api_key=os.environ.get('GEMINI_API_KEY')); response = client.models.generate_content(model='gemini-2.5-flash', contents='Say hello in one sentence'); print(response.text)"

Windows

python -c "from google import genai; import os; client = genai.Client(api_key=os.environ.get('GEMINI_API_KEY')); response = client.models.generate_content(model='gemini-2.5-flash', contents='Say hello in one sentence'); print(response.text)"

What does this command do?

This one-liner runs a quick Python script without creating a file. It imports the Gemini SDK, connects using your API key, asks Gemini to say hello, and prints the response.

You should see a friendly greeting from Gemini printed in your terminal. The exact wording varies each time since the model generates fresh responses.

Seeing an error instead of a greeting?

  • If you see API key not valid, double check that you pasted the full key in the export command with no extra spaces.
  • If you see ModuleNotFoundError, make sure your virtual environment is still active. Look for (venv) in your terminal prompt.
  • If you see None printed instead of a greeting, your API key environment variable might be empty. Run the export command again and make sure the key is inside the quotes.

Help me troubleshoot my Gemini API connection.

Your environment is ready and Gemini is responding. Next up, you'll create the Study Buddy script and build its main menu.

Build the Main Menu and Note Input

Your environment is set up with the SDK installed and your API key configured.

Now it's time to build the actual application. Your Study Buddy needs two foundational pieces before you can add any AI features: a way to collect multi-line study notes from the user, and a menu system that routes users to different study modes.

In this step, get ready to:
  • Create study_buddy.py with the Gemini client connection.
  • Build a reusable function for collecting multi-line notes.
  • Build a main menu loop that routes users to different study modes.
Create study_buddy.py with imports and the Gemini client

Every study mode you build will need access to the Gemini API. The first step is creating the file and establishing that connection.

  • Create a new file called study_buddy.py in your project folder by running this command:
touch study_buddy.py
  • Confirm the file exists by running:
ls

You should see study_buddy.py in the output.

  • Open study_buddy.py in your text editor, then add the following code:
import os

from google import genai
from google.genai import types

# Connect to Gemini using your API key from the environment
client = genai.Client(api_key=os.environ.get("GEMINI_API_KEY"))

# The model all study modes will use
MODEL = "gemini-2.5-flash"

What does this code do?

from google import genai and from google.genai import types import the Gemini SDK. The types module gives you GenerateContentConfig, which you'll use later to pass system instructions to the model.

genai.Client() creates a connection to the Gemini API using the key you saved as an environment variable in Step 1. MODEL stores the model name in one place so every function can reference it without repeating the string.

  • Save study_buddy.py.

You won't see a visible change yet. The next chunk adds the function that makes this file interactive.

Build the note input function

Terminal input normally stops after one line. Your Study Buddy needs to accept entire paragraphs of notes, so you need a function that keeps reading lines until the user signals they're done.

  • In study_buddy.py, add the following function below the MODEL line:
def get_notes():
    print("\nPaste your notes below (type 'END' on a new line when done):")
    lines = []
    while True:
        # Read one line at a time until the user types END
        line = input()
        if line.strip().upper() == "END":
            break
        lines.append(line)
    # Join all collected lines back into a single string
    return "\n".join(lines)

What does this code do?

get_notes() reads lines from the terminal in a loop. Each line gets added to a lines list. When the user types END on its own line, the loop stops.

The .strip().upper() ensures the stop signal works even if the user types end, End, or adds extra spaces. Finally, "\n".join(lines) stitches all the lines back together into a single string with newlines between them.

  • Save study_buddy.py.

You won't see a change yet. You'll test this function through the menu in the next substep.

Build the main menu loop

The menu is the entry point for your Study Buddy. It displays all available modes, takes the user's choice, and calls the right function.

  • Add the following code below the get_notes() function in study_buddy.py:
def main():
    # Display the app banner
    print("=" * 50)
    print("       AI Study Buddy")
    print("=" * 50)

    # Track loaded notes across menu iterations
    notes = None

    while True:
        # Display menu options
        print("\nWhat would you like to do?")
        print("1. Summarize Notes")
        print("2. Quiz Me")
        print("3. Explain a Concept")
        print("4. Create Study Plan")
        print("5. Load New Notes")
        print("6. Quit")

        choice = input("\nEnter your choice (1-6): ").strip()

        # Route to the correct function based on user choice
        if choice == "6":
            print("\nGood luck with your studies! You've got this!")
            break
        elif choice == "4":
            study_plan()
        elif choice == "5":
            notes = get_notes()
            print("Notes loaded!")
        elif choice in ("1", "2", "3"):
            # Auto-load notes on first use
            if notes is None:
                notes = get_notes()
            if choice == "1":
                summarize(notes)
            elif choice == "2":
                quiz(notes)
            elif choice == "3":
                explain(notes)
        else:
            print("Invalid choice. Please enter 1-6.")


# Start the app when the script runs directly
if __name__ == "__main__":
    main()

What does this code do?

notes = None starts with no notes loaded. When the user picks a mode that needs notes (options 1, 2, or 3), the code checks if notes are None and automatically calls get_notes(). This means the user only has to paste their notes once per session.

Option 4 (Study Plan) calls study_plan() directly because the study planner collects its own input (subjects and deadlines) instead of using notes. Option 5 lets the user load fresh notes at any time.

The if __name__ == "__main__" block at the bottom ensures main() only runs when you execute the script directly, not when it's imported as a module.

  • Save study_buddy.py.
  • Run the Study Buddy by running this command:
python study_buddy.py

You should see the banner and the six menu options printed in your terminal.

  • Type 6 and press Enter to quit.

You should see the goodbye message: Good luck with your studies! You've got this!

What about the other menu options?

Options 1 through 4 call functions like summarize() and quiz() that don't exist yet. Choosing them now will cause a NameError. That's expected. You'll build those functions in the next steps.

Seeing an error when you run the script?

  • If you see ModuleNotFoundError: No module named 'google', your virtual environment may not be active. Check for the environment name in parentheses at the start of your terminal prompt. Activate it again if needed, then rerun the script.
  • If you see a SyntaxError, double check that your parentheses, quotes, and colons match the code above exactly. Python is strict about these.

Help me debug an error when running study_buddy.py

✔️ Awesome, I've got everything!

Great. Double check you've saved study_buddy.py before moving on.

ⓧ I'd like to double check the full code

Here's the complete study_buddy.py so far. Compare it with your file and make sure everything matches.

import os

from google import genai
from google.genai import types

client = genai.Client(api_key=os.environ.get("GEMINI_API_KEY"))

MODEL = "gemini-2.5-flash"


def get_notes():
    print("\nPaste your notes below (type 'END' on a new line when done):")
    lines = []
    while True:
        line = input()
        if line.strip().upper() == "END":
            break
        lines.append(line)
    return "\n".join(lines)


def main():
    print("=" * 50)
    print("       AI Study Buddy")
    print("=" * 50)

    notes = None

    while True:
        print("\nWhat would you like to do?")
        print("1. Summarize Notes")
        print("2. Quiz Me")
        print("3. Explain a Concept")
        print("4. Create Study Plan")
        print("5. Load New Notes")
        print("6. Quit")

        choice = input("\nEnter your choice (1-6): ").strip()

        if choice == "6":
            print("\nGood luck with your studies! You've got this!")
            break
        elif choice == "4":
            study_plan()
        elif choice == "5":
            notes = get_notes()
            print("Notes loaded!")
        elif choice in ("1", "2", "3"):
            if notes is None:
                notes = get_notes()
            if choice == "1":
                summarize(notes)
            elif choice == "2":
                quiz(notes)
            elif choice == "3":
                explain(notes)
        else:
            print("Invalid choice. Please enter 1-6.")


if __name__ == "__main__":
    main()

Your Study Buddy's skeleton is in place. It can accept notes and show a menu, but the modes are still empty. Next up, you'll bring the first two modes to life: Summarize and Explain.

Add the Summarize and Explain Modes

Your Study Buddy has a working menu and can accept notes from the terminal. Now it is time to make it actually do something useful with those notes.

In this step, you will write two functions that send the same notes to Gemini but produce completely different outputs. The difference comes entirely from the system instruction you give the model.

In this step, get ready to:
  • Build a summarize() function that condenses notes into organized bullet points.
  • Build an explain() function that breaks down concepts with analogies and examples.
  • Test both modes end-to-end with sample notes.
Build the summarize() function

A system instruction tells the model what role to play and how to format its response. For the summarize mode, you want the model to act as a study assistant that produces bullet points with key terms highlighted.

  • In study_buddy.py, find the get_notes() function.
  • Add the following summarize() function below get_notes(), above the main() function:
def summarize(notes):
    # Send notes to Gemini with a summarization system instruction
    response = client.models.generate_content(
        model=MODEL,
        contents=notes,
        config=types.GenerateContentConfig(
            system_instruction=(
                "You are a study assistant. Summarize the following notes into clear, "
                "concise bullet points. Group related ideas together. Highlight key terms "
                "in CAPS. End with a 2-3 sentence overall summary."
            )
        ),
    )
    print("\n--- Summary ---")
    print(response.text)

What does this code do?

  • client.models.generate_content() sends a request to the Gemini API. The contents parameter is the user's notes.
  • The system_instruction inside GenerateContentConfig sets the model's persona and output rules. This one asks for bullet points, grouped ideas, key terms in CAPS, and an overall summary.
  • response.text extracts the generated text from Gemini's response.
  • Save study_buddy.py.

You will not see a change yet. The next function adds the explain mode, and then you will test both together.

What is a system instruction?

A system instruction is a message that shapes how an LLM behaves for a request. It is separate from the user's input.

Think of it like briefing an actor before a scene. The system instruction says "you are a study assistant" and defines the output format. The user's notes are the actual content to work with. Different system instructions on the same notes produce completely different results.

Build the explain() function

The explain mode needs a different persona. Instead of a study assistant producing bullet points, you want a patient tutor who uses analogies and real-world examples to make a single concept click.

  • In study_buddy.py, add the following explain() function below summarize(), still above main():
def explain(notes):
    # Ask the user which concept they want explained
    topic = input("\nWhich concept from your notes do you want explained? ")
    response = client.models.generate_content(
        model=MODEL,
        # Combine the notes and the specific concept into one prompt
        contents=f"Notes:\n{notes}\n\nConcept to explain: {topic}",
        config=types.GenerateContentConfig(
            system_instruction=(
                "You are a patient tutor. Using the provided notes as context, explain "
                "the requested concept in simple terms. Use an analogy to make it "
                "relatable. Then give a concrete example. Finally, explain why this "
                "concept matters."
            )
        ),
    )
    print("\n--- Explanation ---")
    print(response.text)

What does this code do?

  • input() prompts the user to type which concept they want explained. This gets stored in topic.
  • The contents parameter combines the full notes and the specific concept into a single string using an f-string. This gives the model all the context it needs.
  • The system instruction sets a "patient tutor" persona that produces analogies, a concrete example, and a "why it matters" section. Same model, completely different output style.
  • Save study_buddy.py.

Both functions are now in place. Time to test them with some real notes.

  • Run your Study Buddy by running this command:
python study_buddy.py
  • Enter 1 to choose Summarize Notes.
  • The app will ask you to paste your notes. Paste in some study notes on any topic you like.
  • Type END on a new line and press Enter to submit your notes.

Need sample notes to test with?

Try pasting a paragraph or two from any topic you are currently studying. Even a few bullet points from a textbook work well. The more detail you give, the better the summary.

You should see a --- Summary --- header followed by organized bullet points with key terms in CAPS.

  • Back at the menu, enter 3 to choose Explain a Concept.
  • Type a concept from your notes when prompted.

You should see a --- Explanation --- header followed by an analogy, a concrete example, and a "why it matters" section.

Seeing an error when you pick a mode?

  • If you see NameError: name 'summarize' is not defined, make sure your summarize() and explain() functions are placed above the main() function. Python reads top to bottom, so functions must be defined before they are called.
  • If you see an API error, confirm your GEMINI_API_KEY environment variable is still set in your terminal. Closing and reopening the terminal clears it.
  • Options 2 and 4 will not work yet. Those functions are built in the next steps.

Still stuck? Help me debug an error in my Study Buddy.

Why do both functions use the same API call but produce different outputs?

Both summarize() and explain() call client.models.generate_content() with the same model. The only difference is the system instruction.

"Summarize" asks for bullet points, grouped ideas, and CAPS on key terms. "Explain" asks for analogies, examples, and a "why it matters" section. This is the core pattern behind every AI-powered product: same model, different instructions, different results.

✔️ Awesome, I've got everything!

Make sure you have saved study_buddy.py before moving on.

ⓧ I'd like to double check the full code

import os

from google import genai
from google.genai import types

client = genai.Client(api_key=os.environ.get("GEMINI_API_KEY"))

MODEL = "gemini-2.5-flash"


def get_notes():
    print("\nPaste your notes below (type 'END' on a new line when done):")
    lines = []
    while True:
        line = input()
        if line.strip().upper() == "END":
            break
        lines.append(line)
    return "\n".join(lines)


def summarize(notes):
    response = client.models.generate_content(
        model=MODEL,
        contents=notes,
        config=types.GenerateContentConfig(
            system_instruction=(
                "You are a study assistant. Summarize the following notes into clear, "
                "concise bullet points. Group related ideas together. Highlight key terms "
                "in CAPS. End with a 2-3 sentence overall summary."
            )
        ),
    )
    print("\n--- Summary ---")
    print(response.text)


def explain(notes):
    topic = input("\nWhich concept from your notes do you want explained? ")
    response = client.models.generate_content(
        model=MODEL,
        contents=f"Notes:\n{notes}\n\nConcept to explain: {topic}",
        config=types.GenerateContentConfig(
            system_instruction=(
                "You are a patient tutor. Using the provided notes as context, explain "
                "the requested concept in simple terms. Use an analogy to make it "
                "relatable. Then give a concrete example. Finally, explain why this "
                "concept matters."
            )
        ),
    )
    print("\n--- Explanation ---")
    print(response.text)


def main():
    print("=" * 50)
    print("       AI Study Buddy")
    print("=" * 50)

    notes = None

    while True:
        print("\nWhat would you like to do?")
        print("1. Summarize Notes")
        print("2. Quiz Me")
        print("3. Explain a Concept")
        print("4. Create Study Plan")
        print("5. Load New Notes")
        print("6. Quit")

        choice = input("\nEnter your choice (1-6): ").strip()

        if choice == "6":
            print("\nGood luck with your studies! You've got this!")
            break
        elif choice == "4":
            study_plan()
        elif choice == "5":
            notes = get_notes()
            print("Notes loaded!")
        elif choice in ("1", "2", "3"):
            if notes is None:
                notes = get_notes()
            if choice == "1":
                summarize(notes)
            elif choice == "2":
                quiz(notes)
            elif choice == "3":
                explain(notes)
        else:
            print("Invalid choice. Please enter 1-6.")


if __name__ == "__main__":
    main()

Your Study Buddy can now summarize notes and explain tricky concepts. Next up, you will build the quiz mode, a multi-step AI interaction that generates questions, collects your answers, and grades them all in one session.

Build the Quiz Mode with Grading

The Summarize and Explain modes each make a single API call to Gemini. The Quiz mode is more complex because it needs two separate interactions with the model in sequence.

First, Gemini generates questions from your notes. Then, after you answer them, it grades your responses by comparing them against the original material. This is your first multi-step AI interaction in the project.

In this step, get ready to:
  • Build the first half of the quiz() function to generate questions from your notes.
  • Add answer collection and AI-powered grading to complete the quiz experience.
Generate the quiz questions

The quiz function follows the same generate_content pattern as summarize() and explain(). The key difference is the system instruction, which tells Gemini to produce exactly 5 numbered questions with no answers included.

  • In study_buddy.py, find the explain() function. Add the following quiz() function below it, above def main()::
def quiz(notes):
    # Send notes to Gemini to generate quiz questions
    response = client.models.generate_content(
        model=MODEL,
        contents=notes,
        config=types.GenerateContentConfig(
            system_instruction=(
                "You are a quiz master. Based on the provided notes, generate exactly "
                "5 short-answer questions that test understanding of the key concepts. "
                "Number them 1-5. Only output the questions, not the answers."
            )
        ),
    )
    print("\n--- Quiz Time! ---")
    print(response.text)

What does this code do?

This is the first half of a two-step interaction. The system instruction tells Gemini to act as a "quiz master" and produce exactly 5 numbered questions.

The instruction also says "Only output the questions, not the answers." This is important because the model's response (response.text) gets reused in the grading step. If answers were included, the student could see them before answering.

You won't test this function yet. The next chunk adds answer collection and grading to complete the flow.

Collect answers and grade them

After the student reads the questions and types their answers, Gemini receives everything together: the original notes, the questions it generated, and the student's answers. This gives the model full context to grade fairly.

  • In study_buddy.py, find the quiz() function you just created. Add the following code directly below the print(response.text) line, still inside the quiz() function:
    # Collect the student's answers using the same multi-line input pattern
    print("\nType your answers below (type 'END' on a new line when done):")
    answers = []
    while True:
        line = input()
        if line.strip().upper() == "END":
            break
        answers.append(line)
    student_answers = "\n".join(answers)

    # Send notes, questions, and answers to Gemini for grading
    grade_response = client.models.generate_content(
        model=MODEL,
        contents=(
            f"Notes:\n{notes}\n\n"
            f"Questions:\n{response.text}\n\n"
            f"Student answers:\n{student_answers}"
        ),
        config=types.GenerateContentConfig(
            system_instruction=(
                "You are a supportive grader. Compare the student's answers against "
                "the notes and questions. For each question: state whether the answer "
                "is correct, partially correct, or incorrect. Provide a brief "
                "explanation of the correct answer. End with an overall score out of 5 "
                "and one encouraging tip for improvement."
            )
        ),
    )
    print("\n--- Results ---")
    print(grade_response.text)

What does this code do?

  • The answer collection loop reuses the same "type END to finish" pattern from get_notes(). All the lines get joined into one student_answers string.
  • The grading call bundles three pieces of context into contents: the original notes, the questions from response.text, and the student's answers. This gives Gemini everything it needs to grade accurately.
  • The grading system instruction asks for three things per question: a verdict (correct, partially correct, or incorrect), an explanation, and an overall score out of 5 with encouragement.

Why two API calls instead of one?

You could ask Gemini to generate questions and grade answers in a single prompt. The problem is the student needs to see the questions before they can answer them.

Splitting it into two calls creates a natural pause. The first call generates and displays questions. The second call sends the answers for grading. Each call has its own system instruction tuned for its specific task.

  • Save study_buddy.py.
  • Run the app by running this command:
python study_buddy.py
  • Paste some study notes when prompted, then type END.
  • Enter 2 to choose **Quiz Me**.
  • You should see --- Quiz Time! --- followed by 5 numbered questions based on your notes.
  • Type your answers (one per line or in a block), then type END.
  • You should see --- Results --- followed by a grade for each question and an overall score out of 5.

Quiz not generating questions?

  • Make sure the quiz() function is defined above def main(): in your file. Python needs to see the function definition before main() calls it.
  • Check that the grading code is indented inside the quiz() function. Every line from the answer collection through print(grade_response.text) should be indented one level under def quiz(notes):.
  • If you see a NameError: name 'quiz' is not defined, the function might be missing or misspelled. Double check the function name matches exactly.

Still stuck? Ask for help.

✔️ Awesome, I've got everything!

Great. Double check you have saved study_buddy.py.

ⓧ I'd like to double check the full code

Here is the complete study_buddy.py file as it should look at the end of this step.

import os

from google import genai
from google.genai import types

client = genai.Client(api_key=os.environ.get("GEMINI_API_KEY"))

MODEL = "gemini-2.5-flash"


def get_notes():
    print("\nPaste your notes below (type 'END' on a new line when done):")
    lines = []
    while True:
        line = input()
        if line.strip().upper() == "END":
            break
        lines.append(line)
    return "\n".join(lines)


def summarize(notes):
    response = client.models.generate_content(
        model=MODEL,
        contents=notes,
        config=types.GenerateContentConfig(
            system_instruction=(
                "You are a study assistant. Summarize the following notes into clear, "
                "concise bullet points. Group related ideas together. Highlight key terms "
                "in CAPS. End with a 2-3 sentence overall summary."
            )
        ),
    )
    print("\n--- Summary ---")
    print(response.text)


def explain(notes):
    topic = input("\nWhich concept from your notes do you want explained? ")
    response = client.models.generate_content(
        model=MODEL,
        contents=f"Notes:\n{notes}\n\nConcept to explain: {topic}",
        config=types.GenerateContentConfig(
            system_instruction=(
                "You are a patient tutor. Using the provided notes as context, explain "
                "the requested concept in simple terms. Use an analogy to make it "
                "relatable. Then give a concrete example. Finally, explain why this "
                "concept matters."
            )
        ),
    )
    print("\n--- Explanation ---")
    print(response.text)


def quiz(notes):
    response = client.models.generate_content(
        model=MODEL,
        contents=notes,
        config=types.GenerateContentConfig(
            system_instruction=(
                "You are a quiz master. Based on the provided notes, generate exactly "
                "5 short-answer questions that test understanding of the key concepts. "
                "Number them 1-5. Only output the questions, not the answers."
            )
        ),
    )
    print("\n--- Quiz Time! ---")
    print(response.text)

    print("\nType your answers below (type 'END' on a new line when done):")
    answers = []
    while True:
        line = input()
        if line.strip().upper() == "END":
            break
        answers.append(line)
    student_answers = "\n".join(answers)

    grade_response = client.models.generate_content(
        model=MODEL,
        contents=(
            f"Notes:\n{notes}\n\n"
            f"Questions:\n{response.text}\n\n"
            f"Student answers:\n{student_answers}"
        ),
        config=types.GenerateContentConfig(
            system_instruction=(
                "You are a supportive grader. Compare the student's answers against "
                "the notes and questions. For each question: state whether the answer "
                "is correct, partially correct, or incorrect. Provide a brief "
                "explanation of the correct answer. End with an overall score out of 5 "
                "and one encouraging tip for improvement."
            )
        ),
    )
    print("\n--- Results ---")
    print(grade_response.text)


def main():
    print("=" * 50)
    print("       AI Study Buddy")
    print("=" * 50)

    notes = None

    while True:
        print("\nWhat would you like to do?")
        print("1. Summarize Notes")
        print("2. Quiz Me")
        print("3. Explain a Concept")
        print("4. Create Study Plan")
        print("5. Load New Notes")
        print("6. Quit")

        choice = input("\nEnter your choice (1-6): ").strip()

        if choice == "6":
            print("\nGood luck with your studies! You've got this!")
            break
        elif choice == "4":
            study_plan()
        elif choice == "5":
            notes = get_notes()
            print("Notes loaded!")
        elif choice in ("1", "2", "3"):
            if notes is None:
                notes = get_notes()
            if choice == "1":
                summarize(notes)
            elif choice == "2":
                quiz(notes)
            elif choice == "3":
                explain(notes)
        else:
            print("Invalid choice. Please enter 1-6.")


if __name__ == "__main__":
    main()

Your Study Buddy can now quiz you and grade your answers. Next up, you will build the Study Planner mode that creates a personalized day-by-day schedule from your subjects and deadlines.

Add the Study Planner

Your Study Buddy can already summarize notes, quiz you, and explain concepts. These three modes all work from the same input: your pasted notes.

The Study Planner mode is different. Instead of analyzing notes, it collects structured information about your subjects, deadlines, and available time. Then it uses Gemini to generate a personalized day-by-day study schedule.

In this step, get ready to:
  • Build the study_plan() function with deadline-based input collection.
  • Test the complete app end-to-end across all four modes.
Build the study_plan() function

The other three modes all start with your pasted notes. The Study Planner takes a completely different kind of input: a list of subjects with their exam dates and the number of hours you can study each day.

  • In study_buddy.py, find the line that says def main():.
  • Add the following function above def main():, leaving a blank line between the new function and main():
def study_plan():
    # Prompt the user for subjects and exam deadlines
    print("\nEnter your subjects and deadlines (one per line).")
    print("Format: Subject - Date")
    print("Example: Biology - May 15")
    print("Type 'END' on a new line when done:")
    entries = []
    while True:
        line = input()
        if line.strip().upper() == "END":
            break
        entries.append(line)
    deadlines = "\n".join(entries)

    # Ask how many hours per day the student can study
    hours = input("\nHow many hours per day can you study? ")

    # Send deadlines and hours to Gemini with a planning system instruction
    response = client.models.generate_content(
        model=MODEL,
        contents=f"Subjects and deadlines:\n{deadlines}\n\nAvailable hours per day: {hours}",
        config=types.GenerateContentConfig(
            system_instruction=(
                "You are a study planner. Create a detailed day-by-day study plan "
                "based on the subjects, deadlines, and available hours. Prioritize "
                "subjects with the nearest deadlines. Include specific time blocks. "
                "Add short breaks between sessions. Include review sessions for "
                "previously studied material. Start from today."
            )
        ),
    )
    print("\n--- Your Study Plan ---")
    print(response.text)

What does this code do?

This function collects input differently from the other modes. Instead of receiving notes, it builds a list of subject-deadline pairs using the same END stop signal pattern you used in get_notes().

The system instruction tells Gemini to act as a study planner. It prioritizes subjects with the nearest deadlines, adds time blocks and breaks, and includes review sessions for previously studied material.

Why does this mode skip the notes check?

Look at your main() function. Choices 1, 2, and 3 check whether notes are loaded before calling their function. Choice 4 calls study_plan() directly because it collects its own input. It does not need your notes at all.

  • Save study_buddy.py.

Seeing a syntax error?

  • Make sure your study_plan() function is above def main(): and below the quiz() function. Python reads top to bottom, so the function must be defined before main() tries to call it.
  • Check that the parentheses and quotes in the system_instruction string are balanced. Each opening parenthesis needs a matching close.
  • Still stuck? Help me fix a syntax error in my study_plan() function in study_buddy.py
Test the complete app end-to-end

All four modes are now in place. Time to run the full Study Buddy and verify that every mode produces useful, well-formatted output.

  • Run your Study Buddy by entering this command in your terminal:
python study_buddy.py

You should see the main menu with all six options printed in your terminal.

  • Enter 4 to test the Study Planner first, since it does not require notes.
  • Enter a few subjects with deadlines, one per line. For example:
Biology - May 15
History - May 20
Math - May 12
END
  • When prompted for hours, enter 3.

Gemini should return a day-by-day plan that prioritizes Math first (nearest deadline), then Biology, then History. You should see specific time blocks and breaks in the output.

  • Now test the other three modes. Enter 1 to try Summarize. The app will prompt you to paste notes since none are loaded yet.
  • Paste some study notes on any topic you like. Here is a sample you can use:
Photosynthesis is the process by which plants convert light energy into chemical
energy. It takes place in the chloroplasts, specifically using chlorophyll.
The process has two stages: light-dependent reactions and the Calvin cycle.
Light-dependent reactions happen in the thylakoid membranes and produce ATP and
NADPH. The Calvin cycle happens in the stroma and uses CO2 to produce glucose.
Factors affecting photosynthesis include light intensity, CO2 concentration,
and temperature.
END
  • After seeing the summary, try option 3 (Explain a Concept). When asked which concept, type Calvin cycle.
  • Try option 2 (Quiz Me). Answer the five questions, then type END to submit your answers for grading.
  • When you are finished testing, enter 6 to quit.

What should I see?

Each mode should produce well-structured output. Summarize should give you bullet points with key terms in CAPS. Explain should include an analogy and a concrete example. Quiz should generate five numbered questions, collect your answers, then grade them with explanations.

If any mode produces messy or unhelpful output, try adjusting the wording in that mode's system_instruction string. Small phrasing changes can significantly improve results.

Getting a NameError for study_plan?

  • This means Python cannot find the study_plan function. Check that you defined it above def main(): and that the function name is spelled exactly study_plan with an underscore.
  • If one of the other modes fails, make sure your GEMINI_API_KEY environment variable is still set in your terminal. If you opened a new terminal window, you need to export it again.
  • Need help? Help me debug my Study Buddy app

✔️ Awesome, I've got everything!

Double check you have saved study_buddy.py.

ⓧ I'd like to double check the full code

Here is the complete study_buddy.py file. Compare it against yours to spot any differences.

import os

from google import genai
from google.genai import types

client = genai.Client(api_key=os.environ.get("GEMINI_API_KEY"))

MODEL = "gemini-2.5-flash"


def get_notes():
    print("\nPaste your notes below (type 'END' on a new line when done):")
    lines = []
    while True:
        line = input()
        if line.strip().upper() == "END":
            break
        lines.append(line)
    return "\n".join(lines)


def summarize(notes):
    response = client.models.generate_content(
        model=MODEL,
        contents=notes,
        config=types.GenerateContentConfig(
            system_instruction=(
                "You are a study assistant. Summarize the following notes into clear, "
                "concise bullet points. Group related ideas together. Highlight key terms "
                "in CAPS. End with a 2-3 sentence overall summary."
            )
        ),
    )
    print("\n--- Summary ---")
    print(response.text)


def explain(notes):
    topic = input("\nWhich concept from your notes do you want explained? ")
    response = client.models.generate_content(
        model=MODEL,
        contents=f"Notes:\n{notes}\n\nConcept to explain: {topic}",
        config=types.GenerateContentConfig(
            system_instruction=(
                "You are a patient tutor. Using the provided notes as context, explain "
                "the requested concept in simple terms. Use an analogy to make it "
                "relatable. Then give a concrete example. Finally, explain why this "
                "concept matters."
            )
        ),
    )
    print("\n--- Explanation ---")
    print(response.text)


def quiz(notes):
    response = client.models.generate_content(
        model=MODEL,
        contents=notes,
        config=types.GenerateContentConfig(
            system_instruction=(
                "You are a quiz master. Based on the provided notes, generate exactly "
                "5 short-answer questions that test understanding of the key concepts. "
                "Number them 1-5. Only output the questions, not the answers."
            )
        ),
    )
    print("\n--- Quiz Time! ---")
    print(response.text)

    print("\nType your answers below (type 'END' on a new line when done):")
    answers = []
    while True:
        line = input()
        if line.strip().upper() == "END":
            break
        answers.append(line)
    student_answers = "\n".join(answers)

    grade_response = client.models.generate_content(
        model=MODEL,
        contents=(
            f"Notes:\n{notes}\n\n"
            f"Questions:\n{response.text}\n\n"
            f"Student answers:\n{student_answers}"
        ),
        config=types.GenerateContentConfig(
            system_instruction=(
                "You are a supportive grader. Compare the student's answers against "
                "the notes and questions. For each question: state whether the answer "
                "is correct, partially correct, or incorrect. Provide a brief "
                "explanation of the correct answer. End with an overall score out of 5 "
                "and one encouraging tip for improvement."
            )
        ),
    )
    print("\n--- Results ---")
    print(grade_response.text)


def study_plan():
    print("\nEnter your subjects and deadlines (one per line).")
    print("Format: Subject - Date")
    print("Example: Biology - May 15")
    print("Type 'END' on a new line when done:")
    entries = []
    while True:
        line = input()
        if line.strip().upper() == "END":
            break
        entries.append(line)
    deadlines = "\n".join(entries)

    hours = input("\nHow many hours per day can you study? ")

    response = client.models.generate_content(
        model=MODEL,
        contents=f"Subjects and deadlines:\n{deadlines}\n\nAvailable hours per day: {hours}",
        config=types.GenerateContentConfig(
            system_instruction=(
                "You are a study planner. Create a detailed day-by-day study plan "
                "based on the subjects, deadlines, and available hours. Prioritize "
                "subjects with the nearest deadlines. Include specific time blocks. "
                "Add short breaks between sessions. Include review sessions for "
                "previously studied material. Start from today."
            )
        ),
    )
    print("\n--- Your Study Plan ---")
    print(response.text)


def main():
    print("=" * 50)
    print("       AI Study Buddy")
    print("=" * 50)

    notes = None

    while True:
        print("\nWhat would you like to do?")
        print("1. Summarize Notes")
        print("2. Quiz Me")
        print("3. Explain a Concept")
        print("4. Create Study Plan")
        print("5. Load New Notes")
        print("6. Quit")

        choice = input("\nEnter your choice (1-6): ").strip()

        if choice == "6":
            print("\nGood luck with your studies! You've got this!")
            break
        elif choice == "4":
            study_plan()
        elif choice == "5":
            notes = get_notes()
            print("Notes loaded!")
        elif choice in ("1", "2", "3"):
            if notes is None:
                notes = get_notes()
            if choice == "1":
                summarize(notes)
            elif choice == "2":
                quiz(notes)
            elif choice == "3":
                explain(notes)
        else:
            print("Invalid choice. Please enter 1-6.")


if __name__ == "__main__":
    main()

Four modes, four system instructions

Every mode in your Study Buddy uses the exact same model (gemini-2.5-flash) and the exact same API call (generate_content). The only thing that changes between modes is the system instruction.

This is the core idea behind prompt engineering. You are not changing the model. You are changing the instructions that shape how the model responds. One model, four very different behaviors.

Secret mission

Add a Flashcard Generator

Your Study Buddy can summarize, quiz, explain, and plan. But what about flashcards? Add a fifth mode that generates spaced-repetition flashcards from your notes, complete with a review session where you rate yourself on each card.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally, so there are no ongoing costs. Your Gemini API usage fits well within the free tier. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • study_buddy.py file with all four study modes.
  • Python virtual environment with the google-genai SDK installed.
  • Gemini API key from Google AI Studio.
  • GEMINI_API_KEY environment variable set in your terminal session.

Keep everything running

No action needed. Choose this if you are still actively building or plan to extend the Study Buddy with more features.

  • Your project folder, virtual environment, and API key all stay as they are.
  • The Gemini free tier gives you 1,500 requests per day, so feel free to keep experimenting.

Pause - I'll come back to this later

Deactivate your virtual environment to free up your terminal. Your files and API key stay safe for when you return.

  • Deactivate the virtual environment by running:
deactivate
  • Your project folder, study_buddy.py, and installed packages stay on disk.
  • When you come back, reactivate the virtual environment and re-export your GEMINI_API_KEY environment variable.

Delete - I don't want to use this again

Remove all project resources and start fresh. This deletes your code and virtual environment permanently.

  • Delete your entire project folder by running:
cd ~/Desktop
rm -rf study-buddy
  • Optionally, revoke your Gemini API key in Google AI Studio if you no longer need it.
  • The GEMINI_API_KEY environment variable disappears automatically when you close the terminal session.

Nice Work!

Nice Work!

Nice work! You just built an AI-powered study assistant from scratch using Python and the Google Gemini API.

You've learned how to:

  • Built a Python CLI tool with four AI-powered study modes using the Gemini API.
  • Written distinct system instructions that shape the same model into a summarizer, tutor, quiz master, and planner.
  • Implemented a multi-step AI interaction in the Quiz mode that generates questions, collects answers, and grades them in a single session.
  • Added a Flashcard Generator with structured output parsing and self-assessment in the Secret Mission.

Ready to quiz yourself?