OwnSona is more than a passive store: it learns from use and keeps itself coherent. The mechanisms split cleanly into two groups by what they depend on.
These need no extra configuration and never make a generative-LLM call.
Every memory carries a learned salience weight (seeded from
importance). reinforce — and confirm, which counts
as positive feedback — moves it by a reward-modulated rule,
salience <- clamp((1 - lambda)*salience + eta*reward). Recall
multiplies salience into its ranking. There is no time-based decay:
the (1 - lambda) term is update smoothing applied only on a
feedback event, never with the clock. A memory is never demoted for
being old — only feedback and explicit correction change its standing.
Beyond the global salience scalar, each memory learns a context_vector
— a reward-weighted centroid of the query embeddings it was reinforced
as helpful for (fed by reinforce’s optional query). Recall
adds a term for how close the current query is to that centroid, so a
memory surfaces for the kinds of questions it has proven useful
on. A row with no learned context contributes nothing extra, so this is
dormant until taught.
On a new remember, and on demand via find_conflicts, the
server flags active memories that are semantically close and share a
tag — possible contradictions. This is pure embedding + tag-overlap
math; the server never judges whether two facts truly conflict.
The recall ranking blend is
cosine * (1 + w_s*salience + w_c*context_match) with recency as a
tiebreaker, computed in MemoryRepository.findRanked. The reported
score stays raw cosine, so min_score keeps its meaning.
A second, optional vendor seam, ai.ownsona.llm.GenerativeProvider,
sits beside the embedding provider and is configured independently
(LLM_* keys; see Generative LLM keys). Design invariant: a
generative LLM may be used for background / maintenance work, but
never on the synchronous recall/remember path, and the server runs
fully without it. When LLM_API_KEY is unset the seam is not
constructed and everything below stays off.
Scheduled by Kiss Cron (backend/CronTasks/crontab), each off by default and gated as described in Learning and background-job keys.
ConsolidationJob clusters near-duplicate memories, asks the LLM to
merge each cluster into one canonical fact, stores it, and supersedes the
originals (recoverable soft-delete + replaced_by_id). A second,
separately-gated pass resolves same-topic contradictions by keeping the
current fact and superseding the stale ones. Clusters containing a
keep='Y' memory are skipped entirely.
GraphExtractionJob asks the LLM to pull
(subject, predicate, object) triples from each memory (once) into
the memory_relations table. query_relations then answers
multi-hop questions by breadth-first traversal of that graph — with no
LLM call at query time.
Every destructive action these jobs take is a recoverable soft-delete, never a hard delete, and they are cost-bounded (a fixed per-run cap on LLM calls, and zero calls when there is nothing to do).