-
Notifications
You must be signed in to change notification settings - Fork 133
Description
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
- storeEpisode SQL persistence issue (filed separately)
- AgentDB status command shows 0 records — wrong table names in status.js #127 — Status command wrong table names