CludeMem-E4B

Structured memory operations for AI agents, on your own hardware.

CludeMem-E4B is a LoRA adapter for Gemma 4 E4B that gives an agent a working memory: nine operations, from extracting and classifying memories to reconciling contradictions and answering from cited evidence. Each operation is trained to return one JSON object with a fixed schema, so replies can be validated and stored directly, and the merged GGUF builds run offline in Ollama, so the memories an agent keeps about its user never have to leave the device. RECONCILE, the operation that catches contradictions, is measured on two human-labelled dialogue benchmarks and on four runtimes; the other eight ship with reference pairs checked on every runtime.

At a glance

  • Measured on human-labelled dialogue: 74.0% and 77.3% strict 4-way accuracy. RECONCILE, the contradiction-detection operation, scores 74.0% (95% CI 70.7–77.0) on DNLI_gp and 77.3% (74.0–80.2) on DECODE_gp, 1,450 human-labelled pairs in all. The majority class scores 33.3% and 50.0%, and the strongest surface-cue baseline 36.4% and 54.1%.
  • Contradiction flags you can act on: 93.5% and 92.9% precision. On these label-balanced samples, 93.5% (DNLI_gp) and 92.9% (DECODE_gp) of its contradiction flags are correct, and it flags only 2.6% and 5.1% of the pairs that do not contradict. As a yes/no contradiction detector it is right on 89.7% and 81.0% of the pairs, against 66.7% and 50.0% for the majority class. Precision depends on how common contradictions are in your data; the false-flag rate is the figure to carry over (Usage notes).
  • When it misses, it keeps both memories. It finds 74.4% and 67.1% of the gold contradictions; 177 of the 179 it misses come back as consistent, so a missed contradiction leaves both memories in place rather than dropping one.
  • Runs offline in Ollama on a 5.3 GB file, within 1.1 points of PyTorch. On the same items, GGUF Q8_0 scores 73.7 / 77.9, GGUF Q4_K_M 72.9 / 76.7 and MLX 74.1 / 77.9, each within 1.1 points of PyTorch (74.0 / 77.3). PyTorch and MLX gave the same verdict on 1,440 of the 1,450 pairs. The Q4_K_M build is 5.3 GB, the Q8_0 build 8.0 GB; both include the base model.
  • JSON without a grammar: 5,798 of 5,800 replies parsed. Across those four RECONCILE runs, under plain greedy decoding with no grammar or schema constraint, 5,798 of 5,800 replies parsed as a JSON object with all four keys, including all 1,450 from PyTorch. Of the parsed replies, 25 carried a verdict outside the four labels; they and the 2 that did not parse were scored as wrong and count against the accuracies above.
  • No DNLI or DECODE data was used in training. The adapter was trained on synthetic persona timelines and tested on human-written dialogue (Training has the overlap check).

The nine operations

Each call sends the operation's system prompt as the system message and the memory data as the user message, rendered with the Gemma 4 chat template and decoded greedily. Pass enable_thinking=True: it only adds the <|think|> line that opened the system turn in training. The model does not write a thinking block; it replies with one JSON object. prompts.json documents each input format, the output keys and values, and a reference input/output pair.

Operation What it does System prompt (exact)
CLASSIFY Types one memory as episodic or semantic and scores its importance and emotional valence, with tags and concepts. Classify the memory. Output JSON: {type, importance (0-1), tags[], concepts[], emotional_valence (-1..1)}.
EXTRACT Turns a dialogue into atomic memories, skipping lines that carry none (logs, tables, tool-call JSON, code). Extract atomic memories from the text. Output JSON: {memories:[{content, summary, type}]}.
ENTITIES Extracts people, locations, organizations and projects, with aliases and relations such as lives_in. Extract entities and relations. Output JSON: {entities:[{name,type,aliases}], relations:[{head,type,tail}]}.
TEMPORAL Dates a new event and links it as before or after the known events. Extract the event date and its temporal links to the known events. Output JSON: {event_date, precision, links:[{type,target}]}.
CONSOLIDATE Consolidates a set of memories into insights, each citing its evidence ids. Consolidate the memories into evidence-linked insights. Output JSON: {insights:[{content, evidence[]}]}.
COMPACT Compacts old memories into one summary that preserves the entities and the date range. Compact the old memories into one summary, preserving entities and the date range. Output JSON: {summary, preserved_entities[], date_range:{start,end}}.
RECONCILE Decides whether two memories are consistent, contradict, duplicate, or one supersedes the other; names the weaker one. Decide how the two memories relate. Output JSON: {verdict, resolution, weaker_id, confidence}.
QUERY Expands a question into search queries, entities, time constraints and an intent. Understand the query. Output JSON: {expanded_queries[], temporal_constraints, type_filters[], entities[], intent}.
ANSWER Answers only from the supplied memories, cites them, and abstains when they do not support an answer. Answer ONLY from the provided memories; abstain if unsupported. Output JSON: {rationale, answer, citations[], confidence, abstain}.

