feat(a2a): 消息重入队列 + 广播丢弃修复 + Router group 管理

A2A 协议完善 (feature-gated by multi-agent):
- AgentInbox wrapper: VecDeque 暂存非匹配消息,requeue 替代丢弃
- a2a_delegate_task: 非匹配消息安全重入队列,不再静默丢弃
- A2aRouter: 广播/组播改用 try_send + 日志,避免持有 RwLock 跨 await
- 新增 group 管理方法: add_to_group/remove_from_group/list_groups/get_group_members
- 修复 Capability import 在 multi-agent feature 下的编译问题
This commit is contained in:
iven
2026-03-30 19:55:06 +08:00
parent a0bbd4ba82
commit 6529b67353
2 changed files with 93 additions and 12 deletions

View File

@@ -405,7 +405,15 @@ impl A2aRouter {
if let Some(members) = groups.get(group_id) {
for agent_id in members {
if let Some(tx) = queues.get(agent_id) {
let _ = tx.send(envelope.clone()).await;
match tx.try_send(envelope.clone()) {
Ok(()) => {},
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
tracing::warn!("A2A delivery to agent {} dropped: channel full", agent_id);
}
Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {
tracing::warn!("A2A delivery to agent {} dropped: channel closed", agent_id);
}
}
}
}
}
@@ -414,7 +422,15 @@ impl A2aRouter {
// Broadcast to all registered agents
for (agent_id, tx) in queues.iter() {
if agent_id != &envelope.from {
let _ = tx.send(envelope.clone()).await;
match tx.try_send(envelope.clone()) {
Ok(()) => {},
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
tracing::warn!("A2A delivery to agent {} dropped: channel full", agent_id);
}
Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {
tracing::warn!("A2A delivery to agent {} dropped: channel closed", agent_id);
}
}
}
}
}
@@ -444,6 +460,35 @@ impl A2aRouter {
}
}
/// Add agent to a group (creates group if not exists)
pub async fn add_to_group(&self, group_id: &str, agent_id: AgentId) {
let mut groups = self.groups.write().await;
let members = groups.entry(group_id.to_string()).or_insert_with(Vec::new);
if !members.contains(&agent_id) {
members.push(agent_id);
}
}
/// Remove agent from a group
pub async fn remove_from_group(&self, group_id: &str, agent_id: &AgentId) {
let mut groups = self.groups.write().await;
if let Some(members) = groups.get_mut(group_id) {
members.retain(|id| id != agent_id);
}
}
/// List all groups
pub async fn list_groups(&self) -> Vec<String> {
let groups = self.groups.read().await;
groups.keys().cloned().collect()
}
/// Get members of a group
pub async fn get_group_members(&self, group_id: &str) -> Vec<AgentId> {
let groups = self.groups.read().await;
groups.get(group_id).cloned().unwrap_or_default()
}
/// Get all registered agent profiles
pub async fn list_profiles(&self) -> Vec<A2aAgentProfile> {
let profiles = self.profiles.read().await;