- Where each doc_type’s entities live —
storage: file | index - Which engine stores and searches them —
search.backend - How search behaves — field
searchkind,boost,semantic_weight
1. Where entities live: file vs index
Set on each doc_type, in doc_types/<name>.yaml:
Pick
file when a human or agent authors the entity and you want the change
reviewable: services, runbooks, products, policies. Curated knowledge belongs in
git.
Pick index when the data is generated, high-volume, or temporal: alerts,
deployments, memories, anything a connector pulls on a schedule. Hundreds of rows
churning through git helps nobody.
Changing the policy later is fine, but move the data with it — switch to file and
the existing DB rows will be wiped on the next index unless you export them to
JSON first.
2. Which engine: SQLite vs OpenSearch
One key decides both storage and search:There is no
storage.backend. storage: only sets the SQLite file path. (Older
brains that set storage.backend still load; it never did anything, and now logs a
warning.)
The line is writers, not size. SQLite is single-writer: the moment a second
agent (or a connector running while an agent writes) needs to write, move to
OpenSearch. Entity count matters only for semantic search, where SQLite scans
brute-force.
Switch per environment without editing
brain.yaml:
${ENV} refs resolved at connect time:
Environment overrides
Empty values don’t override, so
docker compose passing an unset variable through
as "" leaves the file’s value alone.
3. Tuning search
Per field
syntactic— keyword and prefix matching. Right for names, owners, statuses, anything you’d filter on.semantic— the field is embedded and matched by meaning, so “checkout is slow” finds an entity that says “latency spike at payment”. Right for prose.none— stored but never searched. Right for opaque ids and blobs that would only add noise.
boost is a genuine multiplier. A match in a boost: 6 field outranks one in a
boost: 1 field 6-to-1 — not approximately. The usual shape is a high boost on
name, a moderate one on a summary, and 1 everywhere else.
Per brain
semantic_weight blends the two scores. The default 0.3 keeps keyword matches
dominant and lets semantic similarity rescue queries that use different words than
the text. Raise it if your users describe things rather than name them; set 0 to
turn vectors off at query time.
Embeddings
Semantic search needs a provider. Without one the backends log a warning once and fall back to keyword-only — search still works, just less well.Worked examples
Local, single agent, curated knowledge — the default. SQLite,storage: file
on everything, semantic on prose fields.