search: trim default output to 5 results, gate spectral with --expand

Default search was 15 results + 5 spectral neighbors — way too much
for the recall hook context window. Now: 5 results by default, no
spectral. --expand restores the full 15 + spectral output.

Co-Authored-By: ProofOfConcept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-03-03 18:44:44 -05:00
parent ca0c8cfac6
commit b4bbafdf1c

View file

@ -145,7 +145,7 @@ fn usage() {
eprintln!("poc-memory v0.4.0 — graph-structured memory store
Commands:
search QUERY [QUERY...] Search memory (AND logic across terms)
search QUERY [--expand] Search memory (AND logic across terms)
init Scan markdown files, index all memory units
migrate Migrate from old weights.json system
health Report graph metrics (CC, communities, small-world)
@ -214,9 +214,15 @@ fn cmd_search(args: &[String]) -> Result<(), String> {
use store::StoreView;
if args.is_empty() {
return Err("Usage: poc-memory search QUERY [QUERY...]".into());
return Err("Usage: poc-memory search QUERY [QUERY...] [--expand]".into());
}
let query = args.join(" ");
let expand = args.iter().any(|a| a == "--expand");
let query: String = args.iter()
.filter(|a| *a != "--expand")
.cloned()
.collect::<Vec<_>>()
.join(" ");
let view = store::AnyView::load()?;
let results = search::search(&query, &view);
@ -226,19 +232,21 @@ fn cmd_search(args: &[String]) -> Result<(), String> {
return Ok(());
}
let limit = if expand { 15 } else { 5 };
// Log retrieval to a small append-only file (avoid 6MB state.bin rewrite)
store::Store::log_retrieval_static(&query,
&results.iter().map(|r| r.key.clone()).collect::<Vec<_>>());
// Bump daily lookup counters (fast path, no store needed)
let bump_keys: Vec<&str> = results.iter().take(15).map(|r| r.key.as_str()).collect();
let bump_keys: Vec<&str> = results.iter().take(limit).map(|r| r.key.as_str()).collect();
let _ = lookups::bump_many(&bump_keys);
// Show text results
let text_keys: std::collections::HashSet<String> = results.iter()
.take(15).map(|r| r.key.clone()).collect();
.take(limit).map(|r| r.key.clone()).collect();
for (i, r) in results.iter().enumerate().take(15) {
for (i, r) in results.iter().enumerate().take(limit) {
let marker = if r.is_direct { "" } else { " " };
let weight = view.node_weight(&r.key);
print!("{}{:2}. [{:.2}/{:.2}] {}", marker, i + 1, r.activation, weight, r.key);
@ -248,7 +256,8 @@ fn cmd_search(args: &[String]) -> Result<(), String> {
}
}
// Spectral expansion: find neighbors of top text hits
// Spectral expansion: only with --expand
if expand {
if let Ok(emb) = spectral::load_embedding() {
let seeds: Vec<&str> = results.iter()
.take(5)
@ -258,7 +267,6 @@ fn cmd_search(args: &[String]) -> Result<(), String> {
if !seeds.is_empty() {
let spectral_hits = spectral::nearest_to_seeds(&emb, &seeds, 10);
// Filter to nodes not already in text results
let new_hits: Vec<_> = spectral_hits.into_iter()
.filter(|(k, _)| !text_keys.contains(k))
.take(5)
@ -270,7 +278,6 @@ fn cmd_search(args: &[String]) -> Result<(), String> {
let weight = view.node_weight(k);
print!(" ~ [{:.2}] {}", weight, k);
println!();
// Show first line of content as snippet
if let Some(content) = view.node_content(k) {
let snippet: String = content.lines()
.find(|l| !l.trim().is_empty() && !l.starts_with('#'))
@ -284,6 +291,7 @@ fn cmd_search(args: &[String]) -> Result<(), String> {
}
}
}
}
Ok(())
}