Slow English
A weekly bilingual English learning platform that turns real international news into structured B2–C1 lessons.
What is this?
A weekly bilingual English learning platform that turns real international news into structured B2-C1 lessons. Each week, headlines are fetched from BBC, Guardian, and Al Jazeera, scored for teaching value, and the best one is generated into a full lesson.
Nemo 🐠 asks the questions. Octo 🐙 guides the thinking. Content presents multiple perspectives with Traditional Chinese translations and vocabulary explanations throughout.
How it works
-
RSS Fetch
Headlines fetched from BBC, Guardian, Al Jazeera, Yonhap, DW
-
AI Scoring
Claude scores each headline for teachability, no web search, plain text only
-
AI Generation
Top candidate → Claude generates bilingual B2-C1 lesson with web search
-
Published
Article saved to DB and published every Monday at 08:00 Taipei time
Iterations
Several pivots shaped the current product.
Daily → Weekly
Daily generation meant no time to review content before publishing. Switching to weekly added a manual review step and reduced monthly costs by 7x.
Three difficulty levels → Single B2-C1
Generating three versions per article tripled output tokens. As the only user, I always read B2-C1. Simplifying to one level reduced cost and allowed a more focused prompt.
讀新聞學英文 → Slow English
The original design felt like a news feed. As the product shifted to a weekly, topic-based format, the name and visual identity were updated to reflect that.
Nested versions schema → Flat structure
Content was originally stored under a versions JSONB column. With a single level, that wrapper became redundant. Flattening the schema simplified queries and frontend code.
Challenges & Solutions
API cost optimization
The initial implementation used Claude's web_search tool at the headline scoring stage. Refactored to fetch RSS directly and pass raw headlines as plain text, reserving web search for article generation only. Adding max_uses: 1 to the search tool reduced per-generation cost by a further ~30%.
Duplicate cron runs
Vercel Cron occasionally triggers multiple times on the free tier. Added a guard that checks whether an article already exists for the current week before proceeding.
Removing rss-parser
rss-parser uses the deprecated url.parse() API internally, causing Node.js warnings on every cold start. Replaced with a lightweight custom regex parser using native fetch.
Model migration
Migrating from claude-sonnet-4 to claude-sonnet-4-6 broke tool result handling. The newer model rejected placeholder tool_result content that the older model had silently accepted. Debugged via Vercel logs and updated the tool call flow.
Teachability scoring drift
Early scoring prompts used vague criteria, letting through celebrity gossip and weather reports. Rewrote the prompt with explicit rejection rules and a structured accept/reject framework.
Schema migration
Moving from a nested versions structure to a flat schema required migrating 45 existing articles. Wrote a migration script to extract content, update each article in place, verify the data, then drop the column.
Accessibility
Built with WCAG 2.1 AA standards throughout:
- Semantic HTML with appropriate landmark regions
langattributes on English (lang="en") and Chinese (lang="zh-TW") contentrole="status"andaria-live="polite"for loading states- Colour contrast audited, all text meets 4.5:1 minimum ratio