Quick start

All three examples send the ANSWER prompt with a test item from prompts.json.

Ollama (offline)

hf download sebs-clude/CludeMem-e4b gguf/cludemem-e4b-v3.Q4_K_M.gguf gguf/Modelfile --local-dir cludemem-v3
cd cludemem-v3/gguf
ollama create cludemem-e4b-v3 -f Modelfile      # the Modelfile's FROM line names cludemem-e4b-v3.Q4_K_M.gguf

curl -s http://localhost:11434/api/chat -d '{
  "model": "cludemem-e4b-v3", "stream": false, "options": {"temperature": 0},
  "messages": [
    {"role": "system", "content": "Answer ONLY from the provided memories; abstain if unsupported. Output JSON: {rationale, answer, citations[], confidence, abstain}."},
    {"role": "user", "content": "Question: Where does Maya live now?\n\nMemories:\n[f2] Maya moved to Austin on 2026-10-22.\n[f1] Maya lives in Lisbon.\n[f3] Maya currently resides in Austin."}
  ]
}'

Leave think unset: the prompt then matches the training prompt token for token, while "think": false drops the <|think|> line. Recorded message.content, parsed (Q4_K_M, Ollama 0.34.2): {"rationale": "Supported by f2.", "answer": "Austin", "citations": ["f2"], "confidence": 0.9, "abstain": false}

Transformers + PEFT

import json, torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel

BASE = "google/gemma-4-e4b-it"   # full precision (bf16), not 4-bit; "unsloth/gemma-4-E4B-it" has the same weights
tok = AutoTokenizer.from_pretrained(BASE)
model = AutoModelForCausalLM.from_pretrained(BASE, dtype=torch.bfloat16, device_map="auto")
model = PeftModel.from_pretrained(model, "sebs-clude/CludeMem-e4b").eval()

def run(system, user, max_new_tokens=512):
    messages = [{"role": "system", "content": system}, {"role": "user", "content": user}]
    inputs = tok.apply_chat_template(messages, add_generation_prompt=True, enable_thinking=True,
                                     return_tensors="pt", return_dict=True).to(model.device)
    out = model.generate(**inputs, max_new_tokens=max_new_tokens, do_sample=False)
    return json.loads(tok.decode(out[0, inputs["input_ids"].shape[1]:], skip_special_tokens=True))

SYSTEM = "Answer ONLY from the provided memories; abstain if unsupported. Output JSON: {rationale, answer, citations[], confidence, abstain}."
USER = "Question: Where does Maya live now?\n\nMemories:\n[f2] Maya moved to Austin on 2026-10-22.\n[f1] Maya lives in Lisbon.\n[f3] Maya currently resides in Austin."
print(run(SYSTEM, USER))

