Work AI

AI assistant.

I'm building a household voice assistant around a locally hosted model on a Mac Studio, usable from any device on my Tailscale network.

The project brings together local inference, custom MCP servers, built-in live tools, a notification system, and a push-to-talk web app.

01 — Architecture

How it fits together

  1. Step 01

    Push-to-talk

    Web app on any device on the tailnet, sending audio over a WebSocket

  2. Step 02

    Speech recognition

    mlx-whisper large-v3-turbo on Apple silicon

  3. Step 03

    Local model

    Qwen3.8-27B or Flash-Next on a Mac Studio, served by oMLX

  4. Step 04

    Tools

    Custom MCP servers and built-in tools

  5. Step 05

    Speech

    Kokoro text-to-speech, running locally

02 — Model

Models

I switch between two Qwen3.8 models on oMLX. Only one fits in memory at a time, and the same model also runs a local coding agent, so the choice is a trade between speed and room for other work.

Default

Qwen3.8-27B

Type
Dense, open weights
Quantization
6-bit MLX, with multi-token prediction
Runtime
oMLX on a Mac Studio (M5 Max, 64 GB), with a prefix cache
Context
128K tokens

Leaves enough memory for cloud coding agents and test devices to run beside it.

Also in use

Qwen3.8 Flash-Next

Type
Mixture of experts, pruned to 320 of 512 experts
Quantization
3-bit MLX, with multi-token prediction
Runtime
oMLX on the same Mac Studio
Context
128K tokens

About twice as fast per request and higher on the assistant eval, but it needs most of the Mac's memory.

Each candidate is scored on an eval of the real assistant and measured for speed and memory use. Earlier versions ran Qwen3.6 35B-A3B and, first, Gemma 4 12B on an RTX 5070 Ti.

03 — Components

Components

Model

Local inference

The assistant's model runs locally on a Mac Studio, sharing its 64 GB of unified memory with coding agents.

Connections

Custom MCP servers

MCP servers connect the assistant to lights and music.

  • Philips Hue
  • Sonos + Spotify
  • Occhio
Built-in

Live tools

Built-in tools look up live grocery offers from local Danish supermarkets and handle the calendar, shared lists, reminders, memory, and web search.

  • Grocery offers
  • Calendar
  • Lists
  • Reminders
  • Memory
  • Web search
Interaction

Voice and notifications

A push-to-talk web app with Whisper speech recognition and Kokoro speech, reached from any device over Tailscale. Notifications go through a self-hosted ntfy server.

04 — Case study

Live grocery offers

Moving work out of the model and into a tool.

Problem

The smaller local models couldn't extract the right offers from the supermarket pages without overflowing their context.

Solution

The extraction became a tool. A script fetches the offers, keeps the ones that are valid today, sorts them by unit price, and returns a short list with only the fields the model needs.

A grocery question now triggers the lookup automatically before the model answers.

The new version widens this into a rule for any changing fact, such as prices, weather or opening hours: if the model answers one from memory without calling a tool, the reply is held back and the model is asked to look it up.

  • Fewer tokens
  • Faster answers
  • Deterministic results
grocery.flow
  1. QuestionGrocery intent detected
  2. Script lookupFetch · filter · sort · trim
  3. Model answersFrom the compact result only
  4. Live-fact checkAnswers from memory held back
05 — Lessons

What I learned

Voice sets a hard latency budget. When I ask for the lights to be turned off, I don't want to wait 30 seconds for it to happen.

Prompting

System prompts

Everything in the system prompt is processed before the first reply. Keeping it lean, and only offering tools whose backend is actually up, keeps answers fast and keeps the model on track.

Latency

Prompt processing

How fast the first word arrives depends on how quickly the model reads the prompt, not just how fast it generates. The fixed part of the prompt is cached when the assistant starts, and anything that changes, like the clock, goes in the user's message, so each turn only reads what's new.

  • Thinking off
  • Short replies
  • Push-to-talk tuning
Speech

Voice recognition

Whisper mishears names like Occhio. A vocabulary file corrects those words after recognition, so a command works the first time. Putting them in Whisper's prompt instead made its English recognition worse.