Skip to content

Instantly share code, notes, and snippets.

@swateek
Created June 20, 2026 07:16
Show Gist options
  • Select an option

  • Save swateek/f591215edacd52b7380fdac9af300fc4 to your computer and use it in GitHub Desktop.

Select an option

Save swateek/f591215edacd52b7380fdac9af300fc4 to your computer and use it in GitHub Desktop.
lm_prompting_hands_on_new
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/swateek/f591215edacd52b7380fdac9af300fc4/copy-of-llm_prompting_hands_on_new.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "puZAFXdVIfpQ"
},
"source": [
"# 🧠 LLM Prompting β€” Hands-On Workshop\n",
"**Duration:** ~1 Hour | **Level:** Beginner to Intermediate\n",
"\n",
"---\n",
"\n",
"## πŸ“‹ Agenda\n",
"\n",
"| Module | Topic | Time |\n",
"|--------|-------|------|\n",
"| 0 | Setup & Environment | 5 min |\n",
"| 1 | Foundation Model Comparison Dashboard | 20 min |\n",
"| 2 | Function Calling | 20 min |\n",
"| 3 | Prompt Library | 15 min |\n",
"\n",
"---\n",
"\n",
"> **What you'll learn:** How to effectively interact with Large Language Models (LLMs), compare model behaviors, harness structured outputs via function calling, and build a reusable prompt library β€” all hands-on with real API calls."
],
"id": "puZAFXdVIfpQ"
},
{
"cell_type": "markdown",
"metadata": {
"id": "hjSvtblZIfpR"
},
"source": [
"---\n",
"## πŸ”§ Module 0 β€” Setup & Environment\n",
"\n",
"Before we begin, let's install the required libraries and verify our connection to the LLM API.\n",
"\n",
"**Libraries used:**\n",
"- `openai` β€” Python SDK to interface with OpenAI-compatible APIs\n",
"- `pandas` β€” for tabular display of results\n",
"- `matplotlib` β€” for visualizations in the dashboard\n",
"- `time` β€” to benchmark latency\n",
"- `json` β€” to parse structured outputs"
],
"id": "hjSvtblZIfpR"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "Ut-8G9tEIfpS"
},
"outputs": [],
"source": [
"# Install required packages (run once)\n",
"!pip install openai pandas matplotlib --quiet"
],
"id": "Ut-8G9tEIfpS"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "HJmMUAuvIfpT",
"outputId": "e2b4c689-824d-462c-d8e0-50fb1c9616c4"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"βœ… Setup complete! Client initialized.\n"
]
}
],
"source": [
"# ── Core Imports ──────────────────────────────────────────────────────────────\n",
"from openai import OpenAI\n",
"import pandas as pd\n",
"import matplotlib.pyplot as plt\n",
"import matplotlib.patches as mpatches\n",
"import time\n",
"import json\n",
"import textwrap\n",
"\n",
"# ── API Client Configuration ──────────────────────────────────────────────────\n",
"# This uses an OpenAI-compatible endpoint pointing to a self-hosted Qwen model.\n",
"# Replace api_key with your actual key if required.\n",
"client = OpenAI( # client object.\n",
" base_url=\"http://103.42.50.41:443/v1\",\n",
" api_key=\"sk-0-hkq2vsq6Cv9ehIzELoAk6rKH9AzHIE\"\n",
")\n",
"\n",
"# Default model we'll use throughout this workshop\n",
"DEFAULT_MODEL = \"Qwen/Qwen2.5-7B-Instruct-AWQ\"\n",
"\n",
"print(\"βœ… Setup complete! Client initialized.\")"
],
"id": "HJmMUAuvIfpT"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "ZgljXQA_IfpT",
"outputId": "6a2559f6-d032-4f9b-84f0-480598be0694"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Model says: API connection\n",
"<class 'openai.types.chat.chat_completion.ChatCompletion'>\n"
]
}
],
"source": [
"# ── Sanity Check: Ping the model ─────────────────────────────────────────────\n",
"response = client.chat.completions.create( # response object can give you the final response, unique id for this call, model name, etc\n",
" model=DEFAULT_MODEL, # response.model, response.id, response.choices[0].message.role, etc\n",
" messages=[{\"role\": \"user\", \"content\": \"Say 'API connection successful!' and nothing else.\"}],\n",
" max_tokens=20\n",
")\n",
"print(\"Model says:\", response.choices[0].message.content)\n",
"# print(response.model)\n",
"print(type(response))"
],
"id": "ZgljXQA_IfpT"
},
{
"cell_type": "markdown",
"metadata": {
"id": "qnbKA4hKIfpU"
},
"source": [
"---\n",
"## πŸ“Š Module 1 β€” Foundation Model Comparison Dashboard\n",
"\n",
"### What is a Foundation Model?\n",
"\n",
"A **Foundation Model** (also called a base model or large language model) is a massive neural network pre-trained on billions of tokens of text. Examples include:\n",
"\n",
"- **GPT-4 / GPT-3.5** (OpenAI)\n",
"- **Qwen 2.5** (Alibaba)\n",
"- **LLaMA 3** (Meta)\n",
"- **Mistral** (Mistral AI)\n",
"- **Claude 3** (Anthropic)\n",
"\n",
"### Why Compare Models?\n",
"\n",
"Different models have different strengths:\n",
"- **Speed vs. Quality tradeoff** β€” smaller models are faster but may be less accurate\n",
"- **Instruction following** β€” how well they obey prompts\n",
"- **Creativity** β€” variation in creative writing\n",
"- **Reasoning** β€” logical/mathematical tasks\n",
"\n",
"\n",
"### What We'll Build\n",
"\n",
"A mini **comparison dashboard** that:\n",
"1. Sends the same prompt to a model with different **temperature** settings\n",
"2. Measures **latency** (response time)\n",
"3. Measures **token count**\n",
"4. Visualizes the results\n",
"\n",
"> πŸ’‘ **Temperature** controls randomness. `0` = deterministic/focused. `1` = creative/varied. `>1` = chaotic."
],
"id": "qnbKA4hKIfpU"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "pSLqoQBkIfpU",
"outputId": "153d6cea-2a21-494d-b519-8b0987a8c481"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"βœ… Helper function defined.\n"
]
}
],
"source": [
"# ── Helper: Call model and collect metrics ────────────────────────────────────\n",
"def query_model(prompt, model=DEFAULT_MODEL, temperature=0.7, max_tokens=200, system_prompt=None):\n",
" \"\"\"\n",
" Send a prompt to the model and return:\n",
" - response text\n",
" - latency in seconds\n",
" - token usage stats\n",
" \"\"\"\n",
" messages = []\n",
" if system_prompt:\n",
" messages.append({\"role\": \"system\", \"content\": system_prompt})\n",
" messages.append({\"role\": \"user\", \"content\": prompt})\n",
"\n",
" start = time.time() # saving the starting timestamp\n",
" response = client.chat.completions.create(\n",
" model=model,\n",
" messages=messages,\n",
" temperature=temperature,\n",
" max_tokens=max_tokens\n",
" )\n",
" latency = round(time.time() - start, 2) # saving the latency in secs, rounds upto 2 decimal places.\n",
"\n",
" text = response.choices[0].message.content # saving the response\n",
" usage = response.usage # to return the no.of prompt tokens and the total no. of tokens generated by the model.\n",
"\n",
" return {\n",
" \"response\": text,\n",
" \"latency_sec\": latency,\n",
" \"prompt_tokens\": usage.prompt_tokens, # no. of tokens in your input.\n",
" \"completion_tokens\": usage.completion_tokens, # no. of tokens in your output.\n",
" \"total_tokens\": usage.total_tokens # total no.of tokens in input and output.\n",
" }\n",
"\n",
"print(\"βœ… Helper function defined.\")\n"
],
"id": "pSLqoQBkIfpU"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "xZk97vIJIfpU",
"outputId": "b81e884d-56ef-4971-89a8-7ac025f94c0d"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Prompt: 'Write a one-sentence tagline in English for a futuristic coffee brand.'\n",
"\n",
"======================================================================\n",
"\n",
"🌑️ Temperature = 0.0\n",
" Response : \"Savor the Future, One Sip at a Time.\"\n",
" Latency : 2.34s\n",
" Tokens used: 57\n",
"\n",
"🌑️ Temperature = 0.7\n",
" Response : \"Savor the Future, One Sip at a Time.\"\n",
" Latency : 0.4s\n",
" Tokens used: 57\n",
"\n",
"🌑️ Temperature = 1.4\n",
" Response : \"Savor the Future Brewed from Tradition.\"\n",
" Latency : 0.37s\n",
" Tokens used: 54\n"
]
}
],
"source": [
"# ── Experiment: Temperature Comparison ───────────────────────────────────────\n",
"# We send the same creative prompt at 3 different temperatures.\n",
"# Notice how the output changes!\n",
"\n",
"PROMPT = \"Write a one-sentence tagline in English for a futuristic coffee brand.\"\n",
"temperatures = [0.0, 0.7, 1.4]\n",
"results = []\n",
"\n",
"print(f\"Prompt: '{PROMPT}'\\n\")\n",
"print(\"=\" * 70)\n",
"\n",
"for temp in temperatures:\n",
" print(f\"\\n🌑️ Temperature = {temp}\")\n",
" result = query_model(PROMPT, temperature=temp, max_tokens=80)\n",
" result[\"temperature\"] = temp\n",
" results.append(result)\n",
" print(f\" Response : {result['response'].strip()}\")\n",
" print(f\" Latency : {result['latency_sec']}s\")\n",
" print(f\" Tokens used: {result['total_tokens']}\")"
],
"id": "xZk97vIJIfpU"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 672
},
"id": "hOsuN8o_IfpU",
"outputId": "8d78f4a8-66b9-4d31-e764-f72c46a73055"
},
"outputs": [
{
"output_type": "stream",
"name": "stderr",
"text": [
"/tmp/ipykernel_3339/800803942.py:47: UserWarning: Glyph 9201 (\\N{STOPWATCH}) missing from font(s) DejaVu Sans.\n",
" plt.tight_layout() #auto-fixing the margins in matplotlib\n",
"/tmp/ipykernel_3339/800803942.py:47: UserWarning: Glyph 128290 (\\N{INPUT SYMBOL FOR NUMBERS}) missing from font(s) DejaVu Sans.\n",
" plt.tight_layout() #auto-fixing the margins in matplotlib\n",
"/tmp/ipykernel_3339/800803942.py:47: UserWarning: Glyph 128221 (\\N{MEMO}) missing from font(s) DejaVu Sans.\n",
" plt.tight_layout() #auto-fixing the margins in matplotlib\n",
"/tmp/ipykernel_3339/800803942.py:48: UserWarning: Glyph 9201 (\\N{STOPWATCH}) missing from font(s) DejaVu Sans.\n",
" plt.savefig(\"dashboard.png\", dpi=120, bbox_inches=\"tight\") # saving the plot.\n",
"/tmp/ipykernel_3339/800803942.py:48: UserWarning: Glyph 128290 (\\N{INPUT SYMBOL FOR NUMBERS}) missing from font(s) DejaVu Sans.\n",
" plt.savefig(\"dashboard.png\", dpi=120, bbox_inches=\"tight\") # saving the plot.\n",
"/tmp/ipykernel_3339/800803942.py:48: UserWarning: Glyph 128221 (\\N{MEMO}) missing from font(s) DejaVu Sans.\n",
" plt.savefig(\"dashboard.png\", dpi=120, bbox_inches=\"tight\") # saving the plot.\n",
"/usr/local/lib/python3.12/dist-packages/IPython/core/pylabtools.py:151: UserWarning: Glyph 9201 (\\N{STOPWATCH}) missing from font(s) DejaVu Sans.\n",
" fig.canvas.print_figure(bytes_io, **kw)\n",
"/usr/local/lib/python3.12/dist-packages/IPython/core/pylabtools.py:151: UserWarning: Glyph 128290 (\\N{INPUT SYMBOL FOR NUMBERS}) missing from font(s) DejaVu Sans.\n",
" fig.canvas.print_figure(bytes_io, **kw)\n",
"/usr/local/lib/python3.12/dist-packages/IPython/core/pylabtools.py:151: UserWarning: Glyph 128221 (\\N{MEMO}) missing from font(s) DejaVu Sans.\n",
" fig.canvas.print_figure(bytes_io, **kw)\n"
]
},
{
"output_type": "display_data",
"data": {
"text/plain": [
"<Figure size 1500x400 with 3 Axes>"
],
"image/png": "iVBORw0KGgoAAAANSUhEUgAABdIAAAGMCAYAAADePVAzAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAn5hJREFUeJzs3XdYFFfbBvB7qUtHUIqCqKiIVMWG2AugiA3FFgV7AWssIdFYosHyGtFYolHBWKJiIRp7Ayu+iBqxETUqimIHBKXP94cf87IuIBBgUe7fde11MWfOOfPM7Cyz++zZMxJBEAQQEREREREREREREVG+lBQdABERERERERERERFRRcZEOhERERERERERERFRIZhIJyIiIiIiIiIiIiIqBBPpRERERERERERERESFYCKdiIiIiIiIiIiIiKgQTKQTERERERERERERERWCiXQiIiIiIiIiIiIiokIwkU5EREREREREREREVAgm0omIiIiIiIiIiIiICsFEOhEREVVKISEhkEgk4qM8hIeHy2zzwYMH5bLdL0lpP2+KOA9IHl8b5a+kx7yyvGby7mNISIiiwyEiIqIKgIl0IiIikkuoFPTw9fVVdKgV1peSCPw4SSaRSDBhwoR8665du1au7pw5c8o3YAXLysrC9u3b4e3tjTp16kBbWxtqamowMzODh4cHVq5ciTdv3ig6TFKAWrVqybw2VFRUoKmpCVNTUzRt2hQjR47EqVOnFB0mERERERWRiqIDICIiIqosLC0tsWTJEnHZwMBAgdEUXUhICBYsWAAdHR2Z8hUrVigooorh+vXr6NevH27evCm3Lj4+HvHx8Th48CBevnxZ6b5gKK7P9bVRHNnZ2Xj//j3ev3+PhIQEXLp0CevXr0ebNm2wdetWmJmZKTpEIiIiIioEE+lEREQkp1+/fmjSpIlcua2trQKi+XKYm5tj6tSpig6j2N6+fYvg4GCZkenHjx/PN4FcWdy+fRtt27bF69evxTJbW1u4u7vDwMAAz58/x5kzZxAdHa3AKCu+1NRUaGhofLavjaKqU6cOxo4di/T0dNy/fx9//vknnj17BgA4ffo0WrVqhYsXL8LY2FjBkX75cs85JSX+OJuIiIiKh+8eiIiISI67uzumTp0q93B3d5ep9+bNG8ybNw9NmjSBnp4e1NTUUKNGDfTu3RvHjh2T63fOnDni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r2LMmDGoW7cupFIppFIpLC0tMXLkSFy5ckXu5ocled6ADzcMvHjxItasWYMOHTqgatWqUFZWhpaWFho0aICvvvoKW7duxbRp00pl3xwcHHDt2jVs3boVXl5esLCwgIaGBlRVVVG9enV069YNISEhMr/KqIjPc6tWrXDu3Dm4u7tDR0cHWlpa6Ny5M06fPo02bdqI9erWrYvTp0/D1dUVmpqa0NbWRtu2bXHixAl06tSpTGKrUqUKVq5ciQEDBqBhw4YwMDCAsrIydHV10aRJE/zwww84ceKEzNzwJTnnyoNEIoFUKoWJiYk4TU14eDhOnTolc5NY4MNc72fPnkXv3r2hq6sLDQ0NNG3aFHv27CnwNWBlZYWlS5eid+/eqF+/PvT09KCsrIwqVarAxcUFy5cvx/bt20t9vw4dOoRZs2ahdu3aUFNTQ61atTB79mzs3r1b5v+gsrIydu7cidDQUHTt2hVGRkZQUVGBnp4emjdvjiVLliAqKkrmWJT1Obd161YsWLAAderUgaqqKmrVqoXvvvsOhw4d+td9ExER0ZdHIgiCoOggiIiIiIio/NSqVUucgmn27Nmf/JKIiIiIiKiy44h0IiIiIiIiIiIiIqJCMJFORERERERERERERFQIJtKJiIiIiIiIiIiIiArBOdKJiIiIiIiIiIiIiArBEelERERERERERERERIVgIp2IiIiIiIiIiIiIqBBMpBMRERERERERERERFYKJdCIiIiIiIiIiIiKiQjCRTkRERERERERERERUCCbSiYiIiIiIiIiIiIgKwUQ6EREREREREREREVEhmEgnIiIiIiIiIiIiIioEE+lERERERERERERERIVgIp2IiIiIiIiIiIiIqBBMpBMRERERERERERERFYKJdCIiIiIiIiIiIiKiQjCRTkRERERERERERERUCCbSiYiIiIiIiIiIiIgKwUQ6EREREREREREREVEhmEgnIiIiIiIiIiIiIioEE+lERERERERERERERIVgIp2IiIiIiIiIiIiIqBBMpBMRERERERERERERFYKJdCIiIiIiIiIiIiKiQjCRTkRERERERERERERUCCbSiYgqgEePHkEqleLcuXNFbpOZmQlzc3OsXr26DCMjIiJFaNeuHWxtbRUdBhEREVGhwsPDIZFIsGvXrn/Vz+LFi9GgQQPk5OQUq51EIoG/v/+/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798f/fv3V2BERERElVexEukXLlzAq1ev0K1bNwBAq1atcO/ePSxevBjv3r1Dz5498fPPP0NdXb1MgiUiIqKS69+/P4yMjBQdBhERERVDYmIi/vvf/+L58+fi6PRcQ4YMUVBURERElU+xEunz5s1Du3btxER6TEwMRo4cCR8fH1hbW2PJkiWoXr065syZUxaxEhERUQl9akoXIiIiqnj279+PQYMGISUlBbq6ujLXc4lEwkQ6ERFROSrWzUavXr0qM33L9u3b0axZM/z666+YMmUKVqxYgZ07d5Z6kERERPTvCIKg6BCIiIiomL7++msMGzYMKSkpSExMxJs3b8QH72tCRERUvoo1Iv3NmzcwNjYWlyMiItClSxdxuWnTpnj06FHpRUdERESl4uOfghMREVHFFx8fjwkTJkBTU1PRoRAREVV6xRqRbmxsjPv37wMAMjIycPnyZbRo0UJc//btW6iqqpZuhERERERERESVkJubGy5duqToMIiIiAjFHJHetWtXfPPNN1i0aBHCwsKgqamJ1q1bi+uvXbsGS0vLUg+SiIiIiIiIqDLYt2+f+LeHhwemTZuGmzdvws7OTm7gWvfu3cs7PCIiokqrWIn0H374Ab1790bbtm2hra2NTZs2QU1NTVy/ceNGuLq6lnqQRERERERERJVBz5495crmzZsnVyaRSJCdnV0OERERERFQzER61apVcfr0aSQlJUFbWxvKysoy60NDQ6GtrV2qARIRERERERFVFryvCRERUcVUrDnSc+np6ckl0QHAwMBAZoQ6ERERERERERXPyZMn0bBhQyQnJ8utS0pKgo2NDc6cOaOAyIiIiCqvEiXSiYiIiIiIiKhsBAUFYeTIkdDV1ZVbp6enh9GjR+Onn35SQGRERESVFxPpRERERERERBXIX3/9BXd39wLXu7q6Ijo6uhwjIiIiIibSiYiIiIiIiCqQZ8+eQVVVtcD1KioqePHiRTlGREREREykExEREREREVUgNWrUwPXr1wtcf+3aNZiampZjRERERMREOhEREREREVEF0rVrV8yaNQtpaWly696/f4/Zs2ejW7duCoiMiIio8lJRdABERERERERE9D8zZ87Enj17UL9+ffj7+8PKygoAcPv2baxatQrZ2dn47rvvFBwlERFR5cJEOhEREREREVEFYmxsjPPnz2Ps2LEICAiAIAgA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\n"
},
"metadata": {}
},
{
"output_type": "stream",
"name": "stdout",
"text": [
"\n",
"πŸ“Š Dashboard rendered!\n"
]
}
],
"source": [
"# ── Dashboard Visualization ───────────────────────────────────────────────────\n",
"# results dictionary has the temperature, response, latency, no.of tokens used as its key.\n",
"df = pd.DataFrame(results)\n",
"\n",
"fig, axes = plt.subplots(1, 3, figsize=(15, 4)) #1->no.of rows of charts; 3->no.of columns of charts.\n",
" #fig->whole canvas, axes-> figsize=(15, 4) β†’ size of the entire figure in inches: 15'' width, 4'' tall\n",
"fig.suptitle(\"Foundation Model Comparison Dashboard\", fontsize=14, fontweight=\"bold\") # main title\n",
"\n",
"colors = [\"#4CAF50\", \"#2196F3\", \"#FF5722\"] # color codes written in hexadecimal numbers accepted by matplotlib\n",
"temp_labels = [f\"Temp={t}\" for t in df[\"temperature\"]] # storing each temperature values for further use\n",
"\n",
"\n",
"# axes[i] -> which chart to draw on\n",
"# bar() -> the matplotlib method that creates a vertical bar chart.\n",
"# temp_labels -> what goes on the x-axis (the category for each bar).\n",
"# df[\"latency_sec\"] -> what determines the height of each bar\n",
"# color=colors -> list of colors for the bars.\n",
"# Syntax of bar() -> bar(x-axis label, height of each bar, colors, label, bottom); label and bottom parameters are optional\n",
"\n",
"# Chart 1: Latency\n",
"axes[0].bar(temp_labels, df[\"latency_sec\"], color=colors) # defining the bars\n",
"axes[0].set_title(\"⏱️ Response Latency (s)\") # title of the figure/plot\n",
"axes[0].set_ylabel(\"Seconds\") # y-axis label\n",
"axes[0].set_ylim(0, max(df[\"latency_sec\"]) * 1.4) # y-axis limits(lower_bound, upper_bound)\n",
"for i, v in enumerate(df[\"latency_sec\"]):\n",
" axes[0].text(i, v + 0.02, f\"{v}s\", ha=\"center\", fontsize=10) # this puts labels over each of the bars.\n",
"# Syntax of text() -> text(x_co-ordinate, y_co-ordinate, text to display, horizontal alignment, font size in points)\n",
"\n",
"\n",
"\n",
"# Chart 2: Token Usage (stacked)\n",
"axes[1].bar(temp_labels, df[\"prompt_tokens\"], color=\"#90CAF9\", label=\"Prompt tokens\") # this is the bottom layer\n",
"axes[1].bar(temp_labels, df[\"completion_tokens\"], bottom=df[\"prompt_tokens\"], # this is the top layer\n",
" color=\"#1565C0\", label=\"Completion tokens\") # two bar() to plot two stacked bars\n",
"axes[1].set_title(\"πŸ”’ Token Usage\") # setting the title of the plot\n",
"axes[1].set_ylabel(\"Tokens\") # y-axis label\n",
"axes[1].legend(fontsize=8)\n",
"\n",
"# Chart 3: Response Length (chars)\n",
"char_counts = df[\"response\"].str.len()\n",
"axes[2].bar(temp_labels, char_counts, color=colors) # setting up the bar\n",
"axes[2].set_title(\"πŸ“ Response Length (chars)\") # setting up the title\n",
"axes[2].set_ylabel(\"Characters\") # y-axis label\n",
"for i, v in enumerate(char_counts):\n",
" axes[2].text(i, v + 2, str(v), ha=\"center\", fontsize=10)\n",
"\n",
"plt.tight_layout() #auto-fixing the margins in matplotlib\n",
"plt.savefig(\"dashboard.png\", dpi=120, bbox_inches=\"tight\") # saving the plot.\n",
"plt.show() # showing the final plot/figure\n",
"print(\"\\nπŸ“Š Dashboard rendered!\")"
],
"id": "hOsuN8o_IfpU"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "NffExvkiIfpU",
"outputId": "74d2c29d-6fe3-422e-8b06-6c14a99ae947"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"\n",
"πŸ“‹ Summary Table:\n",
" Temperature Latency (s) Prompt Tokens Completion Tokens Total Tokens\n",
" 0.0 0.44 42 12 54\n",
" 0.7 0.54 42 24 66\n",
" 1.4 0.81 42 49 91\n"
]
}
],
"source": [
"# ── Structured Table View ─────────────────────────────────────────────────────\n",
"display_df = df[[\"temperature\", \"latency_sec\", \"prompt_tokens\", \"completion_tokens\", \"total_tokens\"]].copy() # copying the entire df datatframe into display_df\n",
"display_df.columns = [\"Temperature\", \"Latency (s)\", \"Prompt Tokens\", \"Completion Tokens\", \"Total Tokens\"] # setting up the new names of the new dataframe\n",
"print(\"\\nπŸ“‹ Summary Table:\")\n",
"print(display_df.to_string(index=False)) # 'index=False' hides the row numbers."
],
"id": "NffExvkiIfpU"
},
{
"cell_type": "markdown",
"metadata": {
"id": "tHfZsCfjIfpV"
},
"source": [
"### πŸ§ͺ Exercise 1\n",
"\n",
"**Try it yourself!** Modify the cell below to compare a **factual** prompt (where temperature shouldn't matter much) vs a **creative** prompt (where it matters a lot).\n",
"\n",
"Prompts to try:\n",
"- Factual: `\"What is the capital of France?\"`\n",
"- Creative: `\"Write a haiku about machine learning.\"`\n",
"\n",
"What do you observe?"
],
"id": "tHfZsCfjIfpV"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "ddxDVz9xIfpV",
"outputId": "41acc185-b37e-4f0c-e700-21f8df45f113"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Temp 0.0: The currency of India is the Indian Rupee (INR). It is denoted by the symbol β‚Ή.\n",
"\n",
"Temp 0.7: The currency of India is the Indian Rupee, denoted by the symbol β‚Ή and its code is INR. It is subdivided into 100 smalle\n",
"\n",
"Temp 1.0: The currency of India is the Indian Rupee (INR). It is denoted by the symbol β‚Ή and has the ISO code INR. The rupee is su\n",
"\n"
]
}
],
"source": [
"# ── πŸ§ͺ Your turn! Change the prompt below ────────────────────────────────────\n",
"MY_PROMPT = \"What is the currency of India?\" # ← Change this!\n",
"\n",
"for temp in [0.0, 0.7, 1.0]:\n",
" r = query_model(MY_PROMPT, temperature=temp, max_tokens=100)\n",
" print(f\"Temp {temp}: {r['response'].strip()[:120]}\")\n",
" print()"
],
"id": "ddxDVz9xIfpV"
},
{
"cell_type": "markdown",
"source": [
"Menti-meter"
],
"metadata": {
"id": "xXZ_9y5oZGh5"
},
"id": "xXZ_9y5oZGh5"
},
{
"cell_type": "markdown",
"metadata": {
"id": "L404sv5wIfpV"
},
"source": [
"---\n",
"## πŸ”© Module 2 β€” Function Calling\n",
"\n",
"### What is Function Calling?\n",
"\n",
"**Function calling** (also called *tool use*) lets you define a set of functions to the LLM. Instead of returning plain text, the model can decide to \"call\" one of your functions with structured arguments β€” which you then execute in your code.\n",
"\n",
"```\n",
"User asks β†’ Model decides to call a function β†’ You run the function β†’ Pass result back β†’ Model gives final answer\n",
"```\n",
"\n",
"### Why is this powerful?\n",
"\n",
"| Without Function Calling | With Function Calling |\n",
"|---|---|\n",
"| Parse free-form text (fragile) | Get structured JSON (reliable) |\n",
"| Model hallucinates data | Model calls real data sources |\n",
"| Hard to integrate with apps | Clean API integration |\n",
"\n",
"### Real-World Use Cases\n",
"- 🌀️ Weather chatbots (call a weather API)\n",
"- πŸ“… Calendar assistants (create/read events)\n",
"- πŸ›’ E-commerce bots (search products, check inventory)\n",
"- πŸ“Š Data analysts (run SQL queries)\n",
"- πŸ” Research agents (search the web)\n",
"\n",
"### How it works (OpenAI-compatible schema)\n",
"\n",
"You define tools as JSON schema objects describing the function name, description, and parameters. The model returns a structured `tool_call` you can execute."
],
"id": "L404sv5wIfpV"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "St1WtE5WIfpV",
"outputId": "22987664-9482-42ad-b5aa-2b66ec31974e"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"βœ… Defined 3 tools: ['get_weather', 'calculate_discount', 'search_product']\n"
]
}
],
"source": [
"# ── Step 1: Define our \"tools\" (mock functions) ───────────────────────────────\n",
"# We define 3 fake tools the model can call:\n",
"# 1. get_weather β€” fetch weather for a city\n",
"# 2. calculate_discount β€” apply a discount to a price\n",
"# 3. search_product β€” look up product info\n",
"# This sort of boilerplate for the model to engage in tool usage\n",
"\n",
"\n",
"TOOLS = [\n",
" {\n",
" \"type\": \"function\",\n",
" \"function\": {\n",
" \"name\": \"get_weather\",\n",
" \"description\": \"Get the current weather for a given city.\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"city\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"The name of the city, e.g. 'Mumbai'\"\n",
" },\n",
" \"unit\": {\n",
" \"type\": \"string\",\n",
" \"enum\": [\"celsius\", \"fahrenheit\"],\n",
" \"description\": \"Temperature unit\"\n",
" }\n",
" },\n",
" \"required\": [\"city\"]\n",
" }\n",
" }\n",
" },\n",
" {\n",
" \"type\": \"function\",\n",
" \"function\": {\n",
" \"name\": \"calculate_discount\",\n",
" \"description\": \"Calculate the final price after applying a discount percentage.\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"original_price\": {\n",
" \"type\": \"number\",\n",
" \"description\": \"The original price in USD\"\n",
" },\n",
" \"discount_percent\": {\n",
" \"type\": \"number\",\n",
" \"description\": \"Discount percentage (0-100)\"\n",
" }\n",
" },\n",
" \"required\": [\"original_price\", \"discount_percent\"]\n",
" }\n",
" }\n",
" },\n",
" {\n",
" \"type\": \"function\",\n",
" \"function\": {\n",
" \"name\": \"search_product\",\n",
" \"description\": \"Search for a product and return its details.\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"query\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"The product search query\"\n",
" },\n",
" \"category\": {\n",
" \"type\": \"string\",\n",
" \"enum\": [\"electronics\", \"clothing\", \"food\", \"books\"],\n",
" \"description\": \"Product category to filter by\"\n",
" }\n",
" },\n",
" \"required\": [\"query\"]\n",
" }\n",
" }\n",
" }\n",
"]\n",
"\n",
"print(f\"βœ… Defined {len(TOOLS)} tools: {[t['function']['name'] for t in TOOLS]}\")"
],
"id": "St1WtE5WIfpV"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "3agzmylKIfpV",
"outputId": "a70acf67-05ec-45e3-ce9f-1b24c5c04633"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"βœ… Mock tool implementations ready.\n"
]
}
],
"source": [
"# ── Step 2: Mock implementations of the tools ─────────────────────────────────\n",
"# If the previous cell was the menu, this cell is the kitchen. The TOOLS list told the model what's available;\n",
"# this cell defines what those functions actually do when called.\n",
"# In a real app these would call actual APIs.\n",
"# Here we return fake data to simulate the behavior.\n",
"\n",
"def get_weather(city, unit=\"celsius\"):\n",
" \"\"\"Mock weather API.\"\"\"\n",
" fake_data = {\n",
" \"Mumbai\": {\"temp\": 32, \"condition\": \"Humid and partly cloudy\"},\n",
" \"Delhi\": {\"temp\": 38, \"condition\": \"Hot and sunny\"},\n",
" \"London\": {\"temp\": 14, \"condition\": \"Overcast with light drizzle\"},\n",
" \"New York\": {\"temp\": 22, \"condition\": \"Clear skies\"},\n",
" \"Tokyo\": {\"temp\": 26, \"condition\": \"Partly cloudy\"},\n",
" }\n",
" data = fake_data.get(city, {\"temp\": 20, \"condition\": \"Unknown\"})\n",
" temp = data[\"temp\"]\n",
" if unit == \"fahrenheit\":\n",
" temp = round(temp * 9/5 + 32, 1)\n",
" return {\"city\": city, \"temperature\": temp, \"unit\": unit, \"condition\": data[\"condition\"]}\n",
"\n",
"def calculate_discount(original_price, discount_percent):\n",
" \"\"\"Calculate discounted price.\"\"\"\n",
" savings = round(original_price * discount_percent / 100, 2)\n",
" final = round(original_price - savings, 2)\n",
" return {\"original\": original_price, \"discount_percent\": discount_percent,\n",
" \"savings\": savings, \"final_price\": final}\n",
"\n",
"def search_product(query, category=None):\n",
" \"\"\"Mock product search.\"\"\"\n",
" products = [\n",
" {\"id\": 1, \"name\": f\"{query} Pro Max\", \"price\": 999, \"rating\": 4.7, \"in_stock\": True},\n",
" {\"id\": 2, \"name\": f\"{query} Lite\", \"price\": 499, \"rating\": 4.2, \"in_stock\": True},\n",
" {\"id\": 3, \"name\": f\"{query} Classic\", \"price\": 299, \"rating\": 3.9, \"in_stock\": False},\n",
" ]\n",
" return {\"query\": query, \"category\": category, \"results\": products}\n",
"\n",
"# ── Dispatcher: routes tool names to actual functions ─────────────────────────\n",
"def execute_tool(tool_name, tool_args):\n",
" if tool_name == \"get_weather\":\n",
" return get_weather(**tool_args)\n",
" elif tool_name == \"calculate_discount\":\n",
" return calculate_discount(**tool_args)\n",
" elif tool_name == \"search_product\":\n",
" return search_product(**tool_args)\n",
" else:\n",
" return {\"error\": f\"Unknown tool: {tool_name}\"}\n",
"\n",
"print(\"βœ… Mock tool implementations ready.\")"
],
"id": "3agzmylKIfpV"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "q4WVdYwkIfpW",
"outputId": "cd21578d-b321-45f3-c92a-effd9316074e"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"βœ… Function calling loop ready.\n"
]
}
],
"source": [
"# ── Step 3: Full Function Calling Loop ───────────────────────────────────────\n",
"# This is the complete agentic loop:\n",
"# 1. User sends message\n",
"# 2. Model may call a tool β†’ we execute it\n",
"# 3. We send result back to model\n",
"# 4. Model gives final human-readable answer\n",
"\n",
"def chat_with_tools(user_message, verbose=True):\n",
" \"\"\"\n",
" Full function-calling loop.\n",
" Returns the final response text.\n",
" \"\"\"\n",
" if verbose:\n",
" print(f\"πŸ‘€ User: {user_message}\")\n",
" print(\"-\" * 60)\n",
"\n",
" messages = [{\"role\": \"user\", \"content\": user_message}]\n",
"\n",
" # ── Round 1: Ask the model (with tools available) ──────────────────────\n",
" response = client.chat.completions.create(\n",
" model=DEFAULT_MODEL,\n",
" messages=messages,\n",
" tools=TOOLS,\n",
" tool_choice=\"auto\", # \"auto\" = model decides whether to call a tool\n",
" max_tokens=500\n",
" )\n",
"\n",
" assistant_msg = response.choices[0].message # llm's response\n",
" finish_reason = response.choices[0].finish_reason # finish reason of the reason(e.g: tool call, stop token reached, token limit reached etc)\n",
"\n",
" # ── Did the model call a tool? ──────────────────────────────────────────\n",
" if finish_reason == \"tool_calls\" and assistant_msg.tool_calls: # assistant_msg.tool_calls: defensive check that the list of tool calls actually exists and isn't empty.\n",
" tool_call = assistant_msg.tool_calls[0] # which tool to call, we are using the first one to call, however multiple tools can be called.\n",
" tool_name = tool_call.function.name # name of the tool/function\n",
" tool_args = json.loads(tool_call.function.arguments) # converts json strings to python dictionary\n",
"\n",
" if verbose:\n",
" print(f\"πŸ”§ Model wants to call: {tool_name}\")\n",
" print(f\" Arguments: {json.dumps(tool_args, indent=2)}\")\n",
"\n",
" # ── Execute the tool ───────────────────────────────────────────────\n",
" tool_result = execute_tool(tool_name, tool_args)\n",
"\n",
" if verbose:\n",
" print(f\" Tool result: {json.dumps(tool_result, indent=2)}\")\n",
" print(\"-\" * 60)\n",
"\n",
" # ── Round 2: Send result back to the model ─────────────────────────\n",
" messages.append(assistant_msg) # append model's tool-call message\n",
" messages.append({ # append our tool result\n",
" \"role\": \"tool\",\n",
" \"tool_call_id\": tool_call.id,\n",
" \"content\": json.dumps(tool_result)\n",
" })\n",
"\n",
" final_response = client.chat.completions.create(\n",
" model=DEFAULT_MODEL,\n",
" messages=messages,\n",
" max_tokens=300\n",
" )\n",
" final_text = final_response.choices[0].message.content\n",
"\n",
" else:\n",
" # Model answered directly without calling a tool\n",
" final_text = assistant_msg.content\n",
" if verbose:\n",
" print(\"ℹ️ Model answered directly (no tool call).\")\n",
"\n",
" if verbose:\n",
" print(f\"\\nπŸ€– Final Answer:\\n{final_text}\")\n",
"\n",
" return final_text\n",
"\n",
"print(\"βœ… Function calling loop ready.\")"
],
"id": "q4WVdYwkIfpW"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "st-TcQxlIfpW",
"outputId": "85dd24a6-29b9-4b33-f48c-3010e6e413c2"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"============================================================\n",
"DEMO 1: Weather Query\n",
"============================================================\n",
"πŸ‘€ User: What is the currency of India? \n",
"------------------------------------------------------------\n",
"ℹ️ Model answered directly (no tool call).\n",
"\n",
"πŸ€– Final Answer:\n",
"The primary currency of India is the Indian Rupee (INR). However, I don't have direct information on the current exchange rate or detailed currency usage. If you need any specific details regarding currency conversion or usage, feel free to ask!\n"
]
}
],
"source": [
"# ── Demo 1: Weather Query ─────────────────────────────────────────────────────\n",
"print(\"=\" * 60)\n",
"print(\"DEMO 1: Weather Query\")\n",
"print(\"=\" * 60)\n",
"chat_with_tools(\"What is the currency of India? \");"
],
"id": "st-TcQxlIfpW"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "o23IWu6BIfpW",
"outputId": "9bc43531-85aa-48ff-bde3-eca7dfdc61d5"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"============================================================\n",
"DEMO 2: Discount Calculation\n",
"============================================================\n",
"πŸ‘€ User: I want to buy a laptop that costs $1,200. There's a 35% sale. How much will I save and what's the final price?\n",
"------------------------------------------------------------\n",
"πŸ”§ Model wants to call: calculate_discount\n",
" Arguments: {\n",
" \"original_price\": 1200,\n",
" \"discount_percent\": 35\n",
"}\n",
" Tool result: {\n",
" \"original\": 1200,\n",
" \"discount_percent\": 35,\n",
" \"savings\": 420.0,\n",
" \"final_price\": 780.0\n",
"}\n",
"------------------------------------------------------------\n",
"\n",
"πŸ€– Final Answer:\n",
"With a 35% sale on a laptop that costs $1,200, you will save $420. The final price you will pay for the laptop is $780.\n"
]
}
],
"source": [
"# ── Demo 2: Discount Calculator ───────────────────────────────────────────────\n",
"print(\"=\" * 60)\n",
"print(\"DEMO 2: Discount Calculation\")\n",
"print(\"=\" * 60)\n",
"chat_with_tools(\"I want to buy a laptop that costs $1,200. There's a 35% sale. How much will I save and what's the final price?\");"
],
"id": "o23IWu6BIfpW"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "G_lL-LvnIfpW",
"outputId": "15ab0cb9-4e38-413f-9b1a-041314552e3b"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"============================================================\n",
"DEMO 3: Product Search\n",
"============================================================\n",
"πŸ‘€ User: Can you search for wireless headphones in the electronics category and tell me which ones are available?\n",
"------------------------------------------------------------\n",
"πŸ”§ Model wants to call: search_product\n",
" Arguments: {\n",
" \"query\": \"wireless headphones\",\n",
" \"category\": \"electronics\"\n",
"}\n",
" Tool result: {\n",
" \"query\": \"wireless headphones\",\n",
" \"category\": \"electronics\",\n",
" \"results\": [\n",
" {\n",
" \"id\": 1,\n",
" \"name\": \"wireless headphones Pro Max\",\n",
" \"price\": 999,\n",
" \"rating\": 4.7,\n",
" \"in_stock\": true\n",
" },\n",
" {\n",
" \"id\": 2,\n",
" \"name\": \"wireless headphones Lite\",\n",
" \"price\": 499,\n",
" \"rating\": 4.2,\n",
" \"in_stock\": true\n",
" },\n",
" {\n",
" \"id\": 3,\n",
" \"name\": \"wireless headphones Classic\",\n",
" \"price\": 299,\n",
" \"rating\": 3.9,\n",
" \"in_stock\": false\n",
" }\n",
" ]\n",
"}\n",
"------------------------------------------------------------\n",
"\n",
"πŸ€– Final Answer:\n",
"Based on your request to search for wireless headphones in the electronics category, here are some options available:\n",
"\n",
"1. **Wireless Headphones Pro Max**\n",
" - Price: $999\n",
" - Rating: 4.7 out of 5\n",
" - In Stock: Yes\n",
"\n",
"2. **Wireless Headphones Lite**\n",
" - Price: $499\n",
" - Rating: 4.2 out of 5\n",
" - In Stock: Yes\n",
"\n",
"3. **Wireless Headphones Classic**\n",
" - Price: $299\n",
" - Rating: 3.9 out of 5\n",
" - In Stock: No\n",
"\n",
"These models vary in price and features, with the Pro Max being the most expensive and likely offering the best performance, while the Classic model is currently out of stock.\n"
]
}
],
"source": [
"# ── Demo 3: Product Search ────────────────────────────────────────────────────\n",
"print(\"=\" * 60)\n",
"print(\"DEMO 3: Product Search\")\n",
"print(\"=\" * 60)\n",
"chat_with_tools(\"Can you search for wireless headphones in the electronics category and tell me which ones are available?\");"
],
"id": "G_lL-LvnIfpW"
},
{
"cell_type": "markdown",
"metadata": {
"id": "U-jqVf8wIfpW"
},
"source": [
"### πŸ§ͺ Exercise 2\n",
"\n",
"**Try it yourself!** Ask the model a question that would trigger one of the tools.\n",
"\n",
"Ideas:\n",
"- `\"What's the temperature in Tokyo in fahrenheit?\"`\n",
"- `\"A shirt costs $80 and there's a 20% discount. What do I pay?\"`\n",
"- `\"Find me some books about Python programming.\"`\n",
"\n",
"**Bonus:** What happens if you ask something that doesn't require a tool?"
],
"id": "U-jqVf8wIfpW"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "f-FxweweIfpW",
"outputId": "7a3fb9d6-dfa2-4390-e40f-d6a33ca8ace5"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"πŸ‘€ User: Who is the current president of India? \n",
"------------------------------------------------------------\n",
"ℹ️ Model answered directly (no tool call).\n",
"\n",
"πŸ€– Final Answer:\n",
"I don't have direct access to real-time data or current news updates, so I can't provide the current president of India. However, as of my last update, the president of India was Ram Nath Kovind. For the most recent information, you might want to check a reliable news source or the official website of the Government of India. Is there any other information you need?\n"
]
}
],
"source": [
"# ── πŸ§ͺ Your turn! ─────────────────────────────────────────────────────────────\n",
"MY_QUESTION = \"Who is the current president of India? \" # ← Change this!\n",
"\n",
"chat_with_tools(MY_QUESTION);"
],
"id": "f-FxweweIfpW"
},
{
"cell_type": "markdown",
"source": [
"Menti-meter"
],
"metadata": {
"id": "WbTFwCeoZRxy"
},
"id": "WbTFwCeoZRxy"
},
{
"cell_type": "markdown",
"metadata": {
"id": "cgzZraBgIfpW"
},
"source": [
"---\n",
"## πŸ“š Module 3 β€” Prompt Library\n",
"\n",
"### What is a Prompt Library?\n",
"\n",
"A **Prompt Library** is a curated collection of reusable prompt templates β€” organized by task type β€” that you can quickly apply to different inputs.\n",
"\n",
"Think of it like a **recipe book for LLMs**: instead of writing a new prompt every time, you have battle-tested templates ready to go.\n",
"\n",
"### Core Prompting Techniques Covered\n",
"\n",
"| Technique | Description |\n",
"|---|---|\n",
"| **Zero-shot** | Ask the model with no examples |\n",
"| **Few-shot** | Provide examples before the task |\n",
"| **Chain-of-Thought (CoT)** | Ask the model to reason step-by-step |\n",
"| **Role Prompting** | Assign a persona or role to the model |\n",
"| **Output Formatting** | Request specific formats (JSON, bullet points, etc.) |\n",
"| **Self-Consistency** | Run the same prompt multiple times and vote |\n",
"\n",
"### Prompt Anatomy\n",
"\n",
"A well-crafted prompt typically has:\n",
"```\n",
"[ROLE] β†’ \"You are an expert data scientist...\"\n",
"[CONTEXT] β†’ \"Here is a dataset summary...\"\n",
"[INSTRUCTION] β†’ \"Analyze and summarize key trends...\"\n",
"[FORMAT] β†’ \"Respond in bullet points with max 5 items.\"\n",
"[INPUT] β†’ The actual user data/question\n",
"```"
],
"id": "cgzZraBgIfpW"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "lTT1XmAPIfpX",
"outputId": "9e3ad82d-925a-4094-ee37-4b6458a00559"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"βœ… Prompt Library initialized with 6 techniques.\n"
]
}
],
"source": [
"# ── Prompt Library Class ──────────────────────────────────────────────────────\n",
"class PromptLibrary:\n",
" \"\"\"\n",
" A reusable library of prompt templates.\n",
" Each method returns a dict with:\n",
" - system_prompt (optional role/persona)\n",
" - user_prompt (the filled-in template)\n",
" - technique (name of the prompting technique)\n",
" \"\"\"\n",
"\n",
" # ── 1. Zero-Shot ──────────────────────────────────────────────────────────\n",
" @staticmethod\n",
" def zero_shot_classify(text, categories):\n",
" \"\"\"Classify text with no examples.\"\"\"\n",
" cats = \", \".join(categories)\n",
" return {\n",
" \"technique\": \"Zero-Shot Classification\",\n",
" \"system_prompt\": \"You are a precise text classifier. Always respond with only the category name.\",\n",
" \"user_prompt\": f\"Classify the following text into one of these categories: {cats}.\\n\\nText: \\\"{text}\\\"\\n\\nCategory:\"\n",
" }\n",
"\n",
" # ── 2. Few-Shot ───────────────────────────────────────────────────────────\n",
" @staticmethod\n",
" def few_shot_sentiment(text):\n",
" \"\"\"Sentiment analysis with examples.\"\"\"\n",
" return {\n",
" \"technique\": \"Few-Shot Sentiment Analysis\",\n",
" \"system_prompt\": \"You are a sentiment analysis expert.\",\n",
" \"user_prompt\": textwrap.dedent(f\"\"\"\n",
" Classify the sentiment of each text as Positive, Negative, or Neutral.\n",
"\n",
" Examples:\n",
" Text: \"The product arrived on time and works perfectly!\"\n",
" Sentiment: Positive\n",
"\n",
" Text: \"Terrible experience. The package was damaged and support was unhelpful.\"\n",
" Sentiment: Negative\n",
"\n",
" Text: \"The item was delivered on Tuesday.\"\n",
" Sentiment: Neutral\n",
"\n",
" Now classify:\n",
" Text: \"{text}\"\n",
" Sentiment:\"\"\"\n",
" ).strip()\n",
" }\n",
"\n",
" # ── 3. Chain-of-Thought ───────────────────────────────────────────────────\n",
" @staticmethod\n",
" def chain_of_thought(problem):\n",
" \"\"\"Solve a problem step-by-step.\"\"\"\n",
" return {\n",
" \"technique\": \"Chain-of-Thought Reasoning\",\n",
" \"system_prompt\": \"You are a careful reasoner. Always think step by step before giving your final answer.\",\n",
" \"user_prompt\": f\"{problem}\\n\\nLet's think through this step by step:\"\n",
" }\n",
"\n",
" # ── 4. Role Prompting ─────────────────────────────────────────────────────\n",
" @staticmethod\n",
" def role_prompt(role, task, context=\"\"):\n",
" \"\"\"Assign a role to the model.\"\"\"\n",
" return {\n",
" \"technique\": f\"Role Prompting ({role})\",\n",
" \"system_prompt\": f\"You are {role}. You speak with domain expertise, use precise terminology, and give actionable advice.\",\n",
" \"user_prompt\": (f\"Context: {context}\\n\\n\" if context else \"\") + task\n",
" }\n",
"\n",
" # ── 5. Structured Output (JSON) ───────────────────────────────────────────\n",
" @staticmethod\n",
" def extract_to_json(text, schema_description):\n",
" \"\"\"Extract information as structured JSON.\"\"\"\n",
" return {\n",
" \"technique\": \"Structured JSON Extraction\",\n",
" \"system_prompt\": \"You are a data extraction specialist. You always respond with valid JSON only, no markdown, no explanation.\",\n",
" \"user_prompt\": f\"Extract the following from the text below.\\nSchema: {schema_description}\\n\\nText:\\n{text}\\n\\nJSON:\"\n",
" }\n",
"\n",
" # ── 6. Summarization with constraints ─────────────────────────────────────\n",
" @staticmethod\n",
" def constrained_summary(text, max_words=50, audience=\"general public\"):\n",
" \"\"\"Summarize with specific constraints.\"\"\"\n",
" return {\n",
" \"technique\": \"Constrained Summarization\",\n",
" \"system_prompt\": f\"You are a professional writer. Tailor all content for a {audience}.\",\n",
" \"user_prompt\": f\"Summarize the following text in at most {max_words} words. Be concise and retain the key message.\\n\\nText:\\n{text}\\n\\nSummary:\"\n",
" }\n",
"\n",
"\n",
"# ── Helper to run any library prompt ─────────────────────────────────────────\n",
"def run_prompt(prompt_dict, temperature=0.3, max_tokens=300):\n",
" \"\"\"Execute a prompt from the library and return the response.\"\"\"\n",
" result = query_model(\n",
" prompt=prompt_dict[\"user_prompt\"],\n",
" system_prompt=prompt_dict.get(\"system_prompt\"),\n",
" temperature=temperature,\n",
" max_tokens=max_tokens\n",
" )\n",
" return result[\"response\"].strip()\n",
"\n",
"\n",
"lib = PromptLibrary() # lib object of PromptLibrary() class\n",
"print(\"βœ… Prompt Library initialized with 6 techniques.\")"
],
"id": "lTT1XmAPIfpX"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "sd_iLM-3IfpX",
"outputId": "faf7e010-0d24-40d5-9c2f-e4e6af750acb"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"πŸ“Œ TECHNIQUE: Zero-Shot Classification\n",
"--------------------------------------------------\n",
"Text : The GPU temperature is hitting 95Β°C under load. Performance is th...\n",
"Category: Technical Issue\n",
"\n",
"Text : I want to cancel my subscription and get a refund.\n",
"Category: Billing\n",
"\n",
"Text : How do I reset my password?\n",
"Category: General Inquiry\n",
"\n"
]
}
],
"source": [
"# ── Demo: Zero-Shot Classification ───────────────────────────────────────────\n",
"print(\"πŸ“Œ TECHNIQUE: Zero-Shot Classification\")\n",
"print(\"-\" * 50)\n",
"\n",
"texts = [\n",
" \"The GPU temperature is hitting 95Β°C under load. Performance is throttling.\",\n",
" \"I want to cancel my subscription and get a refund.\",\n",
" \"How do I reset my password?\"\n",
"]\n",
"categories = [\"Technical Issue\", \"Billing\", \"Account Management\", \"General Inquiry\"]\n",
"\n",
"for t in texts:\n",
" p = lib.zero_shot_classify(t, categories) # returns the specific format of prompt to be given to the llm for inference\n",
" answer = run_prompt(p, max_tokens=20) # llm inference is being done using 'run_prompt'\n",
" print(f\"Text : {t[:65]}...\" if len(t) > 65 else f\"Text : {t}\")\n",
" print(f\"Category: {answer}\\n\")"
],
"id": "sd_iLM-3IfpX"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 391
},
"id": "8zf4Me8xIfpX",
"outputId": "95c4e8df-4fcc-4460-b8c3-4087b1758239"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"πŸ“Œ TECHNIQUE: Few-Shot Sentiment\n",
"--------------------------------------------------\n"
]
},
{
"output_type": "error",
"ename": "RateLimitError",
"evalue": "Error code: 429 - {'detail': 'Rate limit exceeded (20/min)'}",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mRateLimitError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m/tmp/ipykernel_3339/929835472.py\u001b[0m in \u001b[0;36m<cell line: 0>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mreview\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mreviews\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0mp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlib\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfew_shot_sentiment\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreview\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# returns the specific format of prompt to be given to the llm for inference\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 13\u001b[0;31m \u001b[0msentiment\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrun_prompt\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mp\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmax_tokens\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# llm inference is being done using 'run_prompt'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 14\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"Review : {review}\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"Sentiment: {sentiment}\\n\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/tmp/ipykernel_3339/3181267732.py\u001b[0m in \u001b[0;36mrun_prompt\u001b[0;34m(prompt_dict, temperature, max_tokens)\u001b[0m\n\u001b[1;32m 90\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mrun_prompt\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprompt_dict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtemperature\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0.3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmax_tokens\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m300\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 91\u001b[0m \u001b[0;34m\"\"\"Execute a prompt from the library and return the response.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 92\u001b[0;31m result = query_model(\n\u001b[0m\u001b[1;32m 93\u001b[0m \u001b[0mprompt\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mprompt_dict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"user_prompt\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 94\u001b[0m \u001b[0msystem_prompt\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mprompt_dict\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"system_prompt\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/tmp/ipykernel_3339/1937963318.py\u001b[0m in \u001b[0;36mquery_model\u001b[0;34m(prompt, model, temperature, max_tokens, system_prompt)\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0mstart\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtime\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# saving the starting timestamp\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m response = client.chat.completions.create(\n\u001b[0m\u001b[1;32m 16\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0mmessages\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmessages\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/usr/local/lib/python3.12/dist-packages/openai/_utils/_utils.py\u001b[0m in \u001b[0;36mwrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 285\u001b[0m \u001b[0mmsg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34mf\"Missing required argument: {quote(missing[0])}\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 286\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 287\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 288\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 289\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mwrapper\u001b[0m \u001b[0;31m# type: ignore\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/usr/local/lib/python3.12/dist-packages/openai/resources/chat/completions/completions.py\u001b[0m in \u001b[0;36mcreate\u001b[0;34m(self, messages, model, audio, frequency_penalty, function_call, functions, logit_bias, logprobs, max_completion_tokens, max_tokens, metadata, modalities, n, parallel_tool_calls, prediction, presence_penalty, prompt_cache_key, prompt_cache_retention, reasoning_effort, response_format, safety_identifier, seed, service_tier, stop, store, stream, stream_options, temperature, tool_choice, tools, top_logprobs, top_p, user, verbosity, web_search_options, extra_headers, extra_query, extra_body, timeout)\u001b[0m\n\u001b[1;32m 1209\u001b[0m ) -> ChatCompletion | Stream[ChatCompletionChunk]:\n\u001b[1;32m 1210\u001b[0m \u001b[0mvalidate_response_format\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresponse_format\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1211\u001b[0;31m return self._post(\n\u001b[0m\u001b[1;32m 1212\u001b[0m \u001b[0;34m\"/chat/completions\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1213\u001b[0m body=maybe_transform(\n",
"\u001b[0;32m/usr/local/lib/python3.12/dist-packages/openai/_base_client.py\u001b[0m in \u001b[0;36mpost\u001b[0;34m(self, path, cast_to, body, content, options, files, stream, stream_cls)\u001b[0m\n\u001b[1;32m 1312\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"post\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mpath\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mjson_data\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mbody\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcontent\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcontent\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfiles\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mto_httpx_files\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfiles\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1313\u001b[0m )\n\u001b[0;32m-> 1314\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mcast\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mResponseT\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcast_to\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mopts\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstream\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mstream\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstream_cls\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mstream_cls\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1315\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1316\u001b[0m def patch(\n",
"\u001b[0;32m/usr/local/lib/python3.12/dist-packages/openai/_base_client.py\u001b[0m in \u001b[0;36mrequest\u001b[0;34m(self, cast_to, options, stream, stream_cls)\u001b[0m\n\u001b[1;32m 1085\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1086\u001b[0m \u001b[0mlog\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Re-raising status error\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1087\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_make_status_error_from_response\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0merr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresponse\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1088\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1089\u001b[0m \u001b[0;32mbreak\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mRateLimitError\u001b[0m: Error code: 429 - {'detail': 'Rate limit exceeded (20/min)'}"
]
}
],
"source": [
"# ── Demo: Few-Shot Sentiment Analysis ────────────────────────────────────────\n",
"print(\"πŸ“Œ TECHNIQUE: Few-Shot Sentiment\")\n",
"print(\"-\" * 50)\n",
"\n",
"reviews = [\n",
" \"Absolutely love this! Best purchase I've made all year.\",\n",
" \"The battery died after 2 days. Very disappointed.\",\n",
" \"Standard product. Nothing special, nothing bad.\"\n",
"]\n",
"\n",
"for review in reviews:\n",
" p = lib.few_shot_sentiment(review) # returns the specific format of prompt to be given to the llm for inference\n",
" sentiment = run_prompt(p, max_tokens=10) # llm inference is being done using 'run_prompt'\n",
" print(f\"Review : {review}\")\n",
" print(f\"Sentiment: {sentiment}\\n\")"
],
"id": "8zf4Me8xIfpX"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "raAJPj8mIfpX",
"outputId": "c71d42a6-55bd-4a9b-c991-8935eebeaf96"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"πŸ“Œ TECHNIQUE: Chain-of-Thought Reasoning\n",
"--------------------------------------------------\n",
"Problem:\n",
"A train leaves city A at 9:00 AM traveling at 80 km/h.\n",
"Another train leaves city B at 10:00 AM traveling at 100 km/h toward city A.\n",
"The two cities are 420 km apart. At what time do the trains meet?\n",
"\n",
"Model's reasoning:\n",
"Sure, let's solve this problem step by step.\n",
"\n",
"### Step 1: Determine the distance covered by the first train before the second train starts\n",
"- The first train leaves at 9:00 AM and travels at 80 km/h.\n",
"- The second train leaves at 10:00 AM, so it starts 1 hour later.\n",
"- In that 1 hour, the first train covers \\(80 \\text{ km/h} \\times 1 \\text{ h} = 80 \\text{ km}\\).\n",
"\n",
"So, when the second train starts at 10:00 AM, the remaining distance between the two trains is:\n",
"\\[ 420 \\text{ km} - 80 \\text{ km} = 340 \\text{ km} \\]\n",
"\n",
"### Step 2: Calculate the relative speed of the two trains\n",
"- The first train travels at 80 km/h.\n",
"- The second train travels at 100 km/h.\n",
"- Since they are moving towards each other, their relative speed is:\n",
"\\[ 80 \\text{ km/h} + 100 \\text{ km/h} = 180 \\text{ km/h} \\]\n",
"\n",
"### Step 3: Determine the time it takes for the trains to meet after the second train starts\n",
"- They need to cover the remaining 340 km at a relative speed of 180 km/h.\n",
"- The time taken to cover this distance is:\n",
"\\[ \\frac{340 \\text{ km}}{180 \\text{ km/h}} = \\frac{340}{180} \\text{ h} = \\frac{17}{9} \\text{ h} \\approx 1.8889 \\text{ h} \\]\n",
"\n",
"### Step 4: Convert the time into hours and minutes\n",
"- \\(1.8889\n"
]
}
],
"source": [
"# ── Demo: Chain-of-Thought ────────────────────────────────────────────────────\n",
"print(\"πŸ“Œ TECHNIQUE: Chain-of-Thought Reasoning\")\n",
"print(\"-\" * 50)\n",
"\n",
"problem = \"\"\"A train leaves city A at 9:00 AM traveling at 80 km/h.\n",
"Another train leaves city B at 10:00 AM traveling at 100 km/h toward city A.\n",
"The two cities are 420 km apart. At what time do the trains meet?\"\"\"\n",
"\n",
"p = lib.chain_of_thought(problem) # returns the specific format of prompt to be given to the llm for inference\n",
"print(f\"Problem:\\n{problem}\\n\")\n",
"answer = run_prompt(p, temperature=0.1, max_tokens=400) # llm inference is being done using 'run_prompt'\n",
"print(f\"Model's reasoning:\\n{answer}\")"
],
"id": "raAJPj8mIfpX"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "SwTsuEokIfpY",
"outputId": "70efd46b-311b-45b9-9d9e-89cdca2e1424"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"πŸ“Œ TECHNIQUE: Role Prompting\n",
"--------------------------------------------------\n",
"\n",
"🎭 Role: a senior cybersecurity engineer\n",
" Task: What are the top 3 risks of storing API keys in client-side JavaScript?\n",
" Answer: Storing API keys in client-side JavaScript poses several significant security risks. Here are the top three:\n",
"\n",
"1. **Exposure via Network Traffic**:\n",
" - **Risk Description**: When JavaScript is executed on a client's browser, it sends requests to servers that include the API key as part of the request headers or query parameters. If these requests are intercepted during transmission (e.g., through man-in-the-middle attacks or by sniffing network traffic), the API key can be easily extracted.\n",
" - **Mitigation**: Use HTTPS to encrypt all communications between the client and server. Ensure that all sensitive data, including API keys, is transmitted over secure channels. Additionally, consider using token-based authentication instead of directly passing API keys.\n",
"\n",
"2. **Client\n",
"\n",
"🎭 Role: a Michelin-star chef\n",
" Task: How do you properly caramelize onions? What's the most common mistake?\n",
" Answer: Caramelizing onions is a fundamental technique in cooking that can elevate many dishes. Here’s how to do it properly:\n",
"\n",
"1. **Choose the Right Onions**: For caramelization, yellow or red onions work best because they have a higher sugar content and a more robust flavor. White onions are not ideal for this process as they lack the necessary sweetness.\n",
"\n",
"2. **Prepare the Onions**: Peel the onions and slice them into thin half-moons. This ensures even cooking and allows for maximum surface area exposure to the heat.\n",
"\n",
"3. **Use the Right Pan**: A heavy-bottomed pan or Dutch oven works well. It distributes heat evenly and helps achieve a consistent caramelization.\n",
"\n",
"4. **Start with High Heat**: Heat your pan over medium\n",
"\n",
"🎭 Role: a venture capital investor\n",
" Task: What makes an AI startup pitch compelling in 2025?\n",
" Answer: In 2025, a compelling AI startup pitch should be well-structured, data-driven, and focused on solving real-world problems with innovative technology. Here’s a breakdown of key elements that can make your pitch stand out:\n",
"\n",
"### 1. **Problem-Solving Focus**\n",
" - **Identify a Clear Problem:** Clearly articulate the problem your AI solution is addressing. Ensure it is a significant issue that affects a large number of people or businesses.\n",
" - **Market Size:** Provide data on the market size and growth potential. Highlight how your solution fits into existing market trends.\n",
"\n",
"### 2. **Technology and Innovation**\n",
" - **Unique Technology:** Explain the unique aspects of your AI technology. This could include proprietary algorithms, novel data\n"
]
}
],
"source": [
"# ── Demo: Role Prompting ──────────────────────────────────────────────────────\n",
"print(\"πŸ“Œ TECHNIQUE: Role Prompting\")\n",
"print(\"-\" * 50)\n",
"\n",
"roles_and_tasks = [\n",
" (\"a senior cybersecurity engineer\", \"What are the top 3 risks of storing API keys in client-side JavaScript?\"),\n",
" (\"a Michelin-star chef\", \"How do you properly caramelize onions? What's the most common mistake?\"),\n",
" (\"a venture capital investor\", \"What makes an AI startup pitch compelling in 2025?\"),\n",
"]\n",
"\n",
"for role, task in roles_and_tasks:\n",
" print(f\"\\n🎭 Role: {role}\")\n",
" print(f\" Task: {task}\")\n",
" p = lib.role_prompt(role, task) # returns the specific format of prompt to be given to the llm for inference\n",
" answer = run_prompt(p, max_tokens=150) # llm inference is being done using 'run_prompt'\n",
" print(f\" Answer: {answer}\")"
],
"id": "SwTsuEokIfpY"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "MYarGHTwIfpY",
"outputId": "af806130-402c-4c44-b2f0-5f92ff489106"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"πŸ“Œ TECHNIQUE: Structured JSON Extraction\n",
"--------------------------------------------------\n",
"Raw text input (invoice):\n",
"\n",
"Invoice #INV-20251105 from TechSupplies India Pvt. Ltd.\n",
"Date: 5 November 2025\n",
"Bill to: Ramesh Kumar, 14 MG Road, Bengaluru 560001\n",
"Items:\n",
" - 3x USB-C Hub @ β‚Ή1,200 each = β‚Ή3,600\n",
" - 1x Mechanical Keyboard @ β‚Ή4,500 = β‚Ή4,500\n",
"Subtotal: β‚Ή8,100. GST 18%: β‚Ή1,458. Total: β‚Ή9,558.\n",
"Payment due: 20 November 2025\n",
"\n",
"\n",
"Extracted JSON:\n",
"{\n",
" \"invoice_number\": \"INV-20251105\",\n",
" \"date\": \"2025-11-05\",\n",
" \"customer_name\": \"Ramesh Kumar\",\n",
" \"items\": [\n",
" {\n",
" \"name\": \"USB-C Hub\",\n",
" \"quantity\": 3,\n",
" \"unit_price\": 1200\n",
" },\n",
" {\n",
" \"name\": \"Mechanical Keyboard\",\n",
" \"quantity\": 1,\n",
" \"unit_price\": 4500\n",
" }\n",
" ],\n",
" \"total_amount\": 9558,\n",
" \"due_date\": \"2025-11-20\"\n",
"}\n"
]
}
],
"source": [
"# ── Demo: JSON Extraction ─────────────────────────────────────────────────────\n",
"print(\"πŸ“Œ TECHNIQUE: Structured JSON Extraction\")\n",
"print(\"-\" * 50)\n",
"\n",
"raw_text = \"\"\"\n",
"Invoice #INV-20251105 from TechSupplies India Pvt. Ltd.\n",
"Date: 5 November 2025\n",
"Bill to: Ramesh Kumar, 14 MG Road, Bengaluru 560001\n",
"Items:\n",
" - 3x USB-C Hub @ β‚Ή1,200 each = β‚Ή3,600\n",
" - 1x Mechanical Keyboard @ β‚Ή4,500 = β‚Ή4,500\n",
"Subtotal: β‚Ή8,100. GST 18%: β‚Ή1,458. Total: β‚Ή9,558.\n",
"Payment due: 20 November 2025\n",
"\"\"\"\n",
"\n",
"# JSON format\n",
"schema = \"\"\"\n",
"{\n",
" \"invoice_number\": string,\n",
" \"date\": string (YYYY-MM-DD),\n",
" \"customer_name\": string,\n",
" \"items\": [{\"name\": string, \"quantity\": number, \"unit_price\": number}],\n",
" \"total_amount\": number,\n",
" \"due_date\": string (YYYY-MM-DD)\n",
"}\n",
"\"\"\"\n",
"\n",
"p = lib.extract_to_json(raw_text, schema) # returns the specific format of prompt to be given to the llm for inference\n",
"raw_json = run_prompt(p, temperature=0.0, max_tokens=400) # llm inference is being done using 'run_prompt'\n",
"\n",
"print(\"Raw text input (invoice):\")\n",
"print(raw_text)\n",
"print(\"\\nExtracted JSON:\")\n",
"try:\n",
" parsed = json.loads(raw_json)\n",
" print(json.dumps(parsed, indent=2))\n",
"except json.JSONDecodeError:\n",
" print(raw_json) # print raw if not parseable"
],
"id": "MYarGHTwIfpY"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "73w-ySmsIfpY",
"outputId": "0a415ca0-7501-42ac-a2b7-2b7f90365c2d"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"πŸ“Œ TECHNIQUE: Constrained Summarization\n",
"--------------------------------------------------\n",
"\n",
"🎯 Audience: a 10-year-old child (max 40 words)\n",
" Large Language Models can do many things like answer questions and write code, but they sometimes make mistakes and can be biased. People are working on making them better and more reliable.\n",
"\n",
"🎯 Audience: a business executive (max 50 words)\n",
" Large Language Models (LLMs) have advanced AI by generating coherent text and engaging in complex tasks but face challenges like hallucination, bias, and alignment. Techniques such as RLHF, Constitutional AI, and DPO are improving their reliability. Function calling and RAG are enhancing LLMs for real-world applications.\n",
"\n",
"🎯 Audience: an AI researcher (max 60 words)\n",
" Large Language Models (LLMs) have advanced AI by learning complex language patterns from extensive text data. Key challenges include hallucination, bias, high computational costs, and alignment issues. Techniques like RLHF, Constitutional AI, and DPO are addressing these. Function calling and RAG are enabling LLMs to perform real-world tasks autonomously.\n"
]
}
],
"source": [
"# ── Demo: Constrained Summarization ──────────────────────────────────────────\n",
"print(\"πŸ“Œ TECHNIQUE: Constrained Summarization\")\n",
"print(\"-\" * 50)\n",
"\n",
"long_text = \"\"\"\n",
"Large Language Models (LLMs) represent a significant leap in artificial intelligence,\n",
"having been trained on vast corpora of text data spanning books, websites, academic papers,\n",
"and code repositories. These models learn complex statistical patterns in language,\n",
"enabling them to generate coherent text, answer questions, translate languages, write code,\n",
"summarize documents, and engage in nuanced conversation.\n",
"Key challenges in deploying LLMs include hallucination (generating plausible but factually\n",
"incorrect information), bias inherited from training data, computational costs, and the\n",
"difficulty of aligning model behavior with human intentions β€” a problem known as alignment.\n",
"Techniques like Reinforcement Learning from Human Feedback (RLHF), Constitutional AI, and\n",
"Direct Preference Optimization (DPO) aim to make models more helpful, harmless, and honest.\n",
"Function calling, RAG (Retrieval-Augmented Generation), and agent frameworks are extending\n",
"LLMs into autonomous systems capable of taking real-world actions.\n",
"\"\"\"\n",
"\n",
"audiences = [\n",
" (\"a 10-year-old child\", 40),\n",
" (\"a business executive\", 50),\n",
" (\"an AI researcher\", 60),\n",
"]\n",
"\n",
"for audience, max_words in audiences:\n",
" p = lib.constrained_summary(long_text, max_words=max_words, audience=audience) # returns the specific format of prompt to be given to the llm for inference\n",
" summary = run_prompt(p, max_tokens=150) # llm inference is being done using 'run_prompt'\n",
" print(f\"\\n🎯 Audience: {audience} (max {max_words} words)\")\n",
" print(f\" {summary}\")"
],
"id": "73w-ySmsIfpY"
},
{
"cell_type": "markdown",
"metadata": {
"id": "s5hjFzk8IfpY"
},
"source": [
"### πŸ§ͺ Exercise 3\n",
"\n",
"**Try it yourself!** Use the Prompt Library to solve a real task.\n",
"\n",
"1. Write a new customer review and classify its sentiment\n",
"2. Extract structured info from a fake receipt or email\n",
"3. Use role prompting β€” pick an expert and ask something from your domain\n",
"\n",
"You can also **add a new template** to the `PromptLibrary` class above!"
],
"id": "s5hjFzk8IfpY"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "TwBnyqkIIfpY",
"outputId": "c03ef52c-903c-4943-df31-360ce8c2b290"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Sentiment: Sentiment: Mixed, but leaning towards Positive\n",
"\n",
"Python Expert says:\n",
"Managing secrets securely is crucial for the security of any application. Here are three best practices for managing secrets in Python applications:\n",
"\n",
"1. **Use Environment Variables or Configuration Files**:\n",
" - **Environment Variables**: Store sensitive information such as API keys, database passwords, and other secrets in environment variables. This approach keeps your code clean and separates configuration from implementation.\n",
" - **Configuration Files**: Use configuration files (e.g., `.env` files) to store secrets. Tools like `python-dotenv` can load these files into environment variables at runtime. Ensure that these files are not committed to version control by adding them to `.gitignore`.\n",
"\n",
"2. **Encrypt Secrets Before Storing Them**:\n",
" - If you need to store secrets in a database or another persistent storage, encrypt them before storing and decrypt them when needed. Libraries like `cryptography` provide tools for symmetric encryption, which can be used to secure your secrets.\n",
" - For example, you can encrypt a secret using a symmetric key\n"
]
}
],
"source": [
"# ── πŸ§ͺ Your turn! Try any prompt template ─────────────────────────────────────\n",
"\n",
"# Option A: Sentiment\n",
"my_review = \"The delivery was slow but the product quality exceeded my expectations!\"\n",
"p = lib.few_shot_sentiment(my_review)\n",
"print(\"Sentiment:\", run_prompt(p, max_tokens=10))\n",
"\n",
"# Option B: Role Prompting\n",
"p2 = lib.role_prompt(\n",
" role=\"an experienced Python developer\",\n",
" task=\"What are 3 best practices for managing secrets in Python applications?\"\n",
")\n",
"print(\"\\nPython Expert says:\")\n",
"print(run_prompt(p2, max_tokens=200))"
],
"id": "TwBnyqkIIfpY"
},
{
"cell_type": "markdown",
"metadata": {
"id": "5GYMGme-IfpZ"
},
"source": [
"---\n",
"## 🏁 Workshop Summary\n",
"\n",
"Congratulations! You've completed the **LLM Prompting Hands-On Workshop**. Here's what you learned:\n",
"\n",
"### Module 1 β€” Foundation Model Comparison\n",
"- How to measure **latency**, **token usage**, and **response quality**\n",
"- The effect of **temperature** on model creativity vs. determinism\n",
"- How to build a visual **comparison dashboard**\n",
"\n",
"### Module 2 β€” Function Calling\n",
"- How to define **tools** using JSON Schema\n",
"- How to implement the **agentic loop** (model β†’ tool β†’ result β†’ model)\n",
"- Why function calling enables **reliable, structured integrations**\n",
"\n",
"### Module 3 β€” Prompt Library\n",
"- Six core prompting techniques: **Zero-shot, Few-shot, CoT, Role, JSON extraction, Constrained summarization**\n",
"- How to structure prompts with **role + context + instruction + format**\n",
"- How to build a **reusable prompt library** for your projects\n",
"\n",
"---\n",
"\n",
"### πŸ“– Further Reading\n",
"\n",
"- [Prompt Engineering Guide](https://www.promptingguide.ai)\n",
"- [OpenAI Function Calling Docs](https://platform.openai.com/docs/guides/function-calling)\n",
"- [Qwen2.5 Model Card](https://huggingface.co/Qwen/Qwen2.5-7B-Instruct)\n",
"- [LLM Benchmarks: MMLU, HumanEval, HellaSwag](https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboard)\n",
"\n",
"---\n",
"*Happy Prompting! πŸš€*"
],
"id": "5GYMGme-IfpZ"
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.10.0"
},
"colab": {
"provenance": [],
"include_colab_link": true
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment