On-device language models · field notes

PrivateCore SiliconOracle

On-Device Language Models for Personal AI — architecture, findings, and the pivot.

Every serious attempt at a personal AI assistant in 2023–2025 made the same architectural mistake: it shipped your most intimate data — photos, calendar, journals, health records — to a cloud provider in exchange for intelligence. The intelligence was real. The privacy cost was quietly enormous. This is the story of building the opposite, what broke, and what we learned by matching the task to what small models are genuinely good at.

01 — Initial hypothesisThe bet: the model never leaves the device

The baseline question behind PrivateCore was simple: what does a personal AI look like if the model never leaves the device? This was not a philosophical position — it was an engineering bet. Apple Silicon iPhones from the iPhone 15 generation carry a unified-memory architecture, 8 GB of DRAM, a 16-core Neural Engine, and a GPU capable of mixed-precision matrix multiply at meaningful bandwidth. The 2023 wave of open-weight models in the 2B–7B range was being actively quantized to 4-bit, collapsing from multi-gigabyte bloat into a footprint a phone could actually hold. The question was whether quality-per-watt was good enough for the task.

The system

PrivateCore was built as a full-lifecycle personal knowledge-management substrate, entirely on-device:

PrivateCore system architecture, a layered on-device stack with a hard cloud boundary at the top
Figure 1 · illustration in preparation
PrivateCore System Architecture

A layered diagram. Bottom: iOS data sources — Photos Library, EventKit, HealthKit — arrows up labelled "pointer model (no copy)", "event metadata", "daily aggregates". Middle: Storage & Index — SQLite tables, sqlite-vec virtual table (384D MiniLM embeddings), FTS5 index. Left column: Runtime — MLXQueue (serialized GPU access), MLXEngine (Metal inference), ModelRouter (VLM↔text hot-swap). Right column: Services — VLMService, WikiGenerator, VideoAssemblyEngine, EmbeddingService, OCRService. Top: UI — ConceptGraphView, WikiArticleView, LibraryView. A hard cloud boundary across the top that nothing crosses, annotated "all inference, storage, and retrieval on-device."

Figure 1 — The on-device stack. Nothing crosses the cloud boundary at the top.

The ML stack

The inference stack is MLX Swift, Apple's own framework for Apple Silicon unified memory. The choice was deliberate: MLX runs directly against Metal, eliminating the CoreML compilation and weight-conversion overhead of ONNX or llama.cpp paths; the same weights run identically on the developer's Mac Mini M4 and on the iPhone 15 Pro target — critical for fast iteration; and MLX's lazy graph execution fits a streaming-generation UX.

ModelTaskQuantizationRAM footprint
Qwen2.5-VL 2BVision captioningQ4~1.5 GB
Ministral 3BText generationQ4~2.2 GB

With iOS overhead (~2.0 GB) and app storage (~0.3 GB), total memory pressure on an 8 GB device reaches ~6 GB when both models are warm — near the jetsam ceiling for a foreground app. This forced a sequential hot-swap architecture: the two models cannot coexist in memory. ModelRouter enforces exclusive GPU access through MLXQueue, a custom actor that serializes acquire()/release(). Once the vision pipeline enters text generation, incoming vision requests are queued, not rejected — they wait. A hardcoded 20 MB MLX cache limit prevents the allocator from ballooning and triggering an out-of-memory kill before model weights are evicted.

02 — The generation pipelineHow a day becomes a wiki

The daily wiki generator in PrivateCore operated as a single coherent pass: gather sources (photos, events, cards) → even-sample four photos spread across the day to avoid over-weighting burst sessions → pre-fill captions for any uncaptioned sampled photo via the vision model, persisting each caption for reuse → assemble a context block from captions, event titles, geocoded location, and health aggregates → make a single structured-JSON completion from Ministral 3B → persist the article and its source links → guard with a SHA-256 hash of the source IDs so unchanged inputs skip regeneration.

The prompt follows a structured-output pattern familiar from Karpathy's "LLMs as structured programs" framing: we don't ask for prose and then parse it — we specify the exact JSON schema and enforce it through the system prompt with a concrete example. Small models (2–3B) struggle with unconstrained generation at length but reliably produce valid JSON when the schema is short (fewer than three nesting levels, fewer than ten keys).

Monolithic wiki generation pipeline
Figure 2 · illustration in preparation
Wiki Generation Pipeline (monolithic)

A sequential flowchart: "Photos (day)" → "evenSample(4)" → "VLMService [Qwen2.5-VL 2B]" → "Caption → Cards table". In parallel below: "Calendar Events" + "Health Aggregates" + "Cards" → "Context assembler". Both converge at "Prompt builder" → "MLXEngine [Ministral 3B Q4]" → "Structured JSON parser" → "WikiArticle" → "SQLite store". A dashed box around the VLMService + MLXEngine path labelled "MLXQueue — sequential access enforced". A side branch: "content_hash (SHA-256 of source IDs)" → "Cache check" gate before the prompt builder, arrow labelled "skip if unchanged".

Figure 2 — One pass, one large JSON completion. The cache gate skips unchanged days.

The embedding layer

Every piece of text entering the system — captions, card content, wiki leads, event notes — passes through MiniLM-L6-v2, a 22 MB CoreML model producing 384-dimensional sentence embeddings stored in a sqlite-vec virtual table with cosine-similarity lookup. The result is a semantic memory layer that answers "show me everything around the time I visited the mountains" without a date match — the embedding of "alpine hiking" lands near captions like "rocky trail, snow-capped peaks, pine forest." MiniLM is the one model that is always loaded; at 22 MB it fits comfortably and never competes for the GPU, since it runs entirely on the Neural Engine via CoreML.

03 — Performance findingsWhat worked, and what was hard

What worked

What was hard

Memory budget on an 8 GB iPhone 15 Pro
Figure 3 · illustration in preparation
Memory Budget (iPhone 15 Pro, 8 GB)

A stacked bar, total 8 GB. From bottom: "iOS kernel + dyld (~1.5 GB, always)", "App code + frameworks (~0.5 GB)", "SQLite + vec store (~0.2 GB)", "MiniLM (CoreML, always loaded, 0.1 GB)", "Qwen2.5-VL 2B Q4 (~1.5 GB, vision phase)", "Ministral 3B Q4 (~2.2 GB, text phase)", "Free headroom (~2.0 GB)". Two alternating shaded regions, "Vision phase active" and "Text phase active", show only one of Qwen/Ministral loaded at a time; the freed region shown dotted.

Figure 3 — Only one large model fits at a time. The hot-swap is forced by the 8 GB ceiling.

04 — The pivotFrom archivist to oracle

The findings pointed at a fundamental tension in PrivateCore's value proposition:

The app promised factual, accurate personal memory. The models delivered creative, plausible personal narrative.

These are not the same thing. A personal knowledge-management system has a strong correctness contract — you trust it to retrieve facts about your life accurately. A 3B-parameter, 4-bit-quantized model running under thermal pressure on a phone is not a reliable factual archivist. It is an excellent stylist given facts it was handed.

The insight that unlocked the pivot came from watching users: they were most delighted not when the wiki accurately summarized their day, but when it transformed the day into something more — more dramatic, more poetic, more theatrically significant. The generative leap was the feature. Factual accuracy was the expectation they had to manage around.

This is a known property of small models. As Karpathy has articulated it, LLMs at inference time are essentially autocomplete engines shaped by RLHF toward instruction following. At 2–3B parameters they have limited factual memory but substantial stylistic fluency. They know how Shakespeare sounds. They know how a hardboiled detective narrates. They can sustain a persona across a few hundred tokens far more reliably than they can hold factual precision across a multi-entity prompt.

The pivot was to lean into what the models are actually good at: take a photo, extract visual context with the vision model, and render it through a strongly-voiced stylistic persona. Deliberately non-factual output. The tendency to invent becomes a feature, because the output was never meant to be a transcript — it is an interpretation, an augmentation, a play.

The pivot, a 2x2 of model reliability versus user expectation of accuracy
Figure 4 · illustration in preparation
The Pivot: Correctness Contract vs. Stylistic Freedom

A 2×2 matrix. X-axis: "Model reliability for task" (Low→High). Y-axis: "User expectation of accuracy" (Low→High). Quadrants: top-left "Dangerous gap (PrivateCore diary risk)"; top-right "Reference apps"; bottom-left "Creative toys"; bottom-right "Style amplifiers (SiliconOracle)". A dotted arrow from PrivateCore (top-left, circled red) to SiliconOracle (bottom-right, circled green), labelled "The pivot: reframe the contract."

Figure 4 — Same models, different contract. The pivot moves the product into the quadrant the models can actually serve.

What changed

PrivateCore was forked into SiliconOracle in late May 2026. The fork is intentionally permanent — the two codebases share a common ancestor but have divergent contracts.

PrivateCoreSiliconOracle
Output contractFactual personal recordDeliberate non-factual interpretation
Failure modeHallucination (bad)Creative invention (feature)
User contract"This is what happened""This is how the oracle sees it"
ScopeFull PKM (photos, calendar, health, people, places, trips)Photos only
GraphSemantic concept mapOracle cosmology (motif "birth chart")
Style system2 generic presets4 strongly-voiced personas
Thermal recoveryAbort + errorPartial save + resume
GenerationMonolithic (one call)Incremental (one call per section)

The features removed from SiliconOracle — people, trip detection, places, contacts, health, dashboard — are not degradations. They are deliberate scope reductions. Each carried the same correctness contract PrivateCore struggled to deliver. SiliconOracle makes no claim about the world that could be falsified.

05 — SiliconOracleArchitecture and current state

SiliconOracle is a focused iOS app for oracle-register interpretation of personal photos. The user selects a photo, chooses a voice (Shakespeare, Sherlock, Diva, Manga), and receives a short stylized "reading" rendered as a shareable 9:16 card. Daily and weekly wikis persist in the oracle register with the same four styles. The ML stack is inherited from PrivateCore — same two models, same MLX Swift runtime, same serialized GPU access. The improvements are in generation architecture and production readiness.

Incremental wiki generation

SiliconOracle replaces the monolithic call with incremental, per-section generation: a short "lead" call produces the opening paragraph; each subsequent call produces exactly one section body; after each call the partial article is saved; and a stream-state object updates the UI in real time — "Captioning photos… → Writing overview… → Writing timeline…" rather than a spinner.

Incremental generation architecture with a thermal-state monitor
Figure 5 · illustration in preparation
Incremental Generation Architecture

A vertical pipeline with a thermal-state monitor column on the right. Main pipeline: "Source gathering" → "Caption 4 undescribed photos [VLMService]" → "stream phase = 'Captioning…'" → "Lead generation [Ministral 3B]" → "Save partial (isPartial=true)" → loop: "Section N generation" → "Append section, save partial" → end loop → "isPartial=false, save final". At each step an arrow branches right to "ThermalState check"; if serious/critical: "schedule(pendingContext)" → "sleep until cool" → "resume from last saved partial". A left column shows phase text updating: "Captioning… → Writing intro… → Writing section 1…".

Figure 5 — Decomposition buys thermal recovery, visible progress, and per-section retries.

The oracle persona system

The four voices are not cosmetic themes applied afterward — they are system-prompt personas that condition the entire pass from the first token: Shakespeare (early-modern English, dramatic soliloquy), Sherlock (forensic first-person deduction), Diva (pop-tabloid energy, breathless emphasis), Manga (inner monologue, weight-of-moment, destiny vocabulary). Each defines a prompt prefix, section titles, a visual theme, and a card-specific prompt.

At 3B parameters, Ministral cannot reliably recall what the Roman Forum is — but it can sustain early-modern English verse across 600 tokens without breaking register.

The persona acts as a strong prior that pulls the token distribution toward a recognizable stylistic attractor, leaving the model free to invent within those bounds rather than hallucinate against a factual baseline.

Persona as a token distribution prior
Figure 6 · illustration in preparation
Persona as Token-Distribution Prior

Two generation paths. Left: "Unconstrained generation" — a wide bell curve over "plausible output space" with a broad tail labelled "hallucination region". Right: "Persona-conditioned generation" — a narrower curve shifted into a "stylistic attractor basin" (e.g. Early Modern English). The tail still exists but is relabelled "out-of-register invention — still creative, no longer falsifiable." Caption: "Persona conditioning trades factual precision for stylistic reliability — the right trade-off for small models on personal data."

Figure 6 — Conditioning trades factual precision for stylistic reliability — the correct trade for small models.

The cosmology graph, scheduling, downloads

PrivateCore's semantic concept map is reimagined as a cosmological birth chart: a dark star-field with concept nodes in orbital rings and a slow precession. The underlying data is identical — frequency-weighted keyword extraction from captions and journal text — but the metaphor shifts from information architecture to divination, with Day / Week / Full scopes mapping concept frequency to orbital radius and node size.

Background work is centralized in a job queue with typed entries (captioning, embedding, OCR), priority levels, retry with exponential backoff, and explicit thermal gating. A five-second grace period after foreground return precedes any GPU-bound job — rapid background-to-foreground transitions destabilize the Metal command queue and cause silent failures, a behaviour discovered empirically. Vision jobs are excluded from background processing entirely, since iOS revokes GPU access in the background; only the CPU-bound embedding and OCR jobs run off-screen.

The app ships with no model weights — the bundle is ~100 MB over-the-air, comfortably under Apple's 200 MB cellular threshold. Models download on first launch over a background URL session that survives suspension, with per-file progress, SHA-256 checksum verification, and resumable downloads persisted to disk so they survive a process kill.

The accuracy safety system

A deliberately non-factual posture required an explicit safety system — not for technical accuracy, but for psychological risk. An oracle that diagnoses mental state, offers life advice, or makes future-facing predictions about a person can be harmful in ways a wrong historical date cannot. The context builder enforces five constraints entirely through the prompt: tense restriction (present/past only, no "you will…"), register matching (heavy journal entries get grave, never ironic, prose), no mockery, no counsel, and no stamped date/location headers. Enforcing through the prompt rather than a regex filter is deliberate — filters produce false positives on innocent phrasing like "what will linger" in a past-tense reflection.

Human-in-the-loop assuranceA human sampling protocol gates the journal-aware oracle path before any external beta: at least ten oracle outputs are reviewed, including at least two drawn from heavy journal entries (grief, loss, anxiety), each walked through a five-row tonal-safety checklist. No automated test substitutes for human judgment on tonal safety.

Monetization

SiliconOracle monetizes via a soft tip-gate: after every few generations a skippable sheet offers a one-time unlock. Generation is never blocked — the gate is always skippable — and sharing is always free and never gated. The interval is A/B tested through remote config (aggressive / control / lenient cohorts).

06 — Current statusTwo codebases, one foundation

PrivateCore remains the engineering foundation — the substrate from which SiliconOracle was extracted. It is, user-facing, named Lifelore.

Complete & functional (PrivateCore / Lifelore)

  • Full ML inference runtime (serialized GPU access, Metal engine, model router, MLX Swift)
  • Vision captioning pipeline (Qwen2.5-VL 2B Q4)
  • Daily / weekly / trip / person / place wiki generation
  • Full SQLite data model + vector store + full-text index
  • Force-directed, filterable concept graph
  • 9:16 video assembly (motion, music, narration)
  • PDF export, share extension, semantic search

