doc: add blog (#201)

* doc: introduce mental models blog post

Write blog post introducing Mental Models in Hindsight 0.4.0:
- Evolution from observations and opinions
- How mental models work (consolidation, evidence tracking)
- Breaking changes and migration path
- Environment variable to enable (experimental)
- Agentic reflect explanation

* updates

* Update 2026-01-26-learning-capabilities.md

* fix: doc build issues

- Add missing code snippets for versioned docs (recall-opinions-only, recall-include-entities, bank-background)
- Fix broken links by using relative paths for version compatibility
- Update blog post title to sentence case
- Clear versions.json since v0.3 versioned docs don't exist yet
- Enable INCLUDE_CURRENT_VERSION in build script

* fix: update doc links after rebase

- Fix blog post to link to correct pages (/developer/api/mental-models and /developer/observations)
- Fix CLI docs to link to /api-reference instead of /api

* feat: add directives section to blog post

- Update intro to mention three layers of knowledge
- Add concise Directives section for compliance/guardrails
- Add directives to resources section
- Keep focus on learning capabilities (observations and mental models)

* fix: revert intro to focus on learning capabilities only

Directives are a separate feature for compliance/guardrails, not a learning capability. The blog post is about observations and mental models.
This commit is contained in:
Nicolò Boschi 2026-01-28 15:42:14 +01:00 committed by GitHub
parent 2118d0a7cd
commit 1bf90358c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 442 additions and 11 deletions

View file

@ -0,0 +1,284 @@
---
slug: learning-capabilities
title: "Agent memory that learns: observations and mental models"
authors: [hindsight]
image: /img/reflect-operation.webp
hide_table_of_contents: false
---
Today we're releasing Hindsight 0.4.0, which introduces two powerful learning capabilities for AI agents: **Observations** for automatic knowledge consolidation, and **Mental Models** for user-curated summaries.
<!-- truncate -->
## Two Levels of Learning
Hindsight 0.4.0 introduces a hierarchical learning system:
| Level | What It Is | How It's Created |
|-------|------------|------------------|
| **Mental Models** | User-curated summaries for common queries | Manually created via API |
| **Observations** | Consolidated knowledge from facts | Automatically after retain |
During `reflect`, the agent checks these in priority order — mental models first (your curated knowledge), then observations (automatic synthesis), then raw facts.
---
## Observations: Automatic Knowledge Consolidation
### Evolution from Entity Summaries and Opinions
In Hindsight 0.3.0, we had two separate systems for synthesized knowledge:
- **Entity summaries**: Per-entity summaries synthesized from related facts. Generated automatically for frequently-mentioned entities — if "Alice" appeared in many facts, you'd get a summary like "Alice is a software engineer at Google who joined in 2020 and leads the search team." Objective and entity-scoped.
- **Opinions**: Beliefs formed during `reflect` operations, influenced by the bank's disposition traits. These captured subjective judgments with confidence scores, like "Python is best for data science" (confidence: 0.85).
Both systems served their purpose well, but they operated independently. Entity summaries were entity-centric, opinions were belief-centric, and neither captured the full picture of how knowledge evolves over time.
**Observations** unify these concepts into a single, more expressive system that captures patterns, preferences, and learnings as they emerge from accumulated evidence.
### What Are Observations?
Observations are **consolidated knowledge** synthesized from multiple facts. Unlike raw facts which are individual pieces of information, observations represent patterns and insights that emerge from accumulated evidence.
| Raw Facts | Observation |
|-----------|--------------|
| "Alice prefers Python" | "Alice is a Python-focused developer who values readability and simplicity, recommends type hints, and prefers pytest for testing" |
| "Alice dislikes verbose code" | |
| "Alice recommends type hints" | |
### Automatic Background Consolidation
After every `retain()` call, Hindsight's consolidation engine runs automatically:
1. **Analyzes new facts** against existing knowledge
2. **Detects patterns** across related information
3. **Synthesizes observations** that capture higher-order insights
4. **Tracks evidence** linking each observation to its supporting facts
```mermaid
graph LR
A[New Facts] --> B[Consolidation Engine]
B --> C{Existing Observation?}
C -->|Yes| D[Refine Observation]
C -->|No| E[Create Observation]
D --> F[Observations]
E --> F
```
### Evidence-Based Evolution
Observations evolve as new evidence arrives, capturing the full journey rather than just the current state:
| Time | Fact | Observation |
|------|------|--------------|
| Week 1 | "User loves React" | "User prefers React for frontend development" |
| Week 2 | "User praises React's component model" | "User is enthusiastic about React, particularly its component model" |
| Week 3 | "User switched to Vue and won't use React anymore" | "User was previously a React enthusiast who appreciated its component model, but has now switched to Vue" |
Notice how the final observation captures the **full journey** — not just "User prefers Vue" but the complete evolution. Your agent now understands:
- The user deliberately moved away from React (it wasn't ignorance)
- They previously appreciated React's component model (relevant context)
- Recommending React tutorials would be inappropriate
### Mission-Oriented Consolidation
Observations are influenced by your bank's **mission**. When you set a mission, the consolidation engine focuses on extracting knowledge that serves that purpose:
```python
client.create_bank(
bank_id="support-agent",
mission="You're a customer support agent - track customer preferences, "
"past issues, and communication styles."
)
```
With this mission, the engine prioritizes customer-relevant observations while skipping ephemeral details. Without a mission, it performs general-purpose consolidation.
---
## Mental Models: User-Curated Knowledge
While observations are created automatically, **mental models** give you explicit control over how your agent answers common questions.
### What Are Mental Models?
Mental models are **saved reflect responses** that you curate for your memory bank. When you create a mental model, Hindsight runs a reflect operation with your source query and stores the result. During future reflect calls, these pre-computed summaries are checked first.
```mermaid
graph LR
A[Create Mental Model] --> B[Run Reflect]
B --> C[Store Result]
C --> D[Future Queries]
D --> E{Match Found?}
E -->|Yes| F[Return Mental Model]
E -->|No| G[Run Full Reflect]
```
### Why Use Mental Models?
| Benefit | Description |
|---------|-------------|
| **Consistency** | Same answer every time for common questions |
| **Speed** | Pre-computed responses are returned instantly |
| **Quality** | Manually curated summaries you've reviewed |
| **Control** | Define exactly how key topics should be answered |
### Two Ways to Use Mental Models
Mental models work in two ways:
1. **Automatic via Reflect**: During `reflect` calls, the agent automatically checks mental models first. If a relevant one exists, it's used to inform the response.
2. **Direct Lookup**: Mental models work like a key-value store — you can retrieve them instantly by ID, bypassing the reflect reasoning loop entirely.
```python
# Direct lookup by ID — instant response, no LLM call
mental_model = client.get_mental_model(
bank_id="my-bank",
mental_model_id="team-communication"
)
print(mental_model.content) # Pre-computed answer, ready to use
```
This is useful when you know exactly what mental model you need and want the fastest possible response — no LLM reasoning required, just a simple database lookup.
### Creating Mental Models
```python
# Create a mental model for a common question
response = client.create_mental_model(
bank_id="my-bank",
name="Team Communication Preferences",
source_query="How does the team prefer to communicate?",
tags=["team"]
)
```
### Automatic Refresh
Mental models can automatically stay in sync with your observations:
```python
# Mental model that refreshes when observations update
response = client.create_mental_model(
bank_id="my-bank",
name="Project Status",
source_query="What is the current project status?",
trigger={"refresh_after_consolidation": True}
)
```
---
## Directives: Compliance and Guardrails
In addition to learning capabilities, **directives** provide hard rules that your agent must always follow during reflect operations. Unlike disposition traits which *influence* reasoning style, directives are absolute requirements that are enforced in every response.
Use directives for compliance, privacy, and safety constraints:
- "Never provide medical diagnoses or treatment advice"
- "Always respond in formal English"
- "Never share personally identifiable information"
- "Always cite sources when making factual claims"
Directives are injected into reflect prompts as hard constraints and are included in the response's `based_on` field. See the [Directives documentation](../developer/api/memory-banks#directives) for how to create and manage them.
---
## What Changes from 0.3.0
### Unified Memory Types
Opinions and entity summaries are now consolidated into observations:
```python
# 0.3.0 - opinions via types, entity summaries via include_entities
response = client.recall(
bank_id="my-bank",
query="What do you think about Python?",
types=["opinion"],
include_entities=True # to get entity summaries
)
# 0.4.0 - observations unify both
response = client.recall(
bank_id="my-bank",
query="What do you think about Python?",
types=["observation"]
)
```
### From Confidence Scores to Evidence Tracking
Opinions had numeric confidence scores (0.0-1.0). Observations instead track:
- **Supporting facts**: The evidence behind the observation
- **Last updated**: When the observation was last refined
- **Freshness**: Whether the observation reflects recent information
This shift from a single score to evidence tracking means your agent can explain *why* it believes something, not just *how confident* it is.
### Automatic vs On-Demand
Entity summaries were created automatically for top entities, but opinions only formed during `reflect`. Observations are always consolidated automatically after `retain`, ensuring knowledge stays current without explicit queries.
### Background Becomes Mission
The bank's `background` field has been renamed to `mission`. During the migration, your existing background text is automatically copied to the mission field — no action needed.
### Agentic Reflect
The `reflect` operation is now agentic — it reasons more deeply by iteratively retrieving memories and consulting mental models and observations before formulating a response. This makes reflect significantly smarter, especially for complex questions that require synthesizing information across multiple topics.
The trade-off is that reflect may take longer to respond. For latency-sensitive use cases, consider using `recall` directly when you just need to retrieve facts.
### Data Migration
**Important:** When upgrading to 0.4.0, existing opinions and entity summaries will be deleted. The consolidation engine will automatically create new observations from your existing facts. This is a one-time migration — your raw facts are preserved, and observations will be synthesized from them after the upgrade.
### Migration Checklist
**If you were using `types=["opinion"]` in recall:**
1. Update to `types=["observation"]`
2. Observations combine both entity-centric summaries and belief-based insights
**If you were using `include_entities=True` in recall:**
1. Entity summaries are now included in observations
2. Use `types=["observation"]` to retrieve them
**If you were relying on confidence scores:**
1. Use the `based_on` field to access supporting evidence
2. The number and recency of supporting facts indicates strength
**If you were setting `background` on banks:**
1. The field is now called `mission`
2. Existing values are migrated automatically
**No changes needed for reflect:**
Observations are automatically included in reflect responses via the `based_on` field.
---
## What's Next
These learning capabilities are the foundation for more sophisticated agent memory capabilities we're exploring:
- **Temporal reasoning**: Better understanding of how knowledge evolves over time
- **Selective consolidation**: Fine-grained control over what gets synthesized into observations
- **Consolidation insights**: Visibility into how observations are formed and updated
---
**Resources:**
- [Recall API](../developer/api/recall) — retrieve observations alongside facts
- [Reflect API](../developer/api/reflect) — responses now include supporting observations
- [Mental Models API](../developer/api/mental-models) — create and manage curated summaries
- [Observations Guide](../developer/observations) — deep dive into knowledge consolidation
- [Directives](../developer/api/memory-banks#directives) — hard rules for compliance and guardrails
- [Full Changelog](../changelog)

View file

@ -0,0 +1,3 @@
hindsight:
name: Hindsight Team
url: https://github.com/vectorize-io/hindsight

View file

@ -4,7 +4,7 @@ sidebar_position: 3
# CLI Reference
The Hindsight CLI provides command-line access to memory operations and bank management. All commands follow the [OpenAPI specification](/api), so you can use `--help` on any command to see all available options.
The Hindsight CLI provides command-line access to memory operations and bank management. All commands follow the [OpenAPI specification](/api-reference), so you can use `--help` on any command to see all available options.
## Installation

View file

@ -66,7 +66,6 @@ const config: Config = {
{
docs: {
sidebarPath: './sidebars.ts',
editUrl: 'https://github.com/vectorize-io/hindsight/tree/main/hindsight-docs/',
routeBasePath: '/',
// Only show "next" version in development or when INCLUDE_CURRENT_VERSION=true
// In production, only show released versions from versions.json
@ -97,7 +96,14 @@ const config: Config = {
return config;
})(),
},
blog: false,
blog: {
showReadingTime: true,
blogTitle: 'Hindsight Blog',
blogDescription: 'Updates, insights, and deep dives into agent memory',
postsPerPage: 10,
blogSidebarTitle: 'Recent posts',
blogSidebarCount: 'ALL',
},
theme: {
customCss: './src/css/custom.css',
},
@ -151,7 +157,8 @@ const config: Config = {
{
hashed: true,
docsRouteBasePath: '/',
indexBlog: false,
indexBlog: true,
blogRouteBasePath: '/blog',
highlightSearchTermsOnTargetPage: false,
},
],
@ -206,6 +213,12 @@ const config: Config = {
label: 'Cookbook',
className: 'navbar-item-cookbook',
},
{
to: '/blog',
position: 'left',
label: 'Blog',
className: 'navbar-item-blog',
},
{
type: 'doc',
docId: 'changelog/index',

View file

@ -38,6 +38,33 @@ for opinion in response.results:
# [/docs:opinion-search]
# [docs:recall-opinions-only]
# Only retrieve opinions (beliefs and preferences)
opinions = client.recall(
bank_id="my-bank",
query="What are my preferences?",
types=["opinion"]
)
# [/docs:recall-opinions-only]
# [docs:recall-include-entities]
# Include entity summaries in recall results
response = client.recall(
bank_id="my-bank",
query="What do I know about Alice?",
include_entities=True,
max_entity_tokens=500
)
# Results include both facts and entity summaries
for result in response.results:
print(f"- {result.text}")
if hasattr(result, 'entity_summary'):
print(f" Entity: {result.entity_summary}")
# [/docs:recall-include-entities]
# [docs:opinion-disposition]
# Bank disposition affects how opinions are formed
# High skepticism = lower confidence, requires more evidence

View file

@ -39,6 +39,16 @@ await client.createBank('financial-advisor', {
// [/docs:bank-mission]
// [docs:bank-background]
// Legacy snippet for v0.3 docs (background renamed to mission in v0.4)
await client.createBank('legacy-bank', {
name: 'Legacy Example',
mission: `I'm a personal assistant helping a software engineer. I should track their
project preferences, coding style, and technology choices.`
});
// [/docs:bank-background]
// =============================================================================
// Cleanup (not shown in docs)
// =============================================================================

View file

@ -43,6 +43,17 @@ client.create_bank(
# [/docs:bank-mission]
# [docs:bank-background]
# Legacy snippet for v0.3 docs (background renamed to mission in v0.4)
client.create_bank(
bank_id="legacy-bank",
name="Legacy Example",
mission="""I'm a personal assistant helping a software engineer. I should track their
project preferences, coding style, and technology choices."""
)
# [/docs:bank-background]
# [docs:bank-with-disposition]
client.create_bank(
bank_id="architect-bank",

View file

@ -147,6 +147,33 @@ response = client.recall(
# [/docs:recall-tags-all]
# =============================================================================
# Legacy snippets for v0.3 docs (kept for backward compatibility)
# =============================================================================
# [docs:recall-opinions-only]
# Legacy: opinions replaced by observations in v0.4+
# Only retrieve opinions (beliefs and preferences)
opinions = client.recall(
bank_id="my-bank",
query="What are my preferences?",
types=["opinion"]
)
# [/docs:recall-opinions-only]
# [docs:recall-include-entities]
# Legacy: entity summaries replaced by observations in v0.4+
# Include entity summaries in recall results
response = client.recall(
bank_id="my-bank",
query="What do I know about Alice?",
include_entities=True,
max_entity_tokens=500
)
# [/docs:recall-include-entities]
# =============================================================================
# Cleanup (not shown in docs)
# =============================================================================

View file

@ -5,7 +5,7 @@
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"build": "INCLUDE_CURRENT_VERSION=true docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",

View file

@ -1247,3 +1247,59 @@ ul[class*="suggestion"] {
display: none !important;
}
/* ===== Blog Styling ===== */
/* Hide TOC sidebar on blog post pages */
.blog-post-page .col--3,
.blog-post-page [class*="tableOfContents"],
.blog-post-page aside[class*="toc"] {
display: none !important;
}
/* Make blog post content full width when TOC is hidden */
.blog-post-page .col--9 {
--ifm-col-width: 100%;
max-width: 100%;
flex-basis: 100%;
}
/* Hide author avatar/icon on blog posts */
[class*="blogPostAuthor"] img,
[class*="authorImage"],
.avatar__photo {
display: none !important;
}
/* Blog author text visible in dark mode */
[data-theme='dark'] [class*="blogPostAuthor"],
[data-theme='dark'] [class*="blogPostAuthor"] *,
[data-theme='dark'] .avatar__name,
[data-theme='dark'] .avatar__name a,
[data-theme='dark'] .avatar__subtitle,
[data-theme='dark'] [class*="authorName"],
[data-theme='dark'] [class*="blogPostData"] a,
[data-theme='dark'] [class*="blogPostInfo"] a {
color: #e2e8f0 !important;
-webkit-text-fill-color: #e2e8f0 !important;
}
/* Blog navbar icon */
@media (min-width: 997px) {
.navbar-item-blog::before {
display: inline-block;
width: 16px;
height: 16px;
margin-right: 6px;
vertical-align: middle;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
content: '';
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath fill='%23666' d='M216 36H40a20 20 0 0 0-20 20v144a20 20 0 0 0 20 20h176a20 20 0 0 0 20-20V56a20 20 0 0 0-20-20Zm-4 160H44V60h168ZM68 92a12 12 0 0 1 12-12h96a12 12 0 0 1 0 24H80a12 12 0 0 1-12-12Zm0 36a12 12 0 0 1 12-12h96a12 12 0 0 1 0 24H80a12 12 0 0 1-12-12Zm0 36a12 12 0 0 1 12-12h96a12 12 0 0 1 0 24H80a12 12 0 0 1-12-12Z'/%3E%3C/svg%3E");
}
[data-theme='dark'] .navbar-item-blog::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath fill='%23ccc' d='M216 36H40a20 20 0 0 0-20 20v144a20 20 0 0 0 20 20h176a20 20 0 0 0 20-20V56a20 20 0 0 0-20-20Zm-4 160H44V60h168ZM68 92a12 12 0 0 1 12-12h96a12 12 0 0 1 0 24H80a12 12 0 0 1-12-12Zm0 36a12 12 0 0 1 12-12h96a12 12 0 0 1 0 24H80a12 12 0 0 1-12-12Zm0 36a12 12 0 0 1 12-12h96a12 12 0 0 1 0 24H80a12 12 0 0 1-12-12Z'/%3E%3C/svg%3E");
}
}

View file

@ -1 +1 @@
["0.3"]
[]

10
uv.lock
View file

@ -1295,7 +1295,7 @@ wheels = [
[[package]]
name = "hindsight-all"
version = "0.3.0"
version = "0.4.0"
source = { editable = "hindsight" }
dependencies = [
{ name = "hindsight-api" },
@ -1319,7 +1319,7 @@ provides-extras = ["test"]
[[package]]
name = "hindsight-api"
version = "0.3.0"
version = "0.4.0"
source = { editable = "hindsight-api" }
dependencies = [
{ name = "aiohttp" },
@ -1447,7 +1447,7 @@ dev = [
[[package]]
name = "hindsight-client"
version = "0.3.0"
version = "0.4.0"
source = { editable = "hindsight-clients/python" }
dependencies = [
{ name = "aiohttp" },
@ -1481,7 +1481,7 @@ provides-extras = ["test"]
[[package]]
name = "hindsight-dev"
version = "0.3.0"
version = "0.4.0"
source = { editable = "hindsight-dev" }
dependencies = [
{ name = "hindsight-api" },
@ -1527,7 +1527,7 @@ dev = [
[[package]]
name = "hindsight-embed"
version = "0.3.0"
version = "0.4.0"
source = { editable = "hindsight-embed" }
dependencies = [
{ name = "httpx" },