Compare commits

...
Author SHA1 Message Date
Nicolò Boschi 5faba290fc docs(mental-models): clarify that tags filter refresh source memories
Addresses #945 and the related confusion in #1004. The mental model
`tags` field acts as a hard `all_strict` filter on source memories
during refresh, but this wasn't obvious from the parameter tables
or the UI form — users hit empty refresh content while direct reflect
on the same query worked.

- Expand the `tags` parameter description in the mental-models API
  doc and mirror it in the skills reference.
- Add a warning callout in the "Tags and Visibility" section pointing
  users at backfill / trigger.tags_match / tag_groups workarounds.
- Add helper text under the Tags input (both Create and Edit forms)
  in the control plane mental-models view.
2026-04-13 09:48:09 +02:00
4 changed files with 24 additions and 8 deletions
@@ -776,17 +776,13 @@ async def compute_semantic_links_ann(
await conn.execute("SET LOCAL hnsw.ef_search = 60")
t_setup = time_mod.time()
await conn.execute(
"CREATE TEMP TABLE _ann_seeds (unit_id text, emb_text text, fact_type text) ON COMMIT DROP"
)
await conn.execute("CREATE TEMP TABLE _ann_seeds (unit_id text, emb_text text, fact_type text) ON COMMIT DROP")
records = [
(uid, emb if isinstance(emb, str) else str(emb), ft)
for uid, emb, ft in zip(unit_ids, embeddings, fact_types)
]
await conn.copy_records_to_table(
"_ann_seeds", records=records, columns=["unit_id", "emb_text", "fact_type"]
)
await conn.copy_records_to_table("_ann_seeds", records=records, columns=["unit_id", "emb_text", "fact_type"])
logger.debug(f"[ANN] Temp table setup: {time_mod.time() - t_setup:.3f}s ({len(records)} seeds)")
# Run one ANN query per fact_type so each uses the right HNSW index.
@@ -767,6 +767,13 @@ function CreateMentalModelDialog({
onChange={(e) => setForm({ ...form, tags: e.target.value })}
placeholder="e.g., project-x, team-alpha (comma-separated)"
/>
<p className="text-xs text-muted-foreground">
Tags scope the model during reflect <strong>and</strong> filter source memories
during refresh (default <code>all_strict</code>: only memories carrying every listed
tag are read). If no memories have these tags yet, refresh will produce empty
content backfill tags on memories, or adjust <em>Tags Match</em> /{" "}
<em>Tag Groups</em> below.
</p>
</div>
<div className="flex items-center space-x-2">
<Checkbox
@@ -1046,6 +1053,13 @@ function UpdateMentalModelDialog({
onChange={(e) => setForm({ ...form, tags: e.target.value })}
placeholder="e.g., project-x, team-alpha (comma-separated)"
/>
<p className="text-xs text-muted-foreground">
Tags scope the model during reflect <strong>and</strong> filter source memories
during refresh (default <code>all_strict</code>: only memories carrying every listed
tag are read). If no memories have these tags yet, refresh will produce empty
content backfill tags on memories, or adjust <em>Tags Match</em> /{" "}
<em>Tag Groups</em> below.
</p>
</div>
<div className="flex items-center space-x-2">
<Checkbox
@@ -77,7 +77,7 @@ Creating a mental model runs a reflect operation in the background and saves the
| `name` | string | Yes | Human-readable name for the mental model |
| `source_query` | string | Yes | The query to run to generate content |
| `id` | string | No | Custom ID for the mental model (alphanumeric lowercase with hyphens). Auto-generated if omitted. |
| `tags` | list | No | Tags for filtering during retrieval |
| `tags` | list | No | Tags that scope the model during reflect **and** filter source memories during refresh. Defaults to `all_strict` matching, so only memories carrying every listed tag are read. See [Tags and Visibility](#tags-and-visibility). |
| `max_tokens` | int | No | Maximum tokens for the mental model content |
| `trigger` | object | No | Trigger settings (see [Automatic Refresh](#automatic-refresh)) |
@@ -307,6 +307,10 @@ Mental models support the same tag system as memories. When you assign tags to a
### How tags affect mental model refresh
:::warning
Adding tags to a mental model narrows the pool of source memories its refresh can read from. If no memories carry those tags yet, refresh will return empty content (e.g. `"I cannot find any information…"`) even though direct `reflect` on the same query works. Backfill tags on the relevant memories first, or override the default via `trigger.tags_match` / `trigger.tag_groups`.
:::
When a mental model is refreshed (manually or automatically), it runs an internal reflect call to regenerate its content. If the mental model has tags, that reflect call uses `all_strict` tag matching — meaning it will only read memories that carry **all** of the mental model's tags. Untagged memories are excluded.
```
@@ -96,7 +96,7 @@ hindsight mental-model create "$BANK_ID" \
| `name` | string | Yes | Human-readable name for the mental model |
| `source_query` | string | Yes | The query to run to generate content |
| `id` | string | No | Custom ID for the mental model (alphanumeric lowercase with hyphens). Auto-generated if omitted. |
| `tags` | list | No | Tags for filtering during retrieval |
| `tags` | list | No | Tags that scope the model during reflect **and** filter source memories during refresh. Defaults to `all_strict` matching, so only memories carrying every listed tag are read. See [Tags and Visibility](#tags-and-visibility). |
| `max_tokens` | int | No | Maximum tokens for the mental model content |
| `trigger` | object | No | Trigger settings (see [Automatic Refresh](#automatic-refresh)) |
@@ -449,6 +449,8 @@ Mental models support the same tag system as memories. When you assign tags to a
### How tags affect mental model refresh
> **Warning:** Adding tags to a mental model narrows the pool of source memories its refresh can read from. If no memories carry those tags yet, refresh will return empty content (e.g. `"I cannot find any information…"`) even though direct `reflect` on the same query works. Backfill tags on the relevant memories first, or override the default via `trigger.tags_match` / `trigger.tag_groups`.
When a mental model is refreshed (manually or automatically), it runs an internal reflect call to regenerate its content. If the mental model has tags, that reflect call uses `all_strict` tag matching — meaning it will only read memories that carry **all** of the mental model's tags. Untagged memories are excluded.
```