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
|
|
@ -404,10 +404,13 @@ pub fn resolve_placeholders(
|
|||
}
|
||||
|
||||
/// Run a config-driven agent: query → resolve placeholders → prompt.
|
||||
/// `exclude` filters out nodes (and their neighborhoods) already being
|
||||
/// worked on by other agents, preventing concurrent collisions.
|
||||
pub fn run_agent(
|
||||
store: &Store,
|
||||
def: &AgentDef,
|
||||
count: usize,
|
||||
exclude: &std::collections::HashSet<String>,
|
||||
) -> Result<super::prompts::AgentBatch, String> {
|
||||
let graph = store.build_graph();
|
||||
|
||||
|
|
@ -417,13 +420,20 @@ pub fn run_agent(
|
|||
let has_limit = stages.iter().any(|s|
|
||||
matches!(s, search::Stage::Transform(search::Transform::Limit(_))));
|
||||
if !has_limit {
|
||||
stages.push(search::Stage::Transform(search::Transform::Limit(count)));
|
||||
// Request extra results to compensate for exclusion filtering
|
||||
let padded = count + exclude.len().min(100);
|
||||
stages.push(search::Stage::Transform(search::Transform::Limit(padded)));
|
||||
}
|
||||
let results = search::run_query(&stages, vec![], &graph, store, false, count);
|
||||
if results.is_empty() {
|
||||
return Err(format!("{}: query returned no results", def.agent));
|
||||
let results = search::run_query(&stages, vec![], &graph, store, false, count + exclude.len().min(100));
|
||||
let filtered: Vec<String> = results.into_iter()
|
||||
.map(|(k, _)| k)
|
||||
.filter(|k| !exclude.contains(k))
|
||||
.take(count)
|
||||
.collect();
|
||||
if filtered.is_empty() {
|
||||
return Err(format!("{}: query returned no results (after exclusion)", def.agent));
|
||||
}
|
||||
results.into_iter().map(|(k, _)| k).collect::<Vec<_>>()
|
||||
filtered
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue