Skip to content
Menu
Cloud Gal 42
  • Home
    • Affiliate Disclosure
    • Privacy Policy
  • Solution Selling Tutorial
  • AI-SGAP
    • Beta Partners
    • Investors
  • 4022 Reader Updates
Cloud Gal 42

Building a Local Knowledge Wiki Part 1 Architecture and the First Memory Failures

November 11, 2026November 11, 2026 by admin

I wanted a local AI assistant that could answer questions about my work without turning my development machine into a dedicated inference server. I already had a knowledge wiki. The next step was to connect a local model to versioned sources, preserve their relationships, and make answers traceable.

The machine was an Apple M4 with 24 GiB of unified memory. The starting model was Qwen3.5 4B through Ollama. The knowledge layer used RDF/OWL, embedded Oxigraph and SHACL validation.

The first lesson arrived before a single answer: a small model download does not establish that a model will coexist comfortably with your current workload.

This series follows the build as it develops. These results cover 10–11 November 2026. The system is still a pilot; several harder questions remain unresolved.

The architecture I actually built

The knowledge lives in source documents. Qwen does not acquire their contents through training. At inference time, the application retrieves a bounded selection of source text and asks the model to work within it.

There are three distinct kinds of data:

Layer

Contents

Authority

Frozen source snapshot

Original Markdown and imported document evidence, revisions and hashes

Evidence for the local snapshot

Provenance index

Document, chunk and link metadata in Oxigraph

Location and identity of evidence

Reviewed records

Explicitly accepted source-linked statements

Narrow reviewed queries, subject to freshness and conflict checks

A fourth component is a separate fictional graph used for tests. Mixing its demo facts into real answers would be a defect.

This is a retrieval-based assistant with a graph-backed provenance layer and a separate reviewed-record path. Calling the entire system autonomous knowledge-graph reasoning would overstate what it does.

Record the actual runtime

A model name alone is not enough to reproduce a run.

Component

Recorded pilot version or identity

Hardware

Apple M4, 10 cores, 24 GiB unified memory

Operating system

macOS 26.6.2, build 25G83

Python and environment tool

Python 3.15.0; uv 0.13.1

Runtime

Portable Ollama 0.40.16

First model

qwen3.5:4b; MLX, safetensors, nvfp4; API parameter size 4.5B

Comparison model

qwen3.5:4b-q4_K_M; GGUF, llama.cpp runner

Graph packages

pyoxigraph 0.5.13; pyshacl 0.40.2; rdflib 7.6.0

These are recorded build identities, not a claim that every listed version is the current recommended release. A reader should retain the actual resolved artifacts, package hashes and model manifest used for their own run.

The “4B” model label and the API’s “4.5B” parameter-size string both appeared in our record. We kept both rather than silently making them agree.

Measure the starting machine

Our initial inspection found approximately 6,067.88 MiB of swap already in use. That matters: a model experiment begins with the machine’s existing workload, not an empty benchmark machine.

The following individual read-only checks reproduce the kinds of observations we collected on macOS. They are separated here for readability, rather than presented as a verbatim historical shell session.

sw_vers
sysctl -n machdep.cpu.brand_string
sysctl -n hw.memsize
sysctl -n hw.ncpu
sysctl vm.swapusage
/usr/sbin/sysctl -n kern.memorystatus_vm_pressure_level
/usr/bin/memory_pressure -Q

The pressure tool’s “memory free percentage” is a pressure-related indicator. We did not equate it with literal free physical RAM. Similarly, Ollama’s reported allocation is not an independently measured process RSS.

Initial process/hardware checks encountered “Operation not permitted.” A narrower read-only retry succeeded. We did not disable operating-system protections to make the inspection work.

What failed

Two MLX attempts stopped under memory pressure before completing inference. The first Q4 comparison also stopped: approximately 3.073 seconds into its first request, the three-consecutive-warning-sample guard fired.

At that point Docker still had 15 running containers. We preserved the development environment and stopped the pilot. There was no completed Q4 answer, no extraction-quality result and no valid generation-speed measurement from that attempt.

The comparison wrapper exited successfully because it completed its stop-and-record procedure. That exit status did not mean the model experiment passed. An HTTP 500 observed during cancellation was also not evidence of an independent model crash.

What subsequently worked

On 11 November I explicitly quit Docker Desktop before a new bounded experiment. Four small synthetic cases completed:

Case

Recorded API wall time

Result

Positive extraction, first call

5.055 seconds

Expected facts and exact evidence

Positive extraction, repeat

3.536 seconds

Expected facts and exact evidence

Negated statement

1.478 seconds

No claims

Unstated fact

1.652 seconds

No claims

The positive calls generated at about 31.3 tokens per second. All 26 retained memory samples showed normal pressure and zero swap. Each response was followed by verified unloading.

This does not isolate Docker as the cause of the earlier failures. Swap was already zero before Docker quit, and other workload conditions had changed. It establishes that this particular small workload succeeded under the later recorded conditions.

Separate graph cost from inference cost

A tiny graph-only check validated 26 triples in approximately 0.099 seconds, with the Python process peaking at about 66.1 MiB RSS. That does not predict large-graph performance. It does show that the immediate pressure problem was not explained by this small graph operation.

The next experiment should measure load time, prompt evaluation, generation, validation and cleanup separately. A single “request duration” hides where the resource cost occurs.

For a reproduction, begin with positive, repeated-positive, negated and unknown synthetic cases. Save baseline samples and failed attempts. Do not start by importing an entire private corpus or assuming that a successful model load establishes useful answers.

Next: isolate the runtime, bound each request, and validate a graph before allowing model output into it.

Related

Recent Posts

  • Building a Local Knowledge Wiki Part 1 Architecture and the First Memory Failures
  • Selling AI Security Like Life Insurance
  • What We Mean by “Harness” in AI
  • Same Container, Different Findings: What Trivy and Grype Taught Us
  • Role of AI/ML in Cybersecurity

Recent Comments

  • Rafael on Installing Debian on OCI
  • Jorge on Installing Debian on OCI
  • admin on Installing Debian on OCI
  • Andreas on Installing Debian on OCI
  • admin on Installing Debian on OCI

Archives

  • November 2026
  • October 2026
  • December 2022
  • February 2022
  • September 2021
  • July 2021
  • June 2021
  • May 2021
  • April 2021
  • February 2021
  • January 2021
  • November 2020
  • October 2020

Categories

  • aws
  • bcdr
  • cloud
  • cloudsecurity
  • compliance
  • informationsecurity
  • oracle
  • pci
  • QuickGuide
  • security
  • Uncategorized
©2026 Cloud Gal 42 | Powered by WordPress and Superb Themes!