Skip to content

fix(memory/holographic): track retrieval_count on all retrieval methods - #21022

Closed
zons-zhaozhy wants to merge 2 commits into
NousResearch:mainfrom
zons-zhaozhy:fix/holographic-retrieval-count
Closed

fix(memory/holographic): track retrieval_count on all retrieval methods#21022
zons-zhaozhy wants to merge 2 commits into
NousResearch:mainfrom
zons-zhaozhy:fix/holographic-retrieval-count

Conversation

@zons-zhaozhy

@zons-zhaozhy zons-zhaozhy commented May 7, 2026

Copy link
Copy Markdown

Summary

Fixes #17899

The retrieval_count column in the facts table was never incremented during normal operation. While store.search_facts() had the correct UPDATE logic, the production code path goes through FactRetriever.search/probe/related/reason (not store.search_facts()), so every fact's retrieval_count stayed at 0 forever.

This broke the trust scoring model's ability to factor in usage frequency — frequently retrieved facts should gain trust over time, but the signal was always zero.

Root Cause

Two search paths exist in the holographic memory plugin:

Path Increments retrieval_count? Used by production?
store.search_facts() Yes No
FactRetriever.search/probe/related/reason() No Yes (prefetch + fact_store tool)

Changes

1. Extract shared store.increment_retrieval_count() (store.py)

Pulls the SQL UPDATE out of search_facts() into a dedicated method for reuse:

  • Parameterized query (fact_id IN (...)) — safe against SQL injection
  • Filters out None IDs (defensive, shouldn't happen but costs nothing)
  • Wrapped in try/except — non-critical counter, must not break retrieval

2. Add FactRetriever._record_retrieval() (retrieval.py)

Single helper that delegates to store.increment_retrieval_count(). Called at every return point in:

  • search() — full-text + vector hybrid search
  • probe() — entity recall (falls back to search())
  • related() — structural adjacency search
  • reason() — compositional multi-entity search
  • _score_facts_by_vector() — HRR vector fallback scoring

contradict() is intentionally excluded: it returns fact pairs (not individual retrievals), so retrieval_count semantics don't apply.

3. Deduplicate store.search_facts() (store.py)

Now calls increment_retrieval_count() instead of inline SQL — single source of truth for the UPDATE logic.

Relationship to #17910

@luyao618's #17910 correctly identified and fixed the same issue in search(). I independently arrived at the same root cause and fix for that method, but noticed that probe(), related(), reason(), and _score_facts_by_vector() have the same gap. This PR extends the fix to all five retrieval entry points and extracts a shared helper to avoid duplicating the UPDATE SQL.

If the maintainers prefer, the changes here could be folded into #17910 instead — happy to close this PR in that case.

Test Coverage

8 new tests in tests/plugins/memory/test_holographic_retrieval_count.py:

Test What it verifies
test_search_increments_count search() increments matched facts
test_search_accumulates Multiple search() calls accumulate
test_search_empty_results_noop Empty results don't error
test_probe_increments_count probe() (entity recall) increments
test_related_increments_count related() (adjacency) increments
test_reason_increments_count reason() (compositional) increments
test_record_empty_list_noop _record_retrieval([]) is safe
test_record_accumulates Cross-call accumulation works

Regression: 212 existing tests pass with zero failures.

Performance

~0.4ms overhead per retrieval on a 500-fact store (in-process SQLite, single UPDATE with IN clause). Negligible for the typical holographic store size.

The retrieval_count column in the facts table was never incremented by
FactRetriever, even though store.search_facts() had this logic. Since the
production code path uses FactRetriever.search/probe/related/reason (not
store.search_facts()), every fact's retrieval_count stayed at 0 forever.

This broke the trust scoring model's ability to factor in usage frequency
— frequently retrieved facts should gain trust over time, but the signal
was always zero.

Changes:
- Extract store.increment_retrieval_count() from search_facts() for reuse
- Add FactRetriever._record_retrieval() that delegates to the store method
- Call _record_retrieval() at every return point in:
    search(), probe(), related(), reason(), _score_facts_by_vector()
- contradict() intentionally excluded: it returns fact pairs, not
  individual fact retrievals, so retrieval_count semantics don't apply
- store.search_facts() now calls the shared method (no duplication)

Performance: ~0.4ms overhead per retrieval on a 500-fact store (in-process
SQLite), which is negligible for the typical holographic store size.

Test coverage: 8 new tests covering search/probe/related/reason
incrementing, empty-result safety, and accumulation across calls.

212 existing tests pass with zero regressions.
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/plugins Plugin system and bundled plugins tool/memory Memory tool and memory providers labels May 7, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related: #17910 (earlier attempt at same fix by @luyao618). Both PRs fix #17899 — this PR is broader in scope (covers all retrieval methods + deduplicates store.search_facts()), while #17910 only patches retrieval.search().

…e_auto

_resolve_auto() Step 1 only passed explicit_base_url when main_provider
was 'custom', causing named providers (zai, anthropic, gemini, etc.) to
lose model.base_url from config.yaml in the auxiliary auto-chain.

This forced Z.AI users to hit _resolve_zai_base_url() probe results
which randomly hop between api.z.ai and open.bigmodel.cn on every call,
triggering intermittent 'Initializing agent...' rebuilds.

The CLI main-model path (resolve_runtime_provider L1277-1285) already
respects cfg_base_url; this makes the auxiliary path consistent.

Ref: NousResearch#16719, NousResearch#17737, NousResearch#19437
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have tool/memory Memory tool and memory providers type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] Holographic memory: retrieval_count never incremented — search() in retrieval.py bypasses store.search_facts()

3 participants