Few-shot
Classify a support message into one label — billing, bug, feature_request,
Showcase — Few-shot
Classify a support message into one label — billing, bug, feature_request, praise, other. A checkbox flips the few-shot examples on and off. With the examples in the prompt, the label set and the one-line format stay put. Turn them off and the model starts inventing labels and wrapping them in prose.
Run
bash bootstrap-secrets.sh # reads ../../../../.env, writes secrets/
docker compose up --build # default: PROVIDER=openai
Open http://localhost:3000.
To run against Gemini instead:
PROVIDER=gemini docker compose up --build
What's where
backend/ai_openai.py— theINSTRUCTION+EXAMPLESblock (the lesson) and the OpenAI callbackend/ai_gemini.py— same prompt, Gemini callbackend/main.py— identical FastAPI loader; reads PROVIDER and dispatchesfrontend/app/page.tsx— textarea + few-shot checkbox + resultdocker-compose.yml— two services, secrets mounted from./secrets/
The frontend encodes <mode>\n---\n<message> (mode is zeroshot or
fewshot) into the single-string input, keeping the
def run(input: str) -> str contract identical across the week's showcases.
Stop
docker compose down
Run locally
Download the project as a ZIP and run it with Docker. Brings up a FastAPI backend + Next.js frontend on localhost:3000.
unzip few-shot.zip
cd few-shot
bash bootstrap-secrets.sh # one-time: pulls API keys into ./secrets
docker compose up --build # default provider: openai
# or: PROVIDER=gemini docker compose up --build
Type some input, pick a provider, and run the same code shown in Source against the live API. Sign-in required.
The same modules the Run button hits. The whole project (frontend, Dockerfile, compose) is in the ZIP under README.
backend/ai_openai.py
"""Week 2 - Showcase 2 (OpenAI): zero-shot vs few-shot, side by side.
Same classification task, same instruction. The only thing the toggle
changes is whether a handful of worked examples ride along in the prompt.
Zero-shot drifts on the label set and the format; few-shot locks both.
The frontend encodes "<mode>\\n---\\n<text>" where mode is zeroshot|fewshot.
"""
from openai import OpenAI
_client = OpenAI()
LABELS = "billing, bug, feature_request, praise, other"
INSTRUCTION = (
"Classify the support message into exactly one of these labels: "
f"{LABELS}. Respond with only `label: <one label>` and nothing else."
)
# The few-shot block: three worked examples that pin the label vocabulary
# and the output line. This is the entire difference the toggle makes.
EXAMPLES = """
Message: My card got charged twice this month.
label: billing
Message: The export button does nothing when I click it.
label: bug
Message: Any chance you could add dark mode?
label: feature_request
""".strip()
def run(payload: str) -> str:
mode, _, text = payload.partition("\n---\n")
mode = (mode or "fewshot").strip()
text = (text or payload).strip()
if mode == "zeroshot":
prompt = f"{INSTRUCTION}\n\nMessage: {text}"
else:
prompt = f"{INSTRUCTION}\n\n{EXAMPLES}\n\nMessage: {text}"
response = _client.responses.create(
model="gpt-5.4-nano", input=prompt, max_output_tokens=20,
)
return response.output_text
backend/ai_gemini.py
"""Week 2 - Showcase 2 (Gemini): zero-shot vs few-shot, side by side."""
import os
from google import genai
from google.genai import types
_client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
LABELS = "billing, bug, feature_request, praise, other"
INSTRUCTION = (
"Classify the support message into exactly one of these labels: "
f"{LABELS}. Respond with only `label: <one label>` and nothing else."
)
EXAMPLES = """
Message: My card got charged twice this month.
label: billing
Message: The export button does nothing when I click it.
label: bug
Message: Any chance you could add dark mode?
label: feature_request
""".strip()
def run(payload: str) -> str:
mode, _, text = payload.partition("\n---\n")
mode = (mode or "fewshot").strip()
text = (text or payload).strip()
if mode == "zeroshot":
prompt = f"{INSTRUCTION}\n\nMessage: {text}"
else:
prompt = f"{INSTRUCTION}\n\n{EXAMPLES}\n\nMessage: {text}"
response = _client.models.generate_content(
model="gemini-3.1-flash-lite",
contents=prompt,
config=types.GenerateContentConfig(max_output_tokens=20),
)
return response.text
Project files
.gitignoreREADME.mdbackend/Dockerfilebackend/ai_gemini.pybackend/ai_openai.pybackend/main.pybackend/requirements.txtbootstrap-secrets.shdocker-compose.ymlfrontend/Dockerfilefrontend/app/layout.tsxfrontend/app/page.tsxfrontend/next.config.tsfrontend/package.jsonfrontend/tsconfig.json