agents: in-flight node exclusion prevents concurrent collisions
Track which nodes are being processed across all concurrent agents. When an agent claims seeds, it adds them and their strongly-connected neighbors (score = link_strength * node_weight > 0.15) to a shared HashSet. Concurrent agents filter these out when running their query, ensuring they work on distant parts of the graph. This replaces the eager-visit approach with a proper scheduling mechanism: the daemon serializes seed selection while parallelizing LLM work. The in-flight set is released on completion (or error). Previously: core-personality rewritten 12x, irc-regulars 10x, same node superseded 12x — concurrent agents all selected the same high-degree hub nodes. Now they'll spread across the graph. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3fc108a251
commit
d0f126b709
4 changed files with 128 additions and 18 deletions
|
|
@ -41,7 +41,20 @@ pub fn run_and_apply_with_log(
|
|||
llm_tag: &str,
|
||||
log: &dyn Fn(&str),
|
||||
) -> Result<(), String> {
|
||||
let result = run_one_agent(store, agent_name, batch_size, llm_tag, log, false)?;
|
||||
run_and_apply_excluded(store, agent_name, batch_size, llm_tag, log, &Default::default())
|
||||
}
|
||||
|
||||
/// Like run_and_apply_with_log but with an in-flight exclusion set.
|
||||
/// Returns the keys that were processed (for the daemon to track).
|
||||
pub fn run_and_apply_excluded(
|
||||
store: &mut Store,
|
||||
agent_name: &str,
|
||||
batch_size: usize,
|
||||
llm_tag: &str,
|
||||
log: &dyn Fn(&str),
|
||||
exclude: &std::collections::HashSet<String>,
|
||||
) -> Result<(), String> {
|
||||
let result = run_one_agent_excluded(store, agent_name, batch_size, llm_tag, log, false, exclude)?;
|
||||
|
||||
// Mark conversation segments as mined after successful processing
|
||||
if agent_name == "observation" {
|
||||
|
|
@ -88,18 +101,25 @@ pub fn run_one_agent(
|
|||
llm_tag: &str,
|
||||
log: &dyn Fn(&str),
|
||||
debug: bool,
|
||||
) -> Result<AgentResult, String> {
|
||||
run_one_agent_excluded(store, agent_name, batch_size, llm_tag, log, debug, &Default::default())
|
||||
}
|
||||
|
||||
/// Like run_one_agent but excludes nodes currently being worked on by other agents.
|
||||
pub fn run_one_agent_excluded(
|
||||
store: &mut Store,
|
||||
agent_name: &str,
|
||||
batch_size: usize,
|
||||
llm_tag: &str,
|
||||
log: &dyn Fn(&str),
|
||||
debug: bool,
|
||||
exclude: &std::collections::HashSet<String>,
|
||||
) -> Result<AgentResult, String> {
|
||||
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)?;
|
||||
|
||||
// Eagerly record visits so concurrent agents pick different seeds.
|
||||
// The not-visited query filter checks this timestamp.
|
||||
if !agent_batch.node_keys.is_empty() {
|
||||
store.record_agent_visits(&agent_batch.node_keys, agent_name).ok();
|
||||
}
|
||||
let agent_batch = super::defs::run_agent(store, &def, batch_size, exclude)?;
|
||||
|
||||
run_one_agent_inner(store, agent_name, &def, agent_batch, llm_tag, log, debug)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue