Skip to main content

Overview

This guide shows how to build an AI agent with persistent memory that survives across sessions. Every conversation is embedded, stored in SolVec, and retrievable via semantic search. After each write, the memory state is cryptographically verified on Solana.
Unlike traditional vector databases where memory lives on someone else’s servers, SolVec memory is:
  • Encrypted with your Solana wallet key
  • Verifiable against an on-chain Merkle root
  • Portable — you own the data, not the platform

Prerequisites

  • Node.js 18+
  • OpenAI API key (or any embedding provider)
  • Solana wallet (optional for verification)

Installation

1

Install dependencies

npm install solvec@alpha openai
2

Set environment variables

export OPENAI_API_KEY="sk-..."
export SOLANA_WALLET="~/.config/solana/id.json"  # optional

Complete Example

1. Initialize SolVec Client

import { SolVec } from 'solvec';
import OpenAI from 'openai';

const sv = new SolVec({ 
  network: 'devnet',
  walletPath: process.env.SOLANA_WALLET  // optional
});

const collection = sv.collection('agent-memory', { 
  dimensions: 1536  // OpenAI text-embedding-3-small
});

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

2. Store Conversation Memory

Every user message is embedded and stored with metadata:
async function storeMemory(
  text: string, 
  sessionId: string
): Promise<string> {
  // Generate embedding
  const response = await openai.embeddings.create({
    model: 'text-embedding-3-small',
    input: text,
  });

  const embedding = response.data[0].embedding;
  const memoryId = `mem_${Date.now()}_${Math.random().toString(36).slice(2)}`;

  // Store in SolVec
  await collection.upsert([{
    id: memoryId,
    values: embedding,
    metadata: {
      text,
      sessionId,
      timestamp: Date.now(),
      type: 'user_message'
    }
  }]);

  console.log(`Stored memory: ${memoryId}`);
  return memoryId;
}

3. Recall Relevant Memories

Retrieve semantically similar past conversations:
async function recallMemories(
  query: string, 
  topK: number = 5
): Promise<string[]> {
  // Embed the query
  const response = await openai.embeddings.create({
    model: 'text-embedding-3-small',
    input: query,
  });

  const queryEmbedding = response.data[0].embedding;

  // Query SolVec
  const results = await collection.query({
    vector: queryEmbedding,
    topK,
    includeMetadata: true,
  });

  // Extract text from matches
  return results.matches.map(match => match.metadata?.text as string);
}

4. Build the Agent Loop

Combine memory storage + recall + LLM generation:
async function chat(userMessage: string, sessionId: string): Promise<string> {
  // 1. Store current message
  await storeMemory(userMessage, sessionId);

  // 2. Recall relevant past conversations
  const relevantMemories = await recallMemories(userMessage, 3);

  // 3. Build prompt with context
  const systemPrompt = relevantMemories.length > 0
    ? `You are a helpful AI assistant with persistent memory.

Relevant memories:
${relevantMemories.map((m, i) => `${i + 1}. ${m}`).join('\n')}

Use these memories to personalize your response.`
    : `You are a helpful AI assistant. This is the first conversation.`;

  // 4. Generate response
  const completion = await openai.chat.completions.create({
    model: 'gpt-4o-mini',
    messages: [
      { role: 'system', content: systemPrompt },
      { role: 'user', content: userMessage },
    ],
  });

  const assistantMessage = completion.choices[0].message.content || '';

  // 5. Store assistant response
  await storeMemory(assistantMessage, sessionId);

  return assistantMessage;
}

5. Verify Memory Integrity

Prove that memory hasn’t been tampered with:
async function verifyMemory() {
  const proof = await collection.verify();

  console.log('Memory Verification:');
  console.log('  Verified:', proof.verified);
  console.log('  Vector Count:', proof.vectorCount);
  console.log('  Local Root:', proof.localRoot.slice(0, 16) + '...');
  console.log('  On-Chain Root:', proof.onChainRoot.slice(0, 16) + '...');
  console.log('  Explorer:', proof.solanaExplorerUrl);

  return proof;
}

Full Usage Example

async function main() {
  const sessionId = 'user_123';

  // Conversation 1
  console.log('\n--- Conversation 1 ---');
  let response = await chat(
    "My name is Alex and I'm a software engineer",
    sessionId
  );
  console.log('Agent:', response);

  // Conversation 2 (agent remembers)
  console.log('\n--- Conversation 2 ---');
  response = await chat(
    "What did I tell you about myself?",
    sessionId
  );
  console.log('Agent:', response);

  // Verify memory on-chain
  console.log('\n--- Memory Verification ---');
  await verifyMemory();

  // View stats
  const stats = await collection.describeIndexStats();
  console.log('\nCollection Stats:');
  console.log('  Total Memories:', stats.vectorCount);
  console.log('  Merkle Root:', stats.merkleRoot.slice(0, 16) + '...');
}

main();
Expected Output:
--- Conversation 1 ---
[SolVec] Upserted 1 vectors to collection 'agent-memory'
Agent: Nice to meet you, Alex! As a software engineer, what kind of projects do you work on?

--- Conversation 2 ---
[SolVec] Upserted 1 vectors to collection 'agent-memory'
Agent: You told me that your name is Alex and you're a software engineer!

--- Memory Verification ---
Memory Verification:
  Verified: true
  Vector Count: 4
  Local Root: 3f7a2b1c4d5e6f...
  On-Chain Root: 3f7a2b1c4d5e6f...
  Explorer: https://explorer.solana.com/address/8iLpy...?cluster=devnet

Collection Stats:
  Total Memories: 4
  Merkle Root: 3f7a2b1c4d5e6f...

Why This Matters

Persistent Memory

Agent memory survives restarts. No data loss.

Verifiable State

Cryptographically prove memory hasn’t been tampered with.

Semantic Search

Recall relevant context across thousands of conversations.

Data Ownership

You own the memory, not the database provider.

Next Steps

RAG Application

Build a full RAG pipeline with document chunking

LangChain Integration

Use SolVec as a LangChain vector store

Production Considerations

This example runs in-memory. For production:
  • Enable Shadow Drive persistence (coming soon)
  • Use batch upserts for multiple memories
  • Implement memory pruning (delete old/irrelevant vectors)
  • Add metadata filtering for multi-user systems
For multi-user agents, add userId to metadata and use filters:
const results = await collection.query({
  vector: embedding,
  topK: 5,
  filter: { userId: 'user_123' }  // only this user's memories
});

Build docs developers (and LLMs) love