Doing it four hundred times
One call is a demo. The loop is what makes it a tool, and it is four lines — plus the three things that stop a loop over four hundred items from being a bad afternoon.
The loop
notes = [
"Priya to send the revised quote by Friday. Rohit will book the site visit.",
"Discussed Q3 budget. No decisions taken.",
"Meera chasing the missing invoice from Sharma Marble.",
]
results = []
for n in notes:
results.append(extract(n))
for r in results:
print(r)
print("---")That is it. results collects the output of each call, and the second loop prints them.
Now change notes from three strings to four hundred files and you have the tool. Everything else in this lesson is about not regretting it.
Three things to add before you scale up
Print progress. A cell that runs for eleven minutes with no output is indistinguishable from a cell that has hung.
for i, n in enumerate(notes):
print(f"{i+1}/{len(notes)}", end=" ", flush=True)
results.append(extract(n))enumerate gives you the position alongside the item. flush=True makes it appear immediately rather than being buffered.
Do not let one failure kill the run. By default, an error on item 213 stops everything and you lose 212 results.
results = []
for i, n in enumerate(notes):
try:
results.append({"index": i, "ok": True, "text": extract(n)})
except Exception as e:
print(f"\nitem {i} failed: {type(e).__name__}: {e}")
results.append({"index": i, "ok": False, "text": None})try / except means: attempt this, and if it raises an error, do that instead. Recording the failure rather than crashing means you finish the run and deal with a handful of failures at the end, which is always the right trade.
Handle rate limits. APIs cap requests per minute. Hit the cap and you get RateLimitError — a temporary condition that a short wait fixes.
import time
def extract_with_retry(notes, tries=3):
for attempt in range(tries):
try:
return extract(notes)
except Exception as e:
if "rate" in str(e).lower() and attempt < tries - 1:
wait = 2 ** attempt
print(f"rate limited, waiting {wait}s")
time.sleep(wait)
else:
raise2 ** attempt waits 1 second, then 2, then 4. Waiting longer after each failure is called exponential backoff, and it is the standard approach because a fixed short retry against a busy service just adds to the load.
Look before you leap
Two habits that will save you money and time, in that order.
Run on five first. Always. notes[:5] takes the first five items. Check the output is right before spending four hundred calls discovering your prompt has a flaw.
Estimate the cost. You have the per-call number from lesson 4. Multiply. Four hundred calls at $0.0087 is $3.48, which is fine. If your document is twenty times larger, it is $70, which deserves a moment's thought first.
Save as you go
The mistake everyone makes once: a forty-minute run completes, you look at the results, and then the runtime disconnects and everything is gone.
import json
with open("results.json", "w") as f:
json.dump(results, f, indent=2)Run that immediately after the loop. Better still, write inside the loop every twenty items, so a disconnection at item 380 costs you twenty results rather than all of them.
In Colab, files written this way live in the runtime and vanish with it — use the folder icon in the sidebar to download, or mount Drive. Lesson 8 covers this properly.
What a real run looks like
Twelve transcripts through the loop. Ten produced clean action lists. One returned NO ACTIONS FOUND, correctly — it was a status update with no decisions. One failed with a rate limit on the first attempt and succeeded on the retry.
That is a normal result. Not perfect, entirely usable, and every deviation was visible because the loop printed its progress and recorded its failures.
Do this today: take your extract function and run it over five inputs with the progress print and the try/except. Five, not four hundred — the discipline of running small first is the habit this lesson is really teaching.