diff --git a/poc-memory/agents/organize.agent b/poc-memory/agents/organize.agent index dbbb7e0..9377c2c 100644 --- a/poc-memory/agents/organize.agent +++ b/poc-memory/agents/organize.agent @@ -1,4 +1,4 @@ -{"agent":"organize","query":"all | key:*identity* | sort:degree | limit:1","model":"sonnet","schedule":"weekly","tools":["Bash(poc-memory:*)"]} +{"agent":"organize","query":"all | not-visited:organize,0 | sort:degree | limit:5","model":"sonnet","schedule":"weekly","tools":["Bash(poc-memory:*)"]} # Memory Organization Agent diff --git a/poc-memory/src/agents/daemon.rs b/poc-memory/src/agents/daemon.rs index f2da04a..ca309a2 100644 --- a/poc-memory/src/agents/daemon.rs +++ b/poc-memory/src/agents/daemon.rs @@ -125,11 +125,17 @@ fn job_consolidation_agent( ) -> Result<(), TaskError> { let agent = agent_type.to_string(); let batch = batch_size; - run_job(ctx, &format!("c-{}", agent), || { + let job_name = format!("c-{}", agent); + let job_name2 = job_name.clone(); + run_job(ctx, &job_name, || { ctx.log_line("loading store"); let mut store = crate::store::Store::load()?; ctx.log_line(&format!("running agent: {} (batch={})", agent, batch)); - let (total, applied) = super::knowledge::run_and_apply(&mut store, &agent, batch, "consolidate")?; + let log = |msg: &str| { + ctx.log_line(msg); + log_event(&job_name2, "progress", msg); + }; + let (total, applied) = super::knowledge::run_and_apply_with_log(&mut store, &agent, batch, "consolidate", &log)?; ctx.log_line(&format!("done: {} actions ({} applied)", total, applied)); Ok(()) }) @@ -147,7 +153,8 @@ fn job_rename_agent( let batch = if batch_size == 0 { 10 } else { batch_size }; ctx.log_line(&format!("running rename agent (batch={})", batch)); - let result = super::knowledge::run_one_agent(&mut store, "rename", batch, "consolidate")?; + let log = |msg: &str| ctx.log_line(msg); + let result = super::knowledge::run_one_agent(&mut store, "rename", batch, "consolidate", &log)?; // Parse RENAME actions from response (rename uses its own format, not WRITE_NODE/LINK/REFINE) let mut applied = 0; diff --git a/poc-memory/src/agents/defs.rs b/poc-memory/src/agents/defs.rs index d6db80d..0185601 100644 --- a/poc-memory/src/agents/defs.rs +++ b/poc-memory/src/agents/defs.rs @@ -184,6 +184,11 @@ fn resolve( } cluster.sort_by(|a, b| a.0.cmp(&b.0)); + // Cap cluster size — agent has tools to explore more if needed + if cluster.len() > 20 { + cluster.truncate(20); + } + // Similarity pairs let pairs = crate::similarity::pairwise_similar(&cluster, 0.4); diff --git a/poc-memory/src/agents/knowledge.rs b/poc-memory/src/agents/knowledge.rs index 66aeb4b..cbefe2b 100644 --- a/poc-memory/src/agents/knowledge.rs +++ b/poc-memory/src/agents/knowledge.rs @@ -579,13 +579,33 @@ pub fn run_and_apply( batch_size: usize, llm_tag: &str, ) -> Result<(usize, usize), String> { - let result = run_one_agent(store, agent_name, batch_size, llm_tag)?; + run_and_apply_with_log(store, agent_name, batch_size, llm_tag, &|_| {}) +} + +pub fn run_and_apply_with_log( + store: &mut Store, + agent_name: &str, + batch_size: usize, + llm_tag: &str, + log: &dyn Fn(&str), +) -> Result<(usize, usize), String> { + let result = run_one_agent(store, agent_name, batch_size, llm_tag, log)?; let actions = resolve_action_names(store, result.actions); let ts = store::compact_timestamp(); let mut applied = 0; for action in &actions { + let desc = match &action.kind { + ActionKind::WriteNode { key, .. } => format!("WRITE {}", key), + ActionKind::Refine { key, .. } => format!("REFINE {}", key), + ActionKind::Link { source, target } => format!("LINK {} → {}", source, target), + ActionKind::Demote { key } => format!("DEMOTE {}", key), + ActionKind::Delete { key } => format!("DELETE {}", key), + }; if apply_action(store, action, agent_name, &ts, 0) { + log(&format!("applied: {}", desc)); applied += 1; + } else { + log(&format!("skipped: {}", desc)); } } Ok((actions.len(), applied)) @@ -600,13 +620,29 @@ pub fn run_one_agent( agent_name: &str, batch_size: usize, llm_tag: &str, + log: &dyn Fn(&str), ) -> Result { let def = super::defs::get_def(agent_name) .ok_or_else(|| format!("no .agent file for {}", agent_name))?; + + log("building prompt"); let agent_batch = super::defs::run_agent(store, &def, batch_size)?; + let prompt_kb = agent_batch.prompt.len() / 1024; + let tools_desc = if def.tools.is_empty() { "no tools".into() } + else { format!("{} tools", def.tools.len()) }; + log(&format!("prompt {}KB, model={}, {}, {} nodes", + prompt_kb, def.model, tools_desc, agent_batch.node_keys.len())); + for key in &agent_batch.node_keys { + log(&format!(" node: {}", key)); + } + + log("calling LLM"); let output = llm::call_for_def(&def, &agent_batch.prompt)?; + let output_kb = output.len() / 1024; + log(&format!("response {}KB", output_kb)); + // Store raw output for audit trail let ts = store::compact_timestamp(); let report_key = format!("_{}-{}-{}", llm_tag, agent_name, ts); @@ -616,6 +652,8 @@ pub fn run_one_agent( let actions = parse_all_actions(&output); let no_ops = count_no_ops(&output); + log(&format!("parsed {} actions, {} no-ops", actions.len(), no_ops)); + // Record visits for processed nodes if !agent_batch.node_keys.is_empty() { store.record_agent_visits(&agent_batch.node_keys, agent_name).ok(); @@ -889,7 +927,7 @@ fn run_cycle( for agent_name in &agent_names { eprintln!("\n --- {} (n={}) ---", agent_name, config.batch_size); - let result = match run_one_agent(&mut store, agent_name, config.batch_size, "knowledge") { + let result = match run_one_agent(&mut store, agent_name, config.batch_size, "knowledge", &|msg| eprintln!(" {}", msg)) { Ok(r) => r, Err(e) => { eprintln!(" ERROR: {}", e);