Agent-trigger: permanent deltaker vs @bot-nevnelse

To spor for agent-respons:
1. Permanent: agent er member_of → svar på alle meldinger
2. @bot-nevnelse: bruker skriver @bot → engangs-svar, agent
   trenger ikke være deltaker

Femkompis-problemet løst: @bot i en gruppechat gir ett svar,
ikke evig bot-loop. Dedikerte bot-chatter (member_of) fungerer
som før.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
vegard 2026-03-20 06:18:53 +00:00
parent f52d23de54
commit 6cf421dbc7
2 changed files with 58 additions and 34 deletions

View file

@ -203,6 +203,14 @@ async fn load_agent_config(db: &PgPool, agent_node_id: Uuid) -> Result<AgentConf
}) })
} }
/// Finn standard-agenten for @bot-nevnelser (den aktive agenten med høyest prioritet).
pub async fn find_default_agent(db: &PgPool) -> Result<Option<Uuid>, sqlx::Error> {
sqlx::query_scalar(
"SELECT node_id FROM agent_identities WHERE is_active = true ORDER BY created_at ASC LIMIT 1",
).fetch_optional(db).await
}
/// Finn agent som er permanent deltaker i en kommunikasjonsnode.
pub async fn find_agent_participant(db: &PgPool, communication_id: Uuid) -> Result<Option<Uuid>, sqlx::Error> { pub async fn find_agent_participant(db: &PgPool, communication_id: Uuid) -> Result<Option<Uuid>, sqlx::Error> {
// Finn agent-deltaker uansett is_active — kill switch sjekkes i handleren. // Finn agent-deltaker uansett is_active — kill switch sjekkes i handleren.
// Dette sikrer at jobben opprettes slik at eksterne handlers (Claude Code) kan plukke den. // Dette sikrer at jobben opprettes slik at eksterne handlers (Claude Code) kan plukke den.

View file

@ -808,20 +808,44 @@ pub async fn create_node(
None None
}; };
// -- Agent-trigger: sjekk om kommunikasjonsnoden har en agent-deltaker -- // -- Agent-trigger: to spor --
// 1. Permanent deltaker: agent er member_of → svar på alle meldinger
// 2. @bot-nevnelse: bruker nevner @bot i teksten → engangs-svar
if let Some(ctx_id) = req.context_id { if let Some(ctx_id) = req.context_id {
let db_clone = state.db.clone(); let db_clone = state.db.clone();
let user_node_id = user.node_id; let user_node_id = user.node_id;
let created_node_id = node_id; let created_node_id = node_id;
let message_content = content.clone();
tokio::spawn(async move { tokio::spawn(async move {
match crate::agent::find_agent_participant(&db_clone, ctx_id).await { // Spor 1: Permanent deltaker (agent er member_of i chatten)
Ok(Some(agent_id)) if agent_id != effective_identity => { let permanent_agent = match crate::agent::find_agent_participant(&db_clone, ctx_id).await {
// Agent funnet, og melding er ikke fra agenten selv Ok(Some(agent_id)) if agent_id != effective_identity => Some(agent_id),
Ok(_) => None,
Err(e) => {
tracing::error!(communication_id = %ctx_id, error = %e, "Feil ved agent-deltaker-sjekk");
None
}
};
// Spor 2: @bot-nevnelse (agent er IKKE deltaker, men nevnes i teksten)
let mention_agent = if permanent_agent.is_none() && message_content.contains("@bot") {
match crate::agent::find_default_agent(&db_clone).await {
Ok(Some(agent_id)) if agent_id != effective_identity => Some(agent_id),
_ => None,
}
} else {
None
};
// Trigger jobb for den agenten som matcher
if let Some(agent_id) = permanent_agent.or(mention_agent) {
let trigger = if permanent_agent.is_some() { "permanent" } else { "mention" };
let payload = serde_json::json!({ let payload = serde_json::json!({
"communication_id": ctx_id.to_string(), "communication_id": ctx_id.to_string(),
"message_id": created_node_id.to_string(), "message_id": created_node_id.to_string(),
"agent_node_id": agent_id.to_string(), "agent_node_id": agent_id.to_string(),
"sender_node_id": user_node_id.to_string() "sender_node_id": user_node_id.to_string(),
"trigger": trigger
}); });
match crate::jobs::enqueue(&db_clone, "agent_respond", payload, None, 8).await { match crate::jobs::enqueue(&db_clone, "agent_respond", payload, None, 8).await {
Ok(job_id) => { Ok(job_id) => {
@ -829,6 +853,7 @@ pub async fn create_node(
job_id = %job_id, job_id = %job_id,
communication_id = %ctx_id, communication_id = %ctx_id,
agent_node_id = %agent_id, agent_node_id = %agent_id,
trigger = trigger,
"agent_respond-jobb lagt i kø" "agent_respond-jobb lagt i kø"
); );
} }
@ -841,15 +866,6 @@ pub async fn create_node(
} }
} }
} }
Ok(_) => {} // Ingen agent, eller melding fra agenten selv
Err(e) => {
tracing::error!(
communication_id = %ctx_id,
error = %e,
"Feil ved agent-deltaker-sjekk"
);
}
}
}); });
} }