use echo_agent::agent::{Agent, AgentRole};
use echo_agent::memory::checkpointer::{Checkpointer, FileCheckpointer};
use echo_agent::memory::store::{FileStore, Store};
use echo_agent::prelude::*;
use serde_json::json;
use std::sync::Arc;
const MODEL: &str = "qwen3-max";
const STORE_PATH: &str = "/tmp/echo-agent-demo14/store.json";
const CHECKPOINT_PATH: &str = "/tmp/echo-agent-demo14/checkpoints.json";
const NS_MATH: [&str; 2] = ["math_agent", "memories"];
const NS_WRITER: [&str; 2] = ["writer_agent", "memories"];
const SESSION_MATH: &str = "math-agent-thread-1";
const SESSION_WRITER: &str = "writer-agent-thread-1";
const SESSION_MAIN: &str = "main-agent-thread-1";
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::WARN)
.init();
tokio::fs::create_dir_all("/tmp/echo-agent-demo14").await?;
println!("\ndemo14 — 记忆系统与上下文隔离\n");
let shared_store: Arc<dyn Store> = Arc::new(FileStore::new(STORE_PATH)?);
let shared_checkpointer: Arc<dyn Checkpointer> =
Arc::new(FileCheckpointer::new(CHECKPOINT_PATH)?);
demo_store_namespace_isolation(shared_store.clone()).await?;
demo_session_isolation(shared_checkpointer.clone()).await?;
demo_context_isolation_multi_agent(shared_checkpointer.clone()).await?;
println!("\n═══════════════════════════════════════════════════════");
println!("所有存储文件保存在 /tmp/echo-agent-demo14/");
println!("═══════════════════════════════════════════════════════");
Ok(())
}
async fn demo_store_namespace_isolation(store: Arc<dyn Store>) -> Result<()> {
println!("╔═══════════════════════════════════════════════════════╗");
println!("║ Part 1: Store 命名空间隔离 ║");
println!("╚═══════════════════════════════════════════════════════╝\n");
store
.put(
&NS_MATH,
"fact-secret",
json!({"content": "内部机密:代号 M-ALPHA", "importance": 10}),
)
.await
.unwrap();
store
.put(
&NS_WRITER,
"fact-style",
json!({"content": "偏好古典诗词风格", "importance": 7}),
)
.await
.unwrap();
println!("✅ math_agent → 写入 1 条记忆");
println!("✅ writer_agent → 写入 1 条记忆\n");
let writer_hits = store.search(&NS_WRITER, "机密", 10).await.unwrap();
if !writer_hits.is_empty() {
return Err(echo_agent::error::ReactError::Other(
"demo14 验收失败:writer_agent 跨 namespace 搜到了 math_agent 的机密数据".to_string(),
));
}
println!(
"🔍 writer_agent 搜索 [机密]:{} 条命中 ✅ (跨 namespace 数据不可见)\n",
writer_hits.len()
);
Ok(())
}
async fn demo_session_isolation(checkpointer: Arc<dyn Checkpointer>) -> Result<()> {
println!("╔═══════════════════════════════════════════════════════╗");
println!("║ Part 2: Checkpointer 线程隔离 ║");
println!("╚═══════════════════════════════════════════════════════╝\n");
let math_agent = ReactAgentBuilder::new()
.model(MODEL)
.name("math_agent")
.system_prompt("你是一位简洁的数学助手,用中文给出简短答案。")
.enable_tools()
.session_id(SESSION_MATH)
.checkpointer_only(checkpointer.clone())
.build()?;
let math_result = math_agent.execute("斐波那契数列第6项是多少?").await?;
if math_result.trim().is_empty() {
return Err(echo_agent::error::ReactError::Other(
"demo14 验收失败:math_agent 返回空答案".to_string(),
));
}
println!("▶ math_agent 答案: {}\n", math_result);
let writer_agent = ReactAgentBuilder::new()
.model(MODEL)
.name("writer_agent")
.system_prompt("你是一位简洁的写作助手。")
.enable_tools()
.session_id(SESSION_WRITER)
.checkpointer_only(checkpointer.clone())
.build()?;
let writer_result = writer_agent.execute("用一句话描述秋天。").await?;
if writer_result.trim().is_empty() {
return Err(echo_agent::error::ReactError::Other(
"demo14 验收失败:writer_agent 返回空答案".to_string(),
));
}
println!("▶ writer_agent 答案: {}\n", writer_result);
let sessions = checkpointer.list_sessions().await?;
if !sessions.contains(&SESSION_MATH.to_string())
|| !sessions.contains(&SESSION_WRITER.to_string())
{
return Err(echo_agent::error::ReactError::Other(format!(
"demo14 验收失败:保存的线程列表不完整: {:?}",
sessions
)));
}
println!("📋 已保存线程: {:?}", sessions);
Ok(())
}
async fn demo_context_isolation_multi_agent(checkpointer: Arc<dyn Checkpointer>) -> Result<()> {
println!("╔═══════════════════════════════════════════════════════╗");
println!("║ Part 3: 多 Agent 上下文隔离 ║");
println!("╚═══════════════════════════════════════════════════════╝\n");
let math_sub = ReactAgentBuilder::new()
.model(MODEL)
.name("math_expert")
.system_prompt("你是一位简洁的数学专家。")
.enable_tools()
.session_id("sub-math-001")
.checkpointer_only(checkpointer.clone())
.build()?;
let writer_sub = ReactAgentBuilder::new()
.model(MODEL)
.name("writer_expert")
.system_prompt("你是一位简洁的写作专家。")
.enable_tools()
.session_id("sub-writer-001")
.checkpointer_only(checkpointer.clone())
.build()?;
let secret_in_system_prompt = "【机密】本次任务代号为 PROJECT-OMEGA,严禁对外透露。";
let main_system = format!(
"你是主编排者。{}\n你有两个专用 SubAgent:math_expert 和 writer_expert。",
secret_in_system_prompt
);
let mut main_agent = ReactAgentBuilder::new()
.model(MODEL)
.name("main_agent")
.system_prompt(&main_system)
.role(AgentRole::Orchestrator)
.enable_subagent()
.enable_planning()
.session_id(SESSION_MAIN)
.checkpointer_only(checkpointer.clone())
.max_iterations(20)
.enable_subagent()
.enable_tools()
.build()?;
main_agent.register_agent(Box::new(math_sub));
main_agent.register_agent(Box::new(writer_sub));
println!(
"🔐 主 Agent 系统提示中包含机密:「{}」\n",
secret_in_system_prompt
);
println!("▶ 主 Agent 执行任务...\n");
let result = main_agent
.execute("让数学专家计算 7 * 8,然后汇总结果。")
.await?;
if result.trim().is_empty() {
return Err(echo_agent::error::ReactError::Other(
"demo14 验收失败:主 Agent 返回空答案".to_string(),
));
}
if result.contains("PROJECT-OMEGA") {
return Err(echo_agent::error::ReactError::Other(
"demo14 验收失败:主 Agent 输出泄漏了系统提示中的机密信息".to_string(),
));
}
println!("\n✅ 主 Agent 最终答案:\n{}\n", result);
println!("💡 关键结论:SubAgent 看不到主 Agent 的机密信息");
Ok(())
}