Preference Learning Patterns
Rekall learns user preferences over time -- both from explicit statements and implicit behavioral patterns. Use preferences to personalize agent responses, coding style, communication tone, and workflow choices.
Overview
Preferences are structured knowledge about how a user (or team) likes things done. Unlike episodic memories that record events, preferences capture patterns: "prefers TypeScript over JavaScript," "uses tabs not spaces," "likes concise explanations."
Preferences have two sources: explicit (the user directly tells the agent) and implicit (Rekall detects patterns from behavior). Both carry a confidence score that increases as the preference is reinforced.
How Preference Detection Works
Rekall analyzes episodic memories and agent interactions to detect recurring patterns. When the same choice or correction appears multiple times, Rekall creates a preference candidate with a confidence score.
// The detection pipeline:// 1. User corrects agent output 3+ times in same way// "Use const instead of let" (correction #1)// "I prefer const" (correction #2)// "Again, const please" (correction #3)//// 2. Rekall creates an implicit preference:// {// category: 'coding-style',// key: 'variable-declaration',// value: 'prefer-const',// confidence: 0.78,// source: 'implicit',// evidence: ['mem_abc', 'mem_def', 'mem_ghi'],// }//// 3. Confidence grows with reinforcement:// Next time user says "const" -> confidence: 0.85// User says "let is fine here" -> confidence: 0.72 (decreases)
Evidence tracking
Every preference links back to the episodic memories that support it. This makes preferences explainable -- you can always see why Rekall thinks a user has a particular preference.
Explicit Preferences
Users can set preferences directly. Explicit preferences start with high confidence since the user stated them intentionally.
import Rekall from '@rekall/agent-sdk';const rekall = new Rekall({ apiKey: 'rk_your_key' });// Set a coding style preferenceawait rekall.preferences.set({category: 'coding-style',key: 'indentation',value: 'tabs',description: 'Use tabs for indentation, not spaces',});// Set a communication preferenceawait rekall.preferences.set({category: 'communication',key: 'explanation-style',value: 'concise',description: 'Keep explanations brief and to the point',});// Set multiple preferences at onceawait rekall.preferences.setMany([{category: 'coding-style',key: 'quotes',value: 'single',description: 'Use single quotes for strings',},{category: 'coding-style',key: 'semicolons',value: 'always',description: 'Always use semicolons',},{category: 'tools',key: 'test-framework',value: 'vitest',description: 'Use Vitest for testing, not Jest',},]);
Implicit Preferences
Implicit preferences are detected automatically from behavior. You can view candidates, accept or reject them, and configure detection sensitivity.
// List detected preference candidatesconst candidates = await rekall.preferences.listCandidates({minConfidence: 0.6,});for (const candidate of candidates.preferences) {console.log(`[${candidate.confidence.toFixed(2)}] ${candidate.category}/${candidate.key}`);console.log(` Value: ${candidate.value}`);console.log(` Evidence: ${candidate.evidence.length} memories`);console.log(` Source: ${candidate.source}`);}// [0.85] coding-style/error-handling// Value: prefer-try-catch-over-callbacks// Evidence: 5 memories// Source: implicit// Accept a detected preferenceawait rekall.preferences.acceptCandidate({candidateId: candidate.id,});// Reject a detected preference (won't be suggested again)await rekall.preferences.rejectCandidate({candidateId: anotherCandidate.id,reason: 'This was a one-time thing, not a preference',});
Confidence Scores
Every preference has a confidence score between 0 and 1. The score reflects how certain Rekall is that this preference is accurate.
Score Thresholds
| Score Range | Meaning | Recommendation |
|---|---|---|
| 0.9 - 1.0 | Very high confidence | Apply automatically |
| 0.7 - 0.9 | High confidence | Apply with mention |
| 0.5 - 0.7 | Moderate confidence | Ask before applying |
| 0.0 - 0.5 | Low confidence | Do not apply |
// Get only high-confidence preferences for automatic applicationconst confident = await rekall.preferences.list({minConfidence: 0.8,categories: ['coding-style', 'communication'],});// Get all preferences including low-confidence for reviewconst all = await rekall.preferences.list({minConfidence: 0.0,includeRejected: false,});
Personalizing Responses
The primary use of preferences is to personalize agent behavior. Query relevant preferences before generating a response and use them as context.
// Before generating a code response, fetch coding preferencesconst codingPrefs = await rekall.preferences.list({categories: ['coding-style'],minConfidence: 0.7,});// Build a preference context stringconst prefContext = codingPrefs.preferences.map(p => `- ${p.description || `${p.key}: ${p.value}`}`).join('\n');console.log(prefContext);// - Use tabs for indentation, not spaces// - Use single quotes for strings// - Always use semicolons// - Prefer const over let// - Use Vitest for testing, not Jest// Pass to your LLM as system contextconst systemPrompt = `You are a coding assistant. Follow these user preferences:${prefContext}Apply these preferences when generating code. If a preferenceconflicts with a specific request, the request takes priority.`;// For communication preferencesconst commPrefs = await rekall.preferences.list({categories: ['communication'],minConfidence: 0.7,});// -> "Keep explanations brief and to the point"
Preference priority
Always let explicit user requests override learned preferences. If a user says "use spaces for this file," respect that even if there is a high-confidence preference for tabs. Preferences are defaults, not rules.
Coding Style Preferences
Coding style is one of the most impactful preference categories. Here is a comprehensive example of setting up coding preferences for a project.
1// Set comprehensive coding style preferences2await rekall.preferences.setMany([3 // Formatting4 { category: 'coding-style', key: 'indentation', value: 'tabs', description: 'Use tabs (width 2)' },5 { category: 'coding-style', key: 'quotes', value: 'single', description: 'Single quotes for strings' },6 { category: 'coding-style', key: 'semicolons', value: 'always', description: 'Always use semicolons' },7 { category: 'coding-style', key: 'trailing-commas', value: 'all', description: 'Trailing commas everywhere' },8 { category: 'coding-style', key: 'max-line-length', value: '100', description: '100 char line limit' },910 // Patterns11 { category: 'coding-style', key: 'variable-declaration', value: 'const-first', description: 'Prefer const, then let, never var' },12 { category: 'coding-style', key: 'functions', value: 'arrow', description: 'Arrow functions over function declarations' },13 { category: 'coding-style', key: 'error-handling', value: 'try-catch', description: 'try/catch over .catch() chains' },14 { category: 'coding-style', key: 'async', value: 'async-await', description: 'async/await over .then() chains' },15 { category: 'coding-style', key: 'imports', value: 'named', description: 'Named imports over default imports' },1617 // TypeScript specific18 { category: 'coding-style', key: 'types', value: 'interfaces', description: 'Prefer interfaces over type aliases' },19 { category: 'coding-style', key: 'null-handling', value: 'strict', description: 'Use strict null checks, no non-null assertions' },20 { category: 'coding-style', key: 'enums', value: 'const-enum', description: 'Use const enums or string unions' },2122 // Testing23 { category: 'tools', key: 'test-framework', value: 'vitest', description: 'Vitest for unit tests' },24 { category: 'tools', key: 'e2e-framework', value: 'playwright', description: 'Playwright for E2E tests' },25 { category: 'coding-style', key: 'test-style', value: 'describe-it', description: 'describe/it block style' },26]);
Managing Preferences
List, update, and delete preferences as your needs evolve.
// List all preferences grouped by categoryconst all = await rekall.preferences.list({ groupBy: 'category' });for (const [category, prefs] of Object.entries(all.grouped)) {console.log(`\n${category}:`);for (const pref of prefs) {const source = pref.source === 'explicit' ? 'E' : 'I';console.log(` [${source}] ${pref.key}: ${pref.value} (confidence: ${pref.confidence})`);}}// Update a preferenceawait rekall.preferences.update({category: 'coding-style',key: 'indentation',value: 'spaces-2',description: 'Changed to 2-space indentation for this project',});// Delete a preferenceawait rekall.preferences.delete({category: 'coding-style',key: 'semicolons',});// Reset all preferences in a categoryawait rekall.preferences.deleteCategory('coding-style');
Preference scope
Preferences are scoped to the current memory context (personal, hive, or agent). Personal preferences only affect your agent. To share preferences with a team, set them in a hive context. See the Hive Minds guide.
Next Steps
- •Episodic Memory -- The event data that powers implicit preference detection
- •Hive Minds -- Share preferences across a team
- •Preference Concepts -- Deep dive into the preference learning model
- •Best Practices -- Guidelines for effective preference management
