From f68e2e28519b3eb7eef64468acfd509762fcb58c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Boschi?= Date: Tue, 17 Mar 2026 14:03:38 +0100 Subject: [PATCH] docs: add Best Practices unversioned page (#598) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 * 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 * fix: remove leftover merge conflict markers in DocSidebarItem Link * fix: add missing lu-star, lu-circle-help, lu-file-text icons to sidebar map * fix: remove duplicate LuFileText import * fix: add Best Practices and FAQ to Resources navbar dropdown * docs: hide right TOC and add manual TOC to best practices page * docs: hide right TOC and add manual TOC to FAQ page * fix: add lu-star icon to navbar item icon map * fix: correct broken anchor in best practices TOC --- hindsight-docs/docusaurus.config.ts | 10 + hindsight-docs/sidebars.ts | 19 + hindsight-docs/src/pages/best-practices.mdx | 533 ++++++++++++++++++ hindsight-docs/src/pages/faq.mdx | 35 +- .../src/theme/DocSidebarItem/Link/index.tsx | 5 +- .../NavbarItem/DefaultNavbarItem/index.tsx | 3 +- .../version-0.4-sidebars.json | 441 ++------------- scripts/generate-docs-skill.sh | 29 + skills/hindsight-docs/SKILL.md | 10 + .../references/best-practices.md | 519 +++++++++++++++++ .../references/developer/configuration.md | 3 + skills/hindsight-docs/references/faq.md | 272 +++++++++ 12 files changed, 1466 insertions(+), 413 deletions(-) create mode 100644 hindsight-docs/src/pages/best-practices.mdx create mode 100644 skills/hindsight-docs/references/best-practices.md create mode 100644 skills/hindsight-docs/references/faq.md diff --git a/hindsight-docs/docusaurus.config.ts b/hindsight-docs/docusaurus.config.ts index 344e056d..d702fa35 100644 --- a/hindsight-docs/docusaurus.config.ts +++ b/hindsight-docs/docusaurus.config.ts @@ -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', diff --git a/hindsight-docs/sidebars.ts b/hindsight-docs/sidebars.ts index d08770b7..24c86974 100644 --- a/hindsight-docs/sidebars.ts +++ b/hindsight-docs/sidebars.ts @@ -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', diff --git a/hindsight-docs/src/pages/best-practices.mdx b/hindsight-docs/src/pages/best-practices.mdx new file mode 100644 index 00000000..4a6f2c50 --- /dev/null +++ b/hindsight-docs/src/pages/best-practices.mdx @@ -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 1–5. + +| 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:` | `user:alice`, `user:u_123` | Per-user isolation | +| `session:` | `session:s_abc` | Session-scoped memories | +| `team:` | `team:engineering` | Shared team knowledge | +| `topic:` | `topic:billing`, `topic:technical` | Domain filtering | +| `scope:` | `scope:private`, `scope:public` | Visibility tiers | + +**Multi-tenant minimum:** Every retain for user data must include at least `user:`. 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` | 50–100ms | Simple fact lookups, single-hop questions | +| `mid` | 100–300ms | Multi-hop reasoning, relationship queries *(default)* | +| `high` | 300–500ms | 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 | diff --git a/hindsight-docs/src/pages/faq.mdx b/hindsight-docs/src/pages/faq.mdx index f621634e..66cbf2a5 100644 --- a/hindsight-docs/src/pages/faq.mdx +++ b/hindsight-docs/src/pages/faq.mdx @@ -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 -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? diff --git a/hindsight-docs/src/theme/DocSidebarItem/Link/index.tsx b/hindsight-docs/src/theme/DocSidebarItem/Link/index.tsx index 6127e347..b0509f1d 100644 --- a/hindsight-docs/src/theme/DocSidebarItem/Link/index.tsx +++ b/hindsight-docs/src/theme/DocSidebarItem/Link/index.tsx @@ -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 = { 'lu-rss': LuRss, 'lu-cloud': LuCloud, 'lu-message-circle': LuMessageCircle, + 'lu-star': LuStar, + 'lu-circle-help': LuCircleHelp, + 'lu-file-text': LuFileText, }; type Props = WrapperProps; diff --git a/hindsight-docs/src/theme/NavbarItem/DefaultNavbarItem/index.tsx b/hindsight-docs/src/theme/NavbarItem/DefaultNavbarItem/index.tsx index 21dd370a..d4a74d3f 100644 --- a/hindsight-docs/src/theme/NavbarItem/DefaultNavbarItem/index.tsx +++ b/hindsight-docs/src/theme/NavbarItem/DefaultNavbarItem/index.tsx @@ -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 = { 'lu-chart-bar': LuChartBar, 'lu-cpu': LuCpu, 'lu-file-text': LuFileText, + 'lu-star': LuStar, 'si-github': SiGithub, 'si-slack': SiSlack, }; diff --git a/hindsight-docs/versioned_sidebars/version-0.4-sidebars.json b/hindsight-docs/versioned_sidebars/version-0.4-sidebars.json index 6329cd25..57feb953 100644 --- a/hindsight-docs/versioned_sidebars/version-0.4-sidebars.json +++ b/hindsight-docs/versioned_sidebars/version-0.4-sidebars.json @@ -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" } } ] } ] diff --git a/scripts/generate-docs-skill.sh b/scripts/generate-docs-skill.sh index 6d45ed5f..05a241ca 100755 --- a/scripts/generate-docs-skill.sh +++ b/scripts/generate-docs-skill.sh @@ -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) diff --git a/skills/hindsight-docs/SKILL.md b/skills/hindsight-docs/SKILL.md index 10abdf9f..6f30d490 100644 --- a/skills/hindsight-docs/SKILL.md +++ b/skills/hindsight-docs/SKILL.md @@ -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) diff --git a/skills/hindsight-docs/references/best-practices.md b/skills/hindsight-docs/references/best-practices.md new file mode 100644 index 00000000..eb3dd26d --- /dev/null +++ b/skills/hindsight-docs/references/best-practices.md @@ -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 1–5. + +| 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:` | `user:alice`, `user:u_123` | Per-user isolation | +| `session:` | `session:s_abc` | Session-scoped memories | +| `team:` | `team:engineering` | Shared team knowledge | +| `topic:` | `topic:billing`, `topic:technical` | Domain filtering | +| `scope:` | `scope:private`, `scope:public` | Visibility tiers | + +**Multi-tenant minimum:** Every retain for user data must include at least `user:`. 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` | 50–100ms | Simple fact lookups, single-hop questions | +| `mid` | 100–300ms | Multi-hop reasoning, relationship queries *(default)* | +| `high` | 300–500ms | 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 | diff --git a/skills/hindsight-docs/references/developer/configuration.md b/skills/hindsight-docs/references/developer/configuration.md index 012746b7..5fd4d939 100644 --- a/skills/hindsight-docs/references/developer/configuration.md +++ b/skills/hindsight-docs/references/developer/configuration.md @@ -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. 27–36% 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. 36–54% 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` | diff --git a/skills/hindsight-docs/references/faq.md b/skills/hindsight-docs/references/faq.md new file mode 100644 index 00000000..ab4cfef9 --- /dev/null +++ b/skills/hindsight-docs/references/faq.md @@ -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? + + + +--- + +### Which integrations are supported? + + + +--- + +### Which LLM providers are supported? + + + +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).