Skip to main content

Best Practices

Expert tips and patterns for effective memory management with Trix.

Overview

This guide consolidates proven patterns and recommendations for building production-ready applications with Trix. These practices come from real-world deployments and will help you avoid common pitfalls while maximizing performance and reliability.

Writing Effective Memory Content

The quality of your memories directly impacts retrieval quality. Well-structured content retrieves better.

Avoid

"dark mode, notifications off"

"likes coffee"

"Python"

Prefer

"User prefers dark mode and has disabled notifications"

"User drinks coffee every morning, prefers medium roast"

"User is proficient in Python and prefers it for data analysis"

  • Write complete sentences - Fragments lose context and match poorly
  • Include the subject - "User prefers X" retrieves better than just "X"
  • Be specific - Vague content matches too many queries
  • Add context - Include why or when, not just what

Deduplicating Memories

Over time, similar memories accumulate. Deduplicate to keep your memory store clean and retrieval fast.

async function storeWithDedup(content: string, spaceId: string) {
  // Check for existing similar memories
  const existing = await client.memories.list({
    q: content,
    spaceId,
    limit: 1
  });

  // If very similar memory exists, skip or update
  if (existing.data.length > 0 && existing.data[0].score > 0.95) {
    console.log('Similar memory exists, skipping');
    return existing.data[0];
  }

  // No duplicate, create new memory
  return client.memories.create({ content, spaceId });
}

Run deduplication during off-peak hours. For large spaces, process in batches to avoid timeouts.

Tagging Strategy

Tags enable efficient filtering. Design a consistent tagging taxonomy upfront.

// Define your tag taxonomy
const TAG_TAXONOMY = {
  category: ['preference', 'fact', 'interaction', 'feedback'],
  source: ['conversation', 'import', 'system', 'user-provided'],
  confidence: ['verified', 'inferred', 'uncertain'],
  domain: ['work', 'personal', 'health', 'finance']
};

function buildTags(options: TagOptions): string[] {
  const tags: string[] = [];
  if (options.category && TAG_TAXONOMY.category.includes(options.category)) {
    tags.push(`cat:${options.category}`);
  }
  if (options.source && TAG_TAXONOMY.source.includes(options.source)) {
    tags.push(`src:${options.source}`);
  }
  if (options.confidence && TAG_TAXONOMY.confidence.includes(options.confidence)) {
    tags.push(`conf:${options.confidence}`);
  }
  return tags;
}
  • Use prefixes - cat:, src: help organize tags by type
  • Keep tags lowercase - Consistent casing prevents duplicates
  • Limit tag count - 3-5 tags per memory is usually sufficient
  • Document your taxonomy - Share with your team for consistency

Metadata Best Practices

Metadata stores structured information for filtering and auditing. Use it for data that doesn't belong in content.

await client.memories.create({
  content: 'User completed the onboarding tutorial',
  spaceId,
  metadata: {
    createdBy: 'system',
    extractedFrom: 'session_abc123',
    eventType: 'onboarding',
    step: 5,
    completed: true,
    version: 1,
    supersedes: null
  }
});

const onboardingMemories = await client.memories.list({
  spaceId,
  metadata: { eventType: 'onboarding', completed: true }
});

Don't store PII in metadata. Metadata is easier to accidentally expose in logs than content.

Search Optimization

Optimize queries for faster, more relevant results.

function optimizeQuery(rawQuery: string): string {
  const fillers = ['the', 'a', 'an', 'is', 'are', 'was', 'were'];
  let query = rawQuery.toLowerCase();
  for (const filler of fillers) {
    query = query.replace(new RegExp(`\\b${filler}\\b`, 'g'), '');
  }
  return query.trim().replace(/\s+/g, ' ');
}

