Files
agcore/examples/sub_agent_dispatch_demo.rs
T
徐涛 46de111965 feat(engine): 实现 Agent 角色热切换与子代理调度
新增 SessionManager::switch_agent / dispatch / dispatch_all / dispatch_stream
四个核心方法,补齐多 Agent 基础系统原语。

交付物:
- switch_agent 运行时替换 Arc<dyn Agent>,保留上下文并更新 SessionMeta
- dispatch 单任务派发:create_child → inherit_memory → submit_turn
- dispatch_all 并行派发:Semaphore 并发控制 + 部分成功语义
- dispatch_stream 流式派发:unbounded_channel + spawn task 消息重建 + finalize_turn
- DispatchConfig / SubTaskResult / SubTaskStreamEvent 公开类型
- 4 个端到端示例(agent_switch_demo / sub_agent_dispatch_demo /
  bridge_keys_demo / dispatch_stream_demo)

辅助变更:
- EngineError 新增 DispatchFailed 变体
- CostTracker 实现 From<Usage> 转换
- save_session_meta / load_session_meta 改 pub(crate) 供 switch.rs 使用

测试: +17 个内联测试(4 switch + 5 dispatch + 4 dispatch_all +
4 dispatch_stream),全量 391 passed / 0 failed,
clippy 0 警告,doc 0 warning。
2026-07-15 11:15:58 +08:00

142 lines
4.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! sub_agent_dispatch_demo —— SubAgent 并行派发示例。
//!
//! 演示:
//! 1. 创建父 session"主编" agent
//! 2. 并行 dispatch_all 3 个子 agent(研究员 / 写手 / 审校)
//! 3. 收集子任务结果
//! 4. 验证树形结构:children(parent_id) 应返回 3 个子 ID
//!
//! 运行:`cargo run --example sub_agent_dispatch_demo`
use std::sync::Arc;
use agcore::agent::{Agent, AgentBuilder};
use agcore::engine::{SessionManager, SubTaskResult};
use agcore::llm::hooks::HookExecutor;
use agcore::llm::mock::MockProvider;
use agcore::llm::types::Usage;
use agcore::llm::types::message::{ContentBlock, Message};
use agcore::llm::types::response_v2::{MessageResponse, StopReason};
use agcore::memory::store::InMemoryStore;
use agcore::tools::ToolRegistry;
struct EditorAgent;
struct ResearcherAgent;
struct WriterAgent;
struct ReviewerAgent;
impl Agent for EditorAgent {
fn name(&self) -> &str {
"editor"
}
fn system_prompt(&self) -> Option<&str> {
Some("You are an editor coordinating a team.")
}
}
impl Agent for ResearcherAgent {
fn name(&self) -> &str {
"researcher"
}
fn system_prompt(&self) -> Option<&str> {
Some("You are a researcher. Provide 3 key findings.")
}
}
impl Agent for WriterAgent {
fn name(&self) -> &str {
"writer"
}
fn system_prompt(&self) -> Option<&str> {
Some("You are a writer. Draft a section.")
}
}
impl Agent for ReviewerAgent {
fn name(&self) -> &str {
"reviewer"
}
fn system_prompt(&self) -> Option<&str> {
Some("You are a reviewer. Check for accuracy.")
}
}
fn assistant_text(text: &str) -> MessageResponse {
MessageResponse {
id: String::new(),
model: String::new(),
message: Message::Assistant {
content: vec![ContentBlock::Text { text: text.into() }],
},
usage: Usage::from_input_output(8, 4),
stop_reason: StopReason::Stop,
extra: Default::default(),
}
}
fn print_result(name: &str, r: &Result<SubTaskResult, agcore::engine::EngineError>) {
match r {
Ok(res) => println!(
"{name} (child_id={}): {} tokens",
&res.child_id[..20.min(res.child_id.len())],
res.usage.total().total_tokens,
),
Err(e) => println!("{name}: {e}"),
}
}
#[tokio::main]
async fn main() {
println!("=== SubAgent Dispatch Demo ===\n");
let store: Arc<dyn agcore::memory::store::MemoryStore> = Arc::new(InMemoryStore::new());
let provider = Arc::new(MockProvider::new(vec![
assistant_text("Researcher: finding 1, 2, 3"),
assistant_text("Writer: section drafted"),
assistant_text("Reviewer: looks good"),
]));
let bundle = Arc::new(
AgentBuilder::new()
.provider(provider)
.tool_registry(Arc::new(ToolRegistry::new()))
.hook_executor(Arc::new(HookExecutor::new()))
.session_memory_backend(store.clone())
.build()
.expect("RuntimeBundle"),
);
let editor: Arc<dyn Agent> = Arc::new(EditorAgent);
let researcher: Arc<dyn Agent> = Arc::new(ResearcherAgent);
let writer: Arc<dyn Agent> = Arc::new(WriterAgent);
let reviewer: Arc<dyn Agent> = Arc::new(ReviewerAgent);
let sm = Arc::new(SessionManager::new(store));
let parent_id = sm.create(editor, bundle.clone()).await.expect("create");
println!("[1] parent session created: {parent_id}");
// dispatch_all 3 个子 agent
println!("[2] dispatching 3 sub-agents in parallel...\n");
let results = sm
.dispatch_all(
&parent_id,
vec![
(researcher, "Research topic X".to_string()),
(writer, "Draft intro section".to_string()),
(reviewer, "Review draft".to_string()),
],
agcore::engine::DispatchConfig::default(),
)
.await;
print_result("researcher", &results[0]);
print_result("writer", &results[1]);
print_result("reviewer", &results[2]);
let success_count = results.iter().filter(|r| r.is_ok()).count();
assert_eq!(success_count, 3, "all 3 should succeed");
// 验证树形
let children = sm.children(&parent_id).await.expect("children");
println!("\n[3] children(parent) = {} session(s)", children.len());
assert_eq!(children.len(), 3);
println!("\n✓ dispatch_all completed: 3/3 sub-agents succeeded");
}