Skip to content

feat(memory): add an opt-in entity extraction model - #170

Open
audreyt wants to merge 2 commits into
mnemon-dev:masterfrom
audreyt:feat/entity-extraction-model
Open

audreyt wants to merge 2 commits into
mnemon-dev:masterfrom
audreyt:feat/entity-extraction-model

Conversation

@audreyt

@audreyt audreyt commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Stacked on #169: the first commit is that PR. Review 47a2204.

What

An opt-in entity extraction model for remember and import.

  • internal/memory/extract is a client configured by MNEMON_EXTRACT_MODEL, MNEMON_EXTRACT_ENDPOINT (the full URL, for example Workers AI .../ai/run/@cf/cloudflare/clairic), MNEMON_EXTRACT_API_KEY, and MNEMON_EXTRACT_TIMEOUT. It sends the memory as state with one list field and accepts both the Workers AI envelope and a bare System One response.
  • Model output is treated as untrusted. An item survives only if, after trimming edge quotes and clause punctuation, it has a letter, at least two runes, at most 200 bytes, and an occurrence in the memory that is a whole token rather than a fragment (TW in zh-TW, 5.1 in 5.1.0, w/demo in ~/w/demo). Duplicates and items past 50 are dropped, matching the --entities limits.
  • graph.ResolveEntitiesWithModel uses the model's entities only when resolution relies on extraction alone: merge mode with nothing provided, or auto mode. They replace the all-caps acronym guess, while CamelCase, file paths, URLs, @mentions, title marks, the dictionary, and the known-entity index still apply.
  • The request runs before the write transaction, beside the embedding. A failure prints one warning and falls back to heuristic extraction, and import stops asking after the first failure.
  • Memories stored with entities, and recall queries, never call the model.

Why

The regex finds identifiers, paths, and acronyms but misses most names: multi-word people and organizations, and single-segment names on first mention. Agent hosts usually pass entities, but hooks, imports, and other writers often do not. An extraction model returns spans of the input in one input-only pass (about 0.5 to 0.9 s per memory with Clairic, billed only for input tokens), so it fits a write path without a generative LLM.

The exact regex paths stay because, in probes on real memories, the model split paths (~/ and w/...), cut versions, and left trailing brackets, which are the cases those patterns match exactly. Clairic's list-level confidence came back near 1e-12 on every call, so the client does not threshold on it.

Effect

Live remember against Clairic on Workers AI, into a throwaway store:

  • A prose memory about people and organizations went from four acronym guesses and one lowercase word to the full names of the people and organizations it mentions.
  • A dense technical note kept its file path and identifiers and gained model names and config keys. The model also kept a few ordinary words that pass the whole-token check.
  • A bad API key fell back to the heuristic entities with one warning line.

Privacy

Off unless MNEMON_EXTRACT_MODEL is set. When it is set, the full text of each memory stored without entities goes to the endpoint; README and SECURITY.md say so.

Validation

  • internal/memory/extract: request shape, both response shapes, whole-token grounding, the 50-item cap, a null list, and errors that name the failure without the key or the content.
  • internal/memory/graph: resolution per entity mode, nil versus empty model output, and provided entities bypassing the model.
  • cmd/memory: remember with a working, skipped, and failing model; import asking only for drafts without entities and stopping after the first failure.
  • gofmt -l is clean and make test (including go vet ./...) passes.

Relation to #161

Independent of the decision-model judge in #161. Both add rows to the same env tables and a package to the Makefile list and architecture graph, so whichever merges second needs a small rebase. The CLI tests use their own stderr helper so the two PRs do not redeclare one.

Release note

Set MNEMON_EXTRACT_MODEL (for example Cloudflare Clairic) to have remember and import extract entities from memories stored without them.

The file-path pattern also matched decimals, versions, and addresses with no
letter (0.52, 127.0.0.1) and dotted abbreviations (e.g, i.e), and the acronym
pattern admitted ordinary words written in capitals for emphasis or as labels
(NEVER, ONLY, NOTE, FIXED). On a real store these were among the most frequent
stored entities, and entity edges linked memories that shared nothing but a
decimal or an emphasis word.

File-path matches now need a letter and a dot-separated segment longer than
one character, and the acronym stoplist gains the emphasis words while leaving
out words that double as technical names (HEAD, POST, PATH). Validated with
make test.
With MNEMON_EXTRACT_MODEL and MNEMON_EXTRACT_ENDPOINT set, remember and import
ask a System One extraction model such as Cloudflare Clairic for the entities
of a memory that arrives without any. The request runs before the write
transaction, and deterministic code keeps only names that appear in the memory
as whole tokens, so the model can add the multi-word names the regex misses
but cannot invent or truncate one. Model entities replace only the all-caps
acronym guess; the exact patterns and the known-entity index still apply,
because the model splits paths and versions that those patterns match exactly.

A missing or failing model prints one warning and falls back to heuristic
extraction, and import stops asking after the first failure. Memories stored
with entities and recall queries never call the model. Validated with make
test and a live remember against Clairic on Workers AI.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant