How to Summarise Meeting Notes with a Local LLM

Meeting notes are one of the most consistently painful parts of professional work: someone has to write them, they are usually incomplete, and converting raw notes into a structured summary with clear action items takes time that nobody has right after a meeting. A local LLM handles this well — it can take messy raw notes or a transcript and produce a clean structured summary, extract action items with owners and deadlines, and reformat the content for distribution, entirely on your machine in seconds.

The Basic Summarisation Workflow

import ollama

def summarise_meeting_notes(notes: str, model: str = 'llama3.2') -> str:
    prompt = """You are a professional meeting summariser. Given the following raw meeting notes, produce a structured summary with these sections:

1. **Meeting Overview** (1-2 sentences: what was discussed and decided overall)
2. **Key Discussion Points** (bullet points of main topics covered)
3. **Decisions Made** (bullet points of explicit decisions reached)
4. **Action Items** (each as: Task | Owner | Deadline if mentioned)
5. **Open Questions** (unresolved items needing follow-up)

Be specific and concise. Do not pad or add commentary. Only include sections that have content.

Raw notes:
"""
    response = ollama.chat(
        model=model,
        messages=[{'role': 'user', 'content': prompt + notes}],
        options={'temperature': 0.2}  # low temp for factual extraction
    )
    return response['message']['content']

# Usage
with open('meeting_notes.txt') as f:
    raw_notes = f.read()

summary = summarise_meeting_notes(raw_notes)
print(summary)

Processing a Recorded Transcript

If you record meetings, you can transcribe them locally with Whisper and then summarise the transcript:

# Transcribe with Whisper (install: pip install openai-whisper)
whisper meeting_recording.mp3 --model base --output_format txt
# Output: meeting_recording.txt
import whisper
import ollama

def transcribe_and_summarise(audio_file: str, model: str = 'llama3.2') -> dict:
    # Transcribe
    print('Transcribing...')
    whisper_model = whisper.load_model('base')  # or 'small', 'medium' for better accuracy
    result = whisper_model.transcribe(audio_file)
    transcript = result['text']
    print(f'Transcript: {len(transcript.split())} words')

    # Summarise
    print('Summarising...')
    summary = summarise_meeting_notes(transcript, model=model)

    return {'transcript': transcript, 'summary': summary}

output = transcribe_and_summarise('weekly_standup.mp3')
print(output['summary'])

Extracting Action Items Only

def extract_action_items(notes: str, model: str = 'llama3.2') -> list[dict]:
    prompt = """Extract all action items from these meeting notes. Return ONLY a JSON array with objects containing:
- "task": what needs to be done (specific and actionable)
- "owner": who is responsible (person name or team, or "Unassigned" if not mentioned)
- "deadline": when it is due (exact date/time mentioned, or "Not specified")
- "priority": "High", "Medium", or "Low" based on context

Return only the JSON array, no other text.

Meeting notes:
"""
    response = ollama.chat(
        model=model,
        messages=[{'role': 'user', 'content': prompt + notes}],
        options={'temperature': 0.0}
    )
    import json
    try:
        return json.loads(response['message']['content'].strip())
    except json.JSONDecodeError:
        # Fallback: extract JSON from response
        import re
        match = re.search(r'\[.*\]', response['message']['content'], re.DOTALL)
        return json.loads(match.group()) if match else []

actions = extract_action_items(raw_notes)
for item in actions:
    print(f"[{item['priority']}] {item['task']} — {item['owner']} by {item['deadline']}")

Generating a Follow-Up Email

def generate_followup_email(summary: str, recipients: list[str],
                             meeting_name: str, model: str = 'llama3.2') -> str:
    prompt = f"""Write a professional follow-up email based on this meeting summary.
Meeting: {meeting_name}
Recipients: {', '.join(recipients)}

The email should:
- Have a clear subject line
- Open with a brief thank-you for attending
- List the key decisions and action items clearly
- Close professionally
- Be concise (under 200 words)

Meeting summary:
{summary}"""
    response = ollama.chat(
        model=model,
        messages=[{'role': 'user', 'content': prompt}],
        options={'temperature': 0.4}
    )
    return response['message']['content']

email = generate_followup_email(summary, ['alice@co.com', 'bob@co.com'], 'Q2 Planning')
print(email)

Command-Line Tool

#!/usr/bin/env python3
import argparse
import ollama
from pathlib import Path
from datetime import datetime

