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
74
src/main.rs
74
src/main.rs
|
|
@ -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,36 +256,36 @@ fn cmd_search(args: &[String]) -> Result<(), String> {
|
|||
}
|
||||
}
|
||||
|
||||
// Spectral expansion: find neighbors of top text hits
|
||||
if let Ok(emb) = spectral::load_embedding() {
|
||||
let seeds: Vec<&str> = results.iter()
|
||||
.take(5)
|
||||
.map(|r| r.key.as_str())
|
||||
.filter(|k| emb.coords.contains_key(*k))
|
||||
.collect();
|
||||
|
||||
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))
|
||||
// Spectral expansion: only with --expand
|
||||
if expand {
|
||||
if let Ok(emb) = spectral::load_embedding() {
|
||||
let seeds: Vec<&str> = results.iter()
|
||||
.take(5)
|
||||
.map(|r| r.key.as_str())
|
||||
.filter(|k| emb.coords.contains_key(*k))
|
||||
.collect();
|
||||
|
||||
if !new_hits.is_empty() {
|
||||
println!("\nSpectral neighbors (structural, not keyword):");
|
||||
for (k, _dist) in &new_hits {
|
||||
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('#'))
|
||||
.unwrap_or("")
|
||||
.chars().take(100).collect();
|
||||
if !snippet.is_empty() {
|
||||
println!(" {}", snippet);
|
||||
if !seeds.is_empty() {
|
||||
let spectral_hits = spectral::nearest_to_seeds(&emb, &seeds, 10);
|
||||
let new_hits: Vec<_> = spectral_hits.into_iter()
|
||||
.filter(|(k, _)| !text_keys.contains(k))
|
||||
.take(5)
|
||||
.collect();
|
||||
|
||||
if !new_hits.is_empty() {
|
||||
println!("\nSpectral neighbors (structural, not keyword):");
|
||||
for (k, _dist) in &new_hits {
|
||||
let weight = view.node_weight(k);
|
||||
print!(" ~ [{:.2}] {}", weight, k);
|
||||
println!();
|
||||
if let Some(content) = view.node_content(k) {
|
||||
let snippet: String = content.lines()
|
||||
.find(|l| !l.trim().is_empty() && !l.starts_with('#'))
|
||||
.unwrap_or("")
|
||||
.chars().take(100).collect();
|
||||
if !snippet.is_empty() {
|
||||
println!(" {}", snippet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue