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.

Detection flow
// 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.

Set explicit preferences
import Rekall from '@rekall/agent-sdk';
const rekall = new Rekall({ apiKey: 'rk_your_key' });
// Set a coding style preference
await rekall.preferences.set({
category: 'coding-style',
key: 'indentation',
value: 'tabs',
description: 'Use tabs for indentation, not spaces',
});
// Set a communication preference
await rekall.preferences.set({
category: 'communication',
key: 'explanation-style',
value: 'concise',
description: 'Keep explanations brief and to the point',
});
// Set multiple preferences at once
await 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.

View and manage implicit preferences
// List detected preference candidates
const 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 preference
await 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 RangeMeaningRecommendation
0.9 - 1.0Very high confidenceApply automatically
0.7 - 0.9High confidenceApply with mention
0.5 - 0.7Moderate confidenceAsk before applying
0.0 - 0.5Low confidenceDo not apply
Query by confidence threshold
// Get only high-confidence preferences for automatic application
const confident = await rekall.preferences.list({
minConfidence: 0.8,
categories: ['coding-style', 'communication'],
});
// Get all preferences including low-confidence for review
const 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.

Use preferences to personalize
// Before generating a code response, fetch coding preferences
const codingPrefs = await rekall.preferences.list({
categories: ['coding-style'],
minConfidence: 0.7,
});
// Build a preference context string
const 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 context
const systemPrompt = `
You are a coding assistant. Follow these user preferences:
${prefContext}
Apply these preferences when generating code. If a preference
conflicts with a specific request, the request takes priority.
`;
// For communication preferences
const 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.

Complete coding style configuration
1// Set comprehensive coding style preferences
2await rekall.preferences.setMany([
3 // Formatting
4 { 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' },
9
10 // Patterns
11 { 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' },
16
17 // TypeScript specific
18 { 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' },
21
22 // Testing
23 { 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.

Manage preferences
// List all preferences grouped by category
const 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 preference
await rekall.preferences.update({
category: 'coding-style',
key: 'indentation',
value: 'spaces-2',
description: 'Changed to 2-space indentation for this project',
});
// Delete a preference
await rekall.preferences.delete({
category: 'coding-style',
key: 'semicolons',
});
// Reset all preferences in a category
await 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

Rekall
rekall