Vector and Graph Storage
Papr Memory's hybrid approach combines vector similarity search with graph relationships to create a powerful, context-aware memory system. This page explains how these two paradigms work together behind the scenes while keeping the developer experience simple.
Vector Search
How Vector Embeddings Work
At the core of Papr Memory is a vector embedding system that transforms content into numerical representations:
"Meeting notes from our product planning session" → [0.123, -0.456, 0.789, ..., -0.321]These high-dimensional vectors (typically 1536 dimensions) capture the semantic meaning of content, enabling powerful similarity search.
The Power of Semantic Search
Vector search allows you to find relevant content based on meaning, not just keywords:
- "Product roadmap planning" can match "Our Q3 feature timeline discussion"
- "Customer feedback issues" can match "User complaints about the login page"
- "Budget allocation for marketing" can match "Q2 spending plan for advertising campaigns"
Graph Structure
While vector search finds semantically similar content, graph relationships add essential context:
- Temporal connections: What happened before or after?
- Contextual connections: What belongs together?
- Hierarchical relationships: What is part of what?
- Reference relationships: What refers to what?
This creates a rich, interconnected knowledge graph of your memories.
Three Retrieval Approaches
Papr Memory offers three complementary ways to search your memories:
1. Semantic Retrieval (Cosine-only)
Traditional vector similarity search. Fast and efficient, finds content based on semantic meaning.
results = client.memory.search(
query="Find product feedback",
reranking_config={"reranking_provider": "none"} # Cosine-only
)2. External Reranking (cohere or openai)
Uses commercial reranking models to improve result ordering.
results = client.memory.search(
query="Find product feedback",
reranking_config={
"reranking_provider": "cohere", # or "openai"
"reranking_model": "rerank-v3.5" # for Cohere
}
)3. Graph-Native Reranking (papr_enhanced or papr_max)
Advanced signal-based transforms that rerank results using learned domain patterns (CAESAR-8). Provides topical alignment beyond flat similarity.
results = client.memory.search(
query="Find Python sorting code",
reranking_config={
"reranking_provider": "papr_enhanced", # Balanced quality/speed
"domain_id": "cosqa", # Code search domain
"signal_thresholds": {
"language": 0.9 # 90%+ must be Python
}
}
)4. Knowledge Graph Retrieval (enable_agentic_graph: true)
Intelligent entity relationship navigation through Neo4j. Explores connections for multi-hop reasoning.
results = client.memory.search(
query="What does Pen like to eat?",
enable_agentic_graph=True # Follow entity relationships
)Combining Methods
Use reranking with knowledge graph for maximum intelligence:
results = client.memory.search(
query="Find recent Python code for sorting algorithms",
enable_agentic_graph=True, # Knowledge Graph
reranking_config={
"reranking_provider": "papr_enhanced", # Graph-Native Reranking
"domain_id": "cosqa",
"signal_thresholds": {
"language": 0.9,
"primary_operation": 0.8
}
}
)The best part? You choose the level of intelligence you need. Start with reranking_provider: "cohere" (default) for general use, upgrade to "papr_enhanced" for domain-specific quality, and enable enable_agentic_graph when relationships matter.
Real-World Example: Multi-Hop Retrieval
Consider a scenario where you need to plan dinner for a guest. Your memory system contains several disconnected pieces of information:
Memory 1: "Pen is coming over next weekend."
metadata: { eventId: "evt_001", guest: "Pen", date: "2025-05-03" }
Memory 2: "Pen loves eating pasta."
metadata: { customMetadata: { person: "Pen", likes: ["pasta"] } }
Memory 3: "Margherita Pizza, Caesar Salad, Spaghetti Carbonara, Tiramisu"
metadata: { restaurantId: "R1", city: "Seattle", cuisine: "Italian" }Challenge: "What to order for the weekend?"
Let's see how different search approaches handle this question:
Semantic Retrieval Only (Cosine)
Traditional vector search finds Memory 1 because it mentions "weekend," but doesn't connect to food preferences.
results = client.memory.search(
query="Pen is coming, what does she like?",
reranking_config={"reranking_provider": "none"} # Cosine-only
)
# Result: "Pen loves eating pasta." (But fails to connect to actual menu items)Knowledge Graph Retrieval (enable_agentic_graph: true)
Papr's multi-hop approach follows entity relationships:
results = client.memory.search(
query="What should I order for the weekend guest?",
enable_agentic_graph=True
)How it works:
- Identifies that Pen is the weekend guest (from Memory 1)
- Discovers Pen's food preference for pasta (from Memory 2)
- Connects this preference to a specific menu item (from Memory 3)
- Returns: "Pen is coming and she likes pasta. Order Spaghetti Carbonara"
This multi-hop retrieval would be nearly impossible with vector search alone, but becomes natural when combining semantic search with graph relationships.
What makes this powerful:
- No explicit coding of these connections is required
- Context is maintained across multiple memories
- The answer is constructed from multiple independent sources
- Information is connected even when stored in different formats
Using the API
Adding Memories
When you add a memory, Papr automatically:
- Generates vector embeddings
- Extracts and stores metadata
- Creates graph relationships
- Indexes everything for fast retrieval
- Updates existing memories that are similar
# Add a simple memory
memory = client.memory.add(
content="Meeting notes from project kickoff",
type="text"
)
# Add memory with relationships through metadata
memory = client.memory.add(
content="Meeting notes from our Q2 planning session. We discussed the roadmap for our mobile app redesign.",
type="text",
metadata={
"topics": ["planning", "roadmap", "mobile", "design"],
"hierarchical_structures": "Projects/MobileApp/Planning",
"conversationId": "conv-123",
"external_user_id": "user_123"
}
)Searching Memories
When you search, Papr combines vector similarity with graph traversal automatically:
# Search with a detailed query
results = client.memory.search(
query="Find our discussion about the mobile app redesign timeline and resource allocation from last month's planning meeting. I need to review the estimated completion dates we agreed on.",
external_user_id="user_123",
enable_agentic_graph=True
)
# Add parameters for more control
results = client.memory.search(
query="Find all discussions related to customer feedback about the checkout process in the last quarter. I specifically need information about payment-related issues that multiple customers reported.",
max_memories=20,
max_nodes=15,
enable_agentic_graph=True,
external_user_id="user_123",
headers={"Accept-Encoding": "gzip"} # For better performance with large responses
)Best Practices
Writing Effective Queries
For best results:
Be specific and detailed
// Good query: "Find our discussion about API rate limiting from the backend planning meeting last week. I need the specific limits we agreed on for free vs. paid tiers." // Less effective query: "API limits"Include context
// Good query: "Find notes from the marketing strategy sessions where we discussed social media campaigns for the product launch." // Less effective query: "social media"Specify time frames when relevant
// Good query: "Find budget discussions from Q1 planning meetings where we allocated resources for the new product development." // Less effective query: "budget"
Optimizing Performance
Use rich metadata when adding memories
- Add detailed
topicsas arrays - Structure information with
hierarchical_structures - Include temporal and contextual information
- Add detailed
Enable compression for large responses
- Add
Accept-Encoding: gzipin your API request headers - Configure your client to use compression
- Add
Enable agentic graph search
- Set
enable_agentic_graph=Truefor intelligent entity-aware search - Provides better results for ambiguous or complex queries
- Set
Tune result parameters
- Adjust
max_memories(recommended: 15-20) andmax_nodes(recommended: 10-15) based on your needs - Use higher values for comprehensive coverage of complex topics
- Adjust
Behind the Scenes
While you don't need to manage this complexity, here's what happens when you search: