Compare commits

...
Author SHA1 Message Date
Nicolò Boschi e7650d2281 fix: correct broken anchor in best practices TOC 2026-03-17 14:00:44 +01:00
Nicolò Boschi d1033cee87 fix: add lu-star icon to navbar item icon map 2026-03-17 13:52:50 +01:00
Nicolò Boschi 0810c8c536 docs: hide right TOC and add manual TOC to FAQ page 2026-03-17 13:52:20 +01:00
Nicolò Boschi d9f07b7ea0 docs: hide right TOC and add manual TOC to best practices page 2026-03-17 13:51:00 +01:00
Nicolò Boschi 22cb79c432 fix: add Best Practices and FAQ to Resources navbar dropdown 2026-03-17 13:49:53 +01:00
Nicolò Boschi eeb7326261 fix: remove duplicate LuFileText import 2026-03-17 13:42:52 +01:00
Nicolò Boschi 327cf6f6c3 fix: add missing lu-star, lu-circle-help, lu-file-text icons to sidebar map 2026-03-17 13:41:44 +01:00
Nicolò Boschi eca77d9e16 fix: remove leftover merge conflict markers in DocSidebarItem Link 2026-03-17 13:35:53 +01:00
Nicolò Boschi 05a933306f docs: add Best Practices page as unversioned standalone page
- Add src/pages/best-practices.mdx covering core concepts (memory banks,
  taxonomy, memory types), bank configuration (missions, dispositions,
  entity labels), retain (formats, context, document_id, tags, observation
  scopes), recall (budget, tag filtering, include options), reflect
  (recall vs reflect decision, response_schema, auditing), mental models,
  and anti-patterns
- Add Resources section to sidebar with Best Practices and FAQ links
- Update generate-docs-skill.sh to include standalone pages (best-practices,
  faq) from src/pages/ into the agent skill references
- SKILL.md now surfaces best-practices.md as the recommended starting point
2026-03-17 13:31:04 +01:00
Nicolò Boschi 9e58b1b7d3 docs: revamp sidebar with icon grid components and language support
- Merge Clients and Integrations sections into the developer sidebar
  (removed top-level SDKs navbar item)
- Reorder sidebar: Architecture → API → Clients → Integrations → Hosting
- Unify icon system using react-icons (LuXxx/SiXxx) via customProps.icon
- Add uppercase section titles with increased spacing and reduced indentation
- Rename Node.js → "JavaScript / TypeScript" with TypeScript icon
- Add reusable IconGrid and SupportedGrids components (ClientsGrid,
  IntegrationsGrid, LLMProvidersGrid)
- Use grids in FAQ, Models, Overview, and Quick Start pages
- Convert developer/index.md, models.md, faq.md to MDX for JSX support
2026-03-17 12:26:24 +01:00
12 changed files with 1466 additions and 413 deletions
+10
View File
@@ -235,6 +235,16 @@ const config: Config = {
position: 'left',
className: 'navbar-item-resources',
items: [
{
to: '/best-practices',
label: 'Best Practices',
customProps: { icon: 'lu-star' },
},
{
to: '/faq',
label: 'FAQ',
customProps: { icon: 'lu-circle-help' },
},
{
to: '/cookbook',
label: 'Cookbook',
+19
View File
@@ -283,6 +283,25 @@ const sidebars: SidebarsConfig = {
},
],
},
{
type: 'category',
label: 'Resources',
collapsible: false,
items: [
{
type: 'link',
href: '/best-practices',
label: 'Best Practices',
customProps: { icon: 'lu-star', iconAfter: 'lu-arrow-up-right' },
},
{
type: 'link',
href: '/faq',
label: 'FAQ',
customProps: { icon: 'lu-circle-help', iconAfter: 'lu-arrow-up-right' },
},
],
},
{
type: 'category',
label: 'More',
+533
View File
@@ -0,0 +1,533 @@
---
title: Best Practices
description: Practical guidance for agents and developers integrating Hindsight memory into production systems.
hide_table_of_contents: true
---
# Best Practices
Practical guidance for agents and developers integrating Hindsight memory into production systems.
**Contents**
- [Core Concepts](#core-concepts) — Memory banks, taxonomy, memory types
- [Bank Configuration](#bank-configuration) — Missions, dispositions, entity labels
- [Retaining Data](#retaining-data) — Content format, context, document_id, [tags](#tags-naming-conventions), observation scopes
- [Recalling Memories](#recalling-memories) — Budget, tag filtering, include options, query_timestamp
- [Reflecting](#reflecting) — Recall vs reflect, response_schema, auditing
- [Mental Models](#mental-models) — When to create, tag strategy, refresh
- [Anti-patterns](#anti-patterns)
---
## Core Concepts
### Memory Banks
A **memory bank** is an isolated memory store — the unit of separation between users, agents, or contexts. All operations (retain, recall, reflect) target a single bank. Banks do not share data.
- One bank per user is the most common pattern for multi-user applications
- One bank per agent is common for agent-specific long-term memory
- A shared bank with tags can work for cross-user analysis (see [Tags](#tags-naming-conventions))
Banks are auto-created on first use. Configure them before ingesting data to steer behavior.
---
### Taxonomy
| Operation | What it does | When to call it |
|-----------|-------------|-----------------|
| **Retain** | Ingests raw content (conversations, documents, notes). The LLM extracts facts, entities, and relationships — raw content is never stored verbatim. | After each conversation turn or session ends |
| **Recall** | Retrieves relevant memories using 4 parallel strategies: semantic search, BM25, graph traversal, and temporal ranking. Returns a ranked list of facts. | Before generating a response that benefits from past context |
| **Reflect** | Autonomous reasoning loop: searches memory, synthesizes an answer, and returns it directly. Uses mental models and observations hierarchically. | When you want Hindsight to answer a question, not just retrieve facts |
| **Observations** | Auto-synthesized knowledge patterns produced by the consolidation operation, which runs asynchronously after retain completes. Consolidate facts into durable insights (preferences, behavioral patterns, contradictions). | Triggered automatically after retain — not part of the retain call itself |
| **Mental Models** | Pre-computed reflect responses stored for common queries. Return instantly and consistently. | Create for repeated high-traffic queries or slowly-changing user profiles |
---
### Memory Types
Facts extracted during retain are classified into three types:
| Type | Description | Example |
|------|-------------|---------|
| `world` | General knowledge, external facts | "The Eiffel Tower is in Paris" |
| `experience` | Personal events, user-specific facts | "User moved to Berlin in 2024" |
| `observation` | Consolidated patterns synthesized from facts | "User consistently prefers async communication" |
Use `types` filtering in recall to target specific memory types.
---
## Bank Configuration
Configure a bank before first use to steer memory behavior for your domain. Misconfigured missions are the single biggest cause of low-quality memories.
### Writing Effective Missions
All three missions accept plain language. Be specific about your domain — vague missions produce vague results.
#### `retain_mission`
Injected into the fact extraction prompt. Tells the LLM what to extract and what to ignore.
| Quality | Example |
|---------|---------|
| **Good** | `Always extract technical decisions, API design choices, architectural trade-offs, blockers, and error messages. Ignore greetings, small talk, and scheduling logistics.` |
| **Good** | `Extract personal preferences, ongoing commitments, deadlines, health info, and relationship details. Ignore filler phrases and pleasantries.` |
| **Bad** | `Extract all information` — too vague, extracts noise |
| **Bad** | `Be helpful` — not an extraction directive |
**Tips:**
- List the fact *types* you want (preferences, decisions, errors, commitments)
- List what to *ignore* — this is as important as what to include
- Match the mission to your actual data type (conversations vs documents vs tickets)
#### `observations_mission`
Steers what patterns are synthesized during consolidation. Runs after retain.
```
Identify evolving preferences, recurring patterns, behavioral shifts, and contradictions
with prior knowledge. Focus on durable patterns — not transient states. Highlight when
user behavior contradicts previous observations.
```
**Tips:**
- Emphasize "durable patterns" to avoid ephemeral observation noise
- Mention contradiction detection explicitly if you need historical tracking
- Match scope to how often you expect patterns to change
#### `reflect_mission`
Sets the agent persona and reasoning frame for `reflect` operations.
| Use Case | Mission |
|----------|---------|
| Coding assistant | `You are a senior developer helping optimize the user's workflow. Always factor in past technical decisions, current project context, and stated preferences. Be direct and opinionated.` |
| Customer support | `You are a support agent with full context of this customer's history. Reference past tickets and resolutions where relevant. Be concise and solution-focused.` |
| Personal assistant | `You are a personal assistant who remembers everything important to the user. Personalize every response using what you know about their preferences, schedule, and ongoing projects.` |
| Medical assistant | `You are a health assistant. Reference the user's history accurately. Always recommend consulting a professional for medical decisions. Do not speculate.` |
---
### Disposition Traits
Dispositions affect `reflect` only (not `recall`). Scale 15.
| Trait | 1 | 5 |
|-------|---|---|
| `skepticism` | Trusts all memories at face value | Questions contradictions, flags uncertain info |
| `literalism` | Liberal interpretation, infers intent | Strict literal reading, no inference |
| `empathy` | Clinical, neutral tone | Warm, personal, emotionally aware |
**Common profiles:**
| Agent type | Skepticism | Literalism | Empathy |
|------------|------------|------------|---------|
| Code review | 4 | 5 | 1 |
| Customer support | 2 | 3 | 4 |
| Personal assistant | 2 | 2 | 4 |
| Medical assistant | 5 | 4 | 3 |
| Research assistant | 4 | 4 | 2 |
---
### Entity Labels
Define a controlled vocabulary for classification. The LLM will extract and normalize values to your defined set.
```json
{
"entity_labels": [
{
"key": "tech_stack",
"type": "multi-values",
"values": [
{"value": "python", "description": "Python programming language"},
{"value": "typescript", "description": "TypeScript / Node.js"},
{"value": "react", "description": "React frontend framework"}
]
},
{
"key": "priority",
"type": "value",
"tag": true,
"values": [
{"value": "high", "description": "Urgent or blocking"},
{"value": "low", "description": "Nice to have"}
]
}
]
}
```
- **`type: "value"`** — single value per entity (last write wins)
- **`type: "multi-values"`** — accumulates multiple values
- **`tag: true`** — extracted label values are also added as tags (enables filtering by entity value)
Use entity labels when you need consistent classification — domain-specific terms, status values, priority levels, engagement types.
---
## Retaining Data
### Content Format
Pass the richest representation available. Never pre-summarize.
| Format | Recommendation |
|--------|---------------|
| JSON conversation array | **Preferred** for conversations — preserves structure, roles, and relationships |
| Prefixed plain text | Acceptable — `[ISO-timestamp] role: text` per line |
| Markdown / HTML / raw text | Works for documents and notes |
| Pre-summarized text | **Avoid** — loses entity relationships, temporal markers, structural context |
**Conversation JSON (preferred):**
```json
[
{"role": "user", "content": "I'm using React for the frontend.", "timestamp": "2025-06-01T10:30:00Z"},
{"role": "assistant", "content": "Got it. What state management are you using?"},
{"role": "user", "content": "Zustand. We moved away from Redux last quarter."}
]
```
**Why not pre-summarize:** The LLM extracts facts, entities, and relationships from structure. A summary like "user uses React and Zustand" loses the temporal reference ("last quarter"), the entity relationship (React↔frontend, Redux↔migration), and the causal context (moved away from).
---
### The `context` Field
High-impact on extraction quality. Always set it. Describes the *nature and source* of the content.
```python
# Good — specific, descriptive
context="Customer support ticket #12345 from user Alice about a billing discrepancy"
context="Developer's architecture review session for the payments service"
context="User's onboarding form: stated goals, current tools, and team size"
context="Weekly standup notes: blockers, progress, and upcoming tasks"
# Bad — generic, adds no signal
context="some data"
context="conversation"
# Omitted entirely — extraction uses no context
```
---
### The `document_id` Field
Use for upsert behavior. Same `document_id` = delete previous version and reprocess.
**Rules:**
- Use stable, meaningful IDs (session ID, ticket ID, document UUID)
- Always use the same ID for a growing conversation — retain the full conversation with each new message
- Do NOT use random UUIDs per retain call — this creates duplicates
```python
# Good — stable session ID
client.retain(bank_id="user-alice", items=[{
"content": full_conversation,
"document_id": f"session-{session_id}",
}])
# Bad — new random ID every call = duplicates
client.retain(bank_id="user-alice", items=[{
"content": full_conversation,
"document_id": str(uuid.uuid4()), # ❌ creates a new document each time
}])
```
---
### The `timestamp` Field
Set whenever you have temporal context. Enables temporal retrieval strategies.
- ISO 8601 format: `"2025-06-01T10:32:00Z"`
- For conversations: set to when the conversation *started*
- Omitting it disables temporal ranking entirely
---
### Tags: Naming Conventions
Tags scope visibility. A memory tagged `user:alice` is only returned for recall/reflect calls that include `user:alice` in their `tags` filter (with strict matching).
**Standard naming conventions:**
| Pattern | Example | Use for |
|---------|---------|---------|
| `user:<id>` | `user:alice`, `user:u_123` | Per-user isolation |
| `session:<id>` | `session:s_abc` | Session-scoped memories |
| `team:<name>` | `team:engineering` | Shared team knowledge |
| `topic:<name>` | `topic:billing`, `topic:technical` | Domain filtering |
| `scope:<name>` | `scope:private`, `scope:public` | Visibility tiers |
**Multi-tenant minimum:** Every retain for user data must include at least `user:<id>`. Omitting it makes the memory globally visible.
```python
# Multi-tenant retain — always tag with user ID
items=[{
"content": conversation,
"tags": ["user:alice", "session:s_abc", "topic:billing"],
"document_id": f"session-{session_id}",
}]
```
---
### Metadata Schema
Use for source tracking and downstream linking. **Not filterable** — use tags for filtering.
```python
# Source tracking
metadata={"source": "slack", "channel": "#engineering", "thread_id": "T123456"}
# Ticket linking
metadata={"ticket_id": "JIRA-123", "priority": "high", "reporter": "alice"}
# Document provenance
metadata={"url": "https://...", "section": "pricing-faq", "version": "2025-Q1"}
```
Metadata is returned with every recalled memory — use it to link memories back to source systems for UI display, deep-linking, or audit trails.
---
### Observation Scopes
Controls which tag combinations get their own observation pass.
| Value | Behavior | When to use |
|-------|----------|------------|
| `"combined"` | One pass with all tags together | Default — single-user banks, general use |
| `"per_tag"` | One pass per tag independently | Users should have isolated behavioral observations |
| `"all_combinations"` | All possible subsets of tags | Complex multi-dimensional analysis (expensive) |
| Custom list | Explicit scope list | Precise multi-tenant control |
**Custom scope example (recommended for multi-tenant):**
```python
# Observations scoped to: user-level, team-level, and combined
observation_scopes=[
["user:alice"],
["team:engineering"],
["user:alice", "team:engineering"],
]
```
---
### Sync vs Async
| Mode | When to use |
|------|-------------|
| `async_=False` (default) | When you need confirmation before proceeding |
| `async_=True` | End-of-turn or end-of-session retain; user-facing flows where latency matters |
Do not retain and recall in the same turn — retain is a write operation and the extracted memories will not be available immediately.
---
## Recalling Memories
### Budget Selection
| Budget | Latency | Use when |
|--------|---------|----------|
| `low` | 50100ms | Simple fact lookups, single-hop questions |
| `mid` | 100300ms | Multi-hop reasoning, relationship queries *(default)* |
| `high` | 300500ms | Deep exploration, complex cross-domain patterns |
Default to `mid`. Use `low` for high-frequency agent loops. Reserve `high` for explicit "deep recall" user-triggered flows.
---
### Tag Filtering Modes
| Mode | Includes untagged? | Condition |
|------|-------------------|-----------|
| `any` *(default)* | Yes | At least one tag matches, OR untagged |
| `all` | Yes | All specified tags present, OR untagged |
| `any_strict` | No | At least one tag matches |
| `all_strict` | No | All specified tags present |
**Decision guide:**
- Shared global knowledge + per-user: `tags=["user:alice"], tags_match="any"` — returns Alice's memories and untagged global memories
- Fully partitioned (no leakage): `tags=["user:alice"], tags_match="any_strict"` — Alice's memories only
- Multi-condition AND: `tags=["user:alice", "topic:billing"], tags_match="all_strict"` — only where both tags present
**`tag_groups` for complex filters:**
Tag groups use a tree structure with `and`/`or`/`not` compound nodes and `{"tags": [...], "match": "..."}` leaf nodes.
```python
# Alice's billing memories OR shared billing memories (no user tag)
recall(
query="...",
tag_groups=[
{"or": [
{"tags": ["user:alice", "topic:billing"], "match": "all_strict"},
{"and": [
{"tags": ["topic:billing"], "match": "any_strict"},
{"not": {"tags": ["user:alice"], "match": "any_strict"}},
]},
]}
]
)
```
---
### `include` Options
| Option | Default | Enable when |
|--------|---------|-------------|
| `include.entities` | Enabled | — (leave on; provides entity context for graph traversal) |
| `include.chunks` | Disabled | Agent needs exact wording or source quotation |
| `include.source_facts` | Disabled | Tracing observation provenance for auditing |
---
### `types` Filtering
| Value | Returns |
|-------|---------|
| *(not set)* | All types |
| `["observation"]` | Consolidated patterns only — faster for high-level questions |
| `["world", "experience"]` | Raw facts only — for ground-truth or citation-sensitive queries |
---
### `query_timestamp`
Set for time-sensitive queries. Anchors temporal ranking to a specific point in time.
```python
# "What was the team working on in January?"
recall(query="team priorities", query_timestamp="2025-01-31T23:59:59Z")
# Current context (most common)
recall(query="user preferences", query_timestamp=datetime.utcnow().isoformat() + "Z")
```
---
## Reflecting
### Recall vs Reflect
| Use `recall` when | Use `reflect` when |
|-------------------|--------------------|
| Agent will reason over facts itself | You want Hindsight to reason and return an answer |
| You need raw citations | You need a synthesized response |
| You're building a RAG pipeline | You want an autonomous multi-step search loop |
| Latency is critical | Response quality matters more than latency |
| You need precise fact counts | You need a contextual, nuanced answer |
---
### `response_schema`
Use when you need structured output for programmatic consumption.
```python
reflect(
query="What are the user's top 3 technical preferences?",
response_schema={
"type": "object",
"properties": {
"preferences": {
"type": "array",
"items": {"type": "string"},
"maxItems": 3
},
"confidence": {"type": "number", "minimum": 0, "maximum": 1}
},
"required": ["preferences", "confidence"]
}
)
# Returns: result.structured_output["preferences"], result.structured_output["confidence"]
```
---
### Auditing and Debugging
| Option | Purpose |
|--------|---------|
| `include.facts=True` | Exposes which memories and mental models were used (for transparency/auditing) |
| `include.tool_calls=True` | Full execution trace of the internal search loop (for debugging) |
Enable `include.facts` in production for audit trails. Enable `include.tool_calls` only during development.
---
## Mental Models
Mental models are pre-computed `reflect` responses stored for common queries. They return instantly and consistently.
### When to Create
- Common repeated queries that should return consistent answers
- High-traffic agents that need sub-100ms responses
- User profiles or personas read on every request
- Knowledge summaries reviewed or approved by humans
- Cross-session state that changes slowly (preferences, skills, background)
### Tag Strategy
Tags on a mental model filter BOTH which memories are used to build it AND which recall/reflect calls can see it.
```python
# Per-user mental model — uses Alice's memories (all_strict applied automatically during refresh)
create_mental_model(
bank_id="shared-bank",
name="Alice's Technical Profile",
source_query="Summarize Alice's technical background, preferred stack, and current projects",
tags=["user:alice"],
)
# Global mental model — uses all memories, visible to everyone
create_mental_model(
bank_id="shared-bank",
name="Team Engineering Standards",
source_query="What are the team's agreed engineering standards and conventions?",
# No tags — reads all memories, visible to all
)
```
### Refresh Strategy
| Trigger | When to use |
|---------|------------|
| Manual via API | After significant data updates or review cycles |
| `trigger={"refresh_after_consolidation": True}` | When observations update frequently and the model should stay current |
Create narrow, scoped models — one per knowledge dimension. A mental model titled "Everything about the user" is as useful as none.
**Model granularity examples for a personal assistant:**
- "User Profile" — demographics, preferences, stated goals
- "Current Projects" — active work, deadlines, blockers
- "Technical Stack" — languages, tools, frameworks used
- "Communication Style" — formality preferences, response length preferences
---
## Anti-patterns
| Anti-pattern | Problem | Fix |
|-------------|---------|-----|
| Pre-summarizing before retain | Loses entity relationships, temporal markers, structural context | Retain raw content; Hindsight extracts facts |
| Using random UUIDs as `document_id` | Creates duplicate documents on every retain | Use stable session/ticket/document IDs |
| Omitting the `context` field | Reduces extraction quality significantly | Always describe what kind of data this is |
| Using `metadata` for filtering | Metadata is not filterable | Use `tags` for anything you'll filter on |
| Vague or generic missions | Generic extraction = noisy, low-value memories | Be specific about domain, data type, what to ignore |
| `tags_match="any"` for multi-tenant banks | Leaks memories across users | Use `any_strict` or `all_strict` for user-partitioned data |
| Retaining and recalling in the same request | Retained memories not yet indexed | Retain end-of-turn; recall at the start of next turn |
| One mental model for everything | Low accuracy, slow refresh, hard to scope | Create one model per knowledge dimension |
| `high` budget for every recall | Expensive, slow, usually unnecessary | Use `low` for simple lookups, `mid` default |
| Missing `timestamp` on retain | Disables temporal retrieval strategies | Always set from actual content timestamps |
+17 -18
View File
@@ -1,13 +1,29 @@
---
title: Frequently Asked Questions
description: Common questions and answers about Hindsight
hide_table_of_contents: false
hide_table_of_contents: true
---
import {ClientsGrid, IntegrationsGrid, LLMProvidersGrid} from '@site/src/components/SupportedGrids';
# Frequently Asked Questions
**Contents**
- [What is Hindsight and how does it differ from RAG?](#what-is-hindsight-and-how-does-it-differ-from-rag)
- [Why use Hindsight instead of other solutions?](#why-use-hindsight-instead-of-other-solutions)
- [Supported clients, integrations, and LLM providers](#which-clients-and-languages-are-supported)
- [Which model should I use?](#which-model-should-i-use-with-hindsight)
- [Hosting and system requirements](#do-i-need-to-host-my-own-infrastructure)
- [How do I isolate user data?](#how-do-i-isolate-user-data)
- [Retain, recall, and reflect — what's the difference?](#whats-the-difference-between-retain-recall-and-reflect)
- [When should I use recall vs reflect?](#when-should-i-use-recall-vs-reflect)
- [When should I use mental models?](#when-should-i-use-mental-models)
- [Latency expectations](#whats-the-typical-latency-for-recall-operations)
- [Tags, metadata, and entity labels](#does-hindsight-support-metadata-filtering)
- [Recommended format for conversations](#what-is-the-recommended-format-for-retaining-conversations)
---
### What is Hindsight and how does it differ from RAG?
Hindsight is an agent memory system that provides long-term memory for AI agents using biomimetic data structures. Unlike traditional RAG (Retrieval-Augmented Generation), Hindsight:
@@ -43,7 +59,6 @@ Unlike vector databases (just search) or RAG systems (document retrieval), Hinds
<ClientsGrid />
Any language can also use the [HTTP API](/developer/api/quickstart) directly.
---
@@ -59,22 +74,6 @@ Any language can also use the [HTTP API](/developer/api/quickstart) directly.
See [Models](/developer/models) for the full list of supported providers, recommended models, and configuration examples.
**Using local models with Ollama:**
```bash
HINDSIGHT_API_LLM_PROVIDER=ollama
HINDSIGHT_API_LLM_MODEL=llama3.1
HINDSIGHT_API_LLM_BASE_URL=http://localhost:11434
```
**Using local models with LM Studio:**
```bash
HINDSIGHT_API_LLM_PROVIDER=lmstudio
HINDSIGHT_API_LLM_MODEL=your-model-name
HINDSIGHT_API_LLM_BASE_URL=http://localhost:1234/v1
```
Configure your provider using the `HINDSIGHT_API_LLM_PROVIDER` environment variable. See [Configuration](/developer/configuration) and [Models](/developer/models) for details.
---
### Which model should I use with Hindsight?
@@ -11,7 +11,7 @@ import {
LuActivity, LuPlug, LuShield, LuPackage, LuBook,
LuNetwork, LuCode, LuLayers, LuCpu,
LuArrowUpRight, LuBookOpen, LuRss, LuCloud, LuMessageCircle,
LuChartBar, LuChartColumn,
LuChartBar, LuChartColumn, LuStar, LuCircleHelp,
} from 'react-icons/lu';
import {SiGo, SiPython, SiGithub, SiSlack} from 'react-icons/si';
@@ -51,6 +51,9 @@ const ICON_MAP: Record<string, IconType> = {
'lu-rss': LuRss,
'lu-cloud': LuCloud,
'lu-message-circle': LuMessageCircle,
'lu-star': LuStar,
'lu-circle-help': LuCircleHelp,
'lu-file-text': LuFileText,
};
type Props = WrapperProps<typeof LinkType>;
@@ -6,7 +6,7 @@ import type {IconType} from 'react-icons';
import {
LuArrowUpRight, LuCode, LuCircleHelp, LuScrollText,
LuLayoutGrid, LuCloud, LuBook, LuRss, LuBookOpen,
LuChartBar, LuCpu, LuFileText,
LuChartBar, LuCpu, LuFileText, LuStar,
} from 'react-icons/lu';
import {SiGithub, SiSlack} from 'react-icons/si';
@@ -22,6 +22,7 @@ const ICON_MAP: Record<string, IconType> = {
'lu-chart-bar': LuChartBar,
'lu-cpu': LuCpu,
'lu-file-text': LuFileText,
'lu-star': LuStar,
'si-github': SiGithub,
'si-slack': SiSlack,
};
@@ -5,78 +5,15 @@
"label": "Architecture",
"collapsible": false,
"items": [
{
"type": "doc",
"id": "developer/index",
"label": "Overview",
"customProps": {
"icon": "lu-book"
}
},
{
"type": "doc",
"id": "developer/retain",
"label": "Retain",
"customProps": {
"icon": "lu-brain"
}
},
{
"type": "doc",
"id": "developer/retrieval",
"label": "Recall",
"customProps": {
"icon": "lu-search"
}
},
{
"type": "doc",
"id": "developer/reflect",
"label": "Reflect",
"customProps": {
"icon": "lu-message"
}
},
{
"type": "doc",
"id": "developer/observations",
"label": "Observations",
"customProps": {
"icon": "lu-activity"
}
},
{
"type": "doc",
"id": "developer/multilingual",
"label": "Multilingual",
"customProps": {
"icon": "lu-languages"
}
},
{
"type": "doc",
"id": "developer/performance",
"label": "Performance",
"customProps": {
"icon": "lu-zap"
}
},
{
"type": "doc",
"id": "developer/storage",
"label": "Storage",
"customProps": {
"icon": "lu-database"
}
},
{
"type": "doc",
"id": "developer/rag-vs-hindsight",
"label": "RAG vs Memory",
"customProps": {
"icon": "lu-compare"
}
}
{ "type": "doc", "id": "developer/index", "label": "Overview", "customProps": { "icon": "lu-book" } },
{ "type": "doc", "id": "developer/retain", "label": "Retain", "customProps": { "icon": "lu-brain" } },
{ "type": "doc", "id": "developer/retrieval", "label": "Recall", "customProps": { "icon": "lu-search" } },
{ "type": "doc", "id": "developer/reflect", "label": "Reflect", "customProps": { "icon": "lu-message" } },
{ "type": "doc", "id": "developer/observations", "label": "Observations", "customProps": { "icon": "lu-activity" } },
{ "type": "doc", "id": "developer/multilingual", "label": "Multilingual", "customProps": { "icon": "lu-languages" } },
{ "type": "doc", "id": "developer/performance", "label": "Performance", "customProps": { "icon": "lu-zap" } },
{ "type": "doc", "id": "developer/storage", "label": "Storage", "customProps": { "icon": "lu-database" } },
{ "type": "doc", "id": "developer/rag-vs-hindsight", "label": "RAG vs Memory", "customProps": { "icon": "lu-compare" } }
]
},
{
@@ -84,87 +21,16 @@
"label": "API",
"collapsible": false,
"items": [
{
"type": "doc",
"id": "developer/api/quickstart",
"label": "Quick Start",
"customProps": {
"icon": "lu-rocket"
}
},
{
"type": "doc",
"id": "developer/api/retain",
"label": "Retain",
"customProps": {
"icon": "lu-brain"
}
},
{
"type": "doc",
"id": "developer/api/recall",
"label": "Recall",
"customProps": {
"icon": "lu-search"
}
},
{
"type": "doc",
"id": "developer/api/reflect",
"label": "Reflect",
"customProps": {
"icon": "lu-message"
}
},
{
"type": "doc",
"id": "developer/api/mental-models",
"label": "Mental Models",
"customProps": {
"icon": "lu-layers"
}
},
{
"type": "doc",
"id": "developer/api/memory-banks",
"label": "Memory Banks",
"customProps": {
"icon": "lu-memory"
}
},
{
"type": "doc",
"id": "developer/api/documents",
"label": "Documents",
"customProps": {
"icon": "lu-file"
}
},
{
"type": "doc",
"id": "developer/api/operations",
"label": "Operations",
"customProps": {
"icon": "lu-cpu"
}
},
{
"type": "doc",
"id": "developer/api/webhooks",
"label": "Webhooks",
"customProps": {
"icon": "lu-webhook"
}
},
{
"type": "link",
"href": "/api-reference",
"label": "API Reference",
"customProps": {
"icon": "lu-book-open",
"iconAfter": "lu-arrow-up-right"
}
}
{ "type": "doc", "id": "developer/api/quickstart", "label": "Quick Start", "customProps": { "icon": "lu-rocket" } },
{ "type": "doc", "id": "developer/api/retain", "label": "Retain", "customProps": { "icon": "lu-brain" } },
{ "type": "doc", "id": "developer/api/recall", "label": "Recall", "customProps": { "icon": "lu-search" } },
{ "type": "doc", "id": "developer/api/reflect", "label": "Reflect", "customProps": { "icon": "lu-message" } },
{ "type": "doc", "id": "developer/api/mental-models", "label": "Mental Models", "customProps": { "icon": "lu-layers" } },
{ "type": "doc", "id": "developer/api/memory-banks", "label": "Memory Banks", "customProps": { "icon": "lu-memory" } },
{ "type": "doc", "id": "developer/api/documents", "label": "Documents", "customProps": { "icon": "lu-file" } },
{ "type": "doc", "id": "developer/api/operations", "label": "Operations", "customProps": { "icon": "lu-cpu" } },
{ "type": "doc", "id": "developer/api/webhooks", "label": "Webhooks", "customProps": { "icon": "lu-webhook" } },
{ "type": "link", "href": "/api-reference", "label": "API Reference", "customProps": { "icon": "lu-book-open", "iconAfter": "lu-arrow-up-right" } }
]
},
{
@@ -172,46 +38,11 @@
"label": "Clients",
"collapsible": false,
"items": [
{
"type": "doc",
"id": "sdks/python",
"label": "Python",
"customProps": {
"icon": "si-python"
}
},
{
"type": "doc",
"id": "sdks/nodejs",
"label": "TypeScript",
"customProps": {
"icon": "/img/icons/typescript.png"
}
},
{
"type": "doc",
"id": "sdks/go",
"label": "Go",
"customProps": {
"icon": "si-go"
}
},
{
"type": "doc",
"id": "sdks/cli",
"label": "CLI",
"customProps": {
"icon": "lu-terminal"
}
},
{
"type": "doc",
"id": "sdks/embed",
"label": "Embedded Python",
"customProps": {
"icon": "/img/icons/package.svg"
}
}
{ "type": "doc", "id": "sdks/python", "label": "Python", "customProps": { "icon": "si-python" } },
{ "type": "doc", "id": "sdks/nodejs", "label": "TypeScript", "customProps": { "icon": "/img/icons/typescript.png" } },
{ "type": "doc", "id": "sdks/go", "label": "Go", "customProps": { "icon": "si-go" } },
{ "type": "doc", "id": "sdks/cli", "label": "CLI", "customProps": { "icon": "lu-terminal" } },
{ "type": "doc", "id": "sdks/embed", "label": "Embedded Python", "customProps": { "icon": "/img/icons/package.svg" } }
]
},
{
@@ -219,70 +50,14 @@
"label": "Integrations",
"collapsible": false,
"items": [
{
"type": "doc",
"id": "sdks/integrations/local-mcp",
"label": "Local MCP Server",
"customProps": {
"icon": "/img/icons/mcp.png"
}
},
{
"type": "doc",
"id": "sdks/integrations/litellm",
"label": "LiteLLM",
"customProps": {
"icon": "/img/icons/litellm.png"
}
},
{
"type": "doc",
"id": "sdks/integrations/openclaw",
"label": "OpenClaw",
"customProps": {
"icon": "/img/icons/openclaw.png"
}
},
{
"type": "doc",
"id": "sdks/integrations/ai-sdk",
"label": "Vercel AI SDK",
"customProps": {
"icon": "/img/icons/vercel.png"
}
},
{
"type": "doc",
"id": "sdks/integrations/chat",
"label": "Vercel Chat SDK",
"customProps": {
"icon": "/img/icons/vercel.png"
}
},
{
"type": "doc",
"id": "sdks/integrations/crewai",
"label": "CrewAI",
"customProps": {
"icon": "/img/icons/crewai.png"
}
},
{
"type": "doc",
"id": "sdks/integrations/pydantic-ai",
"label": "Pydantic AI",
"customProps": {
"icon": "/img/icons/pydanticai.png"
}
},
{
"type": "doc",
"id": "sdks/integrations/skills",
"label": "Skills",
"customProps": {
"icon": "/img/icons/skills.png"
}
}
{ "type": "doc", "id": "sdks/integrations/local-mcp", "label": "Local MCP Server", "customProps": { "icon": "/img/icons/mcp.png" } },
{ "type": "doc", "id": "sdks/integrations/litellm", "label": "LiteLLM", "customProps": { "icon": "/img/icons/litellm.png" } },
{ "type": "doc", "id": "sdks/integrations/openclaw", "label": "OpenClaw", "customProps": { "icon": "/img/icons/openclaw.png" } },
{ "type": "doc", "id": "sdks/integrations/ai-sdk", "label": "Vercel AI SDK", "customProps": { "icon": "/img/icons/vercel.png" } },
{ "type": "doc", "id": "sdks/integrations/chat", "label": "Vercel Chat SDK", "customProps": { "icon": "/img/icons/vercel.png" } },
{ "type": "doc", "id": "sdks/integrations/crewai", "label": "CrewAI", "customProps": { "icon": "/img/icons/crewai.png" } },
{ "type": "doc", "id": "sdks/integrations/pydantic-ai", "label": "Pydantic AI", "customProps": { "icon": "/img/icons/pydanticai.png" } },
{ "type": "doc", "id": "sdks/integrations/skills", "label": "Skills", "customProps": { "icon": "/img/icons/skills.png" } }
]
},
{
@@ -290,79 +65,15 @@
"label": "Hosting",
"collapsible": false,
"items": [
{
"type": "link",
"href": "https://ui.hindsight.vectorize.io/signup",
"label": "Cloud",
"customProps": {
"icon": "lu-cloud",
"iconAfter": "lu-arrow-up-right"
}
},
{
"type": "doc",
"id": "developer/installation",
"label": "Installation",
"customProps": {
"icon": "lu-package"
}
},
{
"type": "doc",
"id": "developer/services",
"label": "Services",
"customProps": {
"icon": "lu-server"
}
},
{
"type": "doc",
"id": "developer/configuration",
"label": "Configuration",
"customProps": {
"icon": "lu-settings"
}
},
{
"type": "doc",
"id": "developer/admin-cli",
"label": "Admin CLI",
"customProps": {
"icon": "lu-terminal"
}
},
{
"type": "doc",
"id": "developer/extensions",
"label": "Extensions",
"customProps": {
"icon": "lu-plug"
}
},
{
"type": "doc",
"id": "developer/models",
"label": "Models",
"customProps": {
"icon": "lu-cpu"
}
},
{
"type": "doc",
"id": "developer/monitoring",
"label": "Monitoring",
"customProps": {
"icon": "lu-activity"
}
},
{
"type": "doc",
"id": "developer/mcp-server",
"label": "MCP Server",
"customProps": {
"icon": "lu-network"
}
}
{ "type": "link", "href": "https://ui.hindsight.vectorize.io/signup", "label": "Cloud", "customProps": { "icon": "lu-cloud", "iconAfter": "lu-arrow-up-right" } },
{ "type": "doc", "id": "developer/installation", "label": "Installation", "customProps": { "icon": "lu-package" } },
{ "type": "doc", "id": "developer/services", "label": "Services", "customProps": { "icon": "lu-server" } },
{ "type": "doc", "id": "developer/configuration", "label": "Configuration", "customProps": { "icon": "lu-settings" } },
{ "type": "doc", "id": "developer/admin-cli", "label": "Admin CLI", "customProps": { "icon": "lu-terminal" } },
{ "type": "doc", "id": "developer/extensions", "label": "Extensions", "customProps": { "icon": "lu-plug" } },
{ "type": "doc", "id": "developer/models", "label": "Models", "customProps": { "icon": "lu-cpu" } },
{ "type": "doc", "id": "developer/monitoring", "label": "Monitoring", "customProps": { "icon": "lu-activity" } },
{ "type": "doc", "id": "developer/mcp-server", "label": "MCP Server", "customProps": { "icon": "lu-network" } }
]
},
{
@@ -370,69 +81,13 @@
"label": "More",
"collapsible": false,
"items": [
{
"type": "link",
"href": "/cookbook",
"label": "Cookbook",
"customProps": {
"icon": "lu-book",
"iconAfter": "lu-arrow-up-right"
}
},
{
"type": "link",
"href": "/blog",
"label": "Blog",
"customProps": {
"icon": "lu-rss",
"iconAfter": "lu-arrow-up-right"
}
},
{
"type": "link",
"href": "https://join.slack.com/t/hindsight-space/shared_invite/zt-3nhbm4w29-LeSJ5Ixi6j8PdiYOCPlOgg",
"label": "Community",
"customProps": {
"icon": "si-slack",
"iconAfter": "lu-arrow-up-right"
}
},
{
"type": "link",
"href": "https://github.com/vectorize-io/hindsight",
"label": "GitHub",
"customProps": {
"icon": "si-github",
"iconAfter": "lu-arrow-up-right"
}
},
{
"type": "link",
"href": "https://benchmarks.hindsight.vectorize.io/",
"label": "Benchmarks",
"customProps": {
"icon": "lu-chart-bar",
"iconAfter": "lu-arrow-up-right"
}
},
{
"type": "link",
"href": "https://benchmarks.hindsight.vectorize.io/",
"label": "Which Model Should I Use?",
"customProps": {
"icon": "lu-cpu",
"iconAfter": "lu-arrow-up-right"
}
},
{
"type": "link",
"href": "https://arxiv.org/abs/2512.12818",
"label": "Paper",
"customProps": {
"icon": "lu-file-text",
"iconAfter": "lu-arrow-up-right"
}
}
{ "type": "link", "href": "/cookbook", "label": "Cookbook", "customProps": { "icon": "lu-book", "iconAfter": "lu-arrow-up-right" } },
{ "type": "link", "href": "/blog", "label": "Blog", "customProps": { "icon": "lu-rss", "iconAfter": "lu-arrow-up-right" } },
{ "type": "link", "href": "https://join.slack.com/t/hindsight-space/shared_invite/zt-3nhbm4w29-LeSJ5Ixi6j8PdiYOCPlOgg", "label": "Community", "customProps": { "icon": "si-slack", "iconAfter": "lu-arrow-up-right" } },
{ "type": "link", "href": "https://github.com/vectorize-io/hindsight", "label": "GitHub", "customProps": { "icon": "si-github", "iconAfter": "lu-arrow-up-right" } },
{ "type": "link", "href": "https://benchmarks.hindsight.vectorize.io/", "label": "Benchmarks", "customProps": { "icon": "lu-chart-bar", "iconAfter": "lu-arrow-up-right" } },
{ "type": "link", "href": "https://benchmarks.hindsight.vectorize.io/", "label": "Which Model Should I Use?", "customProps": { "icon": "lu-cpu", "iconAfter": "lu-arrow-up-right" } },
{ "type": "link", "href": "https://arxiv.org/abs/2512.12818", "label": "Paper", "customProps": { "icon": "lu-file-text", "iconAfter": "lu-arrow-up-right" } }
]
}
]
+29
View File
@@ -7,6 +7,7 @@ set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
DOCS_DIR="$ROOT_DIR/hindsight-docs/docs"
PAGES_DIR="$ROOT_DIR/hindsight-docs/src/pages"
EXAMPLES_DIR="$ROOT_DIR/hindsight-docs/examples"
SKILL_DIR="$ROOT_DIR/skills/hindsight-docs"
REFS_DIR="$SKILL_DIR/references"
@@ -156,6 +157,24 @@ find "$DOCS_DIR" -type f \( -name "*.md" -o -name "*.mdx" \) | while read -r fil
process_file "$file"
done
# Process standalone pages (e.g. best-practices, faq) from src/pages/
print_info "Processing standalone pages..."
for page in best-practices faq; do
for ext in md mdx; do
src="$PAGES_DIR/$page.$ext"
if [ -f "$src" ]; then
dest="$REFS_DIR/$page.md"
mkdir -p "$(dirname "$dest")"
if [[ "$src" == *.mdx ]]; then
convert_mdx_to_md "$src" "$dest"
else
cp "$src" "$dest"
fi
print_info "Included page: $page.$ext"
fi
done
done
# Generate SKILL.md
print_info "Generating SKILL.md..."
cat > "$SKILL_DIR/SKILL.md" <<'EOF'
@@ -187,6 +206,8 @@ All documentation is in `references/` organized by category:
```
references/
├── best-practices.md # START HERE — missions, tags, formats, anti-patterns
├── faq.md # Common questions and decisions
├── developer/
│ ├── api/ # Core operations: retain, recall, reflect, memory banks
│ └── *.md # Architecture, configuration, deployment, performance
@@ -246,6 +267,14 @@ references/sdks/python.md
references/cookbook/recipes/per-user-memory.md
```
## Start Here: Best Practices
Before reading API docs, read the best practices guide. It covers practical rules for missions, tags, content format, observation scopes, and anti-patterns — the fastest way to integrate correctly.
```
references/best-practices.md
```
## Key Concepts
- **Memory Banks**: Isolated memory stores (one per user/agent)
+10
View File
@@ -26,6 +26,8 @@ All documentation is in `references/` organized by category:
```
references/
├── best-practices.md # START HERE — missions, tags, formats, anti-patterns
├── faq.md # Common questions and decisions
├── developer/
│ ├── api/ # Core operations: retain, recall, reflect, memory banks
│ └── *.md # Architecture, configuration, deployment, performance
@@ -85,6 +87,14 @@ references/sdks/python.md
references/cookbook/recipes/per-user-memory.md
```
## Start Here: Best Practices
Before reading API docs, read the best practices guide. It covers practical rules for missions, tags, content format, observation scopes, and anti-patterns — the fastest way to integrate correctly.
```
references/best-practices.md
```
## Key Concepts
- **Memory Banks**: Isolated memory stores (one per user/agent)
@@ -0,0 +1,519 @@
# Best Practices
Practical guidance for agents and developers integrating Hindsight memory into production systems.
---
## Core Concepts
### Memory Banks
A **memory bank** is an isolated memory store — the unit of separation between users, agents, or contexts. All operations (retain, recall, reflect) target a single bank. Banks do not share data.
- One bank per user is the most common pattern for multi-user applications
- One bank per agent is common for agent-specific long-term memory
- A shared bank with tags can work for cross-user analysis (see [Tags](#tags--naming-conventions))
Banks are auto-created on first use. Configure them before ingesting data to steer behavior.
---
### Taxonomy
| Operation | What it does | When to call it |
|-----------|-------------|-----------------|
| **Retain** | Ingests raw content (conversations, documents, notes). The LLM extracts facts, entities, and relationships — raw content is never stored verbatim. | After each conversation turn or session ends |
| **Recall** | Retrieves relevant memories using 4 parallel strategies: semantic search, BM25, graph traversal, and temporal ranking. Returns a ranked list of facts. | Before generating a response that benefits from past context |
| **Reflect** | Autonomous reasoning loop: searches memory, synthesizes an answer, and returns it directly. Uses mental models and observations hierarchically. | When you want Hindsight to answer a question, not just retrieve facts |
| **Observations** | Auto-synthesized knowledge patterns produced by the consolidation operation, which runs asynchronously after retain completes. Consolidate facts into durable insights (preferences, behavioral patterns, contradictions). | Triggered automatically after retain — not part of the retain call itself |
| **Mental Models** | Pre-computed reflect responses stored for common queries. Return instantly and consistently. | Create for repeated high-traffic queries or slowly-changing user profiles |
---
### Memory Types
Facts extracted during retain are classified into three types:
| Type | Description | Example |
|------|-------------|---------|
| `world` | General knowledge, external facts | "The Eiffel Tower is in Paris" |
| `experience` | Personal events, user-specific facts | "User moved to Berlin in 2024" |
| `observation` | Consolidated patterns synthesized from facts | "User consistently prefers async communication" |
Use `types` filtering in recall to target specific memory types.
---
## Bank Configuration
Configure a bank before first use to steer memory behavior for your domain. Misconfigured missions are the single biggest cause of low-quality memories.
### Writing Effective Missions
All three missions accept plain language. Be specific about your domain — vague missions produce vague results.
#### `retain_mission`
Injected into the fact extraction prompt. Tells the LLM what to extract and what to ignore.
| Quality | Example |
|---------|---------|
| **Good** | `Always extract technical decisions, API design choices, architectural trade-offs, blockers, and error messages. Ignore greetings, small talk, and scheduling logistics.` |
| **Good** | `Extract personal preferences, ongoing commitments, deadlines, health info, and relationship details. Ignore filler phrases and pleasantries.` |
| **Bad** | `Extract all information` — too vague, extracts noise |
| **Bad** | `Be helpful` — not an extraction directive |
**Tips:**
- List the fact *types* you want (preferences, decisions, errors, commitments)
- List what to *ignore* — this is as important as what to include
- Match the mission to your actual data type (conversations vs documents vs tickets)
#### `observations_mission`
Steers what patterns are synthesized during consolidation. Runs after retain.
```
Identify evolving preferences, recurring patterns, behavioral shifts, and contradictions
with prior knowledge. Focus on durable patterns — not transient states. Highlight when
user behavior contradicts previous observations.
```
**Tips:**
- Emphasize "durable patterns" to avoid ephemeral observation noise
- Mention contradiction detection explicitly if you need historical tracking
- Match scope to how often you expect patterns to change
#### `reflect_mission`
Sets the agent persona and reasoning frame for `reflect` operations.
| Use Case | Mission |
|----------|---------|
| Coding assistant | `You are a senior developer helping optimize the user's workflow. Always factor in past technical decisions, current project context, and stated preferences. Be direct and opinionated.` |
| Customer support | `You are a support agent with full context of this customer's history. Reference past tickets and resolutions where relevant. Be concise and solution-focused.` |
| Personal assistant | `You are a personal assistant who remembers everything important to the user. Personalize every response using what you know about their preferences, schedule, and ongoing projects.` |
| Medical assistant | `You are a health assistant. Reference the user's history accurately. Always recommend consulting a professional for medical decisions. Do not speculate.` |
---
### Disposition Traits
Dispositions affect `reflect` only (not `recall`). Scale 15.
| Trait | 1 | 5 |
|-------|---|---|
| `skepticism` | Trusts all memories at face value | Questions contradictions, flags uncertain info |
| `literalism` | Liberal interpretation, infers intent | Strict literal reading, no inference |
| `empathy` | Clinical, neutral tone | Warm, personal, emotionally aware |
**Common profiles:**
| Agent type | Skepticism | Literalism | Empathy |
|------------|------------|------------|---------|
| Code review | 4 | 5 | 1 |
| Customer support | 2 | 3 | 4 |
| Personal assistant | 2 | 2 | 4 |
| Medical assistant | 5 | 4 | 3 |
| Research assistant | 4 | 4 | 2 |
---
### Entity Labels
Define a controlled vocabulary for classification. The LLM will extract and normalize values to your defined set.
```json
{
"entity_labels": [
{
"key": "tech_stack",
"type": "multi-values",
"values": [
{"value": "python", "description": "Python programming language"},
{"value": "typescript", "description": "TypeScript / Node.js"},
{"value": "react", "description": "React frontend framework"}
]
},
{
"key": "priority",
"type": "value",
"tag": true,
"values": [
{"value": "high", "description": "Urgent or blocking"},
{"value": "low", "description": "Nice to have"}
]
}
]
}
```
- **`type: "value"`** — single value per entity (last write wins)
- **`type: "multi-values"`** — accumulates multiple values
- **`tag: true`** — extracted label values are also added as tags (enables filtering by entity value)
Use entity labels when you need consistent classification — domain-specific terms, status values, priority levels, engagement types.
---
## Retaining Data
### Content Format
Pass the richest representation available. Never pre-summarize.
| Format | Recommendation |
|--------|---------------|
| JSON conversation array | **Preferred** for conversations — preserves structure, roles, and relationships |
| Prefixed plain text | Acceptable — `[ISO-timestamp] role: text` per line |
| Markdown / HTML / raw text | Works for documents and notes |
| Pre-summarized text | **Avoid** — loses entity relationships, temporal markers, structural context |
**Conversation JSON (preferred):**
```json
[
{"role": "user", "content": "I'm using React for the frontend.", "timestamp": "2025-06-01T10:30:00Z"},
{"role": "assistant", "content": "Got it. What state management are you using?"},
{"role": "user", "content": "Zustand. We moved away from Redux last quarter."}
]
```
**Why not pre-summarize:** The LLM extracts facts, entities, and relationships from structure. A summary like "user uses React and Zustand" loses the temporal reference ("last quarter"), the entity relationship (React↔frontend, Redux↔migration), and the causal context (moved away from).
---
### The `context` Field
High-impact on extraction quality. Always set it. Describes the *nature and source* of the content.
```python
# Good — specific, descriptive
context="Customer support ticket #12345 from user Alice about a billing discrepancy"
context="Developer's architecture review session for the payments service"
context="User's onboarding form: stated goals, current tools, and team size"
context="Weekly standup notes: blockers, progress, and upcoming tasks"
# Bad — generic, adds no signal
context="some data"
context="conversation"
# Omitted entirely — extraction uses no context
```
---
### The `document_id` Field
Use for upsert behavior. Same `document_id` = delete previous version and reprocess.
**Rules:**
- Use stable, meaningful IDs (session ID, ticket ID, document UUID)
- Always use the same ID for a growing conversation — retain the full conversation with each new message
- Do NOT use random UUIDs per retain call — this creates duplicates
```python
# Good — stable session ID
client.retain(bank_id="user-alice", items=[{
"content": full_conversation,
"document_id": f"session-{session_id}",
}])
# Bad — new random ID every call = duplicates
client.retain(bank_id="user-alice", items=[{
"content": full_conversation,
"document_id": str(uuid.uuid4()), # ❌ creates a new document each time
}])
```
---
### The `timestamp` Field
Set whenever you have temporal context. Enables temporal retrieval strategies.
- ISO 8601 format: `"2025-06-01T10:32:00Z"`
- For conversations: set to when the conversation *started*
- Omitting it disables temporal ranking entirely
---
### Tags: Naming Conventions
Tags scope visibility. A memory tagged `user:alice` is only returned for recall/reflect calls that include `user:alice` in their `tags` filter (with strict matching).
**Standard naming conventions:**
| Pattern | Example | Use for |
|---------|---------|---------|
| `user:<id>` | `user:alice`, `user:u_123` | Per-user isolation |
| `session:<id>` | `session:s_abc` | Session-scoped memories |
| `team:<name>` | `team:engineering` | Shared team knowledge |
| `topic:<name>` | `topic:billing`, `topic:technical` | Domain filtering |
| `scope:<name>` | `scope:private`, `scope:public` | Visibility tiers |
**Multi-tenant minimum:** Every retain for user data must include at least `user:<id>`. Omitting it makes the memory globally visible.
```python
# Multi-tenant retain — always tag with user ID
items=[{
"content": conversation,
"tags": ["user:alice", "session:s_abc", "topic:billing"],
"document_id": f"session-{session_id}",
}]
```
---
### Metadata Schema
Use for source tracking and downstream linking. **Not filterable** — use tags for filtering.
```python
# Source tracking
metadata={"source": "slack", "channel": "#engineering", "thread_id": "T123456"}
# Ticket linking
metadata={"ticket_id": "JIRA-123", "priority": "high", "reporter": "alice"}
# Document provenance
metadata={"url": "https://...", "section": "pricing-faq", "version": "2025-Q1"}
```
Metadata is returned with every recalled memory — use it to link memories back to source systems for UI display, deep-linking, or audit trails.
---
### Observation Scopes
Controls which tag combinations get their own observation pass.
| Value | Behavior | When to use |
|-------|----------|------------|
| `"combined"` | One pass with all tags together | Default — single-user banks, general use |
| `"per_tag"` | One pass per tag independently | Users should have isolated behavioral observations |
| `"all_combinations"` | All possible subsets of tags | Complex multi-dimensional analysis (expensive) |
| Custom list | Explicit scope list | Precise multi-tenant control |
**Custom scope example (recommended for multi-tenant):**
```python
# Observations scoped to: user-level, team-level, and combined
observation_scopes=[
["user:alice"],
["team:engineering"],
["user:alice", "team:engineering"],
]
```
---
### Sync vs Async
| Mode | When to use |
|------|-------------|
| `async_=False` (default) | When you need confirmation before proceeding |
| `async_=True` | End-of-turn or end-of-session retain; user-facing flows where latency matters |
Do not retain and recall in the same turn — retain is a write operation and the extracted memories will not be available immediately.
---
## Recalling Memories
### Budget Selection
| Budget | Latency | Use when |
|--------|---------|----------|
| `low` | 50100ms | Simple fact lookups, single-hop questions |
| `mid` | 100300ms | Multi-hop reasoning, relationship queries *(default)* |
| `high` | 300500ms | Deep exploration, complex cross-domain patterns |
Default to `mid`. Use `low` for high-frequency agent loops. Reserve `high` for explicit "deep recall" user-triggered flows.
---
### Tag Filtering Modes
| Mode | Includes untagged? | Condition |
|------|-------------------|-----------|
| `any` *(default)* | Yes | At least one tag matches, OR untagged |
| `all` | Yes | All specified tags present, OR untagged |
| `any_strict` | No | At least one tag matches |
| `all_strict` | No | All specified tags present |
**Decision guide:**
- Shared global knowledge + per-user: `tags=["user:alice"], tags_match="any"` — returns Alice's memories and untagged global memories
- Fully partitioned (no leakage): `tags=["user:alice"], tags_match="any_strict"` — Alice's memories only
- Multi-condition AND: `tags=["user:alice", "topic:billing"], tags_match="all_strict"` — only where both tags present
**`tag_groups` for complex filters:**
Tag groups use a tree structure with `and`/`or`/`not` compound nodes and `{"tags": [...], "match": "..."}` leaf nodes.
```python
# Alice's billing memories OR shared billing memories (no user tag)
recall(
query="...",
tag_groups=[
{"or": [
{"tags": ["user:alice", "topic:billing"], "match": "all_strict"},
{"and": [
{"tags": ["topic:billing"], "match": "any_strict"},
{"not": {"tags": ["user:alice"], "match": "any_strict"}},
]},
]}
]
)
```
---
### `include` Options
| Option | Default | Enable when |
|--------|---------|-------------|
| `include.entities` | Enabled | — (leave on; provides entity context for graph traversal) |
| `include.chunks` | Disabled | Agent needs exact wording or source quotation |
| `include.source_facts` | Disabled | Tracing observation provenance for auditing |
---
### `types` Filtering
| Value | Returns |
|-------|---------|
| *(not set)* | All types |
| `["observation"]` | Consolidated patterns only — faster for high-level questions |
| `["world", "experience"]` | Raw facts only — for ground-truth or citation-sensitive queries |
---
### `query_timestamp`
Set for time-sensitive queries. Anchors temporal ranking to a specific point in time.
```python
# "What was the team working on in January?"
recall(query="team priorities", query_timestamp="2025-01-31T23:59:59Z")
# Current context (most common)
recall(query="user preferences", query_timestamp=datetime.utcnow().isoformat() + "Z")
```
---
## Reflecting
### Recall vs Reflect
| Use `recall` when | Use `reflect` when |
|-------------------|--------------------|
| Agent will reason over facts itself | You want Hindsight to reason and return an answer |
| You need raw citations | You need a synthesized response |
| You're building a RAG pipeline | You want an autonomous multi-step search loop |
| Latency is critical | Response quality matters more than latency |
| You need precise fact counts | You need a contextual, nuanced answer |
---
### `response_schema`
Use when you need structured output for programmatic consumption.
```python
reflect(
query="What are the user's top 3 technical preferences?",
response_schema={
"type": "object",
"properties": {
"preferences": {
"type": "array",
"items": {"type": "string"},
"maxItems": 3
},
"confidence": {"type": "number", "minimum": 0, "maximum": 1}
},
"required": ["preferences", "confidence"]
}
)
# Returns: result.structured_output["preferences"], result.structured_output["confidence"]
```
---
### Auditing and Debugging
| Option | Purpose |
|--------|---------|
| `include.facts=True` | Exposes which memories and mental models were used (for transparency/auditing) |
| `include.tool_calls=True` | Full execution trace of the internal search loop (for debugging) |
Enable `include.facts` in production for audit trails. Enable `include.tool_calls` only during development.
---
## Mental Models
Mental models are pre-computed `reflect` responses stored for common queries. They return instantly and consistently.
### When to Create
- Common repeated queries that should return consistent answers
- High-traffic agents that need sub-100ms responses
- User profiles or personas read on every request
- Knowledge summaries reviewed or approved by humans
- Cross-session state that changes slowly (preferences, skills, background)
### Tag Strategy
Tags on a mental model filter BOTH which memories are used to build it AND which recall/reflect calls can see it.
```python
# Per-user mental model — uses Alice's memories (all_strict applied automatically during refresh)
create_mental_model(
bank_id="shared-bank",
name="Alice's Technical Profile",
source_query="Summarize Alice's technical background, preferred stack, and current projects",
tags=["user:alice"],
)
# Global mental model — uses all memories, visible to everyone
create_mental_model(
bank_id="shared-bank",
name="Team Engineering Standards",
source_query="What are the team's agreed engineering standards and conventions?",
# No tags — reads all memories, visible to all
)
```
### Refresh Strategy
| Trigger | When to use |
|---------|------------|
| Manual via API | After significant data updates or review cycles |
| `trigger={"refresh_after_consolidation": True}` | When observations update frequently and the model should stay current |
Create narrow, scoped models — one per knowledge dimension. A mental model titled "Everything about the user" is as useful as none.
**Model granularity examples for a personal assistant:**
- "User Profile" — demographics, preferences, stated goals
- "Current Projects" — active work, deadlines, blockers
- "Technical Stack" — languages, tools, frameworks used
- "Communication Style" — formality preferences, response length preferences
---
## Anti-patterns
| Anti-pattern | Problem | Fix |
|-------------|---------|-----|
| Pre-summarizing before retain | Loses entity relationships, temporal markers, structural context | Retain raw content; Hindsight extracts facts |
| Using random UUIDs as `document_id` | Creates duplicate documents on every retain | Use stable session/ticket/document IDs |
| Omitting the `context` field | Reduces extraction quality significantly | Always describe what kind of data this is |
| Using `metadata` for filtering | Metadata is not filterable | Use `tags` for anything you'll filter on |
| Vague or generic missions | Generic extraction = noisy, low-value memories | Be specific about domain, data type, what to ignore |
| `tags_match="any"` for multi-tenant banks | Leaks memories across users | Use `any_strict` or `all_strict` for user-partitioned data |
| Retaining and recalling in the same request | Retained memories not yet indexed | Retain end-of-turn; recall at the start of next turn |
| One mental model for everything | Low accuracy, slow refresh, hard to scope | Create one model per knowledge dimension |
| `high` budget for every recall | Expensive, slow, usually unnecessary | Use `low` for simple lookups, `mid` default |
| Missing `timestamp` on retain | Disables temporal retrieval strategies | Always set from actual content timestamps |
@@ -414,6 +414,9 @@ Supported OpenAI embedding dimensions:
| `HINDSIGHT_API_RERANKER_LOCAL_MODEL` | Model for local provider | `cross-encoder/ms-marco-MiniLM-L-6-v2` |
| `HINDSIGHT_API_RERANKER_LOCAL_MAX_CONCURRENT` | Max concurrent local reranking (prevents CPU thrashing under load) | `4` |
| `HINDSIGHT_API_RERANKER_LOCAL_TRUST_REMOTE_CODE` | Allow loading models with custom code (security risk, disabled by default) | `false` |
| `HINDSIGHT_API_RERANKER_LOCAL_FP16` | Half-precision (FP16) inference for the local reranker. 2736% faster on MPS; quality-identical. Disabled by default to avoid regressions on non-MPS deployments — some CPUs lack native FP16 support. | `false` |
| `HINDSIGHT_API_RERANKER_LOCAL_BUCKET_BATCHING` | Sort pairs by token length before batching to reduce padding waste. 3654% faster across models; quality-identical by construction. | `false` |
| `HINDSIGHT_API_RERANKER_LOCAL_BATCH_SIZE` | Batch size for local reranker `predict()`. Optimal value varies by hardware and model (smaller batches can outperform larger ones on MPS). | `32` |
| `HINDSIGHT_API_RERANKER_TEI_URL` | TEI server URL | - |
| `HINDSIGHT_API_RERANKER_TEI_BATCH_SIZE` | Batch size for TEI reranking | `128` |
| `HINDSIGHT_API_RERANKER_TEI_MAX_CONCURRENT` | Max concurrent TEI reranking requests | `8` |
+272
View File
@@ -0,0 +1,272 @@
# Frequently Asked Questions
### What is Hindsight and how does it differ from RAG?
Hindsight is an agent memory system that provides long-term memory for AI agents using biomimetic data structures. Unlike traditional RAG (Retrieval-Augmented Generation), Hindsight:
- **Stores structured facts** instead of raw document chunks
- **Builds mental models** that consolidate knowledge over time
- **Uses graph-based relationships** between entities and concepts
- **Supports temporal reasoning** with time-aware retrieval
- **Enables disposition-aware reflection** for nuanced reasoning
For a detailed comparison, see [RAG vs Memory](/developer/rag-vs-hindsight).
---
### Why use Hindsight instead of other solutions?
Hindsight is purpose-built for agent memory with unique advantages:
- **State-of-the-art accuracy**: Ranked #1 LongMemEval benchmarks for agent memory (see [details](https://benchmarks.hindsight.vectorize.io/))
- **Built on proven technology**: PostgreSQL - battle-tested, reliable, and widely understood
- **Cloud-native architecture**: Designed for modern cloud deployments with horizontal scalability
- **Flexible deployment**: Self-host or use Hindsight Cloud - works with any LLM provider
- **True long-term memory**: Builds mental models that consolidate knowledge over time, not just retrieval
- **Graph-based reasoning**: Understands relationships between entities and concepts for richer context
- **Production-ready**: Scales to millions of memories with 50-500ms recall latency
- **Developer-friendly**: Simple APIs (retain, recall, reflect), SDKs for Python/TypeScript/Go/Rust, integrations with LiteLLM/Vercel AI SDK
Unlike vector databases (just search) or RAG systems (document retrieval), Hindsight provides **living memory** that evolves with your users.
---
### Which clients and languages are supported?
<ClientsGrid />
---
### Which integrations are supported?
<IntegrationsGrid />
---
### Which LLM providers are supported?
<LLMProvidersGrid />
See [Models](/developer/models) for the full list of supported providers, recommended models, and configuration examples.
---
### Which model should I use with Hindsight?
The **[Model Leaderboard](https://benchmarks.hindsight.vectorize.io/)** benchmarks models across accuracy, speed, cost, and reliability for retain, reflect, and observation consolidation — it's the best place to find the right trade-off for your use case.
[![Model Leaderboard](/img/leaderboard.png)](https://benchmarks.hindsight.vectorize.io/)
See [Models](/developer/models) for the full list of supported and tested models, provider defaults, and configuration examples.
---
### Do I need to host my own infrastructure?
No! You have two options:
1. **Hindsight Cloud** - Fully managed service at [ui.hindsight.vectorize.io](https://ui.hindsight.vectorize.io)
2. **Self-hosted** - Deploy on your own infrastructure using Docker or direct installation
See [Installation](/developer/installation) for self-hosting instructions.
---
### What are the minimum system requirements for self-hosting?
For running the Hindsight API server locally:
- Python 3.11+
- 4GB RAM minimum (8GB recommended for production)
- LLM API key (OpenAI, Anthropic, etc.) or local LLM setup
See [Installation](/developer/installation) for setup instructions.
---
### How do I isolate user data?
A **memory bank** is an isolated memory store (like a "brain") that contains its own memories, entities, relationships, and optional disposition traits (skepticism, literalism, empathy). Banks are completely isolated from each other with no data leakage.
There are two approaches for multi-user applications:
**1. Per-user memory banks** (recommended for most use cases)
- Create one bank per user (e.g., `bank_id="user-123"`)
- Easiest setup and strongest data isolation
- Perfect for per-user queries and personalization
- Each bank can have unique disposition traits and background context
- **Limitation**: Cannot perform cross-user analysis (e.g., "What is the most mentioned topic across all users?")
**2. Single bank with tags** (for applications needing aggregated insights)
- Use one bank for the entire application
- Tag memories with user identifiers during retain (e.g., `tags={"user_id": "user-123"}`)
- Filter by tags during recall/reflect for per-user queries
- **Advantage**: Enables both per-user AND cross-user queries (e.g., analyze specific users or aggregate across all users)
Choose per-user banks for simplicity and privacy, or single bank with tags if you need holistic reasoning across users. See [Memory Banks](/developer/api/memory-banks) for management details.
---
### What's the difference between retain, recall, and reflect?
Hindsight has three core operations:
- **Retain**: Store data (facts, entities, relationships)
- **Recall**: Search and retrieve raw memory data based on a query
- **Reflect**: Use an AI agent to answer a query using retrieved memories
See [Operations](/developer/api/operations) for API details.
---
### When should I use recall vs reflect?
**Use recall when:**
- You want raw facts to feed into your own reasoning or prompt
- You need maximum control over how memories are interpreted
- You're doing simple fact lookup (e.g., "What did Alice say about X?")
- Latency is critical — recall is significantly faster (50-500ms vs 1-10s)
- You want to build your own answer synthesis layer on top of retrieved memories
**Use reflect when:**
- You want a ready-to-use answer generated from memories (no extra LLM call needed)
- You need disposition-aware responses shaped by the bank's personality traits (skepticism, literalism, empathy)
- The query requires multi-step reasoning across facts, observations, and mental models
- You need structured output (via `response_schema`) from memory-grounded reasoning
- You want citations — reflect returns which memories, mental models, and directives informed the answer
**Key difference**: Recall returns data; reflect returns an answer. Recall gives you raw materials, reflect does the reasoning for you using the bank's disposition and an autonomous search loop.
```
recall("What food does Alice like?")
→ ["Alice loves sushi", "Alice prefers vegetarian options"] # raw facts
reflect("What should I order for Alice?")
→ "I'd recommend a vegetarian sushi platter — Alice loves sushi and prefers vegetarian options." # grounded answer
```
See [Recall](/developer/api/recall) and [Reflect](/developer/reflect) for full API details.
---
### When should I use mental models?
**Mental models** are consolidated knowledge patterns synthesized from individual facts over time. Use them when you need:
- Higher-level understanding beyond raw facts (e.g., "User prefers functional programming patterns")
- Long-term behavioral patterns (e.g., "Customer is price-sensitive but values quality")
- Context for AI agent reasoning during **reflect** operations
Mental models are automatically built during retain and used by reflect to provide richer, more contextual responses. See [Mental Models](/developer/api/mental-models).
---
### What's the typical latency for recall operations?
Typical latencies:
- **Without reranking**: 50-100ms
- **With reranking**: 200-500ms (depends on reranker model and installation)
See [Performance](/developer/performance) for tuning options.
---
### Does Hindsight support metadata filtering?
Yes — through **Tags**. Tags are string labels attached to memories at retain time and used as a visibility filter at recall/reflect time. Only memories tagged with a matching value are returned.
```python
# Tag memories at retain time
client.retain(bank_id="my-bank", items=[{
"content": "...",
"tags": ["user:alice"],
}])
# Filter by tag at recall time
client.recall(bank_id="my-bank", query="...", tags=["user:alice"])
```
See [Tags](/developer/api/retain#tags-and-document_tags) for full details including document-level tagging.
**What about filtering by entities?**
Entities (people, places, concepts) extracted from memories are stored in the knowledge graph and drive graph-based retrieval — so querying "tell me about Alice" will naturally surface Alice-related memories without any manual filtering.
If you need explicit tag-based filtering on entity-like values, use **entity labels** with `tag: true`. Entity labels let you define a controlled vocabulary of `key:value` classifiers (e.g. `user:alice`, `topic:algebra`) extracted at retain time. Setting `tag: true` on a label group automatically writes each extracted label as a tag on the memory unit, making them available for standard `tags`/`tags_match` filtering:
```python
# Bank config: entity label group with tag: true
{
"entity_labels": [{
"key": "user",
"type": "text",
"tag": True,
"description": "The user this memory belongs to"
}]
}
# The label "user:alice" is extracted and also written as a tag
# Filter at recall time using the standard tags parameter
client.recall(bank_id="my-bank", query="...", tags=["user:alice"])
```
See [Entity Labels](/developer/retain#entity-labels) for configuration details.
**What about document `metadata`?**
Document metadata (the `metadata` key-value pairs on a retain item) serves a different purpose. It is:
- **Included in the fact extraction prompt**, so the LLM can use it as additional context when extracting facts — for example, knowing the document title or source can improve accuracy.
- **Returned with every recalled memory** as-is, so your application can link memories back to source systems (e.g. a URL, thread ID, or ticket number) without extra lookups.
Metadata is not a filter — use tags when you need recall to be scoped to a subset of documents.
---
### What is the recommended format for retaining conversations?
Pass the **entire conversation as a single document** and upsert it as the conversation grows — Hindsight chunks it automatically, so you don't need to split it yourself.
**Preferred format: JSON array**
```json
[
{"role": "user", "content": "I moved to Berlin last month."},
{"role": "assistant", "content": "How are you finding it?"},
{"role": "user", "content": "Love it, especially the food scene."}
]
```
Hindsight has internal chunking optimizations for the JSON array format, since it's the most common conversation shape.
**Alternative: prefixed plain text**
```
[2025-06-01T10:32:00Z] user: I moved to Berlin last month.
[2025-06-01T10:32:05Z] assistant: How are you finding it?
[2025-06-01T10:32:20Z] user: Love it, especially the food scene.
```
Adding a username and timestamp prefix to each message improves extraction quality — the LLM uses those signals to attribute facts correctly and reason about timing.
**Use a stable document ID to upsert:**
```python
await client.retain(
bank_id="my-bank",
documents=[{
"id": "chat-session-abc123", # stable ID enables upsert
"content": conversation, # full conversation so far
}]
)
```
Re-retaining with the same `id` replaces the old document and its facts, so you won't accumulate duplicates as the conversation grows.
**Don't pre-summarize or pre-extract facts.** Hindsight does this automatically and needs the full conversation for context — a message like "yes, exactly" or "I'll go with option 2" is meaningless without the surrounding exchange.
---
## Still have questions?
Join our [Slack community](https://join.slack.com/t/hindsight-space/shared_invite/zt-3nhbm4w29-LeSJ5Ixi6j8PdiYOCPlOgg) or report issues on [GitHub](https://github.com/vectorize-io/hindsight/issues).