MLX (Apple silicon; converts the PEFT adapter to mlx-lm's layout on the fly)

import json
from pathlib import Path
import mlx.core as mx
from huggingface_hub import snapshot_download
from mlx_lm import generate
from mlx_lm.utils import load_adapters, load_model, load_tokenizer

base = Path(snapshot_download("google/gemma-4-e4b-it"))
peft = Path(snapshot_download("sebs-clude/CludeMem-e4b", allow_patterns=["adapter_*"]))
# convert the PEFT adapter to mlx-lm's layout: lora_a = A.T, lora_b = B.T, scale = lora_alpha / r
cfg = json.loads((peft / "adapter_config.json").read_text())
adapter = Path("cludemem-e4b-mlx")
adapter.mkdir(exist_ok=True)
weights = mx.load(str(peft / "adapter_model.safetensors"))
mx.save_safetensors(str(adapter / "adapters.safetensors"), {
    k.replace("base_model.model.model.language_model.", "language_model.model.")
     .replace(".lora_A.weight", ".lora_a").replace(".lora_B.weight", ".lora_b"): v.T for k, v in weights.items()})
lora = {"keys": ["mlp.gate_proj", "mlp.up_proj", "mlp.down_proj"], "rank": cfg["r"],
        "scale": cfg["lora_alpha"] / cfg["r"], "dropout": 0.0}
(adapter / "adapter_config.json").write_text(json.dumps({"fine_tune_type": "lora", "num_layers": 42, "lora_parameters": lora}))

model, _ = load_model(base, strict=False)   # mlx-lm does not use the base's 54 KV-shared-layer tensors
model = load_adapters(model, str(adapter)).eval()
eos = json.loads((base / "generation_config.json").read_text())["eos_token_id"]
tok = load_tokenizer(base, eos_token_ids=eos if isinstance(eos, list) else [eos])

def run(system, user, max_tokens=512):
    messages = [{"role": "system", "content": system}, {"role": "user", "content": user}]
    prompt = tok.apply_chat_template(messages, add_generation_prompt=True, enable_thinking=True, tokenize=False)
    return json.loads(generate(model, tok, prompt=prompt, max_tokens=max_tokens))  # greedy by default

SYSTEM = "Answer ONLY from the provided memories; abstain if unsupported. Output JSON: {rationale, answer, citations[], confidence, abstain}."
USER = "Question: Where does Maya live now?\n\nMemories:\n[f2] Maya moved to Austin on 2026-10-22.\n[f1] Maya lives in Lisbon.\n[f3] Maya currently resides in Austin."
print(run(SYSTEM, USER))

Both scripts printed {'rationale': 'Supported by f2.', 'answer': 'Austin', 'citations': ['f2'], 'confidence': 0.9, 'abstain': False} when run on the files in this repo.

The adapter the MLX script writes has the same 252 tensors, bit for bit, as the adapter behind the MLX results in Evaluation; strict=False is there because mlx-lm does not use the base's 54 KV-shared-layer tensors.

Checked as printed. The code above was run on the files in this repo for all 10 examples in prompts.json (one per operation, plus an answerable ANSWER item) with Transformers 5.15.1, PEFT 0.20.0 and PyTorch 2.14.0 on Apple MPS, mlx-lm 0.31.3 and Ollama 0.34.2. Every reply was a JSON object with the operation's output keys. Exact matches with the reference outputs: Transformers 10/10, MLX 10/10, Ollama Q8_0 10/10, Ollama Q4_K_M 9/10; the Q4_K_M difference is in RECONCILE (see Usage notes). This checks that the code and files work as printed; accuracy is measured in Evaluation.

Evaluation

RECONCILE is scored on two public, human-labelled benchmarks; the other eight operations ship with reference pairs checked on every runtime:

Operation Evidence in this release
RECONCILE DNLI_gp and DECODE_gp: 1,450 human-labelled pairs, scored on four runtimes (below). They cover consistent, contradicts and duplicate; supersedes needs time-ordered pairs, which these datasets do not have.
CLASSIFY, EXTRACT, ENTITIES, TEMPORAL, CONSOLIDATE, COMPACT, QUERY, ANSWER The reference input/output pairs in prompts.json (for ANSWER, one abstaining and one answerable item), matched exactly on all four runtimes (Quick start). No public benchmark in this release; check them on a sample of your own data before relying on them.

Contradiction detection: DNLI_gp and DECODE_gp

Dialogue NLI (Welleck et al. 2019) and DECODE (Nie et al. 2021) are human-labelled statement pairs from persona dialogues; each pair is sent to RECONCILE as Memory A and Memory B. DNLI's entailment, neutral and contradiction labels map to duplicate, consistent and contradicts. A DECODE contradiction pairs the turn that annotators marked with the final turn; a DECODE non-contradiction pairs an earlier turn by the same speaker with a final turn that annotators verified contradicts nothing before it, and its gold verdict is consistent. Only the verdict is scored.

The _gp sets are gate-passing subsets of the test splits, filtered until lexical rules and a bag-of-words classifier score near chance, so word overlap does not solve them. The baseline rows below confirm that filter: they are a check on the test, not competitors. Because of the filter, these scores are not comparable with published results on the full test sets.

Label-balanced samples (seed 20260821): DNLI_gp 750 of 2,622 items (250 per label), DECODE_gp 700 of 1,454 (350 per label). They were not used in training. Strict 4-way accuracy counts a reply as right only when its verdict, one of four, equals the gold label; replies that do not parse or whose verdict is not one of the four labels count as wrong (27 of the 5,800 across the four runs). DNLI gold uses three verdicts and DECODE two; these pairs carry no time order, so supersedes always counts as wrong. A flag is a contradicts or supersedes verdict; brackets are 95% Wilson intervals. PyTorch ran on unsloth/gemma-4-E4B-it in 16-bit, whose model.safetensors has the same SHA-256 as google/gemma-4-e4b-it's (cfbd3d2f1cd71bd4…), MLX on google/gemma-4-e4b-it, and the GGUF rows are the files in this repo served by Ollama, checked to be the same items by item id and prompt.

System (same items, same harness) DNLI_gp, n = 750 DECODE_gp, n = 700 Flag precision Flag recall
CludeMem-E4B, PyTorch, 16-bit base 74.0 [70.7, 77.0] 77.3 [74.0, 80.2] 93.5 / 92.9 74.4 / 67.1
CludeMem-E4B, MLX, full-precision base 74.1 [70.9, 77.1] 77.9 [74.6, 80.8] 93.5 / 94.0 74.8 / 66.9
CludeMem-E4B, GGUF Q8_0 (Ollama) 73.7 [70.5, 76.8] 77.9 [74.6, 80.8] 93.5 / 93.3 74.4 / 67.4
CludeMem-E4B, GGUF Q4_K_M (Ollama) 72.9 [69.6, 76.0] 76.7 [73.4, 79.7] 94.0 / 92.8 75.6 / 66.0
Majority class 33.3 50.0 n/a n/a
Bag-of-words logistic regression (strongest surface-cue baseline) 36.4 54.1 n/a n/a

Other systems appear in this table only when they were scored on these same items with this harness.

Where the errors go (PyTorch). The model answers consistent on 97.2% of the DNLI_gp and 88.3% of the DECODE_gp pairs whose gold verdict is consistent, and when it misses a gold contradiction it nearly always says consistent (177 of 179 misses). Its high precision and its lower recall are two sides of one conservative operating point: on both datasets it raised fewer flags (199 and 253) than there are gold contradictions (250 and 350), and most of the flags it raised were right. On DNLI_gp duplicate pairs it is right on 50.4%, and 115 of the 124 it misses come back as consistent. A DNLI entailment pair is two sentences annotated with the same fact, and in 164 of the 250 sampled pairs one side is a dialogue turn that carries that fact alongside other content, so duplicate is the loosest of the three label mappings. By annotator agreement: on the 602 DNLI_gp pairs whose three annotations agreed on the label, strict accuracy is 78.6%; on the 148 with a split vote, 55.4%.

Usage notes

Practical tips for getting the most out of the model:

  • Let code do the arithmetic. ANSWER answers from the memories it cites. Compute sums, counts and date differences in code from those cited memories rather than asking the model to work them out.
  • RECONCILE on Q4_K_M: treat an empty weaker_id as null, or use Q8_0. In the 10-example check, Q4_K_M's RECONCILE reply had the right verdict but an empty-string weaker_id and a different resolution sentence; Q8_0 matched the reference exactly.
  • English in, JSON out. Send English, single-turn calls; expect one JSON object per reply. Validate each reply against the output keys in prompts.json and retry on a miss.
  • Plan for the contradiction rate of your own data. The 93.5% and 92.9% precision above come from label-balanced samples; the false-flag rate is the figure to carry over. With 2.6% and 5.1% of non-contradicting pairs flagged, a store where contradictions are rare will see a larger share of false flags among what it flags, so confirm contradicts verdicts on important memories before overwriting anything.
  • Decide on verdicts, not scores. confidence and importance take a few fixed values in the training targets (ANSWER's confidence target is 0.9 for an answer and 0.8 for an abstention). Treat them as coarse labels and base decisions on verdict and abstain.
  • Batch large memory sets. Training sequences, prompt plus reply, were at most 2,816 tokens. Send larger memory sets to CONSOLIDATE, COMPACT and ANSWER in batches that fit.
  • Reference dates. QUERY and TEMPORAL inputs begin with Today is YYYY-MM-DD.; nearly all of their training inputs used Today is 2026-06-01., so check date handling with your own dates.
  • Full-precision base. Load the adapter on the bf16 base, not a 4-bit one; the base's model.safetensors is 16.0 GB. The GGUF builds already include the base: 8.0 GB (Q8_0) and 5.3 GB (Q4_K_M).

Training

  • Method: QLoRA on a 4-bit (NF4) copy of Gemma 4 E4B with bf16 compute; the adapter runs on the full-precision model.
  • Adapter: rank 16, alpha 32 (scale 2.0), dropout 0.05, on the MLP projections (gate_proj, up_proj, down_proj) of all 42 decoder layers; attention is untouched. 25,804,800 trainable parameters.
  • Optimisation: AdamW (8-bit), learning rate 1e-4, cosine decay, weight decay 0, bf16, batch 4 with gradient accumulation 4, sequences up to 2,816 tokens, loss on the assistant reply only.
  • Data: synthetic persona timelines with planted facts, changes, contradictions and duplicates; the examples for all nine operations are derived from each timeline's ground truth. A second seeded generator builds RECONCILE pairs from hidden facts (same or different subject, attribute, value, time span) and derives each verdict from them.
  • No DNLI or DECODE data was used in training. A text-overlap check found none of the 2,765 DNLI_gp memory texts in any training message, and one of the 2,879 DECODE_gp memory texts, a phrase of 1-3 tokens (these texts are model inputs, not labels).
  • Software: PEFT 0.20.0, TRL 0.24.0, Transformers 5.5.0, Unsloth 2026.9.4, PyTorch 2.12.1.

Files

File Size Contents
adapter_model.safetensors 103.3 MB LoRA weights (PEFT)
adapter_config.json 7.6 KB PEFT config; base google/gemma-4-e4b-it
prompts.json 15.5 KB system prompts, input formats, output keys and values, one example per operation
gguf/cludemem-e4b-v3.Q8_0.gguf 8.03 GB adapter merged into the base, 8-bit, text weights; preferred for RECONCILE
gguf/cludemem-e4b-v3.Q4_K_M.gguf 5.34 GB adapter merged into the base, 4-bit, text weights
gguf/Modelfile 1.6 KB Ollama template and parameters (FROM names the Q4_K_M file)
LICENSE 11.4 KB Apache License 2.0
NOTICE 0.6 KB attribution

SHA-256 checksums. After hf download sebs-clude/CludeMem-e4b --local-dir <dir>, save these lines as SHA256SUMS in <dir> and run shasum -a 256 -c SHA256SUMS there:

4a3e16637e7e24a15eb5e2fc6392009e595b8218776971a353647d5d920d27f8  adapter_model.safetensors
12fa78677f246fd57967fcb30fb482312113d15294fd8b77c49fec9062a9f842  adapter_config.json
53abc92c14ce9fda2f517818adcaade7202626ad88b8dcb32174faa7fa1c6070  prompts.json
9bc6dbe4adb0b49aa59ac93db7ffc14fe81b0c5b802d4083e51591127e3d26c1  gguf/cludemem-e4b-v3.Q8_0.gguf
16d63338794b58bb0a62d7ff410ceaf33754dcb419de672506f643d59439dee2  gguf/cludemem-e4b-v3.Q4_K_M.gguf
221ef8867d32d2a768c1297f0760209a30de527c9f61d25f123e9a67f1807b5a  gguf/Modelfile
cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30  LICENSE
3ec43cb8576fed652d4ec1d590eeddf452438dccb7656ac82755bcb68dc5fb16  NOTICE

License and attribution

CludeMem-E4B (the adapter and the merged GGUF builds) is released under the Apache License 2.0 (LICENSE). It is built on Gemma 4 E4B by Google DeepMind (google/gemma-4-e4b-it), whose Hugging Face repository listed its license as apache-2.0 on 2026-09-22 (revision ee0ef602…) and links the Gemma 4 license page; check the repository for the current terms. See NOTICE. Evaluation data: Dialogue NLI (Welleck et al. 2019) and DECODE (Nie et al. 2021).

Citation

@misc{cludemem_e4b,
  title        = {CludeMem-E4B: structured agent-memory operations on Gemma 4 E4B},
  author       = {Clude},
  year         = {2026},
  howpublished = {\url{https://huggingface.co/sebs-clude/CludeMem-e4b}}
}
Downloads last month
112
MLX
Hardware compatibility
Log In to add your hardware

Quantized

GGUF
Model size
8B params
Architecture
gemma4
Hardware compatibility
Log In to add your hardware

4-bit

8-bit

Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Evaluation results