Finish the comparison tool. See three AI-generated prompts side-by-side. Week 4 done.
Day 20 of 80
Yesterday you set up prompt_compare.py and wired the Claude API calls. Today you complete the tool by adding the display section, the save section, and error handling around each API call. Then you test it on a real shot.
| Section | What It Does |
|---|---|
| Display | Print all three generated prompts side-by-side with clear separators |
| Save | Ask which platforms to keep, filter results, append to prompts.json |
| Error handling | Wrap each API call so one failure doesn't kill the whole comparison |
Yesterday you built the generate_prompt() function and the loop that calls it for each platform, storing results in a results dictionary. Today you finish what happens after that loop runs.
These should already be in your file from Day 19. Shown here for reference with full annotations:
import anthropic
import json
# Re-use the same helper functions from prompt_manager_v2.py
PROMPTS_FILE = "prompts.json"
PLATFORMS = ["Kling", "Runway", "Veo"]
# Anthropic client — reads ANTHROPIC_API_KEY from environment automatically
client = anthropic.Anthropic()
def load_prompts():
try:
with open(PROMPTS_FILE, "r") as f:
return json.load(f)
except FileNotFoundError:
return []
def save_prompts(prompts):
with open(PROMPTS_FILE, "w") as f:
json.dump(prompts, f, indent=2)
def generate_prompt(shot, platform):
"""Call Claude to generate a prompt tuned for the given platform."""
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=200,
# system sets Claude's role — it stays in effect for the whole conversation
system="You are an expert AI video prompt engineer.",
messages=[{
"role": "user",
# f-string lets us inject shot and platform into the instruction
"content": f"Write a {platform} AI video prompt for: {shot}. Match {platform}'s strengths. Under 75 words. Only the prompt."
}]
)
# The response is a Message object; .content is a list of blocks;
# [0].text gets the text from the first (and only) block
return message.content[0].text
API calls can fail: the service might be temporarily down, you might hit a rate limit, or your key might have an issue. Without try/except, one failure crashes the entire script and you lose all three results. With it, you get two good prompts and one clear error message.
# Get the shot description from the user
shot = input("\nDescribe your shot: ").strip()
print("\nGenerating prompts — this may take a few seconds...\n")
# results will hold platform → generated prompt text (or error message)
results = {}
for platform in PLATFORMS:
print(f" Generating for {platform}...")
try:
# Call the API — this might take 1-3 seconds per platform
results[platform] = generate_prompt(shot, platform)
except Exception as e:
# Catch ANY exception from the API call.
# Store an error placeholder so display code can check for it.
# The real error is available in 'e' if you need to debug.
results[platform] = f"(Error: {e})"
print(f" Warning: {platform} failed — {e}")
After the loop, print a clean comparison table. The goal is to make it easy to read all three side-by-side and decide which platform's style fits the shot best.
# ── Display comparison ────────────────────────────────────────────────
print(f"\n{'=' * 50}")
print(f" SHOT: {shot}")
print(f"{'=' * 50}\n")
# Loop through results dict — same order as PLATFORMS list
# because Python dicts preserve insertion order (Python 3.7+)
for platform, prompt in results.items():
print(f"--- {platform} ---")
print(f"{prompt}\n")
After reading the three prompts, the user decides what to keep. The save section handles three cases: save all, save specific platforms (comma-separated), or save nothing.
# ── Save options ──────────────────────────────────────────────────────
print("Save options: 'all', 'none', or platform names like 'Kling, Veo'")
choice = input("\nSave: ").strip().lower()
if choice == "all":
# Save every result that isn't an error
prompts = load_prompts()
for platform, prompt in results.items():
if not prompt.startswith("(Error"):
prompts.append({"platform": platform, "shot": shot, "prompt": prompt})
save_prompts(prompts)
print("\n All results saved to prompts.json\n")
elif choice == "none":
# User just wanted to compare — nothing saved
print("\n Nothing saved.\n")
else:
# Parse "kling, veo" → ["kling", "veo"]
# .split(",") splits on commas; .strip() removes spaces around each name
chosen = [c.strip().lower() for c in choice.split(",")]
prompts = load_prompts()
saved_count = 0
for platform, prompt in results.items():
# Check: was this platform in the user's chosen list?
# Was it a successful result (not an error)?
if platform.lower() in chosen and not prompt.startswith("(Error"):
prompts.append({"platform": platform, "shot": shot, "prompt": prompt})
saved_count += 1
save_prompts(prompts)
print(f"\n Saved {saved_count} prompt(s) to prompts.json\n")
$ python prompt_compare.py
Describe your shot: Snowboarder launches off a kicker at sunset, slow motion
Generating prompts — this may take a few seconds...
Generating for Kling...
Generating for Runway...
Generating for Veo...
==================================================
SHOT: Snowboarder launches off a kicker at sunset, slow motion
==================================================
--- Kling ---
Ultra-slow motion snowboarder explodes off kicker ramp, golden-hour sun
behind, snow crystals suspended mid-air, limbs fully extended at peak,
shallow depth of field, 240fps feel, cinematic flare.
--- Runway ---
Dynamic slow-motion sequence: snowboarder launches from kicker, camera
locked below tracking upward, sunset silhouette against blazing orange sky,
snow spray catching light, smooth arc across frame.
--- Veo ---
High-speed aerial kicker jump at magic hour, snowboarder rotates against
vivid sunset gradient, powder trailing in slow-motion arc, photorealistic
skin and gear detail, immersive wide-angle perspective.
Save options: 'all', 'none', or platform names like 'Kling, Veo'
Save: Kling, Veo
Saved 2 prompt(s) to prompts.json
What you just built is something you'd actually use in production. When you have a shot concept and you're deciding whether to send it to Kling, Runway, or Veo, you run prompt_compare.py, read the three platform-tuned outputs in 10 seconds, pick the one that fits, and save it. That's a real workflow. Compare this to writing three separate prompts by hand, adjusting for each platform's style, and deciding blind which one to try. You've automated the hard part.
By the end of Week 4, you can:
client.messages.create(), pass model, max_tokens, system, messagessystem= and inject dynamic values into the content string at runtimetry/except Exception as e around each call so one failure doesn't crash the whole scriptmessage.content[0].text — and understand why it's structured that way (a response can have multiple content blocks)prompt_compare.py on a real shot description and got three distinct prompts backprompts.json and confirmed the saved entry is theremessage.content[0].text is doing and why we need [0]Week 5 starts with a watch day on functions — going deeper on arguments, return values, default parameters, and how to design functions that compose cleanly. You'll use that knowledge all week as the tools grow more complex.