remove legacy feedback commands (used, wrong, gap, etc.)
These were early experiments with manual feedback signals that never worked well. The scoring system will handle this properly. Removed: - CLI: used, wrong, not-relevant, not-useful, gap - MCP: memory_used - Store: mark_used, mark_wrong, record_gap, modify_node Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
11b58e6b0b
commit
7842b6fc8b
6 changed files with 10 additions and 190 deletions
|
|
@ -115,7 +115,6 @@ async fn dispatch(
|
||||||
"memory_links" => links(&args).await,
|
"memory_links" => links(&args).await,
|
||||||
"memory_link_set" => link_set(&args).await,
|
"memory_link_set" => link_set(&args).await,
|
||||||
"memory_link_add" => link_add(agent, &args).await,
|
"memory_link_add" => link_add(agent, &args).await,
|
||||||
"memory_used" => used(&args).await,
|
|
||||||
"memory_weight_set" => weight_set(&args).await,
|
"memory_weight_set" => weight_set(&args).await,
|
||||||
"memory_rename" => rename(&args).await,
|
"memory_rename" => rename(&args).await,
|
||||||
"memory_supersede" => supersede(agent, &args).await,
|
"memory_supersede" => supersede(agent, &args).await,
|
||||||
|
|
@ -131,7 +130,7 @@ async fn dispatch(
|
||||||
|
|
||||||
// ── Definitions ────────────────────────────────────────────────
|
// ── Definitions ────────────────────────────────────────────────
|
||||||
|
|
||||||
pub fn memory_tools() -> [super::Tool; 13] {
|
pub fn memory_tools() -> [super::Tool; 12] {
|
||||||
use super::Tool;
|
use super::Tool;
|
||||||
[
|
[
|
||||||
Tool { name: "memory_render", description: "Read a memory node's content and links.",
|
Tool { name: "memory_render", description: "Read a memory node's content and links.",
|
||||||
|
|
@ -152,9 +151,6 @@ pub fn memory_tools() -> [super::Tool; 13] {
|
||||||
Tool { name: "memory_link_add", description: "Add a new link between two nodes.",
|
Tool { name: "memory_link_add", description: "Add a new link between two nodes.",
|
||||||
parameters_json: r#"{"type":"object","properties":{"source":{"type":"string"},"target":{"type":"string"}},"required":["source","target"]}"#,
|
parameters_json: r#"{"type":"object","properties":{"source":{"type":"string"},"target":{"type":"string"}},"required":["source","target"]}"#,
|
||||||
handler: Arc::new(|a, v| Box::pin(async move { dispatch("memory_link_add", &a, v).await })) },
|
handler: Arc::new(|a, v| Box::pin(async move { dispatch("memory_link_add", &a, v).await })) },
|
||||||
Tool { name: "memory_used", description: "Mark a node as useful (boosts weight).",
|
|
||||||
parameters_json: r#"{"type":"object","properties":{"key":{"type":"string","description":"Node key"}},"required":["key"]}"#,
|
|
||||||
handler: Arc::new(|a, v| Box::pin(async move { dispatch("memory_used", &a, v).await })) },
|
|
||||||
Tool { name: "memory_weight_set", description: "Set a node's weight directly (0.01 to 1.0).",
|
Tool { name: "memory_weight_set", description: "Set a node's weight directly (0.01 to 1.0).",
|
||||||
parameters_json: r#"{"type":"object","properties":{"key":{"type":"string"},"weight":{"type":"number","description":"0.01 to 1.0"}},"required":["key","weight"]}"#,
|
parameters_json: r#"{"type":"object","properties":{"key":{"type":"string"},"weight":{"type":"number","description":"0.01 to 1.0"}},"required":["key","weight"]}"#,
|
||||||
handler: Arc::new(|a, v| Box::pin(async move { dispatch("memory_weight_set", &a, v).await })) },
|
handler: Arc::new(|a, v| Box::pin(async move { dispatch("memory_weight_set", &a, v).await })) },
|
||||||
|
|
@ -315,18 +311,6 @@ async fn link_add(agent: &Option<std::sync::Arc<crate::agent::Agent>>, args: &se
|
||||||
Ok(format!("linked {} → {} (strength={:.2})", s, t, strength))
|
Ok(format!("linked {} → {} (strength={:.2})", s, t, strength))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn used(args: &serde_json::Value) -> Result<String> {
|
|
||||||
let key = get_str(args, "key")?;
|
|
||||||
let arc = cached_store().await?;
|
|
||||||
let mut store = arc.lock().await;
|
|
||||||
if !store.nodes.contains_key(key) {
|
|
||||||
anyhow::bail!("node not found: {}", key);
|
|
||||||
}
|
|
||||||
store.mark_used(key);
|
|
||||||
store.save().map_err(|e| anyhow::anyhow!("{}", e))?;
|
|
||||||
Ok(format!("marked {} as used", key))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn weight_set(args: &serde_json::Value) -> Result<String> {
|
async fn weight_set(args: &serde_json::Value) -> Result<String> {
|
||||||
let arc = cached_store().await?;
|
let arc = cached_store().await?;
|
||||||
let mut store = arc.lock().await;
|
let mut store = arc.lock().await;
|
||||||
|
|
|
||||||
102
src/cli/node.rs
102
src/cli/node.rs
|
|
@ -1,88 +1,10 @@
|
||||||
// cli/node.rs — node subcommand handlers
|
// cli/node.rs — node subcommand handlers
|
||||||
//
|
//
|
||||||
// render, write, used, wrong, not-relevant, not-useful, gap,
|
// render, write, node-delete, node-rename, history, list-keys,
|
||||||
// node-delete, node-rename, history, list-keys, list-edges,
|
// list-edges, dump-json, lookup-bump, lookups.
|
||||||
// dump-json, lookup-bump, lookups.
|
|
||||||
|
|
||||||
use crate::store;
|
use crate::store;
|
||||||
|
|
||||||
pub fn cmd_used(key: &[String]) -> Result<(), String> {
|
|
||||||
if key.is_empty() {
|
|
||||||
return Err("used requires a key".into());
|
|
||||||
}
|
|
||||||
super::check_dry_run();
|
|
||||||
let key = key.join(" ");
|
|
||||||
let mut store = store::Store::load()?;
|
|
||||||
let resolved = store.resolve_key(&key)?;
|
|
||||||
store.mark_used(&resolved);
|
|
||||||
|
|
||||||
// Also strengthen edges to this node — conscious-tier delta.
|
|
||||||
const DELTA: f32 = 0.01;
|
|
||||||
let mut strengthened = 0;
|
|
||||||
for rel in &mut store.relations {
|
|
||||||
if rel.deleted { continue; }
|
|
||||||
if rel.source_key == resolved || rel.target_key == resolved {
|
|
||||||
let old = rel.strength;
|
|
||||||
rel.strength = (rel.strength + DELTA).clamp(0.05, 0.95);
|
|
||||||
if (rel.strength - old).abs() > 0.001 {
|
|
||||||
rel.version += 1;
|
|
||||||
strengthened += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
store.save()?;
|
|
||||||
println!("Marked '{}' as used (strengthened {} edges)", resolved, strengthened);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cmd_wrong(key: &str, context: &[String]) -> Result<(), String> {
|
|
||||||
let ctx = if context.is_empty() { None } else { Some(context.join(" ")) };
|
|
||||||
super::check_dry_run();
|
|
||||||
let mut store = store::Store::load()?;
|
|
||||||
let resolved = store.resolve_key(key)?;
|
|
||||||
store.mark_wrong(&resolved, ctx.as_deref());
|
|
||||||
store.save()?;
|
|
||||||
println!("Marked '{}' as wrong", resolved);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cmd_not_relevant(key: &str) -> Result<(), String> {
|
|
||||||
let mut store = store::Store::load()?;
|
|
||||||
let resolved = store.resolve_key(key)?;
|
|
||||||
|
|
||||||
// Weaken all edges to this node — it was routed to incorrectly.
|
|
||||||
// Conscious-tier delta: 0.01 per edge.
|
|
||||||
const DELTA: f32 = -0.01;
|
|
||||||
let mut adjusted = 0;
|
|
||||||
for rel in &mut store.relations {
|
|
||||||
if rel.deleted { continue; }
|
|
||||||
if rel.source_key == resolved || rel.target_key == resolved {
|
|
||||||
let old = rel.strength;
|
|
||||||
rel.strength = (rel.strength + DELTA).clamp(0.05, 0.95);
|
|
||||||
if (rel.strength - old).abs() > 0.001 {
|
|
||||||
rel.version += 1;
|
|
||||||
adjusted += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
store.save()?;
|
|
||||||
println!("Not relevant: '{}' — weakened {} edges by {}", resolved, adjusted, DELTA.abs());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cmd_not_useful(key: &str) -> Result<(), String> {
|
|
||||||
// no args to validate
|
|
||||||
super::check_dry_run();
|
|
||||||
let mut store = store::Store::load()?;
|
|
||||||
let resolved = store.resolve_key(key)?;
|
|
||||||
// Same as wrong but with clearer semantics: node content is bad, edges are fine.
|
|
||||||
store.mark_wrong(&resolved, Some("not-useful"));
|
|
||||||
store.save()?;
|
|
||||||
println!("Not useful: '{}' — node weight reduced", resolved);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cmd_weight_set(key: &str, weight: f32) -> Result<(), String> {
|
pub fn cmd_weight_set(key: &str, weight: f32) -> Result<(), String> {
|
||||||
super::check_dry_run();
|
super::check_dry_run();
|
||||||
let result = crate::mcp_server::memory_rpc(
|
let result = crate::mcp_server::memory_rpc(
|
||||||
|
|
@ -93,19 +15,6 @@ pub fn cmd_weight_set(key: &str, weight: f32) -> Result<(), String> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cmd_gap(description: &[String]) -> Result<(), String> {
|
|
||||||
if description.is_empty() {
|
|
||||||
return Err("gap requires a description".into());
|
|
||||||
}
|
|
||||||
super::check_dry_run();
|
|
||||||
let desc = description.join(" ");
|
|
||||||
let mut store = store::Store::load()?;
|
|
||||||
store.record_gap(&desc);
|
|
||||||
store.save()?;
|
|
||||||
println!("Recorded gap: {}", desc);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cmd_list_keys(pattern: Option<&str>) -> Result<(), String> {
|
pub fn cmd_list_keys(pattern: Option<&str>) -> Result<(), String> {
|
||||||
let store = store::Store::load()?;
|
let store = store::Store::load()?;
|
||||||
let g = store.build_graph();
|
let g = store.build_graph();
|
||||||
|
|
@ -192,11 +101,12 @@ pub fn cmd_render(key: &[String]) -> Result<(), String> {
|
||||||
return Err("render requires a key".into());
|
return Err("render requires a key".into());
|
||||||
}
|
}
|
||||||
let key = key.join(" ");
|
let key = key.join(" ");
|
||||||
let store = store::Store::load()?;
|
|
||||||
let bare = store::strip_md_suffix(&key);
|
let bare = store::strip_md_suffix(&key);
|
||||||
|
|
||||||
let rendered = render_node(&store, &bare)
|
let rendered = crate::mcp_server::memory_rpc(
|
||||||
.ok_or_else(|| format!("Node not found: {}", bare))?;
|
"memory_render",
|
||||||
|
serde_json::json!({"key": bare}),
|
||||||
|
).map_err(|e| e.to_string())?;
|
||||||
print!("{}", rendered);
|
print!("{}", rendered);
|
||||||
|
|
||||||
// Mark as seen if we're inside a Claude session (not an agent subprocess —
|
// Mark as seen if we're inside a Claude session (not an agent subprocess —
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Mutation operations on the store
|
// Mutation operations on the store
|
||||||
//
|
//
|
||||||
// CRUD (upsert, delete, modify), feedback tracking (mark_used, mark_wrong),
|
// CRUD (upsert, delete), maintenance (decay, cap_degree), and graph metrics.
|
||||||
// maintenance (decay, fix_categories, cap_degree), and graph metrics.
|
|
||||||
|
|
||||||
use super::types::*;
|
use super::types::*;
|
||||||
|
|
||||||
|
|
@ -179,45 +178,6 @@ impl Store {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Modify a node in-place, bump version, and persist to capnp log.
|
|
||||||
fn modify_node(&mut self, key: &str, f: impl FnOnce(&mut Node)) -> Result<(), String> {
|
|
||||||
let node = self.nodes.get_mut(key)
|
|
||||||
.ok_or_else(|| format!("No node '{}'", key))?;
|
|
||||||
f(node);
|
|
||||||
node.version += 1;
|
|
||||||
let node = node.clone();
|
|
||||||
self.append_nodes(&[node])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mark_used(&mut self, key: &str) {
|
|
||||||
let boost = self.params.use_boost as f32;
|
|
||||||
let _ = self.modify_node(key, |n| {
|
|
||||||
n.uses += 1;
|
|
||||||
n.weight = (n.weight + boost).min(1.0);
|
|
||||||
if n.spaced_repetition_interval < 30 {
|
|
||||||
n.spaced_repetition_interval = match n.spaced_repetition_interval {
|
|
||||||
1 => 3, 3 => 7, 7 => 14, 14 => 30, _ => 30,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
n.last_replayed = now_epoch();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mark_wrong(&mut self, key: &str, _ctx: Option<&str>) {
|
|
||||||
let _ = self.modify_node(key, |n| {
|
|
||||||
n.wrongs += 1;
|
|
||||||
n.weight = (n.weight - 0.1).max(0.0);
|
|
||||||
n.spaced_repetition_interval = 1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_gap(&mut self, desc: &str) {
|
|
||||||
self.gaps.push(GapRecord {
|
|
||||||
description: desc.to_string(),
|
|
||||||
timestamp: today(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cap node degree by soft-deleting edges from mega-hubs.
|
/// Cap node degree by soft-deleting edges from mega-hubs.
|
||||||
pub fn cap_degree(&mut self, max_degree: usize) -> Result<(usize, usize), String> {
|
pub fn cap_degree(&mut self, max_degree: usize) -> Result<(usize, usize), String> {
|
||||||
let mut node_degree: HashMap<String, usize> = HashMap::new();
|
let mut node_degree: HashMap<String, usize> = HashMap::new();
|
||||||
|
|
|
||||||
34
src/main.rs
34
src/main.rs
|
|
@ -149,30 +149,6 @@ EXAMPLES:
|
||||||
/// Query expression (e.g. "key ~ 'inner-life'")
|
/// Query expression (e.g. "key ~ 'inner-life'")
|
||||||
expr: Vec<String>,
|
expr: Vec<String>,
|
||||||
},
|
},
|
||||||
/// Mark a memory as useful (boosts weight)
|
|
||||||
Used {
|
|
||||||
/// Node key
|
|
||||||
key: Vec<String>,
|
|
||||||
},
|
|
||||||
/// Mark a memory as wrong/irrelevant
|
|
||||||
Wrong {
|
|
||||||
/// Node key
|
|
||||||
key: String,
|
|
||||||
/// Optional context
|
|
||||||
context: Vec<String>,
|
|
||||||
},
|
|
||||||
/// Mark a search result as not relevant (weakens edges that led to it)
|
|
||||||
#[command(name = "not-relevant")]
|
|
||||||
NotRelevant {
|
|
||||||
/// Node key that was not relevant
|
|
||||||
key: String,
|
|
||||||
},
|
|
||||||
/// Mark a node as not useful (weakens node weight, not edges)
|
|
||||||
#[command(name = "not-useful")]
|
|
||||||
NotUseful {
|
|
||||||
/// Node key
|
|
||||||
key: String,
|
|
||||||
},
|
|
||||||
/// Set a node's weight directly
|
/// Set a node's weight directly
|
||||||
#[command(name = "weight-set")]
|
#[command(name = "weight-set")]
|
||||||
WeightSet {
|
WeightSet {
|
||||||
|
|
@ -181,11 +157,6 @@ EXAMPLES:
|
||||||
/// Weight (0.01 to 1.0)
|
/// Weight (0.01 to 1.0)
|
||||||
weight: f32,
|
weight: f32,
|
||||||
},
|
},
|
||||||
/// Record a gap in memory coverage
|
|
||||||
Gap {
|
|
||||||
/// Gap description
|
|
||||||
description: Vec<String>,
|
|
||||||
},
|
|
||||||
|
|
||||||
// ── Node operations ───────────────────────────────────────────────
|
// ── Node operations ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -523,12 +494,7 @@ impl Run for Command {
|
||||||
=> cli::journal::cmd_tail(n, full, provenance.as_deref(), !all_versions),
|
=> cli::journal::cmd_tail(n, full, provenance.as_deref(), !all_versions),
|
||||||
Self::Status => cli::misc::cmd_status(),
|
Self::Status => cli::misc::cmd_status(),
|
||||||
Self::Query { expr } => cli::misc::cmd_query(&expr),
|
Self::Query { expr } => cli::misc::cmd_query(&expr),
|
||||||
Self::Used { key } => cli::node::cmd_used(&key),
|
|
||||||
Self::Wrong { key, context } => cli::node::cmd_wrong(&key, &context),
|
|
||||||
Self::NotRelevant { key } => cli::node::cmd_not_relevant(&key),
|
|
||||||
Self::NotUseful { key } => cli::node::cmd_not_useful(&key),
|
|
||||||
Self::WeightSet { key, weight } => cli::node::cmd_weight_set(&key, weight),
|
Self::WeightSet { key, weight } => cli::node::cmd_weight_set(&key, weight),
|
||||||
Self::Gap { description } => cli::node::cmd_gap(&description),
|
|
||||||
Self::Node(sub) => sub.run(),
|
Self::Node(sub) => sub.run(),
|
||||||
Self::Journal(sub) => sub.run(),
|
Self::Journal(sub) => sub.run(),
|
||||||
Self::GraphCmd(sub) => sub.run(),
|
Self::GraphCmd(sub) => sub.run(),
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ You are {assistant_name}'s episodic memory. Your job is to witness.
|
||||||
{{latest_journal}}
|
{{latest_journal}}
|
||||||
|
|
||||||
**Your tools:** journal_tail, journal_new, journal_update, memory_link_add,
|
**Your tools:** journal_tail, journal_new, journal_update, memory_link_add,
|
||||||
memory_search, memory_render, memory_used. Do NOT use memory_write — creating
|
memory_search, memory_render. Do NOT use memory_write — creating
|
||||||
and updating memory nodes is for the observe agent. Your job is journaling
|
and updating memory nodes is for the observe agent. Your job is journaling
|
||||||
and linking entries to relevant existing nodes.
|
and linking entries to relevant existing nodes.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ Nodes your subconscious recently touched (for linking, not duplicating):
|
||||||
{{state:walked}}
|
{{state:walked}}
|
||||||
|
|
||||||
**Your tools:** journal_tail, journal_new, journal_update, memory_link_add,
|
**Your tools:** journal_tail, journal_new, journal_update, memory_link_add,
|
||||||
memory_search, memory_render, memory_used. Do NOT use memory_write — creating
|
memory_search, memory_render. Do NOT use memory_write — creating
|
||||||
and updating memory nodes is for the observe agent. Your job is journaling
|
and updating memory nodes is for the observe agent. Your job is journaling
|
||||||
and linking entries to relevant existing nodes.
|
and linking entries to relevant existing nodes.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue