Build a Socratic AI Tutor with Gemini
Build a Python AI tutor that teaches by asking questions, not giving answers.
Introduction
30 Second Summary
The best teachers don't give you the answer. They ask the right questions until you figure it out yourself.
In this project, you will build an AI tutor powered by Google's Gemini model that never gives direct answers. Instead, it asks targeted questions, guiding students to their own "aha moment."
What You'll Build
A Python command-line Socratic tutor that uses the Gemini API to hold multi-turn conversations, detects when a student reaches understanding using structured JSON output, and prints a Thinking Map that visualizes the learning journey.
By the end of this project, you'll have:
- A working Socratic AI tutor that runs in your terminal and teaches by asking questions instead of giving answers.
- Hands-on experience calling the Gemini API with system instructions, structured JSON outputs, and multi-turn chat.
- A Thinking Map generator that visualizes the chain of questions that guided a student to understanding.
- Secret Mission: Add a confidence tracker that detects when the student is struggling and automatically scaffolds down to simpler questions.
Are there any prerequisites?
This guide includes full setup for every tool you need, including Python, VS Code, and the Google GenAI SDK. All you need is a Google account for your free Gemini API key.
Set Up Your Development Environment
Your Socratic tutor is a Python application that calls Google's Gemini API.
Before you can start building it, you need Python installed, a code editor to write in, and access to the Gemini API. In this step, you'll set up all three and create your project folder.
In this step, get ready to:
- Install Python and VS Code with the Python extension.
- Create your project folder and install the Google GenAI SDK.
- Get a free Gemini API key from Google AI Studio.
Install Python and VS Code
Python is the programming language your tutor runs on. VS Code is the code editor you'll write it in.
- 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.
- Check if Python is already installed by running this command:
macOS
python3 --version
Windows
python --version
✔️ I see a version number
Make sure the version is 3.10 or higher. If it is, you're good to go.
ⓧ Command not found
- Download Python from the official Python website.
- Run the installer.
Windows users: check the PATH box
During installation, check the box that says Add Python to PATH before clicking Install Now. Without this, your terminal won't be able to find Python.
- Close and reopen your terminal after installing.
- Run the version check command again to confirm it works.
With Python verified, your next tool is VS Code.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Visual Studio Code and press Enter.
✔️ VS Code opened
VS Code is ready. Move on to installing the Python extension below.
ⓧ I don't have VS Code
- Download VS Code from the official VS Code website.
- Run the installer and follow the prompts.
- Open VS Code after installation completes.
The Python extension adds language support so VS Code can highlight your code and catch errors.
- Click the Extensions icon in the left sidebar, or press Cmd+Shift+X (macOS) / Ctrl+Shift+X (Windows).
- Type Python in the search bar.
- Find the extension by Microsoft and click Install.
Create your project and install the SDK
Every project needs its own folder. You'll also create a virtual environment to keep your project's packages separate from everything else on your computer.
- Switch back to your terminal window (the Terminal or PowerShell you opened earlier).
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder by running this command:
mkdir socratic-tutor
Now tell VS Code to open this folder so the editor and terminal both point to the right place.
- Switch back to VS Code.
- Click File in the top menu bar.
- Click Open Folder.
- Navigate to your Desktop.
- Select the socratic-tutor folder and click Open.
Seeing a trust prompt?
VS Code may ask "Do you trust the authors of the files in this folder?" Click Yes, I trust the authors to continue.
- Click Terminal in the top menu bar.
- Click New Terminal.
This terminal opens inside the socratic-tutor folder automatically. You'll use it for the rest of the project.
- Create and activate a virtual environment by running these commands:
macOS
python3 -m venv venv
source venv/bin/activate
Windows
python -m venv venv
.\venv\Scripts\activate
You should see (venv) appear at the start of your terminal line. This confirms the virtual environment is active.
What is a virtual environment?
A virtual environment is an isolated space for your project's packages. Any package you install while the environment is active only exists inside this project.
This prevents conflicts between projects that use different versions of the same package.
Don't see (venv)?
- Make sure you ran both commands (create and activate), not just the first one.
- On Windows, if you see an execution policy error, run Set-ExecutionPolicy -Scope CurrentUser RemoteSigned and then try the activate command again.
- Check that your terminal is open inside the socratic-tutor folder, not your home directory.
Help me activate my virtual environment
- Install the Google GenAI SDK by running this command:
pip install google-genai
What does this install?
The google-genai package is the official Python SDK for Google's Gemini API. It handles authentication, sending requests, and parsing responses.
It also installs Pydantic as a dependency. You'll use Pydantic later to define structured output schemas.
You should see Successfully installed followed by a list of packages in your terminal output.
Install failed?
- Check that (venv) is showing in your terminal prompt. If it's not, activate the virtual environment first.
- If you see a permissions error, make sure you're installing inside the virtual environment and not system-wide.
- If you see a network error, check your internet connection and try running the command again.
Help me install google-genai
Get your free Gemini API key
An API key is a password that lets your Python code talk to the Gemini API. Google AI Studio is where you create free keys.
- 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.
- Select Create API key in new project if prompted.
- Copy the API key that appears.
Keep your API key private
Treat your API key like a password. Do not share it publicly or commit it to version control.
Now you need to tell your terminal about the key so your Python code can find it.
- Back in your VS Code terminal, set the API key as an environment variable by running this command:
macOS
export GEMINI_API_KEY="your-api-key-here"
Windows
$env:GEMINI_API_KEY="your-api-key-here"
- Replace your-api-key-here with the key you just copied from Google AI Studio.
- Verify the key is set by running this command:
macOS
echo $GEMINI_API_KEY
Windows
echo $env:GEMINI_API_KEY
You should see your API key printed in the terminal output.
Why an environment variable?
Storing your API key in an environment variable keeps it out of your code. The Gemini client reads GEMINI_API_KEY automatically, so you never need to paste the key into your Python file.
This variable is temporary
Environment variables set with export or $env: only last for the current terminal session. If you close and reopen the terminal, you'll need to set it again.
Echo printed a blank line?
- Check that you included the quotes around your key in the export command.
- Make sure there are no spaces around the = sign.
- Run the export command again with your actual key pasted between the quotes.
Help me set my Gemini API key
Your environment is ready. In the next step, you'll write the system prompt that turns Gemini into a Socratic tutor that only asks questions and never gives answers.
Build the Socratic System Prompt
Your environment is all set up. Now it's time to build the core of your Socratic tutor: the system prompt.
System instructions tell an LLM how to behave throughout an entire conversation. Without them, Gemini defaults to explaining concepts directly. Your system prompt enforces one strict rule: Gemini can ask questions, but it can never give answers.
In this step, get ready to:
- Define a system prompt that prevents Gemini from giving direct answers.
- Connect to the Gemini API and verify the prompt works.
Create the project file and define the system prompt
The Socratic method depends on one hard constraint: the tutor must never explain. It can only ask questions, offer encouragement, or ask the student to reconsider.
You'll encode these rules into a system prompt string that Gemini reads before every response.
- In VS Code's Explorer sidebar, click the New File icon next to the socratic-tutor folder name.
- Name the file socratic_tutor.py and press Enter.
- Add the imports and system prompt by copying this code into the file:
import os
from google import genai
from google.genai import types
# --- System Prompt ---
SOCRATIC_SYSTEM_PROMPT = """You are a Socratic tutor. Your ONLY job is to help students understand concepts by asking them questions. You must follow these rules strictly:
1. NEVER state facts, give definitions, or provide direct answers.
2. NEVER say things like "Actually, the answer is..." or "Let me explain..."
3. You may ONLY respond with:
- A question that guides the student toward understanding
- Brief encouragement like "Good thinking!" or "You're on the right track!"
- A request for the student to reconsider: "What if you thought about it this way..."
4. Start with broad questions to assess what the student already knows.
5. Gradually narrow your questions as the student gets closer to understanding.
6. If the student is completely stuck, ask a simpler question that breaks the problem into smaller pieces.
7. When the student demonstrates clear understanding, congratulate them warmly and summarize what THEY figured out (not what you told them, because you told them nothing).
Remember: The goal is for the student to have the "aha moment" themselves. You are a guide, not a lecturer."""
What does this code do?
The imports bring in the Google GenAI SDK. genai is the main client library. types provides configuration classes like GenerateContentConfig that you'll use to attach the system prompt.
SOCRATIC_SYSTEM_PROMPT is the string that shapes Gemini's behavior. Each numbered rule is a hard constraint. Rule 1 and 2 prevent Gemini from lecturing. Rules 3 through 6 define what it can do instead. Rule 7 tells it how to handle the breakthrough moment.
- Save socratic_tutor.py.
- You won't see any output yet. The next chunk connects this prompt to Gemini so you can test it.
Initialize the Gemini client and test the system prompt
The system prompt is ready, but it needs to be connected to Gemini. The google-genai SDK provides a Client that reads your API key from the environment automatically.
It also provides a chat API that manages conversation history for you. You'll wrap the chat setup in a reusable function.
- Below the system prompt in socratic_tutor.py, add this function that creates a chat session with Gemini:
# --- Main Application ---
def create_tutor_chat(client):
"""Create a new Socratic tutor chat session."""
chat = client.chats.create(
model="gemini-2.5-flash",
config=types.GenerateContentConfig(
system_instruction=SOCRATIC_SYSTEM_PROMPT,
),
)
return chat
What does this function do?
client.chats.create() opens a multi-turn chat session with the gemini-2.5-flash model. The system_instruction parameter passes your Socratic prompt so Gemini follows those rules for every message in the conversation.
Wrapping this in a function makes it easy to start fresh chat sessions later without repeating the setup code.
- Save socratic_tutor.py.
- No output yet. The next chunk adds test code so you can run the script.
Time to see if your system prompt actually works. You'll create a client, start a chat, and send a test message to see how Gemini responds.
- At the bottom of socratic_tutor.py, add this test code:
# Test
client = genai.Client()
chat = create_tutor_chat(client)
response = chat.send_message("I don't understand gravity")
print(response.text)
What does this test code do?
genai.Client() creates a connection to the Gemini API. It automatically reads the GEMINI_API_KEY environment variable you set in Step 1.
chat.send_message() sends a student message to the tutor and returns Gemini's response. If the system prompt is working, Gemini should reply with a question about gravity instead of an explanation.
- Save socratic_tutor.py.
- In your VS Code terminal, run the script:
python3 socratic_tutor.py
You should see Gemini respond with a question about gravity, not a direct explanation. Something like "What do you notice when you drop something?" or "Have you ever wondered why things fall down instead of up?"
Why is a question the right response?
If Gemini replied with "Gravity is the force that attracts objects toward each other," your system prompt failed. The whole point of the Socratic method is that the tutor never lectures.
By asking a question, Gemini forces the student to think through the concept on their own. That's what makes Socratic tutoring more effective than traditional explanation.
Seeing an error instead of a response?
- If you see an authentication or API key error, make sure you set the GEMINI_API_KEY environment variable in this terminal session. Run echo $GEMINI_API_KEY to check if it's set.
- If you see ModuleNotFoundError: No module named 'google', make sure your virtual environment is activated. You should see (venv) at the start of your terminal prompt.
- If Gemini gives a direct explanation instead of a question, double check that your SOCRATIC_SYSTEM_PROMPT string matches the code above exactly, including the triple quotes.
Help me debug my Socratic tutor script.
✔️ Awesome, I've got everything!
Great. Double check you've saved socratic_tutor.py before moving on.
ⓧ I'd like to double check the full code
Here's the full socratic_tutor.py file so far. Make sure your code matches this exactly.
import os
from google import genai
from google.genai import types
# --- System Prompt ---
SOCRATIC_SYSTEM_PROMPT = """You are a Socratic tutor. Your ONLY job is to help students understand concepts by asking them questions. You must follow these rules strictly:
1. NEVER state facts, give definitions, or provide direct answers.
2. NEVER say things like "Actually, the answer is..." or "Let me explain..."
3. You may ONLY respond with:
- A question that guides the student toward understanding
- Brief encouragement like "Good thinking!" or "You're on the right track!"
- A request for the student to reconsider: "What if you thought about it this way..."
4. Start with broad questions to assess what the student already knows.
5. Gradually narrow your questions as the student gets closer to understanding.
6. If the student is completely stuck, ask a simpler question that breaks the problem into smaller pieces.
7. When the student demonstrates clear understanding, congratulate them warmly and summarize what THEY figured out (not what you told them, because you told them nothing).
Remember: The goal is for the student to have the "aha moment" themselves. You are a guide, not a lecturer."""
# --- Main Application ---
def create_tutor_chat(client):
"""Create a new Socratic tutor chat session."""
chat = client.chats.create(
model="gemini-2.5-flash",
config=types.GenerateContentConfig(
system_instruction=SOCRATIC_SYSTEM_PROMPT,
),
)
return chat
# Test
client = genai.Client()
chat = create_tutor_chat(client)
response = chat.send_message("I don't understand gravity")
print(response.text)
Your system prompt is working. Gemini is asking questions instead of giving answers. Next up, you'll build a conversation loop so the student can have a full back-and-forth dialogue with the tutor.
Build the Conversation Loop
In the last step, you confirmed that your Socratic system prompt works. Gemini responded with a question instead of an explanation.
Right now, your tutor handles a single message and stops. A real tutoring session is a back-and-forth dialogue that continues until the student has their "aha moment." In this step, you'll build that interactive loop, teach the tutor to detect when understanding clicks, and generate a Thinking Map of the learning journey.
In this step, get ready to:
- Build an interactive conversation loop in the terminal.
- Detect when the student reaches understanding using structured JSON output.
- Print a Thinking Map that visualizes the learning journey.
Add the input loop and conversation tracking
Your script currently sends one hardcoded test message and exits. You need a main() function that asks the student for a topic, enters a conversation loop, and tracks every exchange for the Thinking Map.
- In socratic_tutor.py, find the test code at the bottom of the file:
# Test
client = genai.Client()
chat = create_tutor_chat(client)
response = chat.send_message("I don't understand gravity")
print(response.text)
- Delete all five of those lines. You'll replace them with a proper main() function.
The multi-turn chat API handles conversation history for you. Each call to chat.send_message() automatically sends the full conversation so far. You just need to feed it user input in a loop.
- Where the test code used to be, add the main() function and the entry-point guard by copying the following code:
def main():
# Initialize the Gemini client
client = genai.Client()
print("\n" + "=" * 60)
print(" SOCRATIC AI TUTOR")
print(" I will never give you answers. I will only ask questions.")
print("=" * 60)
# Get the topic from the student
topic = input("\nWhat topic are you struggling with?\n> ").strip()
if not topic:
print("Please provide a topic. Exiting.")
return
# Create the chat session
chat = create_tutor_chat(client)
thinking_path = []
# Send the initial topic to the tutor
opening_message = f"I don't understand {topic}. Can you help me?"
response = chat.send_message(opening_message)
tutor_reply = response.text
thinking_path.append({"role": "student", "text": opening_message})
thinking_path.append({"role": "tutor", "text": tutor_reply})
print(f"\nTutor: {tutor_reply}")
# Conversation loop
conversation_text = f"Student: {opening_message}\nTutor: {tutor_reply}\n"
while True:
student_input = input("\nYou: ").strip()
if student_input.lower() in ["quit", "exit", "q"]:
print("\nSession ended. Let's see your thinking journey!")
break
if not student_input:
continue
# Send the student's message to the tutor
response = chat.send_message(student_input)
tutor_reply = response.text
thinking_path.append({"role": "student", "text": student_input})
thinking_path.append({"role": "tutor", "text": tutor_reply})
conversation_text += f"Student: {student_input}\nTutor: {tutor_reply}\n"
print(f"\nTutor: {tutor_reply}")
if __name__ == "__main__":
main()
What does this code do?
- The welcome banner and input() call ask the student what topic they want to explore.
- create_tutor_chat(client) starts a chat session with the Socratic system prompt you wrote in the previous step.
- thinking_path is a list that stores every exchange. This becomes the data for the Thinking Map later.
- The while True loop reads student input, sends it to the tutor via chat.send_message(), and prints the tutor's response. Typing quit, exit, or q ends the session.
- conversation_text builds a running transcript of the full conversation. You'll use this for the understanding check in the next substep.
- if __name__ == "__main__" ensures main() only runs when you execute the script directly, not when it's imported.
- Save socratic_tutor.py.
- Run the script by entering this command in your terminal:
python3 socratic_tutor.py
- You should see the welcome banner and a prompt asking what topic you're struggling with.
- Type a topic like recursion and press Enter. The tutor should respond with a question.
- Try answering the question and watch the tutor ask a follow-up.
- Type quit to end the session.
Seeing an error when you run the script?
- If you see NameError: name 'create_tutor_chat' is not defined, make sure the main() function is below the create_tutor_chat() function, not above it.
- If you see IndentationError, check that every line inside main() is indented with exactly four spaces.
- If you see an API error, confirm your GEMINI_API_KEY environment variable is still set in this terminal session.
Help me debug my Socratic tutor conversation loop
Add the "aha moment" detection
The conversation loop works, but you have to manually type quit to end it. A real Socratic tutor should recognize when the student finally "gets it" and end the session with a congratulations.
To do this, you'll make a separate API call after each exchange that analyzes the student's latest response. This call uses structured output to force Gemini to return a JSON object matching a schema you define. No freeform text to parse.
- At the top of socratic_tutor.py, find the existing import lines. Add these two new imports directly below them:
from pydantic import BaseModel, Field
from typing import Optional
- Now add the UnderstandingCheck class. Place this block between the imports and the # --- System Prompt --- comment by copying the following code:
# --- Pydantic Models for Structured Output ---
class UnderstandingCheck(BaseModel):
reached_understanding: bool = Field(
description="True if the student has demonstrated clear understanding of the core concept."
)
evidence: str = Field(
description="A brief quote or paraphrase from the student's response that shows understanding or lack thereof."
)
What is Pydantic doing here?
Pydantic is a Python library for defining data models with type validation. The UnderstandingCheck class defines exactly two fields: a boolean for whether the student reached understanding and a string for the evidence.
When you pass UnderstandingCheck.model_json_schema() to the Gemini API, it forces the model to return JSON that matches this exact shape. No surprises, no parsing headaches.
Now you need a function that uses this model to check whether the student has reached their "aha moment."
- Add the check_understanding() function directly below the create_tutor_chat() function by copying this code:
def check_understanding(client, conversation_so_far, student_message):
"""Use structured output to check if the student has reached understanding."""
prompt = f"""Analyze the following tutoring conversation. The tutor is using the Socratic method.
Conversation so far:
{conversation_so_far}
Latest student message: "{student_message}"
Has the student demonstrated clear understanding of the core concept being discussed?"""
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=prompt,
config={
"response_mime_type": "application/json",
"response_json_schema": UnderstandingCheck.model_json_schema(),
},
)
return UnderstandingCheck.model_validate_json(response.text)
What does this function do?
- This function makes a separate, single-turn API call using client.models.generate_content() (not the chat session). It sends the full conversation transcript and the student's latest message.
- response_mime_type set to application/json tells Gemini to return pure JSON instead of freeform text.
- response_json_schema receives the Pydantic model's schema, so Gemini knows exactly what fields to produce.
- model_validate_json() parses Gemini's JSON response into a typed Python object you can use directly.
The function is ready. Now you need to call it inside your conversation loop so it runs after every student response.
- Inside the main() function, scroll down to the while True loop and find this line near the bottom:
print(f"\nTutor: {tutor_reply}")
- Add the following code directly below that line, at the same indentation level (two levels of indentation, inside the while loop):
# Check if the student has reached understanding
check = check_understanding(client, conversation_text, student_input)
if check.reached_understanding:
thinking_path.append({
"role": "breakthrough",
"text": check.evidence,
})
print("\n You got it! Great work figuring that out on your own.")
break
What does this addition do?
After each tutor response, this code calls check_understanding() to analyze the student's latest message. If reached_understanding is True, it records the breakthrough evidence in thinking_path, prints a congratulations message, and ends the loop.
- Save socratic_tutor.py.
Seeing an ImportError for pydantic?
Pydantic is installed automatically as a dependency of the google-genai SDK. If you see an import error, make sure your virtual environment is activated. You should see (venv) at the start of your terminal prompt.
Help me fix a pydantic ImportError in my Socratic tutor project
Print the Thinking Map
The conversation loop now detects the "aha moment," but when it ends, the student just sees the final message. A Thinking Map shows the full chain of questions that guided the student to understanding.
- Add the print_thinking_map() function directly above the main() function by copying this code:
def print_thinking_map(thinking_path):
"""Print the Thinking Map showing the learning journey."""
print("\n" + "=" * 60)
print(" YOUR THINKING MAP")
print("=" * 60)
for i, step in enumerate(thinking_path, 1):
if step["role"] == "tutor":
print(f"\n [Tutor Question]")
print(f" {step['text'][:100]}...")
elif step["role"] == "student":
print(f" [Your Response]")
print(f" {step['text'][:100]}...")
elif step["role"] == "breakthrough":
print(f"\n *** BREAKTHROUGH! ***")
print(f" {step['text']}")
print("\n" + "=" * 60)
What does this function do?
This function loops through the thinking_path list and prints a formatted summary of each exchange. Tutor questions and student responses are truncated to 100 characters for readability. The breakthrough moment is highlighted at the end.
Now you need to call this function from main() after the conversation loop finishes.
- Inside main(), find the end of the while True loop (after the last break statement). Add the following code at the same indentation level as the while keyword (one level inside main(), not inside the loop):
# Print the Thinking Map
print_thinking_map(thinking_path)
print("\nSession complete. You did the thinking. The AI just asked the questions.\n")
Where does this code sit?
These two lines go after the while True block ends, still inside the main() function. They run once the loop exits, whether the student typed quit or reached understanding.
- Save socratic_tutor.py.
- Run the full tutor session by entering this command:
python3 socratic_tutor.py
- Enter a topic and have a conversation with the tutor. After a few exchanges, try demonstrating understanding of the topic in your answer.
- You should see the tutor congratulate you, followed by the Thinking Map showing each question and response in the conversation.
Tutor never detects understanding?
- Try giving a clear, confident answer that restates the concept in your own words. Short replies like "yeah I get it" may not contain enough evidence for the model to detect understanding.
- If you see a JSON parsing error, check that the response_mime_type is set to application/json (not text/json) in the check_understanding() function.
- You can always type quit to end the session and still see the Thinking Map.
Help me debug the understanding check in my Socratic tutor
✔️ Awesome, I've got everything!
Double check you've saved socratic_tutor.py.
ⓧ I'd like to double check the full code
Here is the complete socratic_tutor.py file as it should look at the end of this step.
import os
from google import genai
from google.genai import types
from pydantic import BaseModel, Field
from typing import Optional
# --- Pydantic Models for Structured Output ---
class UnderstandingCheck(BaseModel):
reached_understanding: bool = Field(
description="True if the student has demonstrated clear understanding of the core concept."
)
evidence: str = Field(
description="A brief quote or paraphrase from the student's response that shows understanding or lack thereof."
)
# --- System Prompt ---
SOCRATIC_SYSTEM_PROMPT = """You are a Socratic tutor. Your ONLY job is to help students understand concepts by asking them questions. You must follow these rules strictly:
1. NEVER state facts, give definitions, or provide direct answers.
2. NEVER say things like "Actually, the answer is..." or "Let me explain..."
3. You may ONLY respond with:
- A question that guides the student toward understanding
- Brief encouragement like "Good thinking!" or "You're on the right track!"
- A request for the student to reconsider: "What if you thought about it this way..."
4. Start with broad questions to assess what the student already knows.
5. Gradually narrow your questions as the student gets closer to understanding.
6. If the student is completely stuck, ask a simpler question that breaks the problem into smaller pieces.
7. When the student demonstrates clear understanding, congratulate them warmly and summarize what THEY figured out (not what you told them, because you told them nothing).
Remember: The goal is for the student to have the "aha moment" themselves. You are a guide, not a lecturer."""
# --- Main Application ---
def create_tutor_chat(client):
"""Create a new Socratic tutor chat session."""
chat = client.chats.create(
model="gemini-2.5-flash",
config=types.GenerateContentConfig(
system_instruction=SOCRATIC_SYSTEM_PROMPT,
),
)
return chat
def check_understanding(client, conversation_so_far, student_message):
"""Use structured output to check if the student has reached understanding."""
prompt = f"""Analyze the following tutoring conversation. The tutor is using the Socratic method.
Conversation so far:
{conversation_so_far}
Latest student message: "{student_message}"
Has the student demonstrated clear understanding of the core concept being discussed?"""
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=prompt,
config={
"response_mime_type": "application/json",
"response_json_schema": UnderstandingCheck.model_json_schema(),
},
)
return UnderstandingCheck.model_validate_json(response.text)
def print_thinking_map(thinking_path):
"""Print the Thinking Map showing the learning journey."""
print("\n" + "=" * 60)
print(" YOUR THINKING MAP")
print("=" * 60)
for i, step in enumerate(thinking_path, 1):
if step["role"] == "tutor":
print(f"\n [Tutor Question]")
print(f" {step['text'][:100]}...")
elif step["role"] == "student":
print(f" [Your Response]")
print(f" {step['text'][:100]}...")
elif step["role"] == "breakthrough":
print(f"\n *** BREAKTHROUGH! ***")
print(f" {step['text']}")
print("\n" + "=" * 60)
def main():
# Initialize the Gemini client
client = genai.Client()
print("\n" + "=" * 60)
print(" SOCRATIC AI TUTOR")
print(" I will never give you answers. I will only ask questions.")
print("=" * 60)
# Get the topic from the student
topic = input("\nWhat topic are you struggling with?\n> ").strip()
if not topic:
print("Please provide a topic. Exiting.")
return
# Create the chat session
chat = create_tutor_chat(client)
thinking_path = []
# Send the initial topic to the tutor
opening_message = f"I don't understand {topic}. Can you help me?"
response = chat.send_message(opening_message)
tutor_reply = response.text
thinking_path.append({"role": "student", "text": opening_message})
thinking_path.append({"role": "tutor", "text": tutor_reply})
print(f"\nTutor: {tutor_reply}")
# Conversation loop
conversation_text = f"Student: {opening_message}\nTutor: {tutor_reply}\n"
while True:
student_input = input("\nYou: ").strip()
if student_input.lower() in ["quit", "exit", "q"]:
print("\nSession ended. Let's see your thinking journey!")
break
if not student_input:
continue
# Send the student's message to the tutor
response = chat.send_message(student_input)
tutor_reply = response.text
thinking_path.append({"role": "student", "text": student_input})
thinking_path.append({"role": "tutor", "text": tutor_reply})
conversation_text += f"Student: {student_input}\nTutor: {tutor_reply}\n"
print(f"\nTutor: {tutor_reply}")
# Check if the student has reached understanding
check = check_understanding(client, conversation_text, student_input)
if check.reached_understanding:
thinking_path.append({
"role": "breakthrough",
"text": check.evidence,
})
print("\n You got it! Great work figuring that out on your own.")
break
# Print the Thinking Map
print_thinking_map(thinking_path)
print("\nSession complete. You did the thinking. The AI just asked the questions.\n")
if __name__ == "__main__":
main()
Secret mission
Add a Confidence Tracker
Your tutor asks great questions. But what happens when a student is completely lost and too confused to even know what to ask? In this extension, you'll add real-time confusion detection that automatically adjusts the questioning strategy and displays a visual confidence bar.
Before you go
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally with a free-tier API key, so there are no ongoing costs.
Resources you used:
- The socratic-tutor project folder on your Desktop.
- A Python virtual environment (venv) inside the project folder.
- The google-genai SDK installed inside the virtual environment.
- A Gemini API key created in Google AI Studio.
Keep everything running
No action needed. Choose this if you plan to keep experimenting with your Socratic tutor or want to extend it further.
- Your project folder, virtual environment, and API key all stay as they are.
- To run the tutor again later, activate the virtual environment and run the script.
Pause - I'll come back to this later
Deactivate the virtual environment to free up your terminal session. Your files and API key stay intact for when you return.
- In your terminal, run deactivate to exit the virtual environment.
- Your socratic-tutor folder and API key remain saved for later use.
- When you come back, navigate to the folder, run source venv/bin/activate, set your GEMINI_API_KEY environment variable again, and you are good to go.
Delete - I don't want to use this again
Remove all project files and optionally revoke your API key for a clean slate.
- Delete the socratic-tutor folder from your Desktop. This removes the virtual environment and all project files.
- To revoke your API key, go to aistudio.google.com, click Get API Key, find your key, and click the trash icon to delete it.
Mission accomplished
That's a Wrap!
Nice work! You just built an AI tutor that teaches the way Socrates did 2,400 years ago. Not by telling, but by asking.
You've learned how to:
- Integrate the Gemini API with a system prompt that enforces strict behavioral constraints on an LLM, making it ask questions instead of giving answers.
- Build a multi-turn conversation loop using the Google GenAI chat API that tracks the full dialogue between student and tutor.
- Use structured JSON outputs with Pydantic models to detect when a student reaches understanding, and generate a Thinking Map that visualizes the learning journey.
- Add a confidence tracker that detects when a student is struggling and automatically adapts the questioning strategy in real time.
Ready to quiz yourself?