Case study. Next.js / GenAI.
MIA
10-persona educational AI assistant
- Role
- Solo: persona system, streaming chat, search tool-calling, auth, deployment
- Status
- Livev1.1.0August 2026
- Context
- 2026, personal
- AI personas
- 10
- Languages (ES / EN)
- 2
- Conversations kept per agent
- 10
- File types parsed
- 5

The problem
One generic chatbot can't serve a 9-year-old on the Peruvian primary curriculum, a student prepping for the UNI entrance exam, a small-business owner navigating SUNAT paperwork, and an engineer debugging SQL — the tone, language, and guardrails are different for each. And a frozen model can't answer 'what are the current requirements?' questions that Peruvian bureaucracy personas get asked constantly.
Architecture
- 1.Personas
- 10 agents · 4 categories
- Per-agent system prompt
- ES / EN per persona
- 2.Input
- Text · voice (Web Speech)
- PDF · DOCX · XLSX · CSV
- Server-side extraction
- 3.Chat runtime
- OpenAI streaming
- Function calling
- Markdown render
- 4.Tools
- Google Custom Search
- Model decides when
- 5.Persistence
- S3 conversations
- Last 10 per agent
- HTML export · TTS out
How it is kept honest
Persona system prompts set tone, language and guardrails per audience. The model calls Google Custom Search through function calling only when it decides a question needs current information, so most turns cost nothing extra.
- Guardrails
- Public signup disabled; only admins create accounts, bootstrapped by a CLI script
- Voice input language detection matched to the persona
- File extraction happens server-side; five file types are parsed
- Where it can still be wrong
- A general model can still be wrong about exam dates or paperwork; the search tool exists for exactly those questions, and it is the model that decides to call it
What it does
MIA is a bilingual educational assistant with 10 specialized personas across four categories — Education (primary school, pre-university exam prep, English certification), Business (MYPE consulting, Peruvian bureaucracy), Programming (R, Python, SQL) and General (learning companion, evidence-based parenting). Each persona carries its own system prompt and language, and conversations stream in real time with markdown rendering.
- Agent grid landing page grouped by category
- Last 10 conversations per agent auto-saved to S3
- Chat export as formatted HTML; installable PWA with full dark/light OKLCH theming
Rebuilt from R Shiny
v1.0.0 is a full migration of an earlier R Shiny chatbot — MIA v1, still live on shinyapps.io and listed below — onto Next.js 16 with React 19 and TypeScript: streaming-first, mobile-installable, and cheap to host on Vercel instead of a Shiny server.
Search when the model decides
The chat route exposes Google Custom Search via OpenAI function calling — the model chooses when a question needs current information (exam dates, government requirements) and when it doesn't, so most turns cost nothing extra and searches happen only on demand.
Voice and documents
- Web Speech API voice input with Spanish/English detection matched to the persona
- Per-message text-to-speech playback
- File uploads with server-side extraction — PDF (pdf-parse), DOCX (mammoth), XLSX/CSV (xlsx), and images
Accounts done properly
- Public signup disabled — only admins create accounts, bootstrapped by a CLI script
- Role-based admin panel to add, edit and remove users
- JWT (jose) with bcrypt and httpOnly cookies
- All user and conversation state in S3 under a key prefix — no database
- v1.1.0 (August 2026): language selection, upload and login fixes, a Vitest suite (23 tests) and GitHub Actions CI on every push
Stack
- Next.js 16
- React 19
- TypeScript
- OpenAI (streaming + function calling)
- Google Custom Search
- Web Speech API
- AWS S3
- JWT (jose)