Photo: Unsplash
AI Meeting Notes That Don't Embarrass You — The Local Setup
A friend of mine — product manager at a mid-sized SaaS company — once watched an AI meeting bot email a full transcript to every attendee, including the part after the client dropped off where the team candidly discussed how unrealistic the client’s deadline was. The transcript went to the client too, because the bot grabbed the invite list. Nobody got fired, but it was close.
Cloud meeting bots fail in three reliably embarrassing ways. The announcement problem: “Jakub’s Notetaker has joined the meeting” instantly changes the room’s temperature — people speak differently when a logo is recording them, and some prospects will ask you to remove it. The distribution problem: default settings that mail transcripts and summaries to all attendees, candid parts included. The attribution problem: summaries that confidently state “Anna committed to shipping by Friday” when it was actually Tomáš, because speaker identification guessed wrong and the summary laundered the guess into fact.
The fix is to move the whole pipeline onto your Mac. No bot joins anything. Nothing is emailed to anyone unless you decide. Nothing leaves your machine. Here’s my exact setup, running daily for over a year.
Capturing both sides of the call
The one genuinely fiddly piece is audio capture. Your mic is easy; the other participants come out of your speakers via Zoom/Meet/Teams, and macOS doesn’t let apps record system audio without a virtual audio driver.
The standard free solution is BlackHole (2-channel version):
brew install blackhole-2ch
Then, in Audio MIDI Setup (it’s in /Applications/Utilities):
- Click + → Create Multi-Output Device; check both your normal output (speakers/headphones) and BlackHole 2ch. Set this as your system output — you still hear everything, and BlackHole gets a copy.
- Click + → Create Aggregate Device; check your microphone and BlackHole 2ch. This combines what you say and what you hear into one recordable input.
Point your recorder at the Aggregate Device and you capture the full conversation. MacWhisper Pro (~€59 one-time, by Jordi Bruin) does this elegantly with its built-in meeting recording mode — it can even capture app audio directly on newer macOS versions without BlackHole. If you prefer free and scriptable, record with QuickTime or ffmpeg from the aggregate device, then transcribe with whisper.cpp:
brew install whisper-cpp
whisper-cli -m ggml-large-v3-turbo.bin -f meeting.wav -l auto -otxt
The large-v3-turbo model transcribes a 60-minute meeting in about 3–4 minutes on an M3 Max, and -l auto handles my mixed Czech-English calls — Whisper is genuinely good at Czech, which surprised me, including meetings that drift between languages mid-sentence.
Summarization with a template, not vibes
Raw transcripts are unreadable. The value is in the structured summary — and the failure mode of “summarize this meeting” as a prompt is mush. I feed every transcript to a local model (Qwen 2.5 14B or Llama 3.3 70B via Ollama, depending on which Mac I’m at) with this exact template. Steal it verbatim:
You are a meeting-notes assistant. From the transcript below, produce:
## TL;DR
2-3 sentences maximum.
## Decisions made
Bullet list. Only decisions explicitly agreed in the transcript.
If none, write "None recorded."
## Action items
Format: [Owner if stated, else "UNATTRIBUTED"] — action — deadline if stated.
Do NOT guess owners. Do NOT invent deadlines.
## Open questions
Things raised but not resolved.
## Notable context
Anything a person absent from the meeting would need to know.
Rules: never attribute a statement to a named person unless the
transcript labels the speaker. Preserve all numbers, dates, and
amounts exactly. Do not editorialize.
Transcript:
{transcript}
The two rules at the end do the heavy lifting. “UNATTRIBUTED” looks less polished than a confident name, but unattributed is honest and a wrong name is a credibility grenade. The “preserve numbers exactly” rule exists because smaller models love rounding “€47,500” into “about €50k” — in meeting notes, that’s how budgets quietly inflate.
ollama run qwen2.5:14b "$(cat template.txt meeting-transcript.txt)" > summary.md
The honest section about speaker labels
Diarization — knowing who said what — is the weak point of every local setup, and I won’t pretend otherwise. Whisper alone doesn’t do it. The options:
- MacWhisper Pro has built-in diarization; in my experience it’s roughly 80–90% accurate with 2–3 clear speakers and degrades with crosstalk, similar voices, or more participants.
- whisperX (open source) combines Whisper with pyannote diarization models and is the best free option, but setup involves a Hugging Face token and a Python environment — budget an evening.
- A clever hack for two-person calls: your mic and system audio land on separate channels of the aggregate device. Record stereo, split channels with
ffmpeg -map_channel, transcribe each separately — perfect “me vs. them” attribution for free.
My pragmatic workarounds: for meetings where attribution matters, I spend 90 seconds skimming the summary against the transcript and fixing labels by hand. For most internal meetings, what was decided matters more than precisely who phrased it, and the UNATTRIBUTED convention covers the rest. Treat diarization output as a draft, never as a record.
The consent section you must not skip
Local processing solves privacy, not legality. Recording laws vary, and “the AI did it” is not a defense.
- One-party consent jurisdictions (most US states, and broadly the position for personal-use recordings in many places): your own consent as a participant suffices.
- All-party consent jurisdictions (California, Florida, Germany among others): every participant must agree. Germany’s §201 StGB makes secret recording of non-public speech a criminal offense, not a civil slap.
- The EU layer: under GDPR, a recording containing identifiable voices is personal data. For work meetings this implicates lawful basis, retention, and access rules — your employer may already have a policy; check it before you build this for work.
- Czech specifics, since I’m asked: §86 of the civil code protects voice recordings as personal expressions; recordings for legitimate personal documentation occupy a gray zone the courts assess case by case. Don’t rely on the gray zone.
My rule, which also happens to be good manners: always announce it. “I’m recording this locally for my own notes — it stays on my machine, no bot, nothing gets shared. Anyone mind?” In hundreds of meetings, exactly one person has objected, and I simply took notes by hand that day. The announcement costs ten seconds and converts a legal question into a courtesy.
The full workflow, under five minutes
Here’s the end-to-end shape of it after the meeting ends:
- Stop the recording (MacWhisper or my ffmpeg hotkey via Raycast). ~5 seconds.
- Transcribe. large-v3-turbo, 60-minute meeting: ~3–4 minutes on Apple Silicon. This runs while I refill coffee.
- Summarize. The Ollama one-liner above: 20–40 seconds for a typical transcript.
- File it. The summary lands as a Markdown note in my Obsidian vault, named
YYYY-MM-DD meeting-topic.md, transcript attached below a fold. A two-line shell script does the moving.
Total hands-on time: about a minute. Total elapsed: under five. And the deliverable is mine — I forward the decisions-and-actions section to attendees after a 90-second sanity read, which is the step every embarrassing cloud-bot story was missing: a human glance between transcript and distribution.
The cloud bots optimize for the org chart — automatic distribution, dashboards, engagement metrics. The local setup optimizes for you: a perfect memory of every meeting, on your own disk, shared only on purpose. One of these can embarrass you in front of a client. The other one is just a very fast, very discreet assistant who never speaks unless you ask.
Total cost: €0 with whisper.cpp and Ollama, or ~€59 if you let MacWhisper smooth the edges. Either way, cheaper than the apology email.
One email a month: the upcoming live event + free recording access for subscribers. No spam, unsubscribe anytime.