Compare commits

...
Author SHA1 Message Date
Nicolò Boschi ab968feae1 doc: mental models 2026-01-26 14:11:27 +01:00
Nicolò Boschi a4be464e6f doc: mental models 2026-01-26 12:08:54 +01:00
66 changed files with 1747 additions and 1010 deletions
+2 -3
View File
@@ -7,8 +7,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
Hindsight is an agent memory system that provides long-term memory for AI agents using biomimetic data structures. Memories are organized as:
- **World facts**: General knowledge ("The sky is blue")
- **Experience facts**: Personal experiences ("I visited Paris in 2023")
- **Opinion facts**: Beliefs with confidence scores ("Paris is beautiful" - 0.9 confidence)
- **Observations**: Complex mental models derived from reflection
- **Mental models**: Consolidated knowledge synthesized from facts ("User prefers functional programming patterns")
## Development Commands
@@ -101,7 +100,7 @@ cd hindsight-control-plane && npm run dev
Main operations:
- **Retain**: Store memories, extracts facts/entities/relationships
- **Recall**: Retrieve memories via 4 parallel strategies (semantic, BM25, graph, temporal) + reranking
- **Reflect**: Deep analysis forming new opinions/observations (disposition-aware)
- **Reflect**: Disposition-aware reasoning using memories and mental models
### Database
PostgreSQL with pgvector. Schema managed via Alembic migrations in `hindsight-api/hindsight_api/alembic/`. Migrations run automatically on API startup.
+8 -9
View File
@@ -595,15 +595,6 @@ class ReflectResponse(BaseModel):
{"id": "123", "text": "AI is used in healthcare", "type": "world"},
{"id": "456", "text": "I discussed AI applications last week", "type": "experience"},
],
"mental_models": [
{
"id": "mm-1",
"name": "AI Technology",
"type": "concept",
"subtype": "structural",
"description": "Understanding of AI capabilities",
}
],
},
"structured_output": {
"summary": "AI is transformative",
@@ -613,6 +604,14 @@ class ReflectResponse(BaseModel):
"trace": {
"tool_calls": [{"tool": "recall", "input": {"query": "AI"}, "duration_ms": 150}],
"llm_calls": [{"scope": "agent_1", "duration_ms": 1200}],
"mental_models": [
{
"id": "mm-1",
"name": "AI Technology",
"type": "concept",
"subtype": "structural",
}
],
},
}
}
@@ -22,7 +22,7 @@ This example showcases:
- **Function calling** to bridge them together
- **Streaming responses** for real-time interaction (enabled by default)
- **Bidirectional memory** - both user data AND coach observations stored
- **System-level post-processing** - automatic opinion storage for reliability
- **System-level post-processing** - automatic knowledge consolidation
- **Temporal-semantic memory** queries via function tools
- **Enhanced preference learning** - coach learns and respects user likes/dislikes
- **Real-world integration pattern** for adding memory to AI agents
@@ -46,9 +46,9 @@ Hindsight API (returns workouts + preferences)
|
OpenAI Assistant (analyzes, gives advice)
|
Function Call: store_memory(advice as opinion)
Function Call: store_memory(advice as experience)
|
Hindsight API (stores coach's observation)
Hindsight API (stores coach's advice, consolidates into mental models)
|
Personalized Answer
```
@@ -57,10 +57,10 @@ Personalized Answer
| Component | Standard Demo | OpenAI Integration |
|-----------|---------------|-------------------|
| **Conversation** | Hindsight `/think` endpoint | OpenAI Assistant API |
| **Conversation** | Hindsight `/reflect` endpoint | OpenAI Assistant API |
| **Memory** | Hindsight (built-in) | Hindsight (via function calling) |
| **LLM** | Configured in Hindsight | OpenAI GPT-4 |
| **Opinion Formation** | Automatic in `/think` | Explicit via `store_memory(type="opinion")` |
| **Knowledge Consolidation** | Automatic after retain | Automatic after retain |
| **Best For** | Hindsight-native apps | Integrating memory into existing OpenAI agents |
## Quick Start
@@ -126,7 +126,7 @@ retrieve_memories(query, fact_types, top_k)
search_workouts(after_date, before_date, workout_type)
get_nutrition_summary(after_date, before_date)
get_user_goals()
get_coach_opinions(about)
get_coach_insights(about) # Retrieves mental models
```
Each function makes API calls to Hindsight to fetch relevant memories.
@@ -191,8 +191,8 @@ The agent will automatically:
The OpenAI Agent can retrieve different memory types from Hindsight:
- **World Facts** (`fact_type: "world"`): Workouts, meals, activities
- **Agent Facts** (`fact_type: "agent"`): Goals, intentions
- **Opinions** (`fact_type: "opinion"`): Coach's observations about patterns
- **Experience Facts** (`fact_type: "experience"`): Goals, intentions, coach advice
- **Mental Models** (`fact_type: "mental_model"`): Consolidated knowledge about user patterns
## Customization
@@ -266,9 +266,9 @@ The key benefit: **Separation of concerns**
**Use Hindsight directly when:**
- You want a complete memory-first solution
- You want automatic memory retrieval and opinion formation
- You want automatic memory retrieval and mental model consolidation
- You want to use different LLM providers (not just OpenAI)
- You want the `/think` endpoint's integrated approach
- You want the `/reflect` endpoint's integrated approach
## Learning Points
@@ -127,7 +127,7 @@ for r in results.results:
## Reflect: Generate Insights
The `reflect` operation performs a more thorough analysis of existing memories. This allows the agent to form new connections between memories which are then persisted as opinions and/or observations.
The `reflect` operation performs reasoning over existing memories using the bank's disposition. It retrieves relevant facts and mental models to generate contextual responses.
Example use cases:
- An AI Project Manager reflecting on what risks need to be mitigated
@@ -142,12 +142,11 @@ print(response)
## Memory Types
Hindsight organizes memory into four networks to mimic human memory:
Hindsight organizes knowledge into facts and consolidated mental models:
- **World**: Facts about the world ("The stove gets hot")
- **Experiences**: Agent's own experiences ("I touched the stove and it really hurt")
- **Opinion**: Beliefs with confidence scores ("I shouldn't touch the stove again" - .99 confidence)
- **Observation**: Complex mental models derived by reflecting on facts and experiences
- **Experience**: Agent's own experiences ("I touched the stove and it really hurt")
- **Mental Model**: Consolidated knowledge synthesized from facts ("Always be careful around hot surfaces")
## Cleanup
+1 -1
View File
@@ -78,7 +78,7 @@ The backup includes:
- Memory banks and their configuration
- Documents and chunks
- Entities and their relationships
- Memory units (facts, experiences, opinions, observations)
- Memory units (facts, experiences, mental models)
- Entity cooccurrences and memory links
:::note Consistency
@@ -147,6 +147,5 @@ Deleting a document permanently removes all memories extracted from it. This act
## Next Steps
- [**Entities**](./entities) — Track people, places, and concepts
- [**Operations**](./operations) — Monitor background tasks
- [**Memory Banks**](./memory-banks) — Configure bank settings
@@ -1,112 +0,0 @@
---
sidebar_position: 7
---
# Entities
Entities are the people, organizations, places, and concepts that Hindsight automatically extracts and tracks across your memory bank.
:::info Automatic Feature
You don't need to do anything to use entities—Hindsight extracts them automatically when you call `retain`. However, understanding how entities work is important because they power key features in [recall](./recall) and [reflect](./reflect).
:::
## Why Entities Matter
Entities improve recall quality in two ways:
1. **Co-occurrence tracking** — When entities appear together in facts, Hindsight builds a graph of relationships. This enables graph-based recall to find indirect connections.
2. **Observations** — Hindsight synthesizes high-level summaries about each entity from multiple facts. Including entity observations in recall provides richer context.
## What Gets Extracted?
When you retain information, the LLM extracts named entities from each fact:
- **People** — Names like "Alice", "Dr. Smith", "CEO John"
- **Organizations** — Companies, teams, institutions
- **Places** — Cities, countries, specific locations
- **Products/Objects** — Software, tools, significant items
- **Concepts** — Abstract themes like "career growth", "friendship"
**Example:**
```
Content: "Alice works at Google in Mountain View. She specializes in TensorFlow."
Entities extracted:
- Alice (person)
- Google (organization)
- Mountain View (location)
- TensorFlow (product)
```
## Entity Resolution
When the same entity is mentioned multiple times (possibly with different names), Hindsight resolves them to a single canonical entity using a scoring algorithm:
### Resolution Factors
1. **Name similarity (50%)** — How closely the text matches existing entity names. Handles variations like "Alice" vs "Alice Chen" or partial matches.
2. **Co-occurrence (30%)** — Entities that frequently appear together are more likely to be the same. If "Alice" always appears with "Google" and "TensorFlow", a new mention of "Alice" near those entities scores higher for matching.
3. **Temporal proximity (20%)** — Recent mentions are weighted more heavily. If an entity was seen in the last 7 days, new similar mentions are more likely to match.
### Resolution Threshold
A match requires a combined score above **0.6** (60%). Below this threshold, Hindsight creates a new entity rather than risk merging distinct entities.
This means:
- Exact name matches with recent co-occurring entities → strong match
- Partial name matches without context → likely creates new entity
- Same name in completely different contexts → may create separate entities
## Entity Observations
Observations are **derived state**—high-level summaries that Hindsight automatically synthesizes from the facts associated with an entity. They provide a condensed view of what the system knows about important entities.
**Example:**
Facts about Alice:
- "Alice works at Google"
- "Alice is a software engineer"
- "Alice specializes in ML"
- "Alice joined Google in 2020"
- "Alice leads the search team"
Observation created:
- "Alice is a software engineer at Google who joined in 2020, specializes in ML, and leads the search team"
### How Observations Work
Observations are **not generated for every entity**. When you retain new documents:
1. **Top entities selected** — Hindsight identifies the top 5 most-mentioned entities in the batch
2. **Threshold check** — Only entities with at least 5 facts get observations
3. **Regeneration** — Observations are regenerated using the entity's most recent 50 facts
4. **Old observations replaced** — Previous observations are deleted and new ones created
This means:
- Frequently mentioned entities get observations; rarely mentioned ones don't
- Observations stay up-to-date as new information is retained
- The system prioritizes entities that matter most to your memory bank
### Observations vs Opinions
Observations are **objective summaries**—they synthesize facts without any bias or perspective. This is different from [opinions](./opinions), which are influenced by the memory bank's disposition.
| | Observations | Opinions |
|---|---|---|
| **Purpose** | Summarize what's known about an entity | Express the bank's perspective on a topic |
| **Disposition influence** | No | Yes |
| **Scope** | Per-entity | Any topic |
| **Generation** | Automatic (top entities) | On-demand via reflect |
### Using Observations
Observations are included in recall results when you set `include_entities=True`. They provide quick context about key entities without retrieving all underlying facts.
## Next Steps
- [**Recall**](./recall) — Use entities in memory retrieval
- [**Reflect**](./reflect) — Get entity-aware responses
@@ -89,7 +89,7 @@ hindsight recall my-bank "Tell me about Alice" -v
## Reflect: Reason with Disposition
Generate disposition-aware responses that form opinions based on evidence.
Generate disposition-aware responses using memories and mental models.
<Tabs>
<TabItem value="python" label="Python">
@@ -104,7 +104,7 @@ Generate disposition-aware responses that form opinions based on evidence.
# Basic reflect
hindsight reflect my-bank "Should we adopt TypeScript for our backend?"
# Verbose output (shows sources and opinions)
# Verbose output (shows sources and mental models)
hindsight reflect my-bank "What are Alice's strengths for the team lead role?" -v
# With higher reasoning budget
@@ -114,7 +114,7 @@ hindsight reflect my-bank "Analyze our tech stack" --budget high
</TabItem>
</Tabs>
**What happens:** Memories are recalled, bank disposition is loaded, LLM reasons through evidence, new opinions are formed and stored.
**What happens:** Memories and mental models are recalled, bank disposition is applied, and the LLM reasons through the evidence to generate a response.
**See:** [Reflect Details](./reflect) for disposition configuration.
@@ -126,9 +126,9 @@ hindsight reflect my-bank "Analyze our tech stack" --budget high
|---------|--------|--------|---------|
| **Purpose** | Store information | Find information | Reason about information |
| **Input** | Raw text/documents | Search query | Question/prompt |
| **Output** | Memory IDs | Ranked facts | Reasoned response + opinions |
| **Output** | Memory IDs | Ranked facts + mental models | Reasoned response |
| **Uses LLM** | Yes (extraction) | No | Yes (generation) |
| **Forms opinions** | No | No | Yes |
| **Uses mental models** | No | Yes | Yes |
| **Disposition** | No | No | Yes |
---
@@ -137,5 +137,5 @@ hindsight reflect my-bank "Analyze our tech stack" --budget high
- [**Retain**](./retain) — Advanced options for storing memories
- [**Recall**](./recall) — Tuning search quality and performance
- [**Reflect**](./reflect) — Configuring disposition and opinions
- [**Reflect**](./reflect) — Configuring disposition
- [**Memory Banks**](./memory-banks) — Managing memory bank disposition
@@ -43,8 +43,8 @@ Make sure you've completed the [Quick Start](./quickstart) to install the client
<TabItem value="cli" label="CLI">
```bash
# Set background
hindsight bank background my-bank "I am a research assistant specializing in ML"
# Set mission
hindsight bank mission my-bank "I am a research assistant specializing in ML"
# Set disposition
hindsight bank disposition my-bank \
@@ -56,30 +56,30 @@ hindsight bank disposition my-bank \
</TabItem>
</Tabs>
## Background and Disposition
## Mission and Disposition
Background and disposition are optional settings that influence how the bank forms opinions during [reflect](./reflect) operations.
Mission and disposition are optional settings that influence how the bank reasons during [reflect](./reflect) operations.
:::info
Background and disposition only affect the `reflect` operation (opinion formation). They do not impact `retain`, `recall`, or other memory operations.
Mission and disposition only affect the `reflect` operation. They do not impact `retain`, `recall`, or other memory operations.
:::
### Background
### Mission
The background is a first-person narrative providing context for opinion formation:
The mission is a first-person narrative providing context for reasoning:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={memoryBanksPy} section="bank-background" language="python" />
<CodeSnippet code={memoryBanksPy} section="bank-mission" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={memoryBanksMjs} section="bank-background" language="javascript" />
<CodeSnippet code={memoryBanksMjs} section="bank-mission" language="javascript" />
</TabItem>
</Tabs>
### Disposition Traits
Disposition traits influence how opinions are formed during reflection. Each trait is scored 1 to 5:
Disposition traits influence how reasoning is performed during reflection. Each trait is scored 1 to 5:
| Trait | Low (1) | High (5) |
|-------|---------|----------|
@@ -25,9 +25,7 @@ Support for external streaming platforms like Kafka for scale-out processing is
| Operation | Trigger | Description |
|-----------|---------|-------------|
| **batch_retain** | `retain_batch` with `async=True` | Processes large content batches in the background |
| **form_opinion** | After each `reflect` call | Extracts and stores new opinions formed during reflection |
| **reinforce_opinion** | After `retain` | Updates opinion confidence based on new supporting evidence |
| **regenerate_observations** | Bank profile update | Regenerates entity observations when disposition changes |
| **consolidate** | After `retain` | Consolidates new facts into mental models |
## Async Retain Example
@@ -93,5 +91,4 @@ Response:
## Next Steps
- [**Documents**](./documents) — Track document sources
- [**Entities**](./entities) — Monitor entity tracking
- [**Memory Banks**](./memory-banks) — Configure bank settings
@@ -1,135 +0,0 @@
---
sidebar_position: 5
---
# Opinions
How memory banks form, store, and evolve beliefs.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import opinionsPy from '!!raw-loader!@site/examples/api/opinions.py';
import opinionsMjs from '!!raw-loader!@site/examples/api/opinions.mjs';
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## What Are Opinions?
Opinions are beliefs formed by the memory bank based on evidence and disposition. Unlike world facts (objective information received) or experience (conversations and events), opinions are **judgments** with confidence scores.
| Type | Example | Confidence |
|------|---------|------------|
| World Fact | "Python was created in 1991" | — |
| Experience | "I recommended Python to Bob" | — |
| Opinion | "Python is the best language for data science" | 0.85 |
## How Opinions Form
Opinions are created during `reflect` operations when the memory bank:
1. Retrieves relevant facts
2. Applies disposition traits
3. Forms a judgment
4. Assigns a confidence score
```mermaid
graph LR
F[Facts] --> D[Disposition Filter]
D --> J[Judgment]
J --> O[Opinion + Confidence]
O --> S[(Store)]
```
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={opinionsPy} section="opinion-form" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={opinionsMjs} section="opinion-form" language="javascript" />
</TabItem>
</Tabs>
## Searching Opinions
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={opinionsPy} section="opinion-search" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={opinionsMjs} section="opinion-search" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
hindsight recall my-bank "programming" --types opinion
```
</TabItem>
</Tabs>
## Opinion Evolution
Opinions change as new evidence arrives:
| Evidence Type | Effect |
|---------------|--------|
| **Reinforcing** | Confidence increases (+0.1) |
| **Weakening** | Confidence decreases (-0.15) |
| **Contradicting** | Opinion revised, confidence reset |
**Example evolution:**
```
t=0: "Python is best for data science" (0.70)
↓ New evidence: Python dominates ML libraries
t=1: "Python is best for data science" (0.85)
↓ New evidence: Julia is 10x faster for numerical computing
t=2: "Python is best for data science, though Julia is faster" (0.75)
↓ New evidence: Most teams still use Python
t=3: "Python is best for data science" (0.82)
```
## Disposition Influence
Different dispositions form different opinions from the same facts:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={opinionsPy} section="opinion-disposition" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={opinionsMjs} section="opinion-disposition" language="javascript" />
</TabItem>
</Tabs>
## Opinions in Reflect Responses
When `reflect` uses opinions, they appear in `based_on`:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={opinionsPy} section="opinion-in-reflect" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={opinionsMjs} section="opinion-in-reflect" language="javascript" />
</TabItem>
</Tabs>
## Confidence Thresholds
Opinions below a confidence threshold may be:
- Excluded from responses
- Marked as uncertain
- Revised more easily
```python
# Low confidence opinions are held loosely
# "I think Python might be good for this" (0.45)
# High confidence opinions are stated firmly
# "Python is definitely the right choice" (0.92)
```
@@ -105,5 +105,5 @@ curl -fsSL https://hindsight.vectorize.io/get-cli | bash
- [**Retain**](./retain) — Advanced options for storing memories
- [**Recall**](./recall) — Search and retrieval strategies
- [**Reflect**](./reflect) — Disposition-aware reasoning
- [**Memory Banks**](./memory-banks) — Configure disposition and background
- [**Memory Banks**](./memory-banks) — Configure disposition and mission
- [**Server Deployment**](/developer/installation) — Docker Compose, Helm, and production setup
+6 -26
View File
@@ -42,12 +42,12 @@ Make sure you've completed the [Quick Start](./quickstart) to install the client
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `query` | string | required | Natural language query |
| `types` | list | all | Filter: `world`, `experience`, `opinion` |
| `types` | list | all | Filter: `world`, `experience`, `mental_model` |
| `budget` | string | "mid" | Budget level: `low`, `mid`, `high` |
| `max_tokens` | int | 4096 | Token budget for results |
| `trace` | bool | false | Enable trace output for debugging |
| `include_entities` | bool | false | Include entity observations |
| `max_entity_tokens` | int | 500 | Token budget for entity observations |
| `include_chunks` | bool | false | Include raw text chunks that generated the memories |
| `max_chunk_tokens` | int | 500 | Token budget for chunks |
| `tags` | list | None | Filter memories by tags (see [Tag Filtering](#filter-by-tags)) |
| `tags_match` | string | "any" | How to match tags: `any`, `all`, `any_strict`, `all_strict` |
@@ -68,18 +68,15 @@ Recall specific memory types:
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-world-only" language="python" />
<CodeSnippet code={recallPy} section="recall-experience-only" language="python" />
<CodeSnippet code={recallPy} section="recall-opinions-only" language="python" />
<CodeSnippet code={recallPy} section="recall-mental-models-only" language="python" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-fact-type" language="bash" />
</TabItem>
</Tabs>
:::warning About Opinions
Opinions are beliefs formed during [reflect](/developer/api/reflect) operations. Unlike world facts and experience, opinions are subjective interpretations and may not represent objective truth. Depending on your use case:
- **Exclude opinions** (`types=["world", "experience"]`) when you need factual, verifiable information
- **Include opinions** when you want the agent's perspective or formed beliefs
- **Use opinions alone** (`types=["opinion"]`) only when specifically asking about the agent's views
:::tip About Mental Models
Mental models are consolidated knowledge synthesized from multiple facts. They capture patterns, preferences, and learnings that the memory bank has built up over time. Mental models are automatically created in the background after retain operations.
:::
## Token Budget Management
@@ -96,23 +93,6 @@ The `max_tokens` parameter lets you control how much of your agent's context bud
This design means you never have to guess whether 10 results or 50 results will fit your context. Just specify the token budget and Hindsight returns as many relevant memories as will fit.
## Include Related Context
Beyond the core memory results, you can optionally retrieve additional context—each with its own token budget:
| Option | Parameter | Description |
|--------|-----------|-------------|
| **Chunks** | `include_chunks`, `max_chunk_tokens` | Raw text chunks that generated the memories |
| **Entity Observations** | `include_entities`, `max_entity_tokens` | Related observations about entities mentioned in results |
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-include-entities" language="python" />
</TabItem>
</Tabs>
This gives your agent richer context while maintaining precise control over total token consumption.
## Budget Levels
The `budget` parameter controls graph traversal depth:
+44 -130
View File
@@ -4,15 +4,14 @@ sidebar_position: 3
# Reflect
Generate disposition-aware responses using retrieved memories.
Generate disposition-aware responses using an agentic reasoning loop.
When you call **reflect**, Hindsight performs a multi-step reasoning process:
1. **Recalls** relevant memories from the bank based on your query
When you call **reflect**, Hindsight runs an **agentic loop** that:
1. **Autonomously searches** for relevant information using multiple tools
2. **Applies** the bank's disposition traits to shape the reasoning style
3. **Generates** a contextual answer grounded in the retrieved facts
4. **Forms opinions** in the background based on the reasoning (available in subsequent calls)
3. **Generates** a grounded answer with citations to the sources used
The response includes the generated answer along with the facts that were used, providing full transparency into how the answer was derived.
The agent has access to hierarchical retrieval tools (reflections → mental models → raw facts) and decides what information it needs to answer your query.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
@@ -24,7 +23,7 @@ import reflectMjs from '!!raw-loader!@site/examples/api/reflect.mjs';
import reflectSh from '!!raw-loader!@site/examples/api/reflect.sh';
:::info How Reflect Works
Learn about disposition-driven reasoning and opinion formation in the [Reflect Architecture](/developer/reflect) guide.
Learn about disposition-driven reasoning in the [Reflect Architecture](/developer/reflect) guide.
:::
:::tip Prerequisites
@@ -50,21 +49,41 @@ Make sure you've completed the [Quick Start](./quickstart) to install the client
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `query` | string | required | Question or prompt |
| `budget` | string | "low" | Budget level: `low`, `mid`, `high` |
| `context` | string | None | Additional context for the query |
| `max_tokens` | int | 4096 | Maximum tokens for the response |
| `budget` | string | "low" | Budget level: `low`, `mid`, `high` (see below) |
| `max_tokens` | int | 4096 | Maximum tokens for the final response |
| `response_schema` | object | None | JSON Schema for [structured output](#structured-output) |
| `tags` | list | None | Filter memories by tags during reflection |
| `tags_match` | string | "any" | How to match tags: `any`, `all`, `any_strict`, `all_strict` |
| `trace` | bool | false | Include detailed agent trace in response |
### Budget
The `budget` parameter controls how thoroughly the agent searches for information:
| Budget | Iterations | Use Case |
|--------|------------|----------|
| `low` | 0.5x base | Quick answers, simple lookups |
| `mid` | 1x base | Balanced exploration |
| `high` | 2x base | Complex questions, comprehensive analysis |
Higher budgets allow the agent more iterations to search reflections, mental models, and raw facts before generating a response. Use `high` for questions that require synthesizing information from multiple sources.
### Max Tokens
The `max_tokens` parameter limits the length of the final generated response. This does not affect how much the agent can retrieve during the agentic loop — only the final answer length.
### Response Fields
| Field | Type | Description |
|-------|------|-------------|
| `text` | string | The generated answer text |
| `based_on` | array | Facts used to generate the response |
| `used_memory_ids` | array | Memory IDs cited by the agent |
| `used_reflection_ids` | array | Reflection IDs cited by the agent |
| `used_mental_model_ids` | array | Mental model IDs cited by the agent |
| `structured_output` | object | Parsed structured output (when `response_schema` provided) |
| `usage` | TokenUsage | Token usage metrics for the LLM call |
| `iterations` | int | Number of agent loop iterations |
| `tools_called` | int | Total number of tool calls made |
| `usage` | TokenUsage | Token usage metrics |
The `usage` field contains:
- `input_tokens`: Number of input/prompt tokens consumed
@@ -80,35 +99,6 @@ The `usage` field contains:
</TabItem>
</Tabs>
## The Role of Context
The `context` parameter steers how the reflection is performed without impacting the memory recall. It provides situational information that helps shape the reasoning and response.
**How context is used:**
- **Shapes reasoning**: Helps understand the situation when formulating an answer
- **Disambiguates intent**: Clarifies what aspect of the query matters most
- **Does not affect recall**: The same memories are retrieved regardless of context
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-with-context" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-with-context" language="javascript" />
</TabItem>
</Tabs>
## Opinion Formation
When reflect reasons about a question, it may form new **opinions** based on the evidence in the memory bank. These opinions are created in the background and become available in subsequent `reflect` and `recall` calls.
**Why opinions matter:**
- **Consistent thinking**: Opinions ensure the memory bank maintains a coherent perspective over time
- **Evolving viewpoints**: As more information is retained, opinions can be refined or updated
- **Grounded reasoning**: Opinions are always derived from factual evidence in the memory bank
Opinions are stored as a special memory type and are automatically retrieved when relevant to future queries. This creates a natural evolution of the bank's perspective, similar to how humans form and refine their views based on accumulated experience.
## Disposition Influence
The bank's disposition affects reflect responses:
@@ -128,23 +118,20 @@ The bank's disposition affects reflect responses:
</TabItem>
</Tabs>
## Using Sources
## Citations
The `based_on` field shows which memories informed the response:
The agent cites which sources it used to generate the response:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-sources" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-sources" language="javascript" />
</TabItem>
</Tabs>
- `used_memory_ids` — Raw memory facts that were retrieved and cited
- `used_reflection_ids` — User-curated reflections that were used
- `used_mental_model_ids` — Consolidated mental models that were used
**Important:** Only IDs that were actually retrieved during the agent loop can be cited. The agent validates citations to prevent hallucinated references.
This enables:
- **Transparency** — users see why the bank said something
- **Verification** — check if the response is grounded in facts
- **Debugging** — understand retrieval quality
- **Transparency** — users see exactly which sources informed the answer
- **Verification** — check if the response is grounded in actual memories
- **Debugging** — use `trace=True` for detailed tool call logs
## Structured Output
@@ -154,86 +141,13 @@ The easiest way to define a schema is using **Pydantic models**:
<Tabs>
<TabItem value="python" label="Python">
```python
from pydantic import BaseModel
from hindsight_client import Hindsight
# Define your response structure with Pydantic
class HiringRecommendation(BaseModel):
recommendation: str
confidence: str # "low", "medium", "high"
key_factors: list[str]
risks: list[str] = []
with Hindsight() as client:
response = client.reflect(
bank_id="hiring-team",
query="Should we hire Alice for the ML team lead position?",
response_schema=HiringRecommendation.model_json_schema(),
)
# Parse structured output into Pydantic model
result = HiringRecommendation.model_validate(response.structured_output)
print(f"Recommendation: {result.recommendation}")
print(f"Confidence: {result.confidence}")
print(f"Key factors: {result.key_factors}")
```
<CodeSnippet code={reflectPy} section="reflect-structured-output" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
```javascript
import { Hindsight } from "@anthropic-ai/hindsight";
const client = new Hindsight();
// Define JSON schema directly
const responseSchema = {
type: "object",
properties: {
recommendation: { type: "string" },
confidence: { type: "string", enum: ["low", "medium", "high"] },
key_factors: { type: "array", items: { type: "string" } },
risks: { type: "array", items: { type: "string" } },
},
required: ["recommendation", "confidence", "key_factors"],
};
const response = await client.reflect({
bankId: "hiring-team",
query: "Should we hire Alice for the ML team lead position?",
responseSchema: responseSchema,
});
// Structured output
console.log(response.structuredOutput.recommendation);
console.log(response.structuredOutput.keyFactors);
```
<CodeSnippet code={reflectMjs} section="reflect-structured-output" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
First, create a JSON schema file `schema.json`:
```json
{
"type": "object",
"properties": {
"recommendation": {"type": "string"},
"confidence": {"type": "string", "enum": ["low", "medium", "high"]},
"key_factors": {"type": "array", "items": {"type": "string"}}
},
"required": ["recommendation", "confidence", "key_factors"]
}
```
Then use the `--schema` flag:
```bash
hindsight memory reflect hiring-team \
"Should we hire Alice for the ML team lead position?" \
--schema schema.json
```
<CodeSnippet code={reflectSh} section="reflect-structured-output" language="bash" />
</TabItem>
</Tabs>
@@ -0,0 +1,214 @@
---
sidebar_position: 4
---
# Reflections
User-curated summaries that provide high-quality, pre-computed answers for common queries.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import reflectionsPy from '!!raw-loader!@site/examples/api/reflections.py';
## What Are Reflections?
Reflections are **saved reflect responses** that you curate for your memory bank. When you create a reflection, Hindsight runs a reflect operation with your source query and stores the result. During future reflect calls, these pre-computed summaries are checked first — providing faster, more consistent answers.
```mermaid
graph LR
A[Create Reflection] --> B[Run Reflect]
B --> C[Store Result]
C --> D[Future Queries]
D --> E{Match Found?}
E -->|Yes| F[Return Reflection]
E -->|No| G[Run Full Reflect]
```
### Why Use Reflections?
| 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 |
### Hierarchical Retrieval
During reflect, the agent checks sources in priority order:
1. **Reflections** — User-curated summaries (highest priority)
2. **Mental Models** — Consolidated knowledge
3. **Raw Facts** — Ground truth memories
Reflections are checked first because they represent your explicitly curated knowledge.
---
## Create a Reflection
Creating a reflection runs a reflect operation in the background and saves the result:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectionsPy} section="create-reflection" language="python" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Create a reflection (async operation)
curl -X POST "http://localhost:8888/v1/default/banks/my-bank/reflections" \
-H "Content-Type: application/json" \
-d '{
"name": "Team Communication Preferences",
"source_query": "How does the team prefer to communicate?",
"tags": ["team"]
}'
# Response: {"operation_id": "op-123"}
# Use the operations endpoint to check completion
```
</TabItem>
</Tabs>
### Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Human-readable name for the reflection |
| `source_query` | string | Yes | The query to run to generate content |
| `tags` | list | No | Tags for filtering during retrieval |
| `max_tokens` | int | No | Maximum tokens for the reflection content |
---
## List Reflections
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectionsPy} section="list-reflections" language="python" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
curl "http://localhost:8888/v1/default/banks/my-bank/reflections"
```
</TabItem>
</Tabs>
---
## Get a Reflection
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectionsPy} section="get-reflection" language="python" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
curl "http://localhost:8888/v1/default/banks/my-bank/reflections/{reflection_id}"
```
</TabItem>
</Tabs>
### Response Fields
| Field | Type | Description |
|-------|------|-------------|
| `id` | string | Unique reflection ID |
| `bank_id` | string | Memory bank ID |
| `name` | string | Human-readable name |
| `source_query` | string | The query used to generate content |
| `content` | string | The generated reflection text |
| `tags` | list | Tags for filtering |
| `last_refreshed_at` | string | When the reflection was last updated |
| `created_at` | string | When the reflection was created |
| `reflect_response` | object | Full reflect response including `based_on` facts |
---
## Refresh a Reflection
Re-run the source query to update the reflection with current knowledge:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectionsPy} section="refresh-reflection" language="python" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
curl -X POST "http://localhost:8888/v1/default/banks/my-bank/reflections/{reflection_id}/refresh"
```
</TabItem>
</Tabs>
Refreshing is useful when:
- New memories have been retained that affect the topic
- Mental models have been updated
- You want to ensure the reflection reflects current knowledge
---
## Update a Reflection
Update the reflection's name:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectionsPy} section="update-reflection" language="python" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
curl -X PATCH "http://localhost:8888/v1/default/banks/my-bank/reflections/{reflection_id}" \
-H "Content-Type: application/json" \
-d '{"name": "Updated Team Communication Preferences"}'
```
</TabItem>
</Tabs>
---
## Delete a Reflection
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectionsPy} section="delete-reflection" language="python" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
curl -X DELETE "http://localhost:8888/v1/default/banks/my-bank/reflections/{reflection_id}"
```
</TabItem>
</Tabs>
---
## Use Cases
| Use Case | Example |
|----------|---------|
| **FAQ Answers** | Pre-compute answers to common customer questions |
| **Onboarding Summaries** | "What should new team members know?" |
| **Status Reports** | "What's the current project status?" refreshed weekly |
| **Policy Summaries** | "What are our security policies?" |
---
## Next Steps
- [**Reflect**](./reflect) — How the agentic loop uses reflections
- [**Mental Models**](/developer/mental-models) — How knowledge is consolidated
- [**Operations**](./operations) — Track async reflection creation
+5 -10
View File
@@ -169,15 +169,10 @@ Use consistent naming patterns for tags:
Use the list tags API to discover existing tags, useful for UI autocomplete or wildcard expansion:
```python
# List all tags in a bank
tags = client.list_tags(bank_id="my-bank")
for tag in tags.items:
print(f"{tag.tag}: {tag.count} memories")
# Search with wildcards (* matches any characters)
user_tags = client.list_tags(bank_id="my-bank", q="user:*")
admin_tags = client.list_tags(bank_id="my-bank", q="*-admin")
```
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-list-tags" language="python" />
</TabItem>
</Tabs>
See [Recall API](./recall#filter-by-tags) for filtering memories by tags during retrieval.
@@ -301,15 +301,6 @@ For advanced authentication (JWT, OAuth, multi-tenant schemas), implement a cust
- **`mpfp`**: Multi-Path Fact Propagation - iterative graph traversal with activation spreading. More thorough but slower.
- **`bfs`**: Breadth-first search from seed facts. Simple but less effective for large graphs.
### Entity Observations
Controls when the system generates entity observations (summaries about entities mentioned in retained content).
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_OBSERVATION_MIN_FACTS` | Minimum facts about an entity before generating observations | `5` |
| `HINDSIGHT_API_OBSERVATION_TOP_ENTITIES` | Max entities to process per retain batch | `5` |
### Retain
Controls the retain (memory ingestion) pipeline.
@@ -320,7 +311,6 @@ Controls the retain (memory ingestion) pipeline.
| `HINDSIGHT_API_RETAIN_CHUNK_SIZE` | Max characters per chunk for fact extraction. Larger chunks extract fewer LLM calls but may lose context. | `3000` |
| `HINDSIGHT_API_RETAIN_EXTRACTION_MODE` | Fact extraction mode: `concise` (selective, fewer high-quality facts) or `verbose` (detailed, more facts) | `concise` |
| `HINDSIGHT_API_RETAIN_EXTRACT_CAUSAL_LINKS` | Extract causal relationships between facts | `true` |
| `HINDSIGHT_API_RETAIN_OBSERVATIONS_ASYNC` | Run entity observation generation asynchronously (after retain completes) | `false` |
#### Extraction Modes
+24 -15
View File
@@ -13,7 +13,7 @@ AI agents forget everything between sessions. Every conversation starts from zer
- **Simple vector search isn't enough** — "What did Alice do last spring?" requires temporal reasoning, not just semantic similarity
- **Facts get disconnected** — Knowing "Alice works at Google" and "Google is in Mountain View" should let you answer "Where does Alice work?" even if you never stored that directly
- **AI Agents needs to form opinions** — A coding assistant that remembers "the user prefers functional programming" should weigh that when making recommendations
- **AI Agents need to consolidate knowledge** — A coding assistant that remembers "the user prefers functional programming" should consolidate this into a mental model and weigh it when making recommendations
- **Context matters** — The same information means different things to different memory banks with different personalities
Hindsight solves these problems with a memory system designed specifically for AI agents.
@@ -21,7 +21,7 @@ Hindsight solves these problems with a memory system designed specifically for A
## What Hindsight Does
```mermaid
graph TB
graph LR
subgraph app["<b>Your Application</b>"]
Agent[AI Agent]
end
@@ -30,9 +30,13 @@ graph TB
API[API Server]
subgraph bank["<b>Memory Bank</b>"]
direction TB
MentalModels[Mental Models]
MemEnt[Memories & Entities]
Chunks[Chunks]
Documents[Documents]
Memories[Memories]
Entities[Entities]
MentalModels --> MemEnt --> Chunks --> Documents
end
end
@@ -40,24 +44,22 @@ graph TB
Agent -->|recall| API
Agent -->|reflect| API
API --> Documents
API --> Memories
API --> Entities
API --> bank
```
**Your AI agent** stores information via `retain()`, searches with `recall()`, and reasons with `reflect()` — all interactions with its dedicated **memory bank**
## Key Components
### Three Memory Types
### Memory Types
Hindsight separates memories by type for epistemic clarity:
Hindsight organizes knowledge into facts and consolidated mental models:
| Type | What it stores | Example |
|------|----------------|---------|
| **World** | Objective facts received | "Alice works at Google" |
| **Bank** | Bank's own actions | "I recommended Python to Bob" |
| **Opinion** | Formed beliefs + confidence | "Python is best for ML" (0.85) |
| **Experience** | Bank's own actions and interactions | "I recommended Python to Bob" |
| **Mental Model** | Consolidated knowledge from facts | "The user prefers functional programming patterns"
### Multi-Strategy Retrieval (TEMPR)
@@ -86,9 +88,17 @@ graph LR
| **Graph** | Related entities, indirect connections |
| **Temporal** | "last spring", "in June", time ranges |
### Mental Model Consolidation
After memories are retained, Hindsight automatically consolidates related facts into **mental models** — synthesized knowledge representations that capture patterns and learnings:
- **Automatic synthesis**: New facts are analyzed and consolidated into existing or new mental models
- **Evidence tracking**: Each mental model tracks which facts support it
- **Continuous refinement**: Mental models evolve as new evidence arrives
### Disposition Traits
Memory banks have disposition traits that influence how opinions are formed during Reflect:
Memory banks have disposition traits that influence reasoning during Reflect:
| Trait | Scale | Low (1) | High (5) |
|-------|-------|---------|----------|
@@ -107,14 +117,13 @@ These traits only affect the `reflect` operation, not `recall`.
### Core Concepts
- [**Retain**](/developer/retain) — How memories are stored with multi-dimensional facts
- [**Recall**](/developer/retrieval) — How TEMPR's 4-way search retrieves memories
- [**Reflect**](/developer/reflect) — How disposition influences reasoning and opinion formation
- [**Reflect**](/developer/reflect) — How disposition influences reasoning
### API Methods
- [**Retain**](/developer/api/retain) — Store information in memory banks
- [**Recall**](/developer/api/recall) — Search and retrieve memories
- [**Reflect**](/developer/api/reflect) — Reason with disposition
- [**Memory Banks**](/developer/api/memory-banks) — Configure disposition and background
- [**Entities**](/developer/api/entities) — Track people, places, and concepts
- [**Memory Banks**](/developer/api/memory-banks) — Configure disposition and mission
- [**Documents**](/developer/api/documents) — Manage document sources
- [**Operations**](/developer/api/operations) — Monitor async tasks
@@ -0,0 +1,174 @@
---
sidebar_position: 5
---
import CodeSnippet from '@site/src/components/CodeSnippet';
import recallPy from '!!raw-loader!@site/examples/api/recall.py';
# Mental Models: Knowledge Consolidation
After memories are retained, Hindsight automatically consolidates related facts into **mental models** — synthesized knowledge representations that capture patterns and learnings.
```mermaid
graph LR
A[New Facts] --> B[Consolidation Engine]
B --> C{Existing Model?}
C -->|Yes| D[Refine Model]
C -->|No| E[Create Model]
D --> F[Mental Models]
E --> F
```
---
## What Are Mental Models?
Mental models are **consolidated knowledge** synthesized from multiple facts. Unlike raw facts which are individual pieces of information, mental models represent patterns, preferences, and learnings that emerge from accumulated evidence.
| Raw Facts | Mental Model |
|-----------|--------------|
| "Alice prefers Python" | "Alice is a Python-focused developer who values readability and simplicity" |
| "Alice dislikes verbose code" | |
| "Alice recommends type hints" | |
Mental models provide:
- **Synthesis**: Patterns that emerge from multiple facts
- **Context**: Richer understanding than individual facts
- **Efficiency**: Condensed knowledge for faster retrieval
---
## How Consolidation Works
### Automatic Background Processing
After `retain()` completes, the consolidation engine runs automatically:
1. **New facts analyzed** — Each new fact is compared against existing mental models
2. **Pattern detection** — Related facts are grouped and synthesized
3. **Model creation/update** — New mental models are created or existing ones refined
4. **Evidence tracking** — Each mental model maintains references to supporting facts
### Evidence-Based Evolution
Mental models evolve as new evidence arrives:
| Event | What the bank learns | Mental model state |
|-------|---------------------|----------------|
| **Day 1** | "Redis is open source under BSD license" | "Redis is excellent for caching — fast, reliable, and OSS-friendly" (2 supporting facts) |
| **Day 2** | "Redis has great community support" | Mental model reinforced (3 supporting facts) |
| **Day 30** | "Redis changed license to SSPL" | Mental model refined: "Redis is technically strong, but has license concerns for cloud" |
| **Day 45** | "Valkey forked Redis under BSD" | New mental model: "Consider Valkey for new projects requiring true OSS" |
### Handling Contradictory Evidence
What happens when a new fact contradicts an existing mental model?
The consolidation engine doesn't blindly overwrite — it **reconciles** the contradiction by capturing the evolution:
**Example: User preference changes**
| Time | Fact | Mental Model |
|------|------|--------------|
| Week 1 | "User says they love 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 says they've 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 and no longer uses React" |
Notice how the final mental model captures the **full journey** — not just "User prefers Vue" but the complete evolution of their preference. This nuanced understanding means:
- Your agent won't recommend React tutorials to someone who explicitly moved away from it
- Your agent understands *why* this matters (they were enthusiastic before, so this is a deliberate choice)
- Your agent can reference this history when relevant ("I know you used to work with React...")
The system:
1. **Detects the conflict** — New fact contradicts existing model
2. **Preserves history** — Incorporates the previous understanding into the new model
3. **Creates nuanced model** — Synthesizes a richer understanding that captures the change
4. **Updates freshness** — Marks the model as recently updated
**Example: Correcting misinformation**
| Time | Fact | Mental Model |
|------|------|--------------|
| Day 1 | "Alice works at Google" | "Alice is a Google employee" |
| Day 10 | "Alice actually works at Meta, not Google" | "Alice works at Meta (previously thought to work at Google)" |
When a fact explicitly corrects previous information, the mental model is updated to reflect the correction while noting the previous understanding. The raw facts are always preserved, so you can trace back to see what was originally stated and when it was corrected.
---
## Mental Models in Retrieval
Mental models are automatically included in both `recall()` and `reflect()` operations:
### In Recall
Mental models are returned alongside raw facts, filtered by the `types` parameter:
<CodeSnippet code={recallPy} section="recall-with-mental-models" language="python" />
### In Reflect
The reflect agent uses **hierarchical retrieval**:
1. **[Reflections](/developer/api/reflections)** — User-curated summaries (highest priority)
2. **Mental Models** — Consolidated knowledge with freshness awareness
3. **Raw Facts** — Ground truth for verification
The agent automatically queries mental models and uses them to inform its reasoning.
---
## Freshness Awareness
Mental models track when they were last updated. During reflect, the agent considers freshness:
- **Fresh models**: Used directly for reasoning
- **Stale models**: Agent verifies against current facts before relying on them
This ensures responses stay accurate even as the underlying data changes.
---
## Mission-Oriented Consolidation
The bank's **mission** directly influences what knowledge gets consolidated into mental models. When you set a mission on your memory bank, the consolidation engine focuses on extracting knowledge that serves that mission.
**Example:**
```python
# A support agent bank
client.create_bank(
bank_id="support-agent",
mission="You're a customer support agent - you need to keep track of "
"customer preferences, past issues, and communication styles."
)
```
With this mission, the consolidation engine will:
- **Prioritize** customer preferences, issue patterns, and communication styles
- **Skip** ephemeral details that don't serve support goals
- **Synthesize** mental models focused on helping customers
Without a mission, the engine performs general-purpose consolidation. With a mission, it becomes focused and efficient — extracting only knowledge that matters for your use case.
| Mission | Mental Models Focus |
|---------|-------------------|
| *Customer support agent* | Customer preferences, issue patterns, resolution history |
| *Code review assistant* | Coding patterns, team conventions, common mistakes |
| *Research assistant* | Topic expertise, source reliability, methodology preferences |
---
## Configuration
Mental model consolidation runs automatically. You can monitor consolidation via the [Operations API](./api/operations).
---
## Next Steps
- [**Retain**](./retain) — How facts are stored and trigger consolidation
- [**Recall**](./retrieval) — How mental models are retrieved
- [**Reflect**](./reflect) — How the agentic loop uses mental models
- [**Reflections**](./api/reflections) — User-curated summaries for common queries
+1 -1
View File
@@ -16,7 +16,7 @@ All local models (embedding, cross-encoder) are automatically downloaded from Hu
## LLM
Used for fact extraction, entity resolution, opinion generation, and answer synthesis.
Used for fact extraction, entity resolution, mental model consolidation, and answer synthesis.
**Supported providers:** OpenAI, Anthropic, Gemini, Groq, Ollama, LM Studio, and **any OpenAI-compatible API**
+1 -1
View File
@@ -73,7 +73,7 @@ The `source` label allows distinguishing between:
**Labels:**
- `provider`: LLM provider (`openai`, `anthropic`, `gemini`, `groq`, `ollama`, `lmstudio`)
- `model`: Model name (e.g., `gpt-4`, `claude-3-sonnet`)
- `scope`: What the LLM call is for (`memory`, `reflect`, `entity_observation`, `answer`)
- `scope`: What the LLM call is for (`memory`, `reflect`, `consolidation`, `answer`)
- `success`: Whether the call succeeded (`true`, `false`)
- `token_bucket`: Token count bucket for cardinality control (`0-100`, `100-500`, `500-1k`, `1k-5k`, `5k-10k`, `10k-50k`, `50k+`)
@@ -13,8 +13,8 @@ Traditional RAG (Retrieval-Augmented Generation) retrieves documents similar to
| **Search strategy** | Semantic similarity only | Semantic + keyword + graph + temporal |
| **Multi-hop reasoning** | Limited to retrieved chunks | Graph traversal across entity relationships |
| **Temporal queries** | Keyword matching ("spring") | Date parsing and range filtering |
| **Entity understanding** | None | Entity resolution, observations, co-occurrence |
| **Belief formation** | Stateless | Opinions with confidence scores that evolve |
| **Entity understanding** | None | Entity resolution, co-occurrence tracking |
| **Knowledge consolidation** | Stateless | Mental models that synthesize and evolve |
| **Disposition** | None | 3 traits (skepticism, literalism, empathy) influence interpretation |
## Architecture Comparison
@@ -86,9 +86,9 @@ Multiple retrieval strategies. Persistent state across sessions.
| System | Result |
|--------|--------|
| RAG | Lists disconnected facts |
| Hindsight | Returns synthesized entity observations: subscription status, billing, known issues |
| Hindsight | Returns connected facts via entity graph: subscription status, billing, known issues |
### Belief Evolution
### Knowledge Evolution
**Week 1:** User struggles with async Python, succeeds with threads
**Week 3:** User asks about asyncio, implements async database calls
@@ -96,7 +96,7 @@ Multiple retrieval strategies. Persistent state across sessions.
| System | Behavior |
|--------|----------|
| RAG | No memory of progression |
| Hindsight | Forms opinion "user prefers sync" (0.7) → updates to "user growing comfortable with async" (0.6) |
| Hindsight | Consolidates mental model "user prefers sync" → refines to "user growing comfortable with async" |
## When to Use Each
-186
View File
@@ -1,186 +0,0 @@
---
sidebar_position: 4
---
# Reflect: How Hindsight Reasons with Disposition
When you call `reflect()`, Hindsight doesn't just retrieve facts — it **reasons** about them through the lens of the bank's unique disposition, forming new opinions and generating contextual responses.
```mermaid
graph LR
A[Query] --> B[Recall Memories]
B --> C[Load Disposition]
C --> D[Reason]
D --> E[Form Opinions]
E --> F[Response]
```
---
## Why Reflect?
Most AI systems can retrieve facts, but they can't **reason** about them in a consistent way. Every response is generated fresh without a stable perspective or evolving beliefs.
### The Problem
Without reflect:
- **No consistent character**: "Should we adopt remote work?" gets a different answer each time based on the LLM's randomness
- **No opinion formation**: The system never develops beliefs based on accumulated evidence
- **No reasoning context**: Responses don't reflect what the bank has learned or its perspective
- **Generic responses**: Every AI sounds the same — no disposition, no point of view
### The Value
With reflect:
- **Consistent character**: A bank configured as "detail-oriented, cautious" will consistently emphasize risks and thorough planning
- **Evolving opinions**: As the bank learns more about a topic, its opinions strengthen, weaken, or change — just like a real expert
- **Contextual reasoning**: Responses reflect the bank's accumulated knowledge and perspective: "Based on what I know about your team's remote work success..."
- **Differentiated behavior**: Customer support bots sound diplomatic, code reviewers sound direct, creative assistants sound open-minded
### When to Use Reflect
| Use `recall()` when... | Use `reflect()` when... |
|------------------------|-------------------------|
| You need raw facts | You need reasoned interpretation |
| You're building your own reasoning | You want disposition-consistent responses |
| You need maximum control | You want the bank to "think" for itself |
| Simple fact lookup | Forming recommendations or opinions |
**Example:**
- `recall("Alice")` → Returns all Alice facts
- `reflect("Should we hire Alice?")` → Reasons about Alice's fit based on accumulated knowledge, weighs evidence, forms opinion
---
## Disposition Traits
When you create a memory bank, you can configure its disposition using three traits. These traits influence how the bank interprets information and forms opinions during `reflect()`:
| Trait | Scale | Low (1) | High (5) |
|-------|-------|---------|----------|
| **Skepticism** | 1-5 | Trusting, accepts information at face value | Skeptical, questions and doubts claims |
| **Literalism** | 1-5 | Flexible interpretation, reads between the lines | Literal interpretation, takes things at face value |
| **Empathy** | 1-5 | Detached, focuses on facts | Empathetic, considers emotional context |
### Background: Natural Language Identity
Beyond numeric traits, you can provide a natural language **background** that describes the bank's identity:
```python
client.create_bank(
bank_id="my-bank",
background="I am a senior software architect with 15 years of distributed "
"systems experience. I prefer simplicity over cutting-edge technology.",
disposition={
"skepticism": 4, # Questions new technologies
"literalism": 4, # Focuses on concrete specs
"empathy": 2 # Prioritizes technical facts
}
)
```
The background provides context that shapes how disposition traits are applied:
- "I prefer simplicity" + high skepticism → questions complex solutions
- "15 years experience" → responses reference this expertise
- First-person perspective → creates consistent voice
---
## Opinion Formation
When `reflect()` encounters a question that warrants forming an opinion, disposition shapes the response.
### Same Facts, Different Opinions
Two banks with different dispositions, given identical facts about remote work:
**Bank A** (low skepticism, high empathy):
> "Remote work enables flexibility and work-life balance. The team seems happier and more productive when they can choose their environment."
**Bank B** (high skepticism, low empathy):
> "Remote work claims need verification. What are the actual productivity metrics? The anecdotal benefits may not translate to measurable outcomes."
**Same facts → Different conclusions** because disposition shapes interpretation.
---
## Opinion Evolution
Opinions aren't static — they evolve as new evidence arrives. Here's a real-world example with a database library:
| Event | What the bank learns | Opinion formed |
|-------|---------------------|----------------|
| **Day 1** | "Redis is open source under BSD license" | "Redis is excellent for caching — fast, reliable, and OSS-friendly" (confidence: 0.85) |
| **Day 2** | "Redis has great community support and documentation" | Opinion reinforced (confidence: 0.90) |
| **Day 30** | "Redis changed license to SSPL, restricting cloud usage" | "Redis is still technically strong, but license concerns for cloud deployments" (confidence: 0.65) |
| **Day 45** | "Valkey forked Redis under BSD license with Linux Foundation backing" | "Consider Valkey for new projects requiring true OSS; Redis for existing deployments" (confidence: 0.80) |
**Before the license change:**
> "Should we use Redis for our caching layer?"
> → "Yes, Redis is the industry standard — fast, battle-tested, and fully open source."
**After the license change:**
> "Should we use Redis for our caching layer?"
> → "It depends. For cloud deployments, consider Valkey (the BSD-licensed fork). For on-premise, Redis remains excellent technically."
This **continuous learning** ensures recommendations stay current with real-world changes.
---
## Disposition Presets by Use Case
Different use cases benefit from different disposition configurations:
| Use Case | Recommended Traits | Why |
|----------|-------------------|-----|
| **Customer Support** | skepticism: 2, literalism: 2, empathy: 5 | Trusting, flexible, understanding |
| **Code Review** | skepticism: 4, literalism: 5, empathy: 2 | Questions assumptions, precise, direct |
| **Legal Analysis** | skepticism: 5, literalism: 5, empathy: 2 | Highly skeptical, exact interpretation |
| **Therapist/Coach** | skepticism: 2, literalism: 2, empathy: 5 | Supportive, reads between lines |
| **Research Assistant** | skepticism: 4, literalism: 3, empathy: 3 | Questions claims, balanced interpretation |
---
## What You Get from Reflect
When you call `reflect()`:
**Returns:**
- **Response text** — Disposition-influenced answer
- **Based on** — Which memories were used (with relevance scores)
**Example:**
```json
{
"text": "Based on Alice's ML expertise and her work at Google, she'd be an excellent fit for the research team lead position...",
"based_on": {
"world": [
{"text": "Alice works at Google...", "weight": 0.95},
{"text": "Alice specializes in ML...", "weight": 0.88}
]
}
}
```
**Note:** New opinions are formed asynchronously in the background. They'll influence future `reflect()` calls but aren't returned directly.
---
## Why Disposition Matters
Without disposition, all AI assistants sound the same. With disposition:
- **Customer support bots** can be diplomatic and empathetic
- **Code review assistants** can be direct and thorough
- **Creative assistants** can be open to unconventional ideas
- **Risk analysts** can be appropriately cautious
Disposition creates **consistent character** across conversations while allowing opinions to **evolve with evidence**.
---
## Next Steps
- [**Retain**](./retain) — How rich facts are stored
- [**Recall**](./retrieval) — How multi-strategy search works
- [**Reflect API**](./api/reflect) — Code examples, parameters, and tag filtering
+216
View File
@@ -0,0 +1,216 @@
---
sidebar_position: 4
---
import CodeSnippet from '@site/src/components/CodeSnippet';
import memoryBanksPy from '!!raw-loader!@site/examples/api/memory-banks.py';
# Reflect: Agentic Reasoning with Disposition
When you call `reflect()`, Hindsight runs an **agentic loop** that autonomously gathers evidence and reasons through the lens of the bank's disposition to generate contextual responses.
```mermaid
graph TB
subgraph agent["Reflect Agent Loop"]
A[Query] --> B{Need more info?}
B -->|Yes| C[Call Tools]
C --> D[search_reflections]
C --> E[search_mental_models]
C --> F[recall]
C --> G[expand]
D --> B
E --> B
F --> B
G --> B
B -->|No| H[Generate Response]
end
H --> I[Response + Citations]
```
---
## How It Works
Unlike simple retrieval, reflect is an **agentic system** that:
1. **Autonomously gathers evidence** — The agent decides what information it needs and calls appropriate tools
2. **Uses hierarchical retrieval** — Checks reflections first, then mental models, then raw facts
3. **Applies disposition** — Shapes reasoning based on the bank's personality traits
4. **Cites sources** — Returns which memories and mental models were used
### The Agentic Loop
The reflect agent runs in a loop with access to these tools:
| Tool | Purpose | Priority |
|------|---------|----------|
| `search_reflections` | User-curated summaries | Highest (check first) |
| `search_mental_models` | Consolidated knowledge | High |
| `recall` | Raw facts (ground truth) | Fallback |
| `expand` | Get more context for a memory | As needed |
| `done` | Complete with final answer | When ready |
The agent:
- **Must gather evidence** before answering (guardrail prevents empty responses)
- **Runs up to 10 iterations** to find relevant information
- **Validates citations** — only IDs that were actually retrieved can be cited
### Hierarchical Retrieval Strategy
The agent uses a smart retrieval hierarchy:
1. **[Reflections](/developer/api/reflections)** — User-curated summaries you've pre-computed for common queries
2. **[Mental Models](/developer/mental-models)** — Consolidated knowledge with freshness awareness
3. **Raw Facts** — Ground truth for verification when models are stale
**Reflections** are saved reflect responses that you create for frequently asked questions. They're checked first because they represent explicitly curated knowledge. See the [Reflections API](/developer/api/reflections) for how to create and manage them.
If a mental model is marked as **stale**, the agent automatically verifies it against current facts.
---
## Why Reflect?
Most AI systems can retrieve facts, but they can't **reason** about them in a consistent way.
### The Problem
Without reflect:
- **No consistent character**: Same question gets different answers each time
- **No knowledge synthesis**: System never connects related facts
- **No reasoning context**: Responses don't reflect accumulated knowledge
- **Generic responses**: Every AI sounds the same
### The Value
With reflect:
- **Consistent character**: A "detail-oriented, cautious" bank emphasizes risks and thorough planning
- **Evolving knowledge**: Mental models strengthen and adapt as evidence accumulates
- **Contextual reasoning**: "Based on what I know about your team's remote work success..."
- **Differentiated behavior**: Support bots sound diplomatic, code reviewers sound direct
### When to Use Reflect
| Use `recall()` when... | Use `reflect()` when... |
|------------------------|-------------------------|
| You need raw facts | You need reasoned interpretation |
| You're building your own reasoning | You want disposition-consistent responses |
| You need maximum control | You want the bank to "think" for itself |
| Simple fact lookup | Forming recommendations |
**Example:**
- `recall("Alice")` → Returns all Alice facts and relevant mental models
- `reflect("Should we hire Alice?")` → Agent gathers evidence about Alice, reasons about fit, returns answer with citations
---
## Disposition Traits
When you create a memory bank, you can configure its disposition using three traits. These traits influence how the bank interprets information and reasons during `reflect()`:
| Trait | Scale | Low (1) | High (5) |
|-------|-------|---------|----------|
| **Skepticism** | 1-5 | Trusting, accepts information at face value | Skeptical, questions and doubts claims |
| **Literalism** | 1-5 | Flexible interpretation, reads between the lines | Literal interpretation, takes things at face value |
| **Empathy** | 1-5 | Detached, focuses on facts | Empathetic, considers emotional context |
### Mission: Natural Language Identity
Beyond numeric traits, you can provide a natural language **mission** that describes the bank's identity:
<CodeSnippet code={memoryBanksPy} section="bank-with-disposition" language="python" />
The mission tells Hindsight what knowledge to prioritize and shapes how disposition traits are applied:
- "keep track of system designs" → focuses consolidation on architectural decisions
- "prefer simplicity over cutting-edge" + high skepticism → questions complex solutions
- Explicit guidance → consistent memory focus across conversations
---
## Disposition Shapes Reasoning
Two banks with different dispositions, given identical facts about remote work:
**Bank A** (low skepticism, high empathy):
> "Remote work enables flexibility and work-life balance. The team seems happier and more productive when they can choose their environment."
**Bank B** (high skepticism, low empathy):
> "Remote work claims need verification. What are the actual productivity metrics? The anecdotal benefits may not translate to measurable outcomes."
**Same facts → Different conclusions** because disposition shapes interpretation.
---
## Disposition Presets by Use Case
Different use cases benefit from different disposition configurations:
| Use Case | Recommended Traits | Why |
|----------|-------------------|-----|
| **Customer Support** | skepticism: 2, literalism: 2, empathy: 5 | Trusting, flexible, understanding |
| **Code Review** | skepticism: 4, literalism: 5, empathy: 2 | Questions assumptions, precise, direct |
| **Legal Analysis** | skepticism: 5, literalism: 5, empathy: 2 | Highly skeptical, exact interpretation |
| **Therapist/Coach** | skepticism: 2, literalism: 2, empathy: 5 | Supportive, reads between lines |
| **Research Assistant** | skepticism: 4, literalism: 3, empathy: 3 | Questions claims, balanced interpretation |
---
## What You Get from Reflect
When you call `reflect()`:
**Returns:**
- **Response text** — Disposition-influenced answer from the agent
- **based_on** — Evidence used: memories that grounded the response
- **trace** — Tool calls, LLM calls, and mental models accessed (when `include.tool_calls=True`)
- **structured_output** — Parsed response if `response_schema` was provided
- **usage** — Token usage metrics
**Example:**
```json
{
"text": "Based on Alice's ML expertise and her work at Google, she'd be an excellent fit for the research team lead position...",
"based_on": {
"memories": [
{"id": "mem-123", "text": "Alice has 5 years of ML experience", "type": "world"},
{"id": "mem-456", "text": "Alice worked at Google on search ranking", "type": "experience"}
]
},
"trace": {
"tool_calls": [
{"tool": "recall", "input": {"query": "Alice"}, "duration_ms": 150}
],
"llm_calls": [
{"scope": "agent_1", "duration_ms": 1200}
],
"mental_models": [
{"id": "mm-789", "name": "Alice", "type": "entity", "subtype": "structural"}
]
},
"usage": {"input_tokens": 1500, "output_tokens": 500, "total_tokens": 2000}
}
```
The agent automatically gathers evidence, validates citations, and generates a grounded response.
---
## Why Disposition Matters
Without disposition, all AI assistants sound the same. With disposition:
- **Customer support bots** can be diplomatic and empathetic
- **Code review assistants** can be direct and thorough
- **Creative assistants** can be open to unconventional ideas
- **Risk analysts** can be appropriately cautious
Disposition creates **consistent character** across conversations while mental models **evolve with evidence**.
---
## Next Steps
- [**Mental Models**](./mental-models) — How knowledge is consolidated
- [**Retain**](./retain) — How rich facts are stored
- [**Recall**](./retrieval) — How multi-strategy search works
- [**Reflect API**](./api/reflect) — Code examples and parameters
+18 -20
View File
@@ -63,8 +63,7 @@ Hindsight distinguishes between **world** facts (about others) and **experience*
| **experience** | Conversations and events | "I recommended Python to Alice" |
**Note:** Opinions aren't created during `retain()` — only during `reflect()` when the bank forms beliefs.
This separation is important for `reflect()` — the bank can reason about what it knows versus what happened in conversations.
**Note:** Mental models are consolidated automatically in the background after `retain()` operations complete. This consolidation process synthesizes patterns from new facts into the bank's knowledge base.
---
@@ -151,22 +150,6 @@ Without this distinction, old information would either be unsearchable by date o
---
## Entity Observations
As facts accumulate about an entity, Hindsight synthesizes **observations** — high-level summaries that capture what's known:
**From multiple facts:**
- "Alice works at Google"
- "Alice is a software engineer"
- "Alice specializes in ML"
**Hindsight creates:**
- "Alice is a software engineer at Google specializing in ML"
**Why it helps:** You can quickly understand an entity without reading through dozens of individual facts.
---
## Tagging Memories
Tags enable visibility scoping—useful when one memory bank serves multiple users but each should only see relevant memories.
@@ -187,15 +170,30 @@ After `retain()` completes:
- **Unified entities** that resolve different name variations
- **Knowledge graph** with entity, temporal, semantic, and causal links
- **Temporal grounding** for both historical and recency-based queries
- **Background processing** that generates entity summaries
- **Optional tags** for filtering during recall
All stored in your isolated **memory bank**, ready for `recall()` and `reflect()`.
---
## Mental Model Consolidation
After `retain()` completes, Hindsight automatically triggers **mental model consolidation** in the background. This process:
1. Analyzes new facts against existing mental models
2. Creates new mental models when patterns emerge
3. Refines existing mental models with new evidence
4. Tracks which facts support each mental model
This happens asynchronously — your `retain()` call returns immediately while consolidation runs in the background.
See [Mental Models](./mental-models) for details on how consolidation works.
---
## Next Steps
- [**Mental Models**](./mental-models) — How knowledge is consolidated after retain
- [**Recall**](./retrieval) — How multi-strategy search retrieves relevant memories
- [**Reflect**](./reflect) — How disposition influences reasoning and opinion formation
- [**Reflect**](./reflect) — How the agentic loop uses mental models
- [**Retain API**](./api/retain) — Code examples and parameters
+7 -27
View File
@@ -110,11 +110,11 @@ After the four strategies run, results are **fused together**:
## Why Multiple Strategies?
Consider the query: **"What did Alice think about Python last spring?"**
Consider the query: **"What did Alice say about Python last spring?"**
- **Semantic** finds facts about Alice's opinions on programming
- **Semantic** finds facts about Alice's views on programming
- **Keyword** ensures "Python" is actually mentioned
- **Graph** connects Alice → opinions → programming languages
- **Graph** connects Alice → programming languages → related entities
- **Temporal** filters to "last spring" timeframe
The **fusion** of all four gives you exactly what you're looking for, even though no single strategy would suffice.
@@ -133,18 +133,13 @@ Hindsight is built for AI agents, not humans. Traditional search systems return
**Parameters you control:**
- `max_tokens`: How much memory content to return (default: 4096 tokens)
- `budget`: Search depth level (low, mid, high)
- `types`: Filter by world, experience, opinion, or all
- `types`: Filter by world, experience, mental_model, or all
- `tags`: Filter memories by visibility tags
- `tags_match`: How to match tags (see [Recall API](./api/recall) for all options)
### Expanding Context: Chunks and Entity Observations
### Expanding Context: Chunks
Memories are distilled facts—concise but sometimes missing nuance. When your agent needs deeper context, you can optionally retrieve the source material and related knowledge:
| Option | Parameters | When to Use |
|--------|------------|-------------|
| **Chunks** | `include_chunks`, `max_chunk_tokens` | Need exact quotes, original phrasing, or surrounding context |
| **Entity Observations** | `include_entities`, `max_entity_tokens` | Need broader knowledge about people/things mentioned in results |
Memories are distilled facts—concise but sometimes missing nuance. When your agent needs deeper context, you can optionally retrieve the source material:
**Chunks** return the raw text that generated each memory—useful when the distilled fact loses important nuance:
@@ -155,22 +150,7 @@ Chunk: "Alice mentioned she prefers Python over JavaScript, mainly because
frontend work and she's been learning TypeScript lately."
```
**Entity Observations** pull in related facts about entities mentioned in your results. If a memory mentions "Alice", you automatically get her role, skills, and other relevant context—without needing a separate query:
```
Query: "What programming languages does Alice like?"
Memory: "Alice prefers Python over JavaScript"
Entity Observations (Alice):
- "Alice is a senior data scientist at Google"
- "Alice specializes in machine learning"
- "Alice has been learning TypeScript"
```
**When to include them:**
- **Chunks**: When generating responses that need verbatim quotes or when context matters (e.g., "What exactly did Alice say about the project?")
- **Entity Observations**: When building complete profiles or when the conversation might reference multiple aspects of an entity (e.g., "Tell me about Alice's work")
Each has its own token budget, giving you precise control over total context size.
Use `include_chunks=True` with `max_chunk_tokens` to control the token budget for chunks. This is useful when generating responses that need verbatim quotes or when context matters (e.g., "What exactly did Alice say about the project?").
---
+1 -1
View File
@@ -16,7 +16,7 @@ hindsight-api # Default port: 8888
The API service is stateless and can be horizontally scaled behind a load balancer. All state is stored in PostgreSQL.
By default, the API also processes background tasks (opinion formation, entity observations) internally. For high-throughput deployments, you can disable this and run dedicated workers instead.
By default, the API also processes background tasks (mental model consolidation) internally. For high-throughput deployments, you can disable this and run dedicated workers instead.
## Worker Service
+5 -8
View File
@@ -74,7 +74,7 @@ hindsight memory recall <bank_id> "hiking recommendations" \
--max-tokens 8192
# Filter by fact type
hindsight memory recall <bank_id> "query" --fact-type world,opinion
hindsight memory recall <bank_id> "query" --fact-type world,mental_model
# Show trace information
hindsight memory recall <bank_id> "query" --trace
@@ -120,13 +120,13 @@ hindsight bank stats <bank_id>
hindsight bank name <bank_id> "My Assistant"
```
### Set Background
### Set Mission
```bash
hindsight bank background <bank_id> "I am a helpful AI assistant interested in technology"
hindsight bank mission <bank_id> "I am a helpful AI assistant interested in technology"
# Skip automatic disposition inference
hindsight bank background <bank_id> "Background text" --no-update-disposition
hindsight bank mission <bank_id> "Mission text" --no-update-disposition
```
## Document Management
@@ -150,9 +150,6 @@ hindsight entity list <bank_id>
# Get entity details
hindsight entity get <bank_id> <entity_id>
# Regenerate entity observations
hindsight entity regenerate <bank_id> <entity_id>
```
## Output Formats
@@ -209,7 +206,7 @@ The explorer provides an interactive terminal interface to:
- **Browse memory banks** — View all banks and their statistics
- **Search memories** — Run recall queries with real-time results
- **Inspect entities** — Explore the knowledge graph and entity relationships
- **View facts** — Browse world facts, experiences, and opinions
- **View facts** — Browse world facts, experiences, and mental models
- **Navigate documents** — See source documents and their extracted memories
### Keyboard Shortcuts
@@ -73,7 +73,7 @@ hindsight_litellm.configure(
# Optional - Bank Configuration
bank_name="My Agent", # Human-readable display name for the memory bank
background="This agent...", # Instructions guiding what Hindsight should remember
mission="This agent...", # Instructions guiding what Hindsight should remember
# Optional - Advanced
injection_mode="system_message", # or "prepend_user"
@@ -84,16 +84,16 @@ hindsight_litellm.configure(
### Bank Configuration
The `background` and `bank_name` parameters configure the memory bank itself. When provided, `configure()` will automatically create or update the bank with these settings.
The `mission` and `bank_name` parameters configure the memory bank itself. When provided, `configure()` will automatically create or update the bank with these settings.
```python
hindsight_litellm.configure(
hindsight_api_url="http://localhost:8888",
bank_id="support-router",
bank_name="Customer Support Router",
background="""This agent routes customer support requests to the appropriate team.
Remember which types of issues should go to which teams (billing, technical, sales).
Track customer preferences for communication channels and past issue resolutions.""",
mission="""You're a customer support router - keep track of which types of issues
should go to which teams (billing, technical, sales), customer preferences for
communication channels, and past issue resolutions.""",
)
```
@@ -108,7 +108,7 @@ hindsight_litellm.configure(
bank_id="my-agent",
use_reflect=False, # Default
)
# Injects: "1. [WORLD] User prefers Python\n2. [OPINION] User dislikes Java..."
# Injects: "1. [WORLD] User prefers Python\n2. [MENTAL MODEL] User prefers simple code..."
# Reflect mode - synthesized context
hindsight_litellm.configure(
+2 -2
View File
@@ -83,7 +83,7 @@ for (const r of response.results) {
// With options
const response = await client.recall('my-bank', 'What does Alice do?', {
types: ['world', 'opinion'], // Filter by fact type
types: ['world', 'mental_model'], // Filter by fact type
maxTokens: 4096,
budget: 'high', // 'low', 'mid', or 'high'
});
@@ -107,7 +107,7 @@ console.log(answer.text); // Generated response
```typescript
await client.createBank('my-bank', {
name: 'Assistant',
background: 'I am a helpful AI assistant',
mission: "You're a helpful AI assistant - keep track of user preferences and conversation history.",
disposition: {
skepticism: 3, // 1-5: trusting to skeptical
literalism: 3, // 1-5: flexible to literal
+2 -2
View File
@@ -150,7 +150,7 @@ for r in results.results:
results = client.recall(
bank_id="my-bank",
query="What does Alice do?",
types=["world", "opinion"], # Filter by fact type
types=["world", "mental_model"], # Filter by fact type
max_tokens=4096,
budget="high", # low, mid, or high
)
@@ -201,7 +201,7 @@ print(answer.text) # Generated response
client.create_bank(
bank_id="my-bank",
name="Assistant",
background="I am a helpful AI assistant",
mission="You're a helpful AI assistant - keep track of user preferences and conversation history.",
disposition={
"skepticism": 3, # 1-5: trusting to skeptical
"literalism": 3, # 1-5: flexible to literal
+33 -3
View File
@@ -181,7 +181,7 @@ const config: Config = {
items: [
{
type: 'doc',
docId: 'developer/index',
docId: 'developer/installation',
position: 'left',
label: 'Developer',
className: 'navbar-item-developer',
@@ -223,6 +223,11 @@ const config: Config = {
type: 'docsVersionDropdown',
position: 'right',
},
{
href: 'https://join.slack.com/t/hindsight-space/shared_invite/zt-3nhbm4w29-LeSJ5Ixi6j8PdiYOCPlOgg',
position: 'right',
label: 'Community',
},
{
href: 'https://github.com/vectorize-io/hindsight',
position: 'right',
@@ -241,6 +246,10 @@ const config: Config = {
label: 'Introduction',
to: '/',
},
{
label: 'Developer Guide',
to: '/developer/installation',
},
{
label: 'SDKs',
to: '/sdks/python',
@@ -252,16 +261,37 @@ const config: Config = {
],
},
{
title: 'More',
title: 'Resources',
items: [
{
label: 'Cookbook',
to: '/cookbook',
},
{
label: 'Changelog',
to: '/changelog',
},
{
label: 'Hindsight Cloud',
href: 'https://vectorize.io/hindsight/cloud',
},
],
},
{
title: 'Community',
items: [
{
label: 'GitHub',
href: 'https://github.com/vectorize-io/hindsight',
},
{
label: 'Slack',
href: 'https://join.slack.com/t/hindsight-space/shared_invite/zt-3nhbm4w29-LeSJ5Ixi6j8PdiYOCPlOgg',
},
],
},
],
copyright: `Copyright © ${new Date().getFullYear()} Hindsight.`,
copyright: `Copyright © ${new Date().getFullYear()} Vectorize, Inc.`,
},
prism: {
theme: prismThemes.github,
+5 -1
View File
@@ -18,7 +18,7 @@ This directory contains runnable example scripts that serve as the source of tru
| `reflect.py/mjs/sh` | reflect.md | AI reflection examples |
| `memory-banks.py/mjs` | memory-banks.md | Bank management examples |
| `documents.py/mjs` | documents.md | Document CRUD examples |
| `opinions.py` | opinions.md | Opinion management examples |
| `reflections.py` | reflections.md | Reflections CRUD examples |
| `main-methods.py` | main-methods.md | Core method examples |
| `cli-reference.sh` | cli.md | CLI command examples |
@@ -37,6 +37,10 @@ for f in *.sh; do bash "$f"; done
Requires a running Hindsight server at `http://localhost:8888` (or set `HINDSIGHT_API_URL`).
## Legacy Examples
The `legacy/` folder contains deprecated example files kept only for backward compatibility with older documentation versions. These files are **not runnable** and are skipped by CI tests.
## What's NOT Covered
### 1. OpenAPI Auto-Generated Docs (`/api-reference/*`)
@@ -0,0 +1,67 @@
#!/usr/bin/env node
/**
* Opinions API examples for Hindsight (deprecated - kept for versioned docs).
* This file is preserved for backward compatibility with v0.3 documentation.
* Opinions have been replaced by Mental Models in v0.4+.
*/
import { HindsightClient } from '@vectorize-io/hindsight-client';
const HINDSIGHT_URL = process.env.HINDSIGHT_API_URL || 'http://localhost:8888';
const client = new HindsightClient({ baseUrl: HINDSIGHT_URL });
// [docs:opinion-form]
// Opinions are automatically formed when the bank encounters
// claims, preferences, or judgments in retained content
await client.retain('my-bank',
"I think Python is excellent for data science because of its libraries"
);
// The bank forms an opinion with confidence based on evidence
// [/docs:opinion-form]
// [docs:opinion-search]
// Search for opinions on a topic
const response = await client.recall('my-bank', 'What do you think about Python?', {
types: ['opinion']
});
for (const opinion of response.results) {
console.log(`Opinion: ${opinion.text}`);
console.log(`Confidence: ${opinion.confidence}`);
}
// [/docs:opinion-search]
// [docs:opinion-disposition]
// Bank disposition affects how opinions are formed
// High skepticism = lower confidence, requires more evidence
// Low skepticism = higher confidence, accepts claims more readily
await client.createBank('skeptical-bank', {
disposition: { skepticism: 5, literalism: 3, empathy: 2 }
});
// Same content, different confidence due to disposition
await client.retain('skeptical-bank', 'Python is the best language');
// [/docs:opinion-disposition]
// [docs:opinion-in-reflect]
// Opinions influence reflect responses
const reflectResponse = await client.reflect('my-bank',
'Should I use Python for my data project?'
);
// The response incorporates the bank's opinions with appropriate confidence
console.log(reflectResponse.text);
// [/docs:opinion-in-reflect]
// =============================================================================
// Cleanup (not shown in docs)
// =============================================================================
await fetch(`${HINDSIGHT_URL}/v1/default/banks/my-bank`, { method: 'DELETE' });
await fetch(`${HINDSIGHT_URL}/v1/default/banks/skeptical-bank`, { method: 'DELETE' });
console.log('opinions.mjs: All examples passed');
@@ -0,0 +1,74 @@
#!/usr/bin/env python3
"""
Opinions API examples for Hindsight (deprecated - kept for versioned docs).
This file is preserved for backward compatibility with v0.3 documentation.
Opinions have been replaced by Mental Models in v0.4+.
"""
import os
import requests
from hindsight_client import Hindsight
HINDSIGHT_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888")
client = Hindsight(base_url=HINDSIGHT_URL)
# [docs:opinion-form]
# Opinions are automatically formed when the bank encounters
# claims, preferences, or judgments in retained content
client.retain(
bank_id="my-bank",
content="I think Python is excellent for data science because of its libraries"
)
# The bank forms an opinion with confidence based on evidence
# [/docs:opinion-form]
# [docs:opinion-search]
# Search for opinions on a topic
response = client.recall(
bank_id="my-bank",
query="What do you think about Python?",
types=["opinion"]
)
for opinion in response.results:
print(f"Opinion: {opinion.text}")
print(f"Confidence: {opinion.confidence}")
# [/docs:opinion-search]
# [docs:opinion-disposition]
# Bank disposition affects how opinions are formed
# High skepticism = lower confidence, requires more evidence
# Low skepticism = higher confidence, accepts claims more readily
client.create_bank(
bank_id="skeptical-bank",
disposition={"skepticism": 5, "literalism": 3, "empathy": 2}
)
# Same content, different confidence due to disposition
client.retain(bank_id="skeptical-bank", content="Python is the best language")
# [/docs:opinion-disposition]
# [docs:opinion-in-reflect]
# Opinions influence reflect responses
response = client.reflect(
bank_id="my-bank",
query="Should I use Python for my data project?"
)
# The response incorporates the bank's opinions with appropriate confidence
print(response.text)
# [/docs:opinion-in-reflect]
# =============================================================================
# Cleanup (not shown in docs)
# =============================================================================
requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/my-bank")
requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/skeptical-bank")
print("opinions.py: All examples passed")
+4 -4
View File
@@ -19,7 +19,7 @@ const client = new HindsightClient({ baseUrl: HINDSIGHT_URL });
// [docs:create-bank]
await client.createBank('my-bank', {
name: 'Research Assistant',
background: 'I am a research assistant specializing in machine learning',
mission: 'I am a research assistant specializing in machine learning',
disposition: {
skepticism: 4,
literalism: 3,
@@ -29,14 +29,14 @@ await client.createBank('my-bank', {
// [/docs:create-bank]
// [docs:bank-background]
// [docs:bank-mission]
await client.createBank('financial-advisor', {
name: 'Financial Advisor',
background: `I am a conservative financial advisor with 20 years of experience.
mission: `I am a conservative financial advisor with 20 years of experience.
I prioritize capital preservation over aggressive growth.
I have seen multiple market crashes and believe in diversification.`
});
// [/docs:bank-background]
// [/docs:bank-mission]
// =============================================================================
+18 -4
View File
@@ -23,7 +23,7 @@ client = Hindsight(base_url=HINDSIGHT_URL)
client.create_bank(
bank_id="my-bank",
name="Research Assistant",
mission="I am a research assistant specializing in machine learning",
mission="You're a research assistant specializing in machine learning - keep track of papers, methods, and findings.",
disposition={
"skepticism": 4,
"literalism": 3,
@@ -37,17 +37,31 @@ client.create_bank(
client.create_bank(
bank_id="financial-advisor",
name="Financial Advisor",
mission="""I am a conservative financial advisor with 20 years of experience.
I prioritize capital preservation over aggressive growth.
I have seen multiple market crashes and believe in diversification."""
mission="""You're a conservative financial advisor - keep track of client risk tolerance,
investment preferences, and market conditions. Prioritize capital preservation over growth."""
)
# [/docs:bank-mission]
# [docs:bank-with-disposition]
client.create_bank(
bank_id="architect-bank",
mission="You're a senior software architect - keep track of system designs, "
"technology decisions, and architectural patterns. Prefer simplicity over cutting-edge.",
disposition={
"skepticism": 4, # Questions new technologies
"literalism": 4, # Focuses on concrete specs
"empathy": 2 # Prioritizes technical facts
}
)
# [/docs:bank-with-disposition]
# =============================================================================
# Cleanup (not shown in docs)
# =============================================================================
requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/my-bank")
requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/financial-advisor")
requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/architect-bank")
print("memory-banks.py: All examples passed")
-99
View File
@@ -1,99 +0,0 @@
#!/usr/bin/env node
/**
* Opinions API examples for Hindsight (Node.js)
* Run: node examples/api/opinions.mjs
*/
import { HindsightClient } from '@vectorize-io/hindsight-client';
const HINDSIGHT_URL = process.env.HINDSIGHT_API_URL || 'http://localhost:8888';
// =============================================================================
// Setup (not shown in docs)
// =============================================================================
const client = new HindsightClient({ baseUrl: HINDSIGHT_URL });
// Seed some data about programming languages
await client.retain('my-bank', 'Python is widely used for data science and machine learning');
await client.retain('my-bank', 'Functional programming emphasizes immutability and pure functions');
await client.retain('my-bank', 'Rust has better memory safety than C++');
await client.retain('my-bank', 'C++ has a larger ecosystem and more libraries');
// =============================================================================
// Doc Examples
// =============================================================================
// [docs:opinion-form]
// Ask a question - the system may form opinions based on stored facts
const answer = await client.reflect('my-bank', 'What do you think about functional programming?');
console.log(answer.text);
// [/docs:opinion-form]
// [docs:opinion-search]
// Search for facts about a topic
const results = await client.recall('my-bank', 'programming languages');
for (const result of results.results) {
console.log(`- ${result.text}`);
}
// [/docs:opinion-search]
// [docs:opinion-disposition]
// Create two memory banks with different dispositions
await client.createBank('open-minded', {
name: 'Open Minded',
disposition: { skepticism: 2, literalism: 2, empathy: 4 }
});
await client.createBank('conservative', {
name: 'Conservative',
disposition: { skepticism: 5, literalism: 5, empathy: 2 }
});
// Store the same facts to both
const facts = [
'Rust has better memory safety than C++',
'C++ has a larger ecosystem and more libraries',
'Rust compile times are longer than C++'
];
for (const fact of facts) {
await client.retain('open-minded', fact);
await client.retain('conservative', fact);
}
// Ask both the same question - different dispositions lead to different responses
const q = 'Should we rewrite our C++ codebase in Rust?';
const answer1 = await client.reflect('open-minded', q);
console.log('Open-minded response:', answer1.text.slice(0, 100), '...');
const answer2 = await client.reflect('conservative', q);
console.log('Conservative response:', answer2.text.slice(0, 100), '...');
// [/docs:opinion-disposition]
// [docs:opinion-in-reflect]
const reflectAnswer = await client.reflect('my-bank', 'What language should I learn?');
console.log('Response:', reflectAnswer.text);
// See which facts influenced the response
if (reflectAnswer.based_on) {
console.log('\nBased on these facts:');
for (const fact of reflectAnswer.based_on) {
console.log(` - ${fact.text}`);
}
}
// [/docs:opinion-in-reflect]
// =============================================================================
// Cleanup (not shown in docs)
// =============================================================================
await fetch(`${HINDSIGHT_URL}/v1/default/banks/my-bank`, { method: 'DELETE' });
await fetch(`${HINDSIGHT_URL}/v1/default/banks/open-minded`, { method: 'DELETE' });
await fetch(`${HINDSIGHT_URL}/v1/default/banks/conservative`, { method: 'DELETE' });
console.log('opinions.mjs: All examples passed');
-106
View File
@@ -1,106 +0,0 @@
#!/usr/bin/env python3
"""
Opinions API examples for Hindsight.
Run: python examples/api/opinions.py
"""
import os
import requests
HINDSIGHT_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888")
# =============================================================================
# Setup (not shown in docs)
# =============================================================================
from hindsight_client import Hindsight
client = Hindsight(base_url=HINDSIGHT_URL)
# Seed some data about programming languages
client.retain(bank_id="my-bank", content="Python is widely used for data science and machine learning")
client.retain(bank_id="my-bank", content="Functional programming emphasizes immutability and pure functions")
client.retain(bank_id="my-bank", content="Rust has better memory safety than C++")
client.retain(bank_id="my-bank", content="C++ has a larger ecosystem and more libraries")
# =============================================================================
# Doc Examples
# =============================================================================
# [docs:opinion-form]
# Ask a question - the system may form opinions based on stored facts
answer = client.reflect(
bank_id="my-bank",
query="What do you think about functional programming?"
)
print(answer.text)
# [/docs:opinion-form]
# [docs:opinion-search]
# Search for facts about a topic
results = client.recall(
bank_id="my-bank",
query="programming languages"
)
for result in results.results:
print(f"- {result.text}")
# [/docs:opinion-search]
# [docs:opinion-disposition]
# Create two memory banks with different dispositions
client.create_bank(
bank_id="open-minded",
name="Open Minded",
disposition={"skepticism": 2, "literalism": 2, "empathy": 4}
)
client.create_bank(
bank_id="conservative",
name="Conservative",
disposition={"skepticism": 5, "literalism": 5, "empathy": 2}
)
# Store the same facts to both
facts = [
"Rust has better memory safety than C++",
"C++ has a larger ecosystem and more libraries",
"Rust compile times are longer than C++"
]
for fact in facts:
client.retain(bank_id="open-minded", content=fact)
client.retain(bank_id="conservative", content=fact)
# Ask both the same question - different dispositions lead to different responses
q = "Should we rewrite our C++ codebase in Rust?"
answer1 = client.reflect(bank_id="open-minded", query=q)
print("Open-minded response:", answer1.text[:100], "...")
answer2 = client.reflect(bank_id="conservative", query=q)
print("Conservative response:", answer2.text[:100], "...")
# [/docs:opinion-disposition]
# [docs:opinion-in-reflect]
answer = client.reflect(bank_id="my-bank", query="What language should I learn?")
print("Response:", answer.text)
# See which facts influenced the response
if answer.based_on:
print("\nBased on these facts:")
for fact in answer.based_on:
print(f" - {fact.text}")
# [/docs:opinion-in-reflect]
# =============================================================================
# Cleanup (not shown in docs)
# =============================================================================
requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/my-bank")
requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/open-minded")
requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/conservative")
print("opinions.py: All examples passed")
+23 -25
View File
@@ -39,18 +39,11 @@ response = client.recall(
budget="high",
max_tokens=8000,
trace=True,
include_entities=True,
max_entity_tokens=500
)
# Access results
for r in response.results:
print(f"- {r.text}")
# Access entity observations (if include_entities=True)
if response.entities:
for entity_id, entity in response.entities.items():
print(f"Entity: {entity.canonical_name}")
# [/docs:recall-with-options]
@@ -74,14 +67,31 @@ experience = client.recall(
# [/docs:recall-experience-only]
# [docs:recall-opinions-only]
# Only opinions (formed beliefs)
opinions = client.recall(
# [docs:recall-mental-models-only]
# Only mental models (consolidated knowledge)
mental_models = client.recall(
bank_id="my-bank",
query="What do I think about Python?",
types=["opinion"]
query="What patterns have I learned?",
types=["mental_model"]
)
# [/docs:recall-opinions-only]
# [/docs:recall-mental-models-only]
# [docs:recall-with-mental-models]
# Include mental models in recall
results = client.recall(
bank_id="my-bank",
query="What programming languages does Alice prefer?",
types=["world", "experience", "mental_model"]
)
# Mental models only
models = client.recall(
bank_id="my-bank",
query="What patterns have I learned?",
types=["mental_model"]
)
# [/docs:recall-with-mental-models]
# [docs:recall-token-budget]
@@ -93,18 +103,6 @@ results = client.recall(bank_id="my-bank", query="Alice's email", max_tokens=500
# [/docs:recall-token-budget]
# [docs:recall-include-entities]
response = client.recall(
bank_id="my-bank",
query="What does Alice do?",
max_tokens=4096, # Budget for memories
include_entities=True,
max_entity_tokens=1000 # Budget for entity observations
)
# Access the additional context
entities = response.entities or []
# [/docs:recall-include-entities]
# [docs:recall-budget-levels]
+1 -1
View File
@@ -29,7 +29,7 @@ hindsight memory recall my-bank "hiking recommendations" \
# [docs:recall-fact-type]
hindsight memory recall my-bank "query" --fact-type world,opinion
hindsight memory recall my-bank "query" --fact-type world,mental_model
# [/docs:recall-fact-type]
+23
View File
@@ -70,6 +70,29 @@ for (const fact of sourcesResponse.based_on || []) {
// [/docs:reflect-sources]
// [docs:reflect-structured-output]
// Define JSON schema directly
const responseSchema = {
type: 'object',
properties: {
recommendation: { type: 'string' },
confidence: { type: 'string', enum: ['low', 'medium', 'high'] },
key_factors: { type: 'array', items: { type: 'string' } },
risks: { type: 'array', items: { type: 'string' } },
},
required: ['recommendation', 'confidence', 'key_factors'],
};
const structuredResponse = await client.reflect('hiring-team', 'Should we hire Alice for the ML team lead position?', {
responseSchema: responseSchema,
});
// Structured output
console.log(structuredResponse.structuredOutput.recommendation);
console.log(structuredResponse.structuredOutput.keyFactors);
// [/docs:reflect-structured-output]
// =============================================================================
// Cleanup (not shown in docs)
// =============================================================================
+24
View File
@@ -92,6 +92,30 @@ response = client.reflect(
# [/docs:reflect-with-tags]
# [docs:reflect-structured-output]
from pydantic import BaseModel
# Define your response structure with Pydantic
class HiringRecommendation(BaseModel):
recommendation: str
confidence: str # "low", "medium", "high"
key_factors: list[str]
risks: list[str] = []
response = client.reflect(
bank_id="hiring-team",
query="Should we hire Alice for the ML team lead position?",
response_schema=HiringRecommendation.model_json_schema(),
)
# Parse structured output into Pydantic model
result = HiringRecommendation.model_validate(response.structured_output)
print(f"Recommendation: {result.recommendation}")
print(f"Confidence: {result.confidence}")
print(f"Key factors: {result.key_factors}")
# [/docs:reflect-structured-output]
# =============================================================================
# Cleanup (not shown in docs)
# =============================================================================
+19
View File
@@ -31,6 +31,25 @@ hindsight memory reflect my-bank "Summarize my week" --budget high
# [/docs:reflect-high-budget]
# [docs:reflect-structured-output]
# First, create a JSON schema file schema.json:
# {
# "type": "object",
# "properties": {
# "recommendation": {"type": "string"},
# "confidence": {"type": "string", "enum": ["low", "medium", "high"]},
# "key_factors": {"type": "array", "items": {"type": "string"}}
# },
# "required": ["recommendation", "confidence", "key_factors"]
# }
# Then use the --schema flag:
hindsight memory reflect hiring-team \
"Should we hire Alice for the ML team lead position?" \
--schema schema.json
# [/docs:reflect-structured-output]
# =============================================================================
# Cleanup (not shown in docs)
# =============================================================================
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env python3
"""
Reflections API examples for Hindsight.
Run: python examples/api/reflections.py
"""
import os
import time
import requests
HINDSIGHT_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888")
BANK_ID = "reflections-demo-bank"
# =============================================================================
# Setup (not shown in docs)
# =============================================================================
from hindsight_client import Hindsight
client = Hindsight(base_url=HINDSIGHT_URL)
# Create bank and seed some data
client.create_bank(bank_id=BANK_ID, name="Reflections Demo")
client.retain(bank_id=BANK_ID, content="The team prefers async communication via Slack")
client.retain(bank_id=BANK_ID, content="For urgent issues, use the #incidents channel")
client.retain(bank_id=BANK_ID, content="Weekly syncs happen every Monday at 10am")
# Wait for data to be processed
time.sleep(2)
# =============================================================================
# Doc Examples
# =============================================================================
# [docs:create-reflection]
# Create a reflection (runs reflect in background)
response = requests.post(
f"{HINDSIGHT_URL}/v1/default/banks/{BANK_ID}/reflections",
json={
"name": "Team Communication Preferences",
"source_query": "How does the team prefer to communicate?",
"tags": ["team", "communication"]
}
)
result = response.json()
# Returns an operation_id - check operations endpoint for completion
print(f"Operation ID: {result['operation_id']}")
# [/docs:create-reflection]
# Wait for the reflection to be created
time.sleep(5)
# [docs:list-reflections]
# List all reflections in a bank
response = requests.get(f"{HINDSIGHT_URL}/v1/default/banks/{BANK_ID}/reflections")
reflections = response.json()
for reflection in reflections["items"]:
print(f"- {reflection['name']}: {reflection['source_query']}")
# [/docs:list-reflections]
# Get the reflection ID for subsequent examples
reflection_id = reflections["items"][0]["id"] if reflections["items"] else None
if reflection_id:
# [docs:get-reflection]
# Get a specific reflection
response = requests.get(
f"{HINDSIGHT_URL}/v1/default/banks/{BANK_ID}/reflections/{reflection_id}"
)
reflection = response.json()
print(f"Name: {reflection['name']}")
print(f"Content: {reflection['content']}")
print(f"Last refreshed: {reflection['last_refreshed_at']}")
# [/docs:get-reflection]
# [docs:refresh-reflection]
# Refresh a reflection to update with current knowledge
response = requests.post(
f"{HINDSIGHT_URL}/v1/default/banks/{BANK_ID}/reflections/{reflection_id}/refresh"
)
result = response.json()
print(f"Refresh operation ID: {result['operation_id']}")
# [/docs:refresh-reflection]
# [docs:update-reflection]
# Update a reflection's name
response = requests.patch(
f"{HINDSIGHT_URL}/v1/default/banks/{BANK_ID}/reflections/{reflection_id}",
json={"name": "Updated Team Communication Preferences"}
)
updated = response.json()
print(f"Updated name: {updated['name']}")
# [/docs:update-reflection]
# [docs:delete-reflection]
# Delete a reflection
requests.delete(
f"{HINDSIGHT_URL}/v1/default/banks/{BANK_ID}/reflections/{reflection_id}"
)
# [/docs:delete-reflection]
# =============================================================================
# Cleanup (not shown in docs)
# =============================================================================
requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/{BANK_ID}")
print("reflections.py: All examples passed")
+15
View File
@@ -100,6 +100,21 @@ client.retain_batch(
# [/docs:retain-with-document-tags]
# [docs:retain-list-tags]
# List all tags in a bank
response = requests.get(f"{HINDSIGHT_URL}/v1/default/banks/my-bank/tags")
tags = response.json()
for tag in tags["items"]:
print(f"{tag['tag']}: {tag['count']} memories")
# Search with wildcards (* matches any characters)
response = requests.get(f"{HINDSIGHT_URL}/v1/default/banks/my-bank/tags", params={"q": "user:*"})
user_tags = response.json()
response = requests.get(f"{HINDSIGHT_URL}/v1/default/banks/my-bank/tags", params={"q": "*-admin"})
admin_tags = response.json()
# [/docs:retain-list-tags]
# =============================================================================
# Cleanup (not shown in docs)
# =============================================================================
+9 -4
View File
@@ -27,6 +27,11 @@ const sidebars: SidebarsConfig = {
id: 'developer/reflect',
label: 'Reflect',
},
{
type: 'doc',
id: 'developer/mental-models',
label: 'Mental Models',
},
{
type: 'doc',
id: 'developer/multilingual',
@@ -76,13 +81,13 @@ const sidebars: SidebarsConfig = {
},
{
type: 'doc',
id: 'developer/api/memory-banks',
label: 'Memory Banks',
id: 'developer/api/reflections',
label: 'Reflections',
},
{
type: 'doc',
id: 'developer/api/entities',
label: 'Entities',
id: 'developer/api/memory-banks',
label: 'Memory Banks',
},
{
type: 'doc',
+56 -5
View File
@@ -95,7 +95,7 @@
}
/* Navbar icons (desktop only) */
@media (min-width: 997px) {
@media (min-width: 1400px) {
.navbar-item-developer::before,
.navbar-item-sdks::before,
.navbar-item-api::before,
@@ -186,8 +186,15 @@
background-color: #27272a;
}
/* Mobile navbar */
@media (max-width: 996px) {
/* Desktop navbar - hide hamburger toggle at >= 1400px */
@media (min-width: 1400px) {
.navbar__toggle {
display: none !important;
}
}
/* Mobile navbar - show hamburger, hide desktop items at < 1400px */
@media (max-width: 1399px) {
:root {
--ifm-navbar-height: 3.5rem;
}
@@ -205,8 +212,18 @@
height: 24px !important;
}
/* Hamburger menu toggle */
/* Hide all desktop navbar items except logo and toggle */
.navbar__items--right > .navbar__item {
display: none !important;
}
.navbar__items--left > .navbar__link {
display: none !important;
}
/* Show hamburger toggle */
.navbar__toggle {
display: flex !important;
color: var(--ifm-color-primary);
}
@@ -253,7 +270,7 @@
}
/* Dark mode mobile sidebar */
@media (max-width: 996px) {
@media (max-width: 1399px) {
[data-theme='dark'] .navbar-sidebar {
background: #09090b !important;
}
@@ -270,11 +287,45 @@
color: #e2e8f0 !important;
}
[data-theme='dark'] .navbar-sidebar .menu__link--active,
[data-theme='dark'] .navbar-sidebar .menu__link--active:hover {
color: transparent !important;
background: var(--hindsight-gradient) !important;
-webkit-background-clip: text !important;
-webkit-text-fill-color: transparent !important;
background-clip: text !important;
}
[data-theme='dark'] .navbar-sidebar__close {
color: #e2e8f0 !important;
}
}
/* Mobile sidebar - show right navbar items */
@media (max-width: 1399px) {
/* Ensure right-side items are visible in mobile sidebar */
.navbar-sidebar .navbar__item {
display: block !important;
}
/* GitHub icon - add text label in mobile */
.navbar-sidebar .header-github-link::before {
display: none;
}
.navbar-sidebar .header-github-link::after {
content: 'GitHub';
font-family: 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-weight: 600;
font-size: 1rem;
color: #1e293b;
}
[data-theme='dark'] .navbar-sidebar .header-github-link::after {
color: #e2e8f0;
}
}
/* Hero section */
.hero {
padding: 4rem 0;
@@ -0,0 +1,27 @@
import React, {type ReactNode} from 'react';
import {useColorMode, useThemeConfig} from '@docusaurus/theme-common';
import ColorModeToggle from '@theme/ColorModeToggle';
import type {Props} from '@theme/Navbar/ColorModeToggle';
import styles from './styles.module.css';
export default function NavbarColorModeToggle({className}: Props): ReactNode {
const navbarStyle = useThemeConfig().navbar.style;
const {disableSwitch, respectPrefersColorScheme} = useThemeConfig().colorMode;
const {colorModeChoice, setColorMode} = useColorMode();
if (disableSwitch) {
return null;
}
return (
<ColorModeToggle
className={className}
buttonClassName={
navbarStyle === 'dark' ? styles.darkNavbarColorModeToggle : undefined
}
respectPrefersColorScheme={respectPrefersColorScheme}
value={colorModeChoice}
onChange={setColorMode}
/>
);
}
@@ -0,0 +1,3 @@
.darkNavbarColorModeToggle:hover {
background: var(--ifm-color-gray-800);
}
@@ -0,0 +1,103 @@
import React, {type ReactNode} from 'react';
import clsx from 'clsx';
import {
useThemeConfig,
ErrorCauseBoundary,
ThemeClassNames,
} from '@docusaurus/theme-common';
import {splitNavbarItems} from '@docusaurus/theme-common/internal';
import NavbarItem, {type Props as NavbarItemConfig} from '@theme/NavbarItem';
import NavbarColorModeToggle from '@theme/Navbar/ColorModeToggle';
import SearchBar from '@theme/SearchBar';
import NavbarMobileSidebarToggle from '@theme/Navbar/MobileSidebar/Toggle';
import NavbarLogo from '@theme/Navbar/Logo';
import NavbarSearch from '@theme/Navbar/Search';
import styles from './styles.module.css';
function useNavbarItems() {
// TODO temporary casting until ThemeConfig type is improved
return useThemeConfig().navbar.items as NavbarItemConfig[];
}
function NavbarItems({items}: {items: NavbarItemConfig[]}): ReactNode {
return (
<>
{items.map((item, i) => (
<ErrorCauseBoundary
key={i}
onError={(error) =>
new Error(
`A theme navbar item failed to render.
Please double-check the following navbar item (themeConfig.navbar.items) of your Docusaurus config:
${JSON.stringify(item, null, 2)}`,
{cause: error},
)
}>
<NavbarItem {...item} />
</ErrorCauseBoundary>
))}
</>
);
}
function NavbarContentLayout({
left,
right,
}: {
left: ReactNode;
right: ReactNode;
}) {
return (
<div className="navbar__inner">
<div
className={clsx(
ThemeClassNames.layout.navbar.containerLeft,
'navbar__items',
)}>
{left}
</div>
<div
className={clsx(
ThemeClassNames.layout.navbar.containerRight,
'navbar__items navbar__items--right',
)}>
{right}
</div>
</div>
);
}
export default function NavbarContent(): ReactNode {
const items = useNavbarItems();
const [leftItems, rightItems] = splitNavbarItems(items);
const searchBarItem = items.find((item) => item.type === 'search');
return (
<NavbarContentLayout
left={
// TODO stop hardcoding items?
// Always render toggle, CSS controls visibility at 1400px breakpoint
<>
<NavbarMobileSidebarToggle />
<NavbarLogo />
<NavbarItems items={leftItems} />
</>
}
right={
// TODO stop hardcoding items?
// Ask the user to add the respective navbar items => more flexible
<>
<NavbarItems items={rightItems} />
<NavbarColorModeToggle className={styles.colorModeToggle} />
{!searchBarItem && (
<NavbarSearch>
<SearchBar />
</NavbarSearch>
)}
</>
}
/>
);
}
@@ -0,0 +1,16 @@
/*
Hide color mode toggle in small viewports
*/
@media (max-width: 996px) {
.colorModeToggle {
display: none;
}
}
/*
Restore some Infima style that broke with CSS Cascade Layers
See https://github.com/facebook/docusaurus/pull/11142
*/
:global(.navbar__items--right) > :last-child {
padding-right: 0;
}
@@ -0,0 +1,57 @@
import React, {type ComponentProps, type ReactNode} from 'react';
import clsx from 'clsx';
import {ThemeClassNames, useThemeConfig} from '@docusaurus/theme-common';
import {
useHideableNavbar,
useNavbarMobileSidebar,
} from '@docusaurus/theme-common/internal';
import {translate} from '@docusaurus/Translate';
import NavbarMobileSidebar from '@theme/Navbar/MobileSidebar';
import type {Props} from '@theme/Navbar/Layout';
import styles from './styles.module.css';
function NavbarBackdrop(props: ComponentProps<'div'>) {
return (
<div
role="presentation"
{...props}
className={clsx('navbar-sidebar__backdrop', props.className)}
/>
);
}
export default function NavbarLayout({children}: Props): ReactNode {
const {
navbar: {hideOnScroll, style},
} = useThemeConfig();
const mobileSidebar = useNavbarMobileSidebar();
const {navbarRef, isNavbarVisible} = useHideableNavbar(hideOnScroll);
return (
<nav
ref={navbarRef}
aria-label={translate({
id: 'theme.NavBar.navAriaLabel',
message: 'Main',
description: 'The ARIA label for the main navigation',
})}
className={clsx(
ThemeClassNames.layout.navbar.container,
'navbar',
'navbar--fixed-top',
hideOnScroll && [
styles.navbarHideable,
!isNavbarVisible && styles.navbarHidden,
],
{
'navbar--dark': style === 'dark',
'navbar--primary': style === 'primary',
'navbar-sidebar--show': mobileSidebar.shown,
},
)}>
{children}
<NavbarBackdrop onClick={mobileSidebar.toggle} />
<NavbarMobileSidebar />
</nav>
);
}
@@ -0,0 +1,7 @@
.navbarHideable {
transition: transform var(--ifm-transition-fast) ease;
}
.navbarHidden {
transform: translate3d(0, calc(-100% - 2px), 0);
}
@@ -0,0 +1,12 @@
import React, {type ReactNode} from 'react';
import Logo from '@theme/Logo';
export default function NavbarLogo(): ReactNode {
return (
<Logo
className="navbar__brand"
imageClassName="navbar__logo"
titleClassName="navbar__title text--truncate"
/>
);
}
@@ -0,0 +1,33 @@
import React, {type ReactNode} from 'react';
import {useNavbarMobileSidebar} from '@docusaurus/theme-common/internal';
import {translate} from '@docusaurus/Translate';
import NavbarColorModeToggle from '@theme/Navbar/ColorModeToggle';
import IconClose from '@theme/Icon/Close';
import NavbarLogo from '@theme/Navbar/Logo';
function CloseButton() {
const mobileSidebar = useNavbarMobileSidebar();
return (
<button
type="button"
aria-label={translate({
id: 'theme.docs.sidebar.closeSidebarButtonAriaLabel',
message: 'Close navigation bar',
description: 'The ARIA label for close button of mobile sidebar',
})}
className="clean-btn navbar-sidebar__close"
onClick={() => mobileSidebar.toggle()}>
<IconClose color="var(--ifm-color-emphasis-600)" />
</button>
);
}
export default function NavbarMobileSidebarHeader(): ReactNode {
return (
<div className="navbar-sidebar__brand">
<NavbarLogo />
<NavbarColorModeToggle className="margin-right--md" />
<CloseButton />
</div>
);
}
@@ -0,0 +1,63 @@
import React, {version, type ReactNode} from 'react';
import clsx from 'clsx';
import {useNavbarSecondaryMenu} from '@docusaurus/theme-common/internal';
import {ThemeClassNames} from '@docusaurus/theme-common';
import type {Props} from '@theme/Navbar/MobileSidebar/Layout';
// TODO Docusaurus v4: remove temporary inert workaround
// See https://github.com/facebook/react/issues/17157
// See https://github.com/radix-ui/themes/pull/509
function inertProps(inert: boolean) {
const isBeforeReact19 = parseInt(version!.split('.')[0]!, 10) < 19;
if (isBeforeReact19) {
return {inert: inert ? '' : undefined};
}
return {inert};
}
function NavbarMobileSidebarPanel({
children,
inert,
}: {
children: ReactNode;
inert: boolean;
}) {
return (
<div
className={clsx(
ThemeClassNames.layout.navbar.mobileSidebar.panel,
'navbar-sidebar__item menu',
)}
{...inertProps(inert)}>
{children}
</div>
);
}
export default function NavbarMobileSidebarLayout({
header,
primaryMenu,
secondaryMenu,
}: Props): ReactNode {
const {shown: secondaryMenuShown} = useNavbarSecondaryMenu();
return (
<div
className={clsx(
ThemeClassNames.layout.navbar.mobileSidebar.container,
'navbar-sidebar',
)}>
{header}
<div
className={clsx('navbar-sidebar__items', {
'navbar-sidebar__items--show-secondary': secondaryMenuShown,
})}>
<NavbarMobileSidebarPanel inert={secondaryMenuShown}>
{primaryMenu}
</NavbarMobileSidebarPanel>
<NavbarMobileSidebarPanel inert={!secondaryMenuShown}>
{secondaryMenu}
</NavbarMobileSidebarPanel>
</div>
</div>
);
}
@@ -0,0 +1,31 @@
import React, {type ReactNode} from 'react';
import {useThemeConfig} from '@docusaurus/theme-common';
import {useNavbarMobileSidebar} from '@docusaurus/theme-common/internal';
import NavbarItem, {type Props as NavbarItemConfig} from '@theme/NavbarItem';
function useNavbarItems() {
// TODO temporary casting until ThemeConfig type is improved
return useThemeConfig().navbar.items as NavbarItemConfig[];
}
// The primary menu displays the navbar items
export default function NavbarMobilePrimaryMenu(): ReactNode {
const mobileSidebar = useNavbarMobileSidebar();
// TODO how can the order be defined for mobile?
// Should we allow providing a different list of items?
const items = useNavbarItems();
return (
<ul className="menu__list">
{items.map((item, i) => (
<NavbarItem
mobile
{...item}
onClick={() => mobileSidebar.toggle()}
key={i}
/>
))}
</ul>
);
}
@@ -0,0 +1,32 @@
import React, {type ComponentProps, type ReactNode} from 'react';
import {useThemeConfig} from '@docusaurus/theme-common';
import {useNavbarSecondaryMenu} from '@docusaurus/theme-common/internal';
import Translate from '@docusaurus/Translate';
function SecondaryMenuBackButton(props: ComponentProps<'button'>) {
return (
<button {...props} type="button" className="clean-btn navbar-sidebar__back">
<Translate
id="theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel"
description="The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)">
Back to main menu
</Translate>
</button>
);
}
// The secondary menu slides from the right and shows contextual information
// such as the docs sidebar
export default function NavbarMobileSidebarSecondaryMenu(): ReactNode {
const isPrimaryMenuEmpty = useThemeConfig().navbar.items.length === 0;
const secondaryMenu = useNavbarSecondaryMenu();
return (
<>
{/* edge-case: prevent returning to the primaryMenu when it's empty */}
{!isPrimaryMenuEmpty && (
<SecondaryMenuBackButton onClick={() => secondaryMenu.hide()} />
)}
{secondaryMenu.content}
</>
);
}
@@ -0,0 +1,23 @@
import React, {type ReactNode} from 'react';
import {useNavbarMobileSidebar} from '@docusaurus/theme-common/internal';
import {translate} from '@docusaurus/Translate';
import IconMenu from '@theme/Icon/Menu';
export default function MobileSidebarToggle(): ReactNode {
const {toggle, shown} = useNavbarMobileSidebar();
return (
<button
onClick={toggle}
aria-label={translate({
id: 'theme.docs.sidebar.toggleSidebarButtonAriaLabel',
message: 'Toggle navigation bar',
description:
'The ARIA label for hamburger menu button of mobile navigation',
})}
aria-expanded={shown}
className="navbar__toggle clean-btn"
type="button">
<IconMenu />
</button>
);
}
@@ -0,0 +1,27 @@
import React, {type ReactNode} from 'react';
import {
useLockBodyScroll,
useNavbarMobileSidebar,
} from '@docusaurus/theme-common/internal';
import NavbarMobileSidebarLayout from '@theme/Navbar/MobileSidebar/Layout';
import NavbarMobileSidebarHeader from '@theme/Navbar/MobileSidebar/Header';
import NavbarMobileSidebarPrimaryMenu from '@theme/Navbar/MobileSidebar/PrimaryMenu';
import NavbarMobileSidebarSecondaryMenu from '@theme/Navbar/MobileSidebar/SecondaryMenu';
export default function NavbarMobileSidebar(): ReactNode {
const mobileSidebar = useNavbarMobileSidebar();
useLockBodyScroll(mobileSidebar.shown);
// Always render when shown - breakpoint controlled by CSS at 1400px
if (!mobileSidebar.shown) {
return null;
}
return (
<NavbarMobileSidebarLayout
header={<NavbarMobileSidebarHeader />}
primaryMenu={<NavbarMobileSidebarPrimaryMenu />}
secondaryMenu={<NavbarMobileSidebarSecondaryMenu />}
/>
);
}
@@ -0,0 +1,13 @@
import React, {type ReactNode} from 'react';
import clsx from 'clsx';
import type {Props} from '@theme/Navbar/Search';
import styles from './styles.module.css';
export default function NavbarSearch({children, className}: Props): ReactNode {
return (
<div className={clsx(className, styles.navbarSearchContainer)}>
{children}
</div>
);
}
@@ -0,0 +1,20 @@
/*
Workaround to avoid rendering empty search container
See https://github.com/facebook/docusaurus/pull/9385
*/
.navbarSearchContainer:empty {
display: none;
}
@media (max-width: 996px) {
.navbarSearchContainer {
position: absolute;
right: var(--ifm-navbar-padding-horizontal);
}
}
@media (min-width: 997px) {
.navbarSearchContainer {
padding: 0 var(--ifm-navbar-item-padding-horizontal);
}
}
+11
View File
@@ -0,0 +1,11 @@
import React, {type ReactNode} from 'react';
import NavbarLayout from '@theme/Navbar/Layout';
import NavbarContent from '@theme/Navbar/Content';
export default function Navbar(): ReactNode {
return (
<NavbarLayout>
<NavbarContent />
</NavbarLayout>
);
}
+8 -9
View File
@@ -5547,15 +5547,6 @@
"text": "I discussed AI applications last week",
"type": "experience"
}
],
"mental_models": [
{
"description": "Understanding of AI capabilities",
"id": "mm-1",
"name": "AI Technology",
"subtype": "structural",
"type": "concept"
}
]
},
"structured_output": {
@@ -5573,6 +5564,14 @@
"scope": "agent_1"
}
],
"mental_models": [
{
"id": "mm-1",
"name": "AI Technology",
"subtype": "structural",
"type": "concept"
}
],
"tool_calls": [
{
"duration_ms": 150,
@@ -11,8 +11,8 @@ import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import opinionsPy from '!!raw-loader!@site/examples/api/opinions.py';
import opinionsMjs from '!!raw-loader!@site/examples/api/opinions.mjs';
import opinionsPy from '!!raw-loader!@site/examples/api/legacy/opinions.py';
import opinionsMjs from '!!raw-loader!@site/examples/api/legacy/opinions.mjs';
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.