Store and Search Messages

How to store conversation messages and retrieve them using semantic search.

Message chain: Conversation linked to Messages via FIRST_MESSAGE and NEXT_MESSAGE

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[tabs] blocks below show both. For TypeScript-specific patterns (Vercel AI middleware, edge runtime) see the TypeScript SDK section of the how-to index and the TypeScript API reference.

TypeScript equivalents at a glance

Operation Python (Bolt) TypeScript (NAMS)

Add a message

client.short_term.add_message(session_id, role, content)

memory.shortTerm.addMessage(conversationId, role, content)

Search messages

client.short_term.search_messages(query, limit=)

memory.shortTerm.searchMessages(query, { limit })

List sessions/conversations

client.short_term.list_sessions()

memory.shortTerm.listConversations({ userId })

Get a conversation

client.short_term.get_conversation(session_id)

memory.shortTerm.getConversation(conversationId)

Delete a session/conversation

client.short_term.delete_session(session_id)

memory.shortTerm.clearSession(conversationId) or deleteConversation(id)

Three-tier context (recent + observations + reflections)

client.get_context(query, session_id=)

memory.shortTerm.getContext(conversationId)

Bulk-load messages

client.short_term.add_messages_batch(messages, session_id)

memory.shortTerm.bulkAddMessages(conversationId, messages)

Metadata-filtered search

client.short_term.search_messages(query, metadata_filter={…​})

Not built-in. Use client.query.cypher(…​) against NAMS Cypher endpoint, or fetch + filter in JS.

Time-range search (after=)

client.short_term.search_messages(query, after=ts)

Not built-in. Filter client-side on message.timestamp from getConversation() result.

Auto-summarize

client.short_term.summarize_session(session_id)

Not exposed. NAMS surfaces inline observations and reflections via getObservations() / getReflections() — see "Generate summaries" below.

Prerequisites

  • Neo4j database running with vector index support (Neo4j 5.13+)

  • neo4j-agent-memory installed

  • 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:

Financial Services Example
# 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,
    },
)
Ecommerce Retail Example
# 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

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:

Find high-priority support messages about returns
results = await client.short_term.search_messages(
    query="product return refund",
    metadata_filter={
        "priority": "high",
        "channel": "phone",
    },
    limit=20,
)
Find compliance-relevant advisory discussions
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}`);
}

Delete Sessions

Remove a session and all its messages:

# Delete completed support session
await client.short_term.delete_session(
    session_id="support-session-001",
)

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:

Financial Services Summary
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
    """,
)
Ecommerce Support Summary
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)