def main():
    parser = argparse.ArgumentParser(description='Summarise meeting notes with a local LLM')
    parser.add_argument('input', help='Path to notes file (.txt or .md)')
    parser.add_argument('--model', default='llama3.2')
    parser.add_argument('--output', help='Save summary to file')
    parser.add_argument('--actions-only', action='store_true')
    args = parser.parse_args()

    notes = Path(args.input).read_text()
    print(f'Processing {len(notes.split())} words with {args.model}...')

    if args.actions_only:
        actions = extract_action_items(notes, args.model)
        result = '\n'.join(f"- [{a['priority']}] {a['task']} ({a['owner']}, {a['deadline']})" for a in actions)
    else:
        result = summarise_meeting_notes(notes, args.model)

    print(result)

    if args.output:
        Path(args.output).write_text(result)
        print(f'\nSaved to {args.output}')
    else:
        # Auto-save with date prefix
        date_str = datetime.now().strftime('%Y-%m-%d')
        out_file = Path(args.input).stem + f'_{date_str}_summary.md'
        Path(out_file).write_text(result)
        print(f'\nSaved to {out_file}')

if __name__ == '__main__':
    main()
# Usage
python meeting_summariser.py notes.txt
python meeting_summariser.py notes.txt --model mistral-nemo --actions-only
python meeting_summariser.py notes.txt --output summary.md

Choosing the Right Model

Llama 3.2 8B is the default and handles most meeting notes well — it follows the structured output prompt reliably and extracts action items accurately for typical business meeting content. For very long meeting transcripts (90+ minutes) that exceed an 8K context window, use Mistral Nemo 12B with num_ctx set to 32768, which can process the full transcript in a single pass rather than requiring chunking. Qwen2.5 7B is worth trying if you find that Llama 3.2 misses action items or does not adhere to the output format strictly enough — Qwen2.5 tends to follow structured output instructions more precisely, which matters for the JSON extraction workflow.

Handling Different Note Styles

Meeting notes come in highly variable formats — bullet points, prose, half-sentences, abbreviations, speaker labels, timestamps. The summarisation prompt above handles most of these because Llama 3.2 has seen enough meeting-style text in training to interpret common note-taking conventions. For organisations with very specific note formats or domain-specific terminology, adding a brief description of the format to the system prompt improves accuracy: “These notes use the format: [Speaker initials]: [their point]. Action items are prefixed with ‘AI:’.” This context helps the model parse the structure correctly rather than guessing, which reduces missed action items and misattributed decisions.

Privacy Value

Meeting notes frequently contain sensitive business information — personnel decisions, financial discussions, strategic plans, client details. Sending this content to a cloud summarisation service creates a data handling exposure that is easy to overlook when the convenience is obvious. Processing meeting notes locally keeps every word on your machine. For organisations where meeting content is confidential by default — which is most professional environments — local summarisation is not just a convenience but the responsible default. The fact that a 7B model running on a laptop can produce useful summaries means the privacy cost of cloud alternatives is no longer justified by a quality advantage that, in 2026, has largely closed.

Why Local LLMs Work Well for Meeting Notes

Meeting summarisation is one of the tasks where local LLMs punch at or above their weight class compared to cloud models. The reason is that summarisation is fundamentally a retrieval and restructuring task rather than a knowledge task — the model needs to extract and reorganise information that is already present in the notes, not draw on world knowledge or complex reasoning. A 7B model can do this well because the cognitive requirement is lower than tasks like code generation or multi-step reasoning. The quality difference between a local 7B model and GPT-4 on well-formatted meeting notes is small enough to be irrelevant for most practical purposes.

The low temperature setting (0.2 for summaries, 0.0 for JSON extraction) is important. Meeting summarisation is a factual extraction task — you want the model to report what was in the notes accurately, not to generate creative interpretations or fill in gaps. Low temperature reduces hallucination significantly on extractive tasks like this, keeping the summary grounded in the source material rather than drifting toward plausible-sounding but invented content. Always use temperature 0.0–0.3 for action item extraction where accuracy matters, and reserve higher temperatures for the follow-up email generation where some creative rewriting is acceptable and desirable.

Handling Messy Real-World Notes

Professional meeting notes are rarely clean. They contain abbreviations only the team understands, half-finished sentences where the note-taker got distracted, names without context, and topics that were discussed across multiple points in the meeting without being explicitly connected. A few techniques improve output quality on genuinely messy notes.

First, add a pre-processing step that lightly formats the notes before passing them to the model. Replace common abbreviations (AI for action item, FYI, ASAP, TBD) with their full forms in the prompt or as a find-replace step before summarisation. This prevents the model from misinterpreting abbreviations in context — “AI: John to review” should be interpreted as “Action item: John to review” not as something about artificial intelligence.

