Turn Kindle Highlights Into Real Book Summaries: A Local AI Agent with Python and Ollama
Your highlights are sitting unread in a folder. Build a fully offline agent that reads them and produces a structured summary — core thesis, key takeaways, action items — straight into your Obsidian vault.
If you read a lot, you know the two failure modes: you read it and forget it, or you highlighted it and never came back.
Highlights pile up — exported from Kindle, typed into Obsidian while reading — and quietly become digital hoarding. Turning them into something useful means sitting down and re-reading every line to write a summary, which takes exactly the kind of focused time you rarely have.
This is where cognitive automation earns its keep. In this guide we build a private AI agent with Python and Ollama that reads your raw highlights, extracts the core thesis, the key takeaways and actionable insights, and writes a structured book summary into your Obsidian vault — fully offline, with no monthly API bill.
Why do this locally
Summarising books with AI is not new. Doing it locally is meaningfully different:
1. Your thinking stays yours. Reading notes are rarely neutral — they carry opinions, reactions, personal goals. Processing them on your own machine means none of that leaves it.
2. Output shaped to your vault. You control the prompt, so the agent writes in your Markdown structure, uses your tags, and emits real bi-directional links ([[Note Name]]) that fit the vault you already have.
3. No cap on volume. Five books or a hundred, run it as often as you like. No token counting, no per-request cost.
How it works
[ Obsidian highlight note ] → [ Python script ] → [ Local Ollama LLM ] → [ Structured summary ]
raw highlights read + prompt synthesise note in Obsidian
1. Input — read a raw highlights file from Reading/Highlights/
2. Analysis — send it to Ollama with a prompt that asks the model to think like an executive editor
3. Output — write a clean summary note into Reading/Summaries/
What you need
- Python 3.8+ (standard library only — nothing to install)
- Ollama running at
http://localhost:11434 - A model —
gemma2orllama3.1works well (ollama pull gemma2) - An Obsidian vault
The agent
Create book_summary_agent.py:
import os
import json
import urllib.request
from datetime import datetime
OBSIDIAN_VAULT_DIR = os.path.expanduser("~/ObsidianVault")
HIGHLIGHTS_DIR = os.path.join(OBSIDIAN_VAULT_DIR, "Reading", "Highlights")
SUMMARIES_DIR = os.path.join(OBSIDIAN_VAULT_DIR, "Reading", "Summaries")
OLLAMA_MODEL = "gemma2"
OLLAMA_URL = "http://localhost:11434/api/generate"
PROMPT_TEMPLATE = """
You are an expert at knowledge synthesis and writing book summaries.
Read the raw highlights and reading notes below, then synthesise them into a
structured, genuinely useful book summary.
[RAW HIGHLIGHTS]:
{raw_highlights}
Follow this structure exactly:
1. **Core Thesis:** the central argument of the book in 2-3 sentences
2. **Key Takeaways:** 3-5 main points, each with a short plain-language explanation
3. **Actionable Insights:** 3 things the reader can start doing immediately
4. **Memorable Quote:** the 1-2 most striking lines from the highlights
5. **Suggested Tags:** 3-5 tags in #tag format
Write concisely, in Markdown.
"""
def query_ollama(prompt):
payload = {"model": OLLAMA_MODEL, "prompt": prompt, "stream": False}
data = json.dumps(payload).encode("utf-8")
req = urllib.request.Request(
OLLAMA_URL, data=data, headers={"Content-Type": "application/json"})
try:
with urllib.request.urlopen(req) as response:
return json.loads(response.read().decode("utf-8")).get("response", "")
except Exception as e:
print(f"Could not reach Ollama: {e}")
return None
def process_book_highlights(filename):
input_path = os.path.join(HIGHLIGHTS_DIR, filename)
if not os.path.exists(input_path):
print(f"File not found: {input_path}")
return
with open(input_path, "r", encoding="utf-8") as f:
raw_highlights = f.read()
book_name = os.path.splitext(filename)[0]
print(f"Synthesising summary for: {book_name}...")
ai_response = query_ollama(PROMPT_TEMPLATE.format(raw_highlights=raw_highlights))
if not ai_response:
print("No summary produced.")
return
os.makedirs(SUMMARIES_DIR, exist_ok=True)
today_str = datetime.now().strftime("%Y-%m-%d")
output_path = os.path.join(SUMMARIES_DIR, f"Summary - {book_name}.md")
file_content = f"""---
title: "Summary: {book_name}"
date: "{today_str}"
type: "book-summary"
source_note: "[[{book_name}]]"
---
# Book Summary: {book_name}
> Synthesised locally on {today_str}
{ai_response}
---
*Generated from the raw highlights in `[[{filename}]]`*
"""
with open(output_path, "w", encoding="utf-8") as f:
f.write(file_content)
print(f"Saved: {output_path}")
if __name__ == "__main__":
process_book_highlights("Atomic-Habits-Highlights.md")
Running it
Drop a highlights file into Reading/Highlights/ — an export from Kindle works as-is — and run:
python3 book_summary_agent.py
The summary lands in Reading/Summaries/, already linked back to the source note. Open Obsidian and it is there, tagged and ready to connect to the rest of your vault.
Making it better
Two changes are worth making once it works:
- Batch the whole folder. Loop over every file in
Reading/Highlights/and skip any that already have a summary. Then a year of reading gets processed in one run. - Tune the prompt to your own thinking. The default asks for an executive-editor summary. If you read mostly technical books, ask for the argument structure and the evidence behind each claim instead. This is the part worth iterating on — the model is the same, the prompt is what makes the output yours.
The point is not the summary itself. It is that highlights stop being a graveyard and start becoming a connected part of what you actually know.