In 2025, I wrote 162 journal entries. 193,761 words. I fed every single one to a local LLM. The big discovery? Mostly stuff I already knew.
It started in December. Year-end reflection mood. I had almost 200,000 words of unfiltered personal writing sitting on my hard drive, and I wondered: could an LLM dig through it and find something I missed?
I already knew LLMs could extract structured data from text. I'd done it with web pages, long PDFs, meeting notes. A journal is just text. How hard could it be?
Harder than I thought. But not for the usual reasons.
Privacy wall
The thing about journals: they contain your actual thoughts. The messy ones. The ones you would not want a random support engineer in a data center to read.
I looked at the big LLM APIs. Even the ones with promises about not training on your data still keep logs for 30 days. And their safety classifiers? They flag certain mental health topics. Get flagged enough and your account gets reviewed by a human. Or banned.
So no cloud models. Not for this.
Local LLM it was. My MacBook Pro with an M4 Pro and 48GB of RAM would have to do the heavy lifting. I fired up Apple's mlx-lm package and started experimenting.
Model jail
Picking a model took longer than writing the actual code. Anyone who's spent time on the local LLM subreddits knows the vibe: ten people will give you twelve opinions on the "best" model. There is no consensus, just strong feelings.
I tried a bunch. My final lineup:
- Qwen 2.5 Instruct 32b at 8-bit quantization for initial runs. Fit in RAM alongside a browser and editor, barely.
- Llama-3.3 70B Instruct at 3-bit for the final pass. This thing needed me to quit every other app and tweak GPU RAM allocation to even fit. My MacBook sounded like it was reconsidering its life choices.
- Qwen 3 4b Instruct at 4-bit for quick iteration. Small, fast, good enough for testing prompts.
The 70B model gave the best results. The smaller ones hallucinated more. Qwen 2.5 was particularly bad about inventing things that sounded plausible but weren't true.
The pipeline
I spent several evenings building Python scripts to pipe journal entries through the model one at a time. The code is mostly AI-generated slop (I yelled at Claude Code a lot), but it works. The prompts are the interesting part.
My approach had two layers:
- A core prompt that sets context for every analysis
- Task-specific prompts for each extraction field
The core prompt told the model: "You are helping someone reflect on their year. You will be given a journal entry wrapped in tags. Extract only what's requested. Return only valid JSON."
Then I appended task-specific instructions. One prompt for hobbies. One for health issues. One for social events. One for people mentioned. Twelve different analyses total, though only six produced anything worth looking at.
I extracted the following from each entry:
- Things I was grateful for
- Hobbies and side projects
- Locations visited
- Media consumed (books, movies, games, music)
- Mental health: good day or bad day
- Specific mental health issues mentioned
- Physical health: good day or bad day
- Physical health issues mentioned
- Things I was proud of
- Social activities
- Travel destinations
- Friends, family, acquaintances mentioned
- New people met
The Sarah problem
I learned something the hard way: LLMs love examples. They love them so much they'll inject them into your data.
My prompts included example outputs. In one, I wrote about having "dinner with Sarah" to demonstrate the format. Every single model I tried decided that I must have had dinner with Sarah several times during my year. I don't know anyone named Sarah.
This happened consistently across models. The example names and scenarios leaked into the extraction output every single time. I had to make my examples deliberately weird and obviously fake so I could filter them out with Python afterwards. Real person names in examples? Disaster.
Synonym hell
Another fun problem: the model could only see one journal entry at a time. So "exhaustion" in one entry and "fatigue" in another became two separate health issues. "Migraine" and "bad headache" split the same way.
My first fix was to keep a running list of already-discovered terms and append them to each subsequent prompt: "If the entry mentions something similar to an existing term, use the existing term." This backfired badly. The model started hallucinating connections between unrelated concepts. I still don't fully understand why a list of 20 health terms caused such strange behavior.
My second fix worked better: a separate normalization pass after all extraction completed. I dumped every unique term into a prompt and asked the LLM to produce a synonym mapping. Wasteful, but it worked.
The graphs
After all the extraction and normalization, I had structured data. I asked Claude to write matplotlib code to visualize it. Tweaked the charts until they looked presentable.
The big letdown
The honest truth: all of this work produced graphs that confirmed what I already knew.
I knew which months were rough. I knew which hobbies I'd been obsessed with. I knew who I'd spent time with. The visualizations were clean and satisfying to look at, but they didn't teach me anything.
I spent weeks building a pipeline, fighting hallucinations, normalizing synonyms, writing and rewriting prompts. The output was a prettier version of my own memory.
What I actually learned
The problem was me. I didn't know what questions to ask.
I went into this project excited about the technology. I had a local LLM, and I went looking for something to do with it. I skipped the boring, important step of figuring out what I actually wanted to learn.
What hypotheses did I want to test? What patterns was I looking for? I never answered those before writing code. No amount of AI fixes not knowing what you're trying to find.
The closest thing to a real insight: if you know what you're looking for and have access to good models, you can process unstructured personal data at a scale that would take a human weeks or months. But the bottleneck is never the model. It's knowing what to ask.
What's next
I'm going to try again at the end of this year. But this time I'll start with the questions instead of the technology. I need to spend real time thinking about what I want to discover, and which assumptions I want to test because their answers might genuinely surprise me.
The code and prompts are on GitHub. I won't link them here because honestly the prompts are the only interesting part and they're easy to reconstruct from this post.
If you try this yourself: use obviously fake examples in your prompts, run a normalization pass, and for the love of god figure out what you want to ask before you start coding.