async function smartSearch(query: string, spaceId: string, context: SearchContext) {
  let limit = 5;
  if (context.needsComprehensive) limit = 20;
  else if (context.quickLookup) limit = 1;
  return client.memories.list({ q: optimizeQuery(query), spaceId, limit });
}
  • Use specific queries - "user's coffee preference" beats "coffee"
  • Request only needed fields - Reduce payload size when possible
  • Set appropriate limits - Don't fetch 100 when you need 5
  • Cache frequent queries - Reduce API calls for common lookups

Error Handling

Robust error handling keeps your application reliable when issues occur.

import { Trix, TrixError, RateLimitError, NotFoundError } from '@trixdb/client';

async function safeMemoryOperation<T>(
  operation: () => Promise<T>,
  fallback: T
): Promise<T> {
  try {
    return await operation();
  } catch (error) {
    if (error instanceof RateLimitError) {
      const retryAfter = error.retryAfter || 1000;
      await sleep(retryAfter);
      return operation();
    }
    if (error instanceof NotFoundError) {
      console.warn('Resource not found:', error.message);
      return fallback;
    }
    if (error instanceof TrixError) {
      console.error('Trix API error:', error.code, error.message);
      return fallback;
    }
    throw error;
  }
}

Always have a fallback strategy. Your AI should still work, even if memory retrieval fails temporarily.

Async Processing

Store memories asynchronously to avoid adding latency to user interactions.

class AsyncMemoryService {
  private queue: MemoryJob[] = [];
  private processing = false;

  enqueue(content: string, spaceId: string, metadata?: object) {
    this.queue.push({ content, spaceId, metadata, timestamp: Date.now() });
    this.processQueue();
  }

  private async processQueue() {
    if (this.processing || this.queue.length === 0) return;
    this.processing = true;
    while (this.queue.length > 0) {
      const batch = this.queue.splice(0, 10);
      try {
        await Promise.all(
          batch.map(job =>
            client.memories.create({
              content: job.content,
              spaceId: job.spaceId,
              metadata: job.metadata
            })
          )
        );
      } catch (error) {
        this.queue.unshift(...batch);
        await sleep(1000);
      }
    }
    this.processing = false;
  }
}

Security Practices

Protect your users' data with proper security measures.

  • Never expose admin keys - Use scoped keys for client-side access
  • Validate spaceId ownership - Ensure users can only access their spaces
  • Sanitize content - Don't store raw user input without validation
  • Audit access - Log who accessed what, when
  • Rotate keys periodically - Especially after team member departures

Never store passwords, API keys, or other secrets as memory content. Memories are optimized for search, not secure storage.

Monitoring and Observability

Track performance and catch issues before they impact users.

  • API latency (p50, p95, p99)
  • Error rates by operation
  • Results per search (are queries returning useful results?)
  • Memory count per space (growth over time)
  • Cache hit rates (if caching)

Common Pitfalls

Avoid these frequently encountered mistakes:

Storing Too Much

Don't store every message. Extract and store only meaningful facts. "User said hello" is rarely useful.

Ignoring Relevance Scores

Low-scoring results are noise. Set thresholds (0.6-0.8) to filter out irrelevant matches.

No Space Scoping

Always include spaceId in multi-user apps. Querying without it risks data leakage.

Sync Storage in Hot Path

Memory writes block response time. Store asynchronously after responding to users.

Performance Checklist

  • Reuse the Trix client instance (don't recreate per request)
  • Cache space IDs to avoid database lookups
  • Use parallel requests when fetching from multiple spaces
  • Set appropriate limits (don't over-fetch)
  • Store memories asynchronously
  • Batch operations when creating multiple memories
  • Implement retry logic with exponential backoff
  • Monitor latency percentiles, not just averages

Following these practices, most applications see sub-100ms retrieval times even with thousands of memories per space.

Summary

The most impactful practices to implement immediately:

  1. Write complete, contextual memory content - Better content means better retrieval
  2. Always scope by space - Essential for multi-user apps
  3. Store asynchronously - Don't slow down user responses
  4. Handle errors gracefully - AI should work even if memory fails
  5. Monitor performance - Track latency and error rates from day one