In progress & planned

  • UI polish, PDF export, onboarding, and freemium (StoreKit 2) work
  • An infrastructure-hardening merge from SiliconOracle: deadlock-safe GPU queue, model-download recovery, caption checkpointing for interrupted sessions, generation telemetry
  • Incremental wiki generation (pending a schema change and SQLite migration) and thermal-resume, best executed as a dedicated sprint
  • The cosmology graph views (pending a design decision on branding vs. the richer concept taxonomy)

SiliconOracle's near-term priority is its first external beta: a single-photo "Character Reading" feature and cosmology-graph scope navigation are the headline features, gated behind completion of the human sampling protocol for the journal-aware path.

07 — Architectural lessonsWhat generalizes

Match the task to the model's actual strengths

The most general lesson: a 3B-parameter Q4 model is not a smaller GPT-4. It is a different kind of tool — strong short-range coherence (under ~500 tokens), reliable structured output for small schemas, excellent stylistic fluency for well-represented registers; weak factual recall, poor cross-document coherence, rapid degradation under thermal pressure. Designing for these actual characteristics — rather than papering over the limitations — is what made SiliconOracle viable. The oracle register converts the model's primary failure mode (hallucination) into its primary value proposition (interpretation).

Strong personas outperform weak prompts at small scale

"Write a diary entry about my day" produces generic, slightly uncanny prose. "Thou art the Bard; speak of this day as Hamlet might" produces something genuinely entertaining. The persona is a strong distributional prior, pulling generation toward a well-represented basin in the training distribution. It is not a trick — instruction-tuned small models saw far more Shakespeare-style exercises than "personal diary" writing in fine-tuning, so persona conditioning is genuinely more reliable.

Incremental beats monolithic in thermal environments

A 600-token monolithic completion is thermally risky on a phone under load. Five 120-token completions are individually safer, and a failure at step three preserves steps one and two. The per-call overhead is negligible against inference cost. For any LLM pipeline in a thermally-constrained environment, incremental is simply the more robust architecture.

The pointer model is the correct abstraction; sequential GPU access is non-negotiable

Identifiers are stable, the library is append-only, and the system manages asset lifecycle correctly — any approach that copies pixel data into app storage is fighting the platform. And concurrent Metal command buffers from two MLX model instances on iPhone produce crashes that are hard to reproduce and impossible to debug from logs. A serialized GPU queue with explicit acquire/release is the minimum viable discipline; any future multi-model architecture (e.g. a small draft model for speculative decoding) must reason explicitly about GPU ownership.

Actor-based GPU serialization timeline
Figure 7 · illustration in preparation
Actor-Based GPU Serialization

A timeline with three swim lanes: "VLMService (vision)", "WikiGenerator (text)", "BackgroundScheduler". Time flows left to right. VLMService calls acquire() and holds the GPU (coloured block). WikiGenerator calls acquire() and shows a grey "waiting" block until VLMService release(). BackgroundScheduler acquire() waits until WikiGenerator release(). Annotation: "only one consumer holds the GPU at any time." An inset shows the consequence of NOT serializing: two overlapping coloured blocks with a red "CRASH" label.

Figure 7 — One consumer holds the GPU at any time. The inset shows what happens without serialization.

08 — What comes nextThe research horizon

The sequential hot-swap constraint is the current ceiling on user-experience quality. One path forward — once Apple Silicon reaches 12 GB+ on iPhone — is persistent loading of both models simultaneously. At today's memory configurations, careful pipelining (begin loading the text model while the vision model finishes captioning) can cut the swap penalty from 3–8 seconds to 1–2.

The deeper open question is whether a small, fast draft model (e.g. a 135M-parameter speculative draft for the 3B target) could accelerate time-to-first-token enough to meaningfully change the perceived latency of on-device generation for personal AI. That is on the research horizon — and it is exactly the kind of question that decides whether private, on-device intelligence becomes the default rather than the exception.