
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
- 10 agents · 4 categories
- Per-agent system prompt
- ES / EN per persona
- Text · voice (Web Speech)
- PDF · DOCX · XLSX · CSV
- Server-side extraction
- OpenAI streaming
- Function calling
- Markdown render
- Google Custom Search
- Model decides when
- S3 conversations
- Last 10 per agent
- HTML export · TTS out
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 — the app this portfolio previously linked on shinyapps.io — 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