Second, for very fragmented notes, ask the model to first reconstruct a coherent version before summarising: “First, rewrite these notes as coherent sentences preserving all information. Then produce the structured summary.” This two-pass approach produces noticeably better summaries on low-quality input because the model fills in obvious context gaps during reconstruction before extracting structure.

Third, for recurring meetings with consistent structure (weekly standups, sprint reviews, monthly business reviews), include the meeting format in the system prompt. “This is a weekly engineering standup. Standard sections are: blockers, progress since last standup, planned work for this week, and any team announcements.” The model uses this structure to organise its output around the expected format rather than inferring structure from the notes themselves, which is more reliable when the notes are sparse.

Integrating with Your Existing Workflow

The meeting summariser script is most useful when integrated into your existing workflow rather than run as a separate manual step. A few practical integration patterns:

Clipboard integration: Add a keyboard shortcut that takes whatever text is on the clipboard, runs it through the summariser, and puts the result back on the clipboard or opens it in a text editor. On macOS this is straightforward with Automator or a shell script using pbpaste and pbcopy. This makes summarisation a one-keystroke operation immediately after copying notes from wherever you keep them.

Obsidian integration: If you keep meeting notes in Obsidian, the Ollama plugin or a templater script can run the summariser on the current note and insert the result as a new section. This keeps the raw notes and the structured summary in the same file, linked by a clear separator, making both searchable within your vault.

Folder watch: A script that watches a designated folder for new .txt files and automatically produces a summary .md file alongside each new note. Drop your raw notes into the folder after a meeting, find the summary waiting when you open it. This pattern works well for teams using a shared folder on a network drive — one member runs the watcher script and everyone benefits from automatic summarisation.

Batch Processing Archived Meetings

If you have months or years of meeting notes that were never properly summarised, batch processing makes it practical to catch up. A simple loop over all note files in a directory produces summaries for the entire archive overnight, creating a searchable summary layer on top of the raw notes without requiring any manual review during processing. The time investment is writing the script once and letting it run — the summariser handles the volume automatically. For a folder of 200 meeting note files, a 7B model on a mid-range GPU completes the batch in under an hour, producing structured summaries for every meeting in the archive.

Calibrating Output Quality

The first time you run the summariser on a real set of notes, compare the output carefully against the source material. Three types of errors occur in practice. The first is missed action items — the model did not extract something that was clearly an action item in the notes. This usually indicates the action item was phrased ambiguously in the notes (“John said he would look at the pricing”) rather than explicitly (“AI: John to review pricing by Friday”). Fix this by adding to the prompt: “Interpret implicit commitments as action items even when not labelled explicitly.” The second is invented content — the model included a decision or action item that was not in the notes. This is the most serious error type and is addressed by lowering temperature further (to 0.0) and adding to the prompt: “Only include information explicitly present in the notes. Do not infer, extrapolate, or add context not in the source.” The third is attribution errors — an action item is assigned to the wrong person. This happens most often when notes use pronouns without clear antecedents. Encouraging note-takers to use names rather than pronouns is the most reliable fix, though you can also add a post-processing step that flags any action item with an ambiguous owner for human review.

Once you have calibrated the prompt against a representative sample of your typical meeting notes, the output quality is consistent enough for direct use without careful review on every run. The investment in calibration — 30–60 minutes of testing and prompt iteration — pays back quickly for anyone who attends multiple meetings per week.

Getting Started

The fastest path: copy the basic summarisation function from this article, paste in some recent meeting notes, and run it. The structured output — overview, discussion points, decisions, action items, open questions — is immediately useful for most professional contexts with no further configuration. Add the action item extractor when you want machine-readable output for task management tools. Add the follow-up email generator when you regularly send post-meeting emails and want a first draft in seconds rather than minutes. Each piece is independently useful, and the combination covers the full post-meeting workflow from raw notes to distributed summary to tracked tasks.

A Note on Accuracy

Like any LLM-based workflow, this summariser is a tool that assists human judgment rather than replacing it. For high-stakes meetings — board decisions, legal discussions, contractual commitments — always verify the summary against the source notes before distributing. For routine operational meetings (standups, project check-ins, team syncs), the model’s accuracy is typically sufficient for direct use once you have calibrated the prompt. The practical rule: apply more review time proportionally to the consequences of an error in the summary, and less review time where a missed nuance has limited impact. This tiered approach captures most of the time savings from automation while preserving appropriate human oversight where it actually matters. Used this way, the local meeting summariser becomes a reliable part of professional workflow rather than an experimental tool you have to second-guess on every run.

Leave a Comment