We are building a conversational Spanish tutor that corrects grammar in real time and appends new vocabulary to a local JSONL file. It runs entirely on Oxlo.ai's request-based API, so long system prompts and extended practice sessions do not increase the per-turn cost. Intermediate learners get immediate feedback, and engineers get a clean, stateful CLI tool they can extend into a web app or voice bot.
What you'll need
- Python 3.10 or later
- The OpenAI SDK:
pip install openai - An Oxlo.ai API key from https://portal.oxlo.ai
Create a new directory and a file named tutor.py. I also recommend setting your key as an environment variable so it never hits disk.
export OXLO_API_KEY="sk-oxlo.ai-..."
Step 1: Scaffold the project
We need the OpenAI client, JSON handling, and a message history list that persists across turns. I import os so the key stays out of the source.
import os
import json
from openai import OpenAI
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.environ.get("OXLO_API_KEY")
)
MODEL = "llama-3.3-70b"
messages = []
Step 2: Design the system prompt
The system prompt is the only place we teach the model its persona, correction style, and output format. We ask it to append a machine-readable vocab block after the delimiter |||VOCAB||| so we can split the text cleanly.
SYSTEM_PROMPT = """You are a patient Spanish tutor for an intermediate English speaker.
Rules:
1. Conduct the conversation entirely in Spanish.
2. When the student makes a grammar or vocabulary mistake, politely correct it in Spanish, then briefly explain the rule in English inside parentheses.
3. Ask a follow-up question to keep the dialogue alive.
4. At the very end of every response, on its own line, output the delimiter |||VOCAB||| followed by a JSON array of new or corrected words drawn from the student's last message. Each object must have keys: word, correction, meaning. If nothing is new, output an empty array.
5. Do not mention the vocab block to the student.
"""
Step 3: Build the conversation loop
We append the user's message to the history, send the full context to Oxlo.ai, and split the assistant's raw output into the human reply and the hidden vocab payload.
def chat(user_message):
messages.append({"role": "user", "content": user_message})
response = client.chat.completions.create(
model=MODEL,
messages=[{"role": "system", "content": SYSTEM_PROMPT}] + messages,
temperature=0.7,
)
raw = response.choices[0].message.content
messages.append({"role": "assistant", "content": raw})
if "|||VOCAB|||" in raw:
parts = raw.split("|||VOCAB|||")
reply = parts[0].strip()
try:
vocab = json.loads(parts[1].strip())
except json.JSONDecodeError:
vocab = []
else:
reply = raw.strip()
vocab = []
return reply, vocab
Step 4: Add vocabulary tracking
We persist every extracted word to a JSONL file so the student can review later. A separate helper keeps the chat logic clean.
def save_vocab(vocab, path="vocab.jsonl"):
if not vocab:
return
with open(path, "a", encoding="utf-8") as f:
for item in vocab:
f.write(json.dumps(item, ensure_ascii=False) + "\n")
Step 5: Wrap it in a CLI
The main loop reads lines from stdin, calls our tutor, prints the reply, and flushes new vocabulary to disk. Typing exit or hitting Ctrl-D ends the session gracefully.
def main():
print("Spanish tutor ready. Type 'exit' to quit.\n")
while True:
try:
user_input = input("You: ").strip()
except (EOFError, KeyboardInterrupt):
break
if not user_input or user_input.lower() == "exit":
break
reply, vocab = chat(user_input)
print(f"Tutor: {reply}\n")
if vocab:
save_vocab(vocab)
print(f"Saved {len(vocab)} item(s) to vocab.jsonl.\n")
if __name__ == "__main__":
main()
Run it
Set your key and start the script.
export OXLO_API_KEY="sk-oxlo.ai-..."
python tutor.py
Here is a sample session. I entered a deliberate mistake, "yo tengo", to see the correction behavior.
Spanish tutor ready. Type 'exit' to quit.
You: Hola, me llamo Juan y yo tengo dos gatos.
Tutor: ¡Hola, Juan! Bienvenido. Me alegra conocerte.
En español no necesitas el pronombre "yo" antes del verbo "tengo" porque la conjugación ya indica el sujeto. Di: "me llamo Juan y tengo dos gatos".
¿Cómo se llaman tus gatos?
Saved 1 item(s) to vocab.jsonl.
You: Mi perro es muy grande y le gusta correr en el parque.
Tutor: ¡Qué bien! Tu perro suena muy enérgico.
Un pequeño detalle: dijiste "mi perro es muy grande", y eso está perfecto. ¿A tu perro le gusta correr todos los días?
Saved 0 item(s) to vocab.jsonl.
After the session, vocab.jsonl contains a structured record you can import into Anki or flashcard software.
Next steps
Swap in qwen-3-32b if you want to support Mandarin, Arabic, or other multilingual pairs, or switch to kimi-k2.6 for longer reading-comprehension passages with vision support. If you deploy this to a classroom or tutoring product, keep in mind that Oxlo.ai charges per request, not per token, so you can safely lengthen the system prompt or add retrieval context without worrying about ballooning costs. See the current plans at https://oxlo.ai/pricing.
Top comments (0)