Store and Search Messages
How to store conversation messages and retrieve them using semantic search.
|
This page’s prose and the deeper examples (custom summaries,
metadata-filtered search, time-range search, batch import) target the
Python SDK against a Bolt-connected Neo4j. The canonical operations
(add message, search, list sessions, get conversation, delete) port
cleanly to the TypeScript SDK against NAMS — |
TypeScript equivalents at a glance
| Operation | Python (Bolt) | TypeScript (NAMS) |
|---|---|---|
Add a message |
|
|
Search messages |
|
|
List sessions/conversations |
|
|
Get a conversation |
|
|
Delete a session/conversation |
|
|
Three-tier context (recent + observations + reflections) |
|
|
Bulk-load messages |
|
|
Metadata-filtered search |
|
Not built-in. Use |
Time-range search ( |
|
Not built-in. Filter client-side on |
Auto-summarize |
|
Not exposed. NAMS surfaces inline observations and reflections via |
Prerequisites
-
Neo4j database running with vector index support (Neo4j 5.13+)
-
neo4j-agent-memoryinstalled -
Embedding service configured (OpenAI, Sentence Transformers, etc.)
Store Messages
Basic Message Storage
Store a single message with automatic embedding generation:
- Python (Bolt)
-
from neo4j_agent_memory import MemoryClient, MemorySettings settings = MemorySettings( neo4j={"uri": "bolt://localhost:7687", "password": "password"}, ) client = MemoryClient(settings) # Store a customer service message message = await client.short_term.add_message( session_id="support-session-001", role="user", content="I'd like to return the Nike Air Max shoes I ordered last week. Order #12345.", ) print(f"Stored message: {message.id}") - TypeScript (NAMS)
-
import { MemoryClient } from "@neo4j-labs/agent-memory"; // Reads MEMORY_API_KEY from env; targets NAMS by default. const memory = new MemoryClient(); // Create a conversation up front (NAMS pattern). const conv = await memory.shortTerm.createConversation({ userId: "support-user-001", }); const message = await memory.shortTerm.addMessage( conv.id, "user", "I'd like to return the Nike Air Max shoes I ordered last week. Order #12345.", ); console.log(`Stored message: ${message.id}`);
Store with Metadata
Add custom metadata to messages for filtering and context:
# Store an advisory conversation message
message = await client.short_term.add_message(
session_id="advisory-call-2024-01-15",
role="user",
content="I want to increase my exposure to technology stocks, maybe 20% of the portfolio.",
metadata={
"client_id": "CL-78901",
"advisor_id": "ADV-456",
"account_type": "investment",
"risk_profile": "moderate-growth",
"compliance_recorded": True,
},
)
# Store a shopping assistant message
message = await client.short_term.add_message(
session_id="shopping-assistant-001",
role="assistant",
content="Based on your previous purchases, I recommend the new Nike ZoomX running shoes. They're similar to the Air Max you loved but with better cushioning for long runs.",
metadata={
"customer_id": "CUST-12345",
"channel": "mobile_app",
"page_context": "product_discovery",
"recommended_products": ["SKU-NKE-ZMX-001", "SKU-NKE-ZMX-002"],
},
)
Batch Message Loading
Load multiple messages at once, such as importing conversation history:
# Import historical support tickets
messages = [
{
"role": "user",
"content": "My order hasn't arrived yet. It's been 10 days.",
"timestamp": "2024-01-10T09:00:00Z",
"metadata": {"ticket_id": "TKT-001", "priority": "high"}
},
{
"role": "assistant",
"content": "I apologize for the delay. Let me check the shipping status for you.",
"timestamp": "2024-01-10T09:01:00Z",
"metadata": {"ticket_id": "TKT-001", "agent_id": "AGT-123"}
},
{
"role": "assistant",
"content": "Your order was delayed due to weather. It's now out for delivery.",
"timestamp": "2024-01-10T09:03:00Z",
"metadata": {"ticket_id": "TKT-001", "resolution": "shipping_update"}
},
]
await client.short_term.add_messages_batch(
messages=messages,
session_id="historical-import-001",
)
Search Messages
Semantic Search
Find messages by meaning, not just keywords:
- Python (Bolt)
-
# Find discussions about portfolio allocation results = await client.short_term.search_messages( query="client wants to adjust investment allocation", limit=10, ) for msg in results: print(f"[{msg.role}] {msg.content[:100]}...") print(f" Session: {msg.session_id}, Score: {msg.score:.3f}") - TypeScript (NAMS)
-
// Find discussions about portfolio allocation const results = await memory.shortTerm.searchMessages( "client wants to adjust investment allocation", { limit: 10 }, ); for (const msg of results) { console.log(`[${msg.role}] ${msg.content.slice(0, 100)}...`); console.log(` Conversation: ${msg.conversationId}`); }
Filter by Session
Search within a specific conversation:
# Search within a specific advisory call
results = await client.short_term.search_messages(
query="risk tolerance",
session_id="advisory-call-2024-01-15",
limit=5,
)
Filter by Metadata
Combine semantic search with metadata filters:
results = await client.short_term.search_messages(
query="product return refund",
metadata_filter={
"priority": "high",
"channel": "phone",
},
limit=20,
)
results = await client.short_term.search_messages(
query="concentrated position single stock",
metadata_filter={
"risk_profile": ["aggressive", "moderate-growth"],
"compliance_recorded": True,
},
limit=50,
)
Time-Based Filtering
Search messages within a time range:
from datetime import datetime, timedelta
# Find recent messages about shipping issues
one_week_ago = datetime.now() - timedelta(days=7)
results = await client.short_term.search_messages(
query="shipping delay delivery problem",
after=one_week_ago,
limit=50,
)
Manage Sessions
List Sessions
Get all conversation sessions:
sessions = await client.short_term.list_sessions(
limit=20,
metadata_filter={"channel": "mobile_app"},
)
for session in sessions:
print(f"Session: {session.id}")
print(f" Messages: {session.message_count}")
print(f" Started: {session.created_at}")
print(f" Last activity: {session.last_message_at}")
Get Session Messages
Retrieve all messages in a session:
- Python (Bolt)
-
# Get full conversation history conversation = await client.short_term.get_conversation( session_id="support-session-001", ) messages = conversation.messages for msg in messages: print(f"[{msg.timestamp}] {msg.role}: {msg.content}") - TypeScript (NAMS)
-
// Get full conversation history const conversation = await memory.shortTerm.getConversation(conv.id); for (const msg of conversation.messages) { console.log(`[${msg.timestamp}] ${msg.role}: ${msg.content}`); }
Generate Summaries
Create summaries of long conversations for efficient context:
Auto-Summarize Session
# Generate summary of advisory call
summary = await client.short_term.summarize_session(
session_id="advisory-call-2024-01-15",
)
print(f"Summary: {summary.content}")
print(f"Key topics: {summary.topics}")
print(f"Action items: {summary.action_items}")
Custom Summary Prompt
Provide domain-specific summarization instructions:
summary = await client.short_term.summarize_session(
session_id="advisory-call-2024-01-15",
prompt="""
Summarize this financial advisory conversation. Include:
1. Client's stated investment goals
2. Risk tolerance discussed
3. Specific securities or sectors mentioned
4. Any compliance-relevant statements
5. Agreed next steps
""",
)
summary = await client.short_term.summarize_session(
session_id="support-session-001",
prompt="""
Summarize this customer support interaction. Include:
1. Customer's issue or request
2. Products mentioned (with SKUs if available)
3. Resolution provided
4. Customer satisfaction indicators
5. Any follow-up actions needed
""",
)
Build Conversation Context
Combine message history with other memory types for rich context:
Get Combined Context
- Python (Bolt)
-
# Get comprehensive context for agent response context = await client.get_context( query="What running shoes should I recommend?", session_id="shopping-assistant-001", include_short_term=True, # Recent messages include_long_term=True, # Customer preferences and entities include_reasoning=True, # Past successful recommendations short_term_limit=10, long_term_limit=5, ) print("Recent conversation:") for msg in context.messages: print(f" [{msg.role}]: {msg.content[:80]}...") print("\nCustomer preferences:") for pref in context.preferences: print(f" {pref.category}: {pref.preference}") print("\nRelevant entities:") for entity in context.entities: print(f" {entity.name} ({entity.type})") - TypeScript (NAMS)
-
// NAMS returns a structured three-tier window: reflections, observations, // recentMessages. Entities and preferences are fetched separately. const context = await memory.shortTerm.getContext(conv.id); console.log("Reflections (higher-level summaries):"); for (const r of context.reflections) { console.log(` ${r.content}`); } console.log("\nObservations (inline notes):"); for (const o of context.observations) { console.log(` ${o.content}`); } console.log("\nRecent messages:"); for (const msg of context.recentMessages) { console.log(` [${msg.role}]: ${msg.content.slice(0, 80)}...`); } // Fetch entities and preferences separately if needed const entities = await memory.longTerm.searchEntities( "running shoes", { limit: 5 }, );
Format for LLM
Format context for inclusion in LLM prompts:
def format_context_for_prompt(context):
parts = []
# Add conversation history
if context.messages:
parts.append("## Recent Conversation")
for msg in context.messages[-5:]: # Last 5 messages
parts.append(f"**{msg.role}**: {msg.content}")
# Add preferences
if context.preferences:
parts.append("\n## Customer Preferences")
for pref in context.preferences:
parts.append(f"- {pref.category}: {pref.preference}")
# Add relevant entities
if context.entities:
parts.append("\n## Relevant Information")
for entity in context.entities:
if entity.description:
parts.append(f"- **{entity.name}**: {entity.description}")
return "\n".join(parts)
# Use in agent prompt
context = await client.get_context(query=user_message, session_id=session_id)
system_prompt = f"""You are a helpful shopping assistant.
{format_context_for_prompt(context)}
Use the above context to provide personalized recommendations.
"""
Best Practices
1. Use Meaningful Session IDs
Structure session IDs for easy filtering and debugging:
# Good: Descriptive and filterable
session_id = f"support-{customer_id}-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
session_id = f"advisory-{advisor_id}-{client_id}-{date}"
# Avoid: Generic UUIDs with no context
session_id = str(uuid.uuid4()) # Hard to debug
2. Include Searchable Metadata
Add metadata that supports your search patterns:
# Metadata enables powerful filtering
metadata = {
"customer_id": customer_id,
"channel": "web", # Filter by channel
"intent": "product_return", # Filter by intent
"sentiment": "negative", # Filter by sentiment
"language": "en", # Filter by language
"agent_id": agent_id, # Filter by agent
}
3. Summarize Long Sessions
Generate summaries to avoid context window limits:
# Check if session is getting long
conversation = await client.short_term.get_conversation(session_id)
messages = conversation.messages
if len(messages) > 20:
# Generate summary of older messages
summary = await client.short_term.summarize_session(
session_id=session_id,
message_limit=15, # Summarize first 15 messages
)
# Use summary + recent messages for context
recent_messages = messages[-5:]
4. Archive Completed Sessions
Move completed conversations to long-term storage:
# After support ticket resolved
summary = await client.short_term.summarize_session(session_id)
# Store summary as an entity for long-term reference
entity, dedup_result = await client.long_term.add_entity(
name=f"Support Case {ticket_id}",
entity_type="EVENT",
attributes={
"summary": summary.content,
"customer_id": customer_id,
"resolution": resolution_type,
"resolved_at": datetime.now().isoformat(),
},
)
# Optionally delete the detailed messages
await client.short_term.delete_session(session_id)