Memory Retrieval Guide
This guide covers effective strategies for searching and retrieving memories using the Papr Memory API. Learn how to write effective queries, handle search results, and optimize retrieval performance.
Search Capabilities
The Papr Memory API provides powerful semantic search capabilities:
- Natural Language Queries: Write detailed, descriptive queries
- Metadata Filtering: Use metadata for better targeting
- Context-Aware Search: Include conversation and emotional context
- Result Ranking: Automatic relevance-based ranking
- Agentic Graph Search: Intelligent entity relationship navigation
Writing Effective Queries
The search API works best with detailed, contextual queries:
const searchMemories = async (query: string) => {
const response = await fetch('https://memory.papr.ai/v1/memory/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': '<your-api-key>',
'X-Client-Type': 'your_app_name',
'Accept-Encoding': 'gzip'
},
body: JSON.stringify({
query,
enable_agentic_graph: true, // HIGHLY RECOMMENDED for intelligent search
rank_results: false // Results are already semantically ranked
})
});
return response.json();
};
// Good query examples
const queries = [
"Find detailed discussions about API performance issues from the last month, focusing on timeout errors and customer impact",
"Locate product feedback about the new dashboard features, particularly comments about usability and suggested improvements",
"Retrieve meeting notes from recent planning sessions that discuss Q2 objectives and resource allocation"
];
Recommended Search Parameters
For best search results, follow these recommendations:
Enable Agentic Graph Search: Set
enable_agentic_graph: true
for more intelligent, context-aware search that can understand ambiguous referencesSet Appropriate Memory Limits: Use
max_memories: 15-20
(via query parameter) for comprehensive coverage. Lower values may miss relevant information.Set Appropriate Node Limits: Use
max_nodes: 10-15
(via query parameter) for comprehensive graph entity relationships.Use Accept-Encoding Header: Add
'Accept-Encoding': 'gzip'
header to enable response compression for improved performance.
Search Patterns
1. Topic-Based Search
async function searchByTopic(
topic: string,
subtopics: string[] = [],
timeframe?: string
) {
let query = `Find comprehensive information about ${topic}`;
if (subtopics.length > 0) {
query += ` specifically relating to ${subtopics.join(', ')}`;
}
if (timeframe) {
query += ` from ${timeframe}`;
}
return searchMemories(query);
}
// Example usage
const results = await searchByTopic(
'user authentication',
['password reset', 'two-factor authentication'],
'the past quarter'
);
2. Context-Based Search
async function searchWithContext(
topic: string,
context: {
project?: string,
team?: string,
status?: string,
priority?: string
}
) {
let query = `Find information about ${topic}`;
if (context.project) {
query += ` within the ${context.project} project`;
}
if (context.team) {
query += ` involving the ${context.team} team`;
}
if (context.status) {
query += ` with ${context.status} status`;
}
if (context.priority) {
query += ` marked as ${context.priority} priority`;
}
return searchMemories(query);
}
// Example usage
const results = await searchWithContext('implementation timeline', {
project: 'Dashboard Redesign',
team: 'Frontend',
status: 'in-progress',
priority: 'high'
});
3. Emotional Context Search
async function searchByEmotion(
topic: string,
emotion: string,
intensity?: 'high' | 'moderate' | 'low'
) {
let query = `Find ${topic} discussions`;
if (intensity) {
query += ` with ${intensity} levels of ${emotion} sentiment`;
} else {
query += ` expressing ${emotion}`;
}
return searchMemories(query);
}
// Example usage
const results = await searchByEmotion(
'product feedback',
'frustration',
'high'
);
Processing Search Results
Handle and organize search results effectively:
interface SearchResult {
memories: Memory[];
nodes: Node[]; // Graph nodes for relationships
}
class MemoryProcessor {
// Group results by hierarchy
static groupByHierarchy(results: SearchResult) {
return results.memories.reduce((acc, memory) => {
const hierarchy = memory.metadata?.hierarchical_structures || 'Uncategorized';
acc[hierarchy] = acc[hierarchy] || [];
acc[hierarchy].push(memory);
return acc;
}, {});
}
// Sort results by relevance and date
static sortResults(memories: Memory[]) {
return memories.sort((a, b) => {
const dateA = new Date(a.metadata?.createdAt || 0);
const dateB = new Date(b.metadata?.createdAt || 0);
return dateB.getTime() - dateA.getTime();
});
}
// Extract key insights
static extractInsights(memories: Memory[]) {
return memories.map(memory => ({
id: memory.id,
content: memory.content,
topics: memory.metadata?.topics?.split(',').map(t => t.trim()) || [],
emotions: memory.metadata?.["emotion tags"]?.split(',').map(e => e.trim()) || [],
created: new Date(memory.metadata?.createdAt)
}));
}
}
// Example usage
const response = await searchMemories('feature feedback');
const grouped = MemoryProcessor.groupByHierarchy(response.data);
const sorted = MemoryProcessor.sortResults(response.data.memories);
const insights = MemoryProcessor.extractInsights(response.data.memories);
Search Optimization
1. Query Building
class QueryBuilder {
private query: string[];
private filters: string[];
constructor(baseQuery: string) {
this.query = [baseQuery];
this.filters = [];
}
addTimeframe(timeframe: string) {
this.filters.push(`from ${timeframe}`);
return this;
}
addContext(context: string) {
this.filters.push(`in the context of ${context}`);
return this;
}
addFocus(focus: string) {
this.filters.push(`focusing on ${focus}`);
return this;
}
build() {
return [...this.query, ...this.filters].join(' ');
}
}
// Example usage
const builder = new QueryBuilder('Find discussions about API performance')
.addTimeframe('the last month')
.addContext('customer support tickets')
.addFocus('response time issues');
const query = builder.build();
const results = await searchMemories(query);
2. Result Caching
class MemoryCache {
private cache: Map<string, {
results: SearchResult,
timestamp: number,
search_id: string // Store search_id for feedback submission
}>;
private ttl: number; // Time to live in milliseconds
constructor(ttlMinutes: number = 5) {
this.cache = new Map();
this.ttl = ttlMinutes * 60 * 1000;
}
async get(query: string) {
const key = this.generateKey(query);
const cached = this.cache.get(key);
if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.results;
}
const results = await searchMemories(query);
this.cache.set(key, {
results,
timestamp: Date.now(),
search_id: results.search_id
});
return results;
}
getSearchId(query: string) {
const key = this.generateKey(query);
return this.cache.get(key)?.search_id;
}
private generateKey(query: string) {
return query.toLowerCase().trim();
}
}
3. Search with Feedback
Improve future search results by providing feedback:
async function searchWithFeedback(query: string) {
const results = await searchMemories(query);
const searchId = results.search_id;
// Store search_id for later feedback submission
localStorage.setItem('last_search_id', searchId);
return results;
}
async function submitFeedback(searchId: string, isHelpful: boolean, comments?: string) {
const response = await fetch('https://memory.papr.ai/v1/feedback', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': '<your-api-key>',
'X-Client-Type': 'your_app_name'
},
body: JSON.stringify({
search_id: searchId,
feedbackData: {
feedbackType: isHelpful ? 'thumbs_up' : 'thumbs_down',
feedbackSource: 'inline',
feedbackText: comments
}
})
});
return response.json();
}
Working with Graph Nodes
When enable_agentic_graph
is enabled, search results include both memories and graph nodes:
interface Node {
label: 'Memory' | 'Person' | 'Company' | 'Project' | 'Task' | 'Insight' | 'Meeting' | 'Opportunity' | 'Code';
properties: {
id: string;
[key: string]: any; // Additional properties based on node type
};
}
function processNodes(nodes: Node[]) {
const personNodes = nodes.filter(node => node.label === 'Person');
const projectNodes = nodes.filter(node => node.label === 'Project');
const taskNodes = nodes.filter(node => node.label === 'Task');
// Process each node type
const people = personNodes.map(node => ({
id: node.properties.id,
name: node.properties.name,
role: node.properties.role
}));
const projects = projectNodes.map(node => ({
id: node.properties.id,
name: node.properties.name,
type: node.properties.type
}));
return {
people,
projects,
tasks: taskNodes
};
}
Next Steps
- Learn about Memory Management
- Explore Batch Operations
- See the complete API Reference