agent visits: track when agents successfully process nodes
New append-only visits.capnp log records which agent processed which
node and when. Only recorded on successful completion — transient
errors don't mark nodes as "seen."
Schema: AgentVisit{nodeUuid, nodeKey, agent, timestamp, outcome}
Storage: append_visits(), replay_visits(), in-memory VisitIndex
Recording: daemon records visits after successful LLM call
API: agent_prompt() returns AgentBatch{prompt, node_keys} so callers
know which nodes to mark as visited.
Groundwork for using visit recency in agent node selection — agents
will deprioritize recently-visited nodes.
This commit is contained in:
parent
9f14a29181
commit
0e1e5a1981
6 changed files with 237 additions and 34 deletions
|
|
@ -137,10 +137,11 @@ fn job_consolidation_agent(
|
|||
};
|
||||
ctx.log_line(&format!("building prompt: {}", label));
|
||||
|
||||
let prompt = super::prompts::agent_prompt(&store, &agent, batch)?;
|
||||
ctx.log_line(&format!("prompt: {} chars, calling Sonnet", prompt.len()));
|
||||
let agent_batch = super::prompts::agent_prompt(&store, &agent, batch)?;
|
||||
ctx.log_line(&format!("prompt: {} chars ({} nodes), calling Sonnet",
|
||||
agent_batch.prompt.len(), agent_batch.node_keys.len()));
|
||||
|
||||
let response = super::llm::call_sonnet("consolidate", &prompt)?;
|
||||
let response = super::llm::call_sonnet("consolidate", &agent_batch.prompt)?;
|
||||
|
||||
let ts = crate::store::format_datetime(crate::store::now_epoch())
|
||||
.replace([':', '-', 'T'], "");
|
||||
|
|
@ -148,6 +149,13 @@ fn job_consolidation_agent(
|
|||
store.upsert_provenance(&report_key, &response,
|
||||
crate::store::Provenance::AgentConsolidate).ok();
|
||||
|
||||
// Record visits for successfully processed nodes
|
||||
if !agent_batch.node_keys.is_empty() {
|
||||
if let Err(e) = store.record_agent_visits(&agent_batch.node_keys, &agent) {
|
||||
ctx.log_line(&format!("visit recording: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
ctx.log_line(&format!("done: {} lines → {}", response.lines().count(), report_key));
|
||||
Ok(())
|
||||
})
|
||||
|
|
@ -165,14 +173,16 @@ fn job_rename_agent(
|
|||
let batch = if batch_size == 0 { 10 } else { batch_size };
|
||||
ctx.log_line(&format!("building prompt: rename (batch={})", batch));
|
||||
|
||||
let prompt = super::prompts::agent_prompt(&store, "rename", batch)?;
|
||||
ctx.log_line(&format!("prompt: {} chars, calling Sonnet", prompt.len()));
|
||||
let agent_batch = super::prompts::agent_prompt(&store, "rename", batch)?;
|
||||
ctx.log_line(&format!("prompt: {} chars ({} nodes), calling Sonnet",
|
||||
agent_batch.prompt.len(), agent_batch.node_keys.len()));
|
||||
|
||||
let response = super::llm::call_sonnet("consolidate", &prompt)?;
|
||||
let response = super::llm::call_sonnet("consolidate", &agent_batch.prompt)?;
|
||||
|
||||
// Parse RENAME actions directly from response
|
||||
let mut applied = 0;
|
||||
let mut skipped = 0;
|
||||
let mut successfully_renamed: Vec<String> = Vec::new();
|
||||
for line in response.lines() {
|
||||
let trimmed = line.trim();
|
||||
if !trimmed.starts_with("RENAME ") { continue; }
|
||||
|
|
@ -208,6 +218,7 @@ fn job_rename_agent(
|
|||
match store.rename_node(&resolved, new_key) {
|
||||
Ok(()) => {
|
||||
ctx.log_line(&format!("renamed: {} → {}", resolved, new_key));
|
||||
successfully_renamed.push(new_key.to_string());
|
||||
applied += 1;
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
@ -221,6 +232,13 @@ fn job_rename_agent(
|
|||
store.save()?;
|
||||
}
|
||||
|
||||
// Record visits for successfully renamed nodes
|
||||
if !successfully_renamed.is_empty() {
|
||||
if let Err(e) = store.record_agent_visits(&successfully_renamed, "rename") {
|
||||
ctx.log_line(&format!("visit recording: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// Also store the report for auditing
|
||||
let ts = crate::store::format_datetime(crate::store::now_epoch())
|
||||
.replace([':', '-', 'T'], "");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue