Prompt template
Paste a raw, half-formed change description. Get a release note back in the
Showcase — Prompt template
Paste a raw, half-formed change description. Get a release note back in the same three sections every time: Summary, Impact, Action. The template is baked into the prompt, so the structure is something you can count on instead of something you hope the model remembers.
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— theTEMPLATEstring (the lesson) + the OpenAI callbackend/ai_gemini.py— same template, Gemini callbackend/main.py— identical FastAPI loader; reads PROVIDER and dispatchesfrontend/app/page.tsx— textarea + resultdocker-compose.yml— two services, secrets mounted from./secrets/
The input is the raw change description, passed straight through as the
single string the def run(input: str) -> str contract expects.
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 prompt-template.zip
cd prompt-template
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 3 (OpenAI): a fixed template pins the output shape.
Drop a raw, messy change description in; get a release note back in the
same three sections every time. The template lives in the prompt, so the
structure is a property of the prompt, not something you hope the model
remembers. Plain def run(input: str) -> str — the input is the raw text.
"""
from openai import OpenAI
_client = OpenAI()
# The template is the lesson. The model fills the slots; it does not get to
# redesign the document. Same input shape in, same three headings out.
TEMPLATE = """\
Rewrite the change description below as a release note. Use exactly this
template, keep the three headings verbatim, and fill each with one line:
Summary: <what changed, in plain language>
Impact: <who is affected and how>
Action: <what the reader should do, or "None" if nothing>
Return only the filled template. Do not add sections or commentary.
Change description:
{text}"""
def run(text: str) -> str:
prompt = TEMPLATE.format(text=text.strip())
response = _client.responses.create(
model="gpt-5.4-nano", input=prompt, max_output_tokens=300,
)
return response.output_text
backend/ai_gemini.py
"""Week 2 - Showcase 3 (Gemini): a fixed template pins the output shape."""
import os
from google import genai
from google.genai import types
_client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
TEMPLATE = """\
Rewrite the change description below as a release note. Use exactly this
template, keep the three headings verbatim, and fill each with one line:
Summary: <what changed, in plain language>
Impact: <who is affected and how>
Action: <what the reader should do, or "None" if nothing>
Return only the filled template. Do not add sections or commentary.
Change description:
{text}"""
def run(text: str) -> str:
prompt = TEMPLATE.format(text=text.strip())
response = _client.models.generate_content(
model="gemini-3.1-flash-lite",
contents=prompt,
config=types.GenerateContentConfig(max_output_tokens=300),
)
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