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:
parent
ca0c8cfac6
commit
b4bbafdf1c
1 changed files with 41 additions and 33 deletions
26
src/main.rs
26
src/main.rs
|
|
@ -145,7 +145,7 @@ fn usage() {
|
||||||
eprintln!("poc-memory v0.4.0 — graph-structured memory store
|
eprintln!("poc-memory v0.4.0 — graph-structured memory store
|
||||||
|
|
||||||
Commands:
|
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
|
init Scan markdown files, index all memory units
|
||||||
migrate Migrate from old weights.json system
|
migrate Migrate from old weights.json system
|
||||||
health Report graph metrics (CC, communities, small-world)
|
health Report graph metrics (CC, communities, small-world)
|
||||||
|
|
@ -214,9 +214,15 @@ fn cmd_search(args: &[String]) -> Result<(), String> {
|
||||||
use store::StoreView;
|
use store::StoreView;
|
||||||
|
|
||||||
if args.is_empty() {
|
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 view = store::AnyView::load()?;
|
||||||
let results = search::search(&query, &view);
|
let results = search::search(&query, &view);
|
||||||
|
|
@ -226,19 +232,21 @@ fn cmd_search(args: &[String]) -> Result<(), String> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let limit = if expand { 15 } else { 5 };
|
||||||
|
|
||||||
// Log retrieval to a small append-only file (avoid 6MB state.bin rewrite)
|
// Log retrieval to a small append-only file (avoid 6MB state.bin rewrite)
|
||||||
store::Store::log_retrieval_static(&query,
|
store::Store::log_retrieval_static(&query,
|
||||||
&results.iter().map(|r| r.key.clone()).collect::<Vec<_>>());
|
&results.iter().map(|r| r.key.clone()).collect::<Vec<_>>());
|
||||||
|
|
||||||
// Bump daily lookup counters (fast path, no store needed)
|
// 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);
|
let _ = lookups::bump_many(&bump_keys);
|
||||||
|
|
||||||
// Show text results
|
// Show text results
|
||||||
let text_keys: std::collections::HashSet<String> = results.iter()
|
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 marker = if r.is_direct { "→" } else { " " };
|
||||||
let weight = view.node_weight(&r.key);
|
let weight = view.node_weight(&r.key);
|
||||||
print!("{}{:2}. [{:.2}/{:.2}] {}", marker, i + 1, r.activation, 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() {
|
if let Ok(emb) = spectral::load_embedding() {
|
||||||
let seeds: Vec<&str> = results.iter()
|
let seeds: Vec<&str> = results.iter()
|
||||||
.take(5)
|
.take(5)
|
||||||
|
|
@ -258,7 +267,6 @@ fn cmd_search(args: &[String]) -> Result<(), String> {
|
||||||
|
|
||||||
if !seeds.is_empty() {
|
if !seeds.is_empty() {
|
||||||
let spectral_hits = spectral::nearest_to_seeds(&emb, &seeds, 10);
|
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()
|
let new_hits: Vec<_> = spectral_hits.into_iter()
|
||||||
.filter(|(k, _)| !text_keys.contains(k))
|
.filter(|(k, _)| !text_keys.contains(k))
|
||||||
.take(5)
|
.take(5)
|
||||||
|
|
@ -270,7 +278,6 @@ fn cmd_search(args: &[String]) -> Result<(), String> {
|
||||||
let weight = view.node_weight(k);
|
let weight = view.node_weight(k);
|
||||||
print!(" ~ [{:.2}] {}", weight, k);
|
print!(" ~ [{:.2}] {}", weight, k);
|
||||||
println!();
|
println!();
|
||||||
// Show first line of content as snippet
|
|
||||||
if let Some(content) = view.node_content(k) {
|
if let Some(content) = view.node_content(k) {
|
||||||
let snippet: String = content.lines()
|
let snippet: String = content.lines()
|
||||||
.find(|l| !l.trim().is_empty() && !l.starts_with('#'))
|
.find(|l| !l.trim().is_empty() && !l.starts_with('#'))
|
||||||
|
|
@ -284,6 +291,7 @@ fn cmd_search(args: &[String]) -> Result<(), String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue