Skip to content

retrieveRelevant() returns 0 results after HNSW rebuild via inner.insert() #129

@PogeystickJoe

Description

@PogeystickJoe

Summary

After rebuilding the HNSW index from SQLite (necessary workaround for the storeEpisode persistence gap), reflexion.retrieveRelevant() returns 0 results even though vectorBackend.getInner().search() finds the data correctly.

Package: agentdb@3.0.0-alpha.10
Backend: ruvector (384-dim)

Reproduction

const { AgentDB } = await import('agentdb');
const db = new AgentDB({ path: './test.db', vectorBackend: 'ruvector', dimensions: 384 });
await db.initialize();

// Rebuild HNSW from SQLite (workaround for storeEpisode not persisting)
const inner = db.vectorBackend.getInner();
await inner.insert('1', vectorArray, { task: 'HVAC permit Tacoma WA', reward: 0.85 });
await inner.insert('2', vectorArray2, { task: 'ICE machine permit Ferndale WA', reward: 0.9 });

// Direct search works:
const directResults = await inner.search(queryVector, 5);
console.log(directResults.length); // → 2 ✅

// retrieveRelevant returns nothing:
const results = await db.reflexion.retrieveRelevant({ task: 'Tacoma WA permit', k: 5 });
console.log(results.length); // → 0 ❌

Expected behavior

retrieveRelevant() should search the same HNSW index that inner.insert() writes to, and return matching results.

Actual behavior

retrieveRelevant() returns an empty array. It appears to maintain a separate internal count or index reference that doesn't account for data inserted via getInner().insert().

Note: retrieveRelevant() DOES work for data stored via storeEpisode() in the same process (before restart). The issue is specifically with data loaded via the inner insert path.

Context

This comes up when implementing persistence for the storeEpisode gap (see related issue). The natural recovery pattern is: read episodes from SQLite → insert into HNSW via inner.insert() → search. The last step fails if using retrieveRelevant() but works fine with inner.search().

Workaround

Use inner.search() directly instead of retrieveRelevant():

async function searchHNSW(db, queryVector, k) {
  const inner = db.vectorBackend.getInner();
  return await inner.search(queryVector, k || 5);
}

Then enrich results from SQLite as needed. This has been reliable in production.

Also noted

db.vectorBackend.insert() (with the guard) fails with "Received undefined" due to GuardedBackend requiring a mutation proof. getInner().insert() bypasses this but then retrieveRelevant() doesn't see the data. A bulkInsert() or rebuild mode that works with both the guard and the retrieval path would solve both issues.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions