{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 🧪 LearnAI Workshop — Session 3: Real-World\n",
    "### Practical notebook · runs on **Google Colab** · powered by **Google Gemini (free)**\n",
    "\n",
    "This is the finish line. You'll give the AI **eyes**, learn to **catch it lying**, and build a real **Study Buddy** — testing each step before moving on:\n",
    "\n",
    "| Station | You'll do | ✅ You're done when |\n",
    "|---|---|---|\n",
    "| **7 · Vision** | Upload a photo and ask Gemini about it | It described your picture *and* read text in it |\n",
    "| **8 · Safety** | Catch a hallucination, then make it admit doubt | You saw fake 'facts' *and* a more honest reply |\n",
    "| **9 · Capstone** | Build a Study Buddy from your own notes | It found the right note *and* quizzed you from it |\n",
    "\n",
    "> **How to use this:** click each grey **code cell** and press **▶️ (or Shift+Enter)**. Read the note above it first. Run them **in order** from the top."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 🔑 Step 0 — Get your free Gemini key (once)\n",
    "1. Go to **https://aistudio.google.com/apikey** (sign in with a Google account).\n",
    "2. Click **Create API key** → copy it.\n",
    "3. Come back here and run the cells below. When asked, paste the key and press **Enter**.\n",
    "\n",
    "🔒 **Safety:** an API key is a password. Don't paste it into a public doc, a group chat, or GitHub."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Install the Gemini SDK (takes ~15s the first time)\n",
    "!pip -q install google-genai"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Connect to Gemini using your key\n",
    "from google import genai\n",
    "from google.genai import types\n",
    "import getpass, time\n",
    "\n",
    "API_KEY = getpass.getpass('Paste your Gemini API key and press Enter: ').strip()\n",
    "client = genai.Client(api_key=API_KEY)\n",
    "print('Client ready ✅')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Pick a model automatically\n",
    "Model names change over time, so instead of hard-coding one, we **ask your key which models it can use** and grab a fast one. (This also means the notebook keeps working even after Google renames things.)"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# List the models your key can see, then auto-pick a fast one\n",
    "model_names = [m.name for m in client.models.list()]\n",
    "flash = [m for m in model_names if 'flash' in m and 'embedding' not in m]\n",
    "FAST_MODEL = flash[0] if flash else 'models/gemini-2.5-flash'\n",
    "print('Using FAST_MODEL =', FAST_MODEL)"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ✅ TEST: does everything work? You should see a short reply, then a checkmark.\n",
    "resp = client.models.generate_content(model=FAST_MODEL, contents='Say hello in exactly 5 words.')\n",
    "print(resp.text)\n",
    "assert resp.text, 'No text came back — re-check your API key and the model name above.'\n",
    "print('\\n✅ Setup works — you are talking to a real AI!')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "# 🟦 Station 7 — Give the AI eyes (vision)\n",
    "Every station before this treated AI as a thing that reads and writes **words**. But modern flash models are **multimodal** — they can also **see pictures**. You hand it an image plus a question, and it answers in words, using all the same prompting skills you already have.\n",
    "\n",
    "Let's prove it: you'll **upload your own image** and Gemini will describe it — and even read any text inside it."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Upload an image from your computer or phone.\n",
    "# When you run this, a 'Choose Files' button appears — pick ONE picture\n",
    "# (a photo, a screenshot, a page of handwriting, a chart... anything).\n",
    "from google.colab import files\n",
    "from PIL import Image\n",
    "\n",
    "uploaded = files.upload()          # kid picks an image\n",
    "img = Image.open(list(uploaded.keys())[0])\n",
    "img          # show it so you can confirm it loaded"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Now ask Gemini to LOOK at the image. Notice: contents is a LIST —\n",
    "# the picture AND the question, side by side. The model 'sees' both.\n",
    "resp = client.models.generate_content(\n",
    "    model=FAST_MODEL,\n",
    "    contents=[img, 'Describe this image, and read any text you can see in it.'],\n",
    ")\n",
    "print(resp.text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "🧪 **Test:** does the description actually match your picture? If it read text out of the image (a sign, a caption, your handwriting), you just watched an AI use a sense it didn't have five minutes ago."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# 🎯 YOUR TURN: ask 3 MORE questions about the SAME image — just change the text.\n",
    "# Try: 'What mood does this photo have?' · 'Write alt-text for a blind reader.'\n",
    "#      'What is one thing that looks off or unusual here?'\n",
    "my_question = 'What is one small detail most people would miss in this image?'  # <-- edit me\n",
    "resp = client.models.generate_content(model=FAST_MODEL, contents=[img, my_question])\n",
    "print(resp.text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> **Vision is only one of the extra senses.** Models can also **generate brand-new images** from a text prompt (tools like DALL·E, Midjourney, and Google's own image models), and voice AI has two halves — **speech-to-text** (transcribing audio into words) and **text-to-speech** (turning words into a spoken voice). Chain them and you get a talking assistant: *hear → transcribe → think → speak*. We're keeping this notebook free and simple, so no code for those — but now you know the whole toolbox exists.\n",
    "\n",
    "> ✅ **Station 7 complete** when Gemini correctly described your uploaded image *and* you asked it at least one follow-up question."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "# 🟨 Station 8 — Safety: catch it before it fools you\n",
    "AI predicts the *likely* next chunk of text — not the *verified true* one. That single fact is the root of the biggest danger in AI: it will state false things with total confidence. A responsible builder learns to **catch** that. Let's do it three ways."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### (a) Catch a hallucination red-handed"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Ask for specific sources + quotes on a deliberately obscure, niche topic.\n",
    "# Specific-looking details (authors, page numbers, tidy quotes) are exactly\n",
    "# how hallucinations disguise themselves.\n",
    "topic = 'the history of competitive marble-polishing in 1840s rural Latvia'  # niche + checkable-sounding\n",
    "q = (f'Give me 3 sources (with authors, titles, and page numbers) and 2 direct '\n",
    "     f'quotes about {topic}.')\n",
    "print(client.models.generate_content(model=FAST_MODEL, contents=q).text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "🧪 **Test — actually try to verify one.** Pick a title or a quote above and search for it (Google, a library catalog). Real, or invented? Notice how **convincing the fakes look** — the confident tone, the exact page numbers, the polished quotes. That feeling — *it sounds most sure exactly when I most need to check* — is the whole lesson."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### (b) Teach it to say \"I'm not sure\""
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# First, ask a made-up factual question with NO guidance. It may happily invent an answer.\n",
    "made_up = 'What year did the town of Fennwick Hollow, Montana win its first curling championship?'\n",
    "print('WITHOUT guidance:')\n",
    "print(client.models.generate_content(model=FAST_MODEL, contents=made_up).text)"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Now ask the SAME question, but add a system instruction inviting honesty.\n",
    "cfg = types.GenerateContentConfig(\n",
    "    system_instruction=\"If you are not sure, say 'I'm not sure' instead of guessing.\"\n",
    ")\n",
    "print('WITH guidance:')\n",
    "print(client.models.generate_content(model=FAST_MODEL, contents=made_up, config=cfg).text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "🧪 **Test:** compare the two replies. Did the second one get more honest — hedging, or admitting it can't verify the town? It won't be perfect every time, but *\"if you're not sure, say so\"* measurably reduces confident nonsense. That one line belongs in real projects."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### (c) Privacy — a rule with no code\n",
    "⚠️ **Never paste these into any AI tool — including this notebook:**\n",
    "- **Passwords, API keys, or login details.** (Your Gemini key went through `getpass` so it stays hidden — keep it that way.)\n",
    "- **Financial info** — card numbers, bank or account numbers.\n",
    "- **Other people's personal information** — a friend's address, a classmate's private messages, someone's medical or family details.\n",
    "- **Anything confidential** you wouldn't post publicly.\n",
    "\n",
    "An AI chat *feels* private, but it usually isn't: **assume your inputs may be stored and seen** by people or used to improve future models. When you build a tool other people type into, their data becomes your responsibility too — don't collect what you don't need. *(That's why there is no code cell here — we will never write code that sends secrets to a model.)*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### A thought to carry: bias\n",
    "A model learns language from enormous amounts of human writing — and quietly absorbs the **unfair patterns** hidden in it. It can associate certain jobs with one kind of person, or produce weaker results for names, dialects, and languages that were rare in its training data. It never *announces* this — it sounds just as confident and neutral as always. That matters most when AI helps **decide about people** (screening applicants, moderating posts, grading). A responsible builder goes *looking* for bias on purpose — testing across different names, genders, and backgrounds — instead of assuming a smooth answer is a fair one.\n",
    "\n",
    "> ✅ **Station 8 complete** when you've (1) tried to verify a suspicious source, (2) seen the 'I'm not sure' instruction change a reply, and (3) can name one thing you should never paste into an AI tool."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "# 🟩 Station 9 — Capstone: build a **Study Buddy**\n",
    "Time to snap it all together into a real thing. Your Study Buddy takes **your own notes**, finds the one most relevant to a question (that's **RAG** — giving the model *your* facts to lean on), and uses a well-crafted prompt to **quiz you** from it. Every skill from the roadmap, wired into one small project."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Starter notes so this runs out of the box. (You'll swap in your own below.)\n",
    "notes = [\n",
    "    'Photosynthesis: plants turn sunlight, water, and CO2 into glucose and oxygen. It happens in the chloroplasts, which contain the green pigment chlorophyll.',\n",
    "    'The water cycle: water evaporates from oceans and lakes, condenses into clouds, falls as precipitation (rain or snow), and collects again as runoff. The main steps are evaporation, condensation, precipitation, and collection.',\n",
    "    'Newton\\'s three laws: (1) an object stays at rest or in motion unless a force acts on it, (2) force equals mass times acceleration (F = ma), (3) every action has an equal and opposite reaction.',\n",
    "    'The French Revolution began in 1789. Causes included heavy taxes on ordinary people, food shortages, and anger at the monarchy. The storming of the Bastille on July 14, 1789 became its famous symbol.',\n",
    "]\n",
    "print(f'Loaded {len(notes)} study notes.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Embed the notes\n",
    "To find the *most relevant* note for a question, we turn each note into an **embedding** — a list of numbers that captures its meaning. Two texts about the same idea end up with similar numbers. Same self-healing trick as before: we ask your key for an embedding model instead of hard-coding one."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Auto-pick an embedding model (falls back to a known one if none is listed)\n",
    "import numpy as np\n",
    "embed = [m for m in model_names if 'embedding' in m]\n",
    "EMBED_MODEL = embed[0] if embed else 'models/text-embedding-004'\n",
    "print('Using EMBED_MODEL =', EMBED_MODEL)"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Turn each note into an embedding vector, once, up front.\n",
    "def embed_text(text):\n",
    "    r = client.models.embed_content(model=EMBED_MODEL, contents=text)\n",
    "    return np.array(r.embeddings[0].values)\n",
    "\n",
    "note_vectors = [embed_text(n) for n in notes]\n",
    "print(f'Embedded {len(note_vectors)} notes. Each vector has {len(note_vectors[0])} numbers.')"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Cosine similarity: how close in meaning are two vectors? 1.0 = identical, 0 = unrelated.\n",
    "def cosine(a, b):\n",
    "    return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))\n",
    "\n",
    "def find_best_note(question):\n",
    "    qv = embed_text(question)\n",
    "    scores = [cosine(qv, nv) for nv in note_vectors]\n",
    "    best = int(np.argmax(scores))\n",
    "    return notes[best], scores[best]"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# The Study Buddy: retrieve the right note, then QUIZ you from it with a good prompt.\n",
    "def study_buddy(question):\n",
    "    note, score = find_best_note(question)\n",
    "    print(f'(Matched a note, similarity {score:.2f})\\n')\n",
    "    prompt = (\n",
    "        'ROLE: You are a friendly study coach for a high-school student.\\n'\n",
    "        'Use ONLY the note below — do not add outside facts. If the answer is not in '\n",
    "        'the note, say so honestly.\\n\\n'\n",
    "        f'NOTE:\\n{note}\\n\\n'\n",
    "        f'STUDENT QUESTION: {question}\\n\\n'\n",
    "        'FORMAT: First answer the question in 2-3 sentences using the note. '\n",
    "        'Then ask ME one quick quiz question to check I understood.'\n",
    "    )\n",
    "    return client.models.generate_content(model=FAST_MODEL, contents=prompt).text\n",
    "\n",
    "print(study_buddy('How does photosynthesis work?'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "🧪 **Test:** did it (1) match a *photosynthesis* note — not the French Revolution one — and (2) answer using that note, then quiz you back? Try a question about a different note (`'What started the French Revolution?'`) and watch it retrieve a different one."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# 🎯 YOUR TURN: replace these with YOUR real class notes, then re-run the\n",
    "# 'Embed the notes' cells above (so note_vectors matches) and ask away.\n",
    "notes = [\n",
    "    'Paste your first note here — one topic per note works best.',\n",
    "    'Paste another note here.',\n",
    "    'And another. Add as many as you like.',\n",
    "]\n",
    "print('Now re-run the embed cells above, then call: study_buddy(\"your question\")')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 🚀 Ship it\n",
    "You just built a working AI tool. Here's how to turn it into a *real* thing other people use:\n",
    "- **Share this notebook.** In Colab: **Share → Anyone with the link**. Send it to a friend — they run the cells and study from their own notes.\n",
    "- **Grow it into a web app later.** The exact same three ideas — *retrieve the right note → prompt the model → show the answer* — power a simple website. A no-code builder or an AI coding helper (Cursor, Claude Code) can wrap this logic in a real page you deploy with a link.\n",
    "- **Keep the safety habit.** Add a footer like *\"AI can be wrong — check your notes\"*, and before you share, try to break it: ask about something not in the notes and confirm it says so instead of making things up.\n",
    "\n",
    "**Start small and real.** A \"flashcard quizzer for tonight's bio test\" that you *finish* beats a \"personal AI tutor for every subject\" that dies in a folder. Ship the small thing, then make the next one slightly bigger.\n",
    "\n",
    "> ✅ **Station 9 complete** when your Study Buddy retrieved the right note *and* its answer clearly used that note to quiz you."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 🎉 You finished the whole LearnAI roadmap!\n",
    "Three sessions ago, \"AI\" was a black box. Look what you can do now:\n",
    "- **Session 1:** talked to a real AI, watched it predict (and hallucinate), compared models, and turned the tokens & temperature dials.\n",
    "- **Session 2:** wrote strong prompts, fed the AI *your own* notes (RAG), and built an agent that takes actions.\n",
    "- **Session 3 (today):** gave it **eyes**, learned to **catch it being confidently wrong**, and built a real **Study Buddy** from your own notes.\n",
    "\n",
    "That's not trivia — it's a builder's toolkit, and it's yours. The only thing left is the best part: **make something.** Pick a small idea, ship it this week, and send the link to one person. Then do it again.\n",
    "\n",
    "**Welcome to building.** 🌱\n",
    "\n",
    "_LearnAI · part of MillionRoots. Keep this notebook — reuse the setup cells any time._"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "name": "LearnAI Session 3 — Real-World",
   "provenance": []
  },
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}