Agentic Maths is a small end-to-end demo of a kids' maths quiz chatbot built with a local LLM. The project combines a React chat interface, an Express backend, a LangChain agent, and Ollama so you can run the whole experience on your own machine with an open model such as Qwen 2.5.
The app is designed as a practical example of agentic AI patterns rather than a production-ready product. The agent is given a tutor persona, can call quiz-specific tools, streams responses back to the browser, and keeps per-session chat history and scores in memory.
If you want the shortest path to running the app locally with Ollama and Qwen:
curl -fsSL https://ollama.com/install.sh | sh
ollama pull qwen2.5:7b
cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.env.local
npm install
npm run setup
npm run devThen open the Vite URL shown in the terminal, usually http://localhost:5173.
- Presents a chatbot-style maths quiz UI for children.
- Uses a LangChain agent backed by
ChatOllamato run the tutoring flow. - Streams assistant output from the backend to the frontend over Server-Sent Events (SSE).
- Uses simple tools for question generation, answer evaluation, and score tracking.
- Runs fully locally when Ollama and the selected model are installed on your machine.
At a high level, the system looks like this:
- The React frontend sends the user's message and a generated
sessionIdto the backend. - The Express backend builds the conversation context, including an in-memory chat history for that session.
- A LangChain agent calls a local Ollama model using
ChatOllama. - The agent can use three tools:
generate_question,evaluate_answer, andtrack_score. - The backend streams the model's response back to the frontend token by token via SSE.
- The final assistant reply is appended to in-memory history so the next turn has context.
flowchart LR
User[Child using chat UI] --> Frontend[React + Vite frontend]
Frontend -->|POST /api/chat| Backend[Express backend]
Backend -->|stream tokens via SSE| Frontend
Backend --> Agent[LangChain maths agent]
Agent --> Ollama[Ollama local runtime]
Agent --> GQ[generate_question tool]
Agent --> EA[evaluate_answer tool]
Agent --> TS[track_score tool]
Ollama --> Model[Qwen 2.5 model]
- Frontend: React, TypeScript, Vite
- Backend: Node.js, Express, TypeScript
- Agent framework: LangChain
- Local model runtime: Ollama
- Default model family: Qwen 2.5
frontend/ React chat UI
backend/ Express API + LangChain agent + Ollama integration
Key backend pieces:
backend/src/server.tsstarts the Express server.backend/src/routes/chatRouter.tshandles the streaming chat endpoint.backend/src/agent/mathsAgent.tscreates the LangChain agent and in-memory history store.backend/src/agent/tools/contains the quiz tools.backend/src/agent/prompts/system.tsdefines the tutor persona and behaviour rules.
Key frontend pieces:
frontend/src/hooks/useChat.tsmanages chat state.frontend/src/services/api.tsconsumes the streaming API.frontend/src/components/contains the chat UI components.
Before running the project, make sure you have:
- Node.js and npm installed
- Ollama installed locally
- At least one Ollama model pulled locally
This project is configured to work with a local Ollama server at http://localhost:11434.
On macOS, the Ollama download page currently offers two straightforward options:
Option A: install from the official app
- Download the macOS installer from https://ollama.com/download/mac
- Open the
.dmg - Drag Ollama into Applications
- Launch Ollama once so the local service is available
Option B: install from the command line
curl -fsSL https://ollama.com/install.sh | shNotes:
- The Ollama macOS download page currently states that macOS 14 Sonoma or later is required.
- If you are on Linux or Windows, use the relevant installer from https://ollama.com/download.
If you installed the macOS app, launching the app is usually enough to start the local server.
You can verify that Ollama is available with:
ollama listIf Ollama is running correctly, that command should return a model list, even if it is empty.
The backend defaults to the qwen2.5 model family. A good starting point for local development is qwen2.5:7b.
Pull it with:
ollama pull qwen2.5:7bYou can also try other sizes depending on your hardware:
qwen2.5:1.5bfor a lighter setupqwen2.5:3bfor a smaller but more capable optionqwen2.5:7bfor a better quality default
After pulling the model, you can sanity-check it with:
ollama run qwen2.5:7bIf the prompt opens and the model responds, Ollama is ready.
The backend reads its config from environment variables.
The code-level fallback model is qwen2.5, while the provided example env file pins qwen2.5:7b, which is a clearer starting point for local development.
There is already an example file at backend/.env.example:
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_MODEL=qwen2.5:7b
PORT=3001Create your local backend env file:
cd backend
cp .env.example .envIf you want to use a different local model tag, update OLLAMA_MODEL so it exactly matches the model you pulled. For example:
OLLAMA_MODEL=qwen2.5:3bInstall backend dependencies:
cd backend
npm installInstall frontend dependencies:
cd frontend
npm installYou need two terminals: one for the backend and one for the frontend.
If you prefer running everything from the repository root, use the convenience scripts described below.
npm install
npm run setup
npm run devThis starts:
- the backend in watch mode on
http://localhost:3001 - the frontend dev server on
http://localhost:5173
cd backend
npm run devThe backend starts on:
http://localhost:3001
By default it expects:
OLLAMA_BASE_URL=http://localhost:11434OLLAMA_MODEL=qwen2.5if no env file is set, or the explicit model tag frombackend/.envif you configured one
In a second terminal:
cd frontend
npm run devVite will print the local URL, which is typically:
http://localhost:5173
The frontend defaults to calling the backend at http://localhost:3001.
If your backend is running elsewhere, copy the example frontend env file and point it at the correct API base URL:
cd frontend
cp .env.example .env.localThen edit the value if needed:
VITE_API_URL=http://localhost:3001- Open the frontend URL in your browser.
- Send a message such as
Hi,Start quiz, orI'm ready. - The assistant should respond as the maths tutor and begin the quiz flow.
- Answers and score are tracked within the current backend process.
Repository root:
npm run setupinstalls dependencies for bothbackendandfrontendnpm run devstarts backend and frontend togethernpm run dev:backendstarts only the backendnpm run dev:frontendstarts only the frontendnpm run buildbuilds backend and frontendnpm run testruns backend and frontend testsnpm run lintruns the frontend linter
Backend:
npm run devruns the backend in watch mode usingtsxnpm run buildcompiles TypeScriptnpm run startruns the compiled backendnpm run testruns backend tests
Frontend:
npm run devstarts the Vite dev servernpm run buildbuilds the frontendnpm run previewserves the production build locallynpm run testruns frontend testsnpm run lintruns ESLint
This repo is intentionally simple. A few things to know:
- Chat history is stored in memory, so it is lost when the backend restarts.
- Score tracking is also stored in memory per session.
- The question generation tool currently returns structured tool data rather than a full question bank or persistent quiz engine.
- This is a demo of local agent orchestration, not a hardened tutoring product.
If you wanted to take this beyond a demo, the main gaps to address are:
- Persistence: move chat history and score tracking out of in-memory maps into a database or durable store.
- Session management: replace the generated client-side session ID with proper user or anonymous session handling.
- Observability: add structured logging, request tracing, and monitoring around the streaming endpoint and agent/tool calls.
- Rate limiting and abuse protection: protect the chat endpoint from spam and excessive local model usage.
- Safety hardening: do not rely only on the system prompt for behavioural constraints; add server-side validation and moderation policy handling.
- Deployment shape: run the frontend as static assets, host the backend separately, and ensure the Ollama host is reachable only where intended.
If the backend cannot reach Ollama:
- Make sure the Ollama app is running
- Check that
OLLAMA_BASE_URLmatches your local Ollama address - Verify Ollama is responding with
ollama list
If you see errors about a missing model:
- Run
ollama listto see what is installed - Pull the missing model with
ollama pull <model-name> - Make sure
OLLAMA_MODELexactly matches the installed tag
Example:
ollama pull qwen2.5:7bOLLAMA_MODEL=qwen2.5:7bIf the browser UI loads but messages fail:
- Confirm the backend is running on port
3001 - Check
VITE_API_URLif you changed ports or hostnames - Confirm CORS and network access are not being blocked locally
That is expected with the current implementation. Both chat history and score state are held in memory only.