Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
- MockLlmDriver 基础设施 (zclaw-runtime/src/test_util.rs) - 经验闭环 E-01~06: 累积/溢出/反序列化/跨行业/并发/阈值 - Embedding 管道 EM-01~08: 路由/降级/维度不匹配/空查询/CJK/LLM Fallback/热更新 - Skill 执行 SK-01~03: 工具传递/纯 Prompt/锁竞争
144 lines
5.0 KiB
Rust
144 lines
5.0 KiB
Rust
//! Memory embedding tests (EM-07 ~ EM-08)
|
||
//!
|
||
//! Validates memory retrieval with embedding enhancement and configuration hot-update.
|
||
|
||
use std::sync::Arc;
|
||
use async_trait::async_trait;
|
||
use zclaw_growth::{
|
||
EmbeddingClient, MemoryEntry, MemoryRetriever, MemoryType, SqliteStorage, VikingAdapter,
|
||
};
|
||
use zclaw_types::AgentId;
|
||
|
||
/// Mock embedding client that returns deterministic 128-dim vectors.
|
||
struct MockEmbeddingClient {
|
||
dim: usize,
|
||
}
|
||
|
||
impl MockEmbeddingClient {
|
||
fn new() -> Self {
|
||
Self { dim: 128 }
|
||
}
|
||
}
|
||
|
||
#[async_trait::async_trait]
|
||
impl EmbeddingClient for MockEmbeddingClient {
|
||
async fn embed(&self, text: &str) -> Result<Vec<f32>, String> {
|
||
let mut vec = vec![0.0f32; self.dim];
|
||
for (i, b) in text.as_bytes().iter().enumerate() {
|
||
vec[i % self.dim] += (*b as f32) / 255.0;
|
||
}
|
||
let norm: f32 = vec.iter().map(|v| v * v).sum::<f32>().sqrt().max(1e-8);
|
||
for v in vec.iter_mut() {
|
||
*v /= norm;
|
||
}
|
||
Ok(vec)
|
||
}
|
||
|
||
fn is_available(&self) -> bool {
|
||
true
|
||
}
|
||
}
|
||
|
||
/// EM-07: Memory retrieval with embedding enhancement.
|
||
#[tokio::test]
|
||
async fn em07_memory_retrieval_embedding_enhanced() {
|
||
let storage = Arc::new(SqliteStorage::in_memory().await);
|
||
let adapter = Arc::new(VikingAdapter::new(storage));
|
||
|
||
let agent_id = AgentId::new();
|
||
|
||
// Store 20 mixed Chinese/English memories
|
||
let entries = vec![
|
||
("pref-theme", MemoryType::Preference, "用户偏好深色模式"),
|
||
("pref-language", MemoryType::Preference, "用户使用中文沟通"),
|
||
("know-rust", MemoryType::Knowledge, "Rust async programming with tokio"),
|
||
("know-python", MemoryType::Knowledge, "Python data science with pandas"),
|
||
("exp-report", MemoryType::Experience, "月度报表生成经验:使用Excel宏自动化"),
|
||
("know-react", MemoryType::Knowledge, "React hooks patterns"),
|
||
("pref-editor", MemoryType::Preference, "偏好 VS Code 编辑器"),
|
||
("exp-schedule", MemoryType::Experience, "排班冲突解决方案:协商调换"),
|
||
("know-sql", MemoryType::Knowledge, "SQL query optimization techniques"),
|
||
("exp-deploy", MemoryType::Experience, "部署失败经验:端口冲突检测"),
|
||
("know-docker", MemoryType::Knowledge, "Docker container networking"),
|
||
("pref-font", MemoryType::Preference, "字体大小偏好 14px"),
|
||
("know-tokio", MemoryType::Knowledge, "Tokio runtime configuration"),
|
||
("exp-review", MemoryType::Experience, "代码审查经验:关注错误处理"),
|
||
("know-git", MemoryType::Knowledge, "Git rebase vs merge strategies"),
|
||
("exp-perf", MemoryType::Experience, "性能优化经验:数据库索引"),
|
||
("pref-timezone", MemoryType::Preference, "时区 UTC+8"),
|
||
("know-linux", MemoryType::Knowledge, "Linux system administration basics"),
|
||
("exp-test", MemoryType::Experience, "测试经验:TDD方法论实践"),
|
||
("know-api", MemoryType::Knowledge, "RESTful API design principles"),
|
||
];
|
||
|
||
for (key, mtype, content) in &entries {
|
||
let entry = MemoryEntry::new(
|
||
&agent_id.to_string(),
|
||
*mtype,
|
||
key,
|
||
content.to_string(),
|
||
);
|
||
adapter.store(&entry).await.unwrap();
|
||
}
|
||
|
||
// Create retriever with embedding
|
||
let retriever = MemoryRetriever::new(adapter);
|
||
retriever.set_embedding_client(Arc::new(MockEmbeddingClient::new()));
|
||
|
||
// Retrieve memories about user preferences
|
||
let result = retriever
|
||
.retrieve(&agent_id, "我之前说过什么偏好?")
|
||
.await
|
||
.unwrap();
|
||
|
||
let total =
|
||
result.knowledge.len() + result.preferences.len() + result.experience.len();
|
||
assert!(
|
||
total > 0,
|
||
"embedding-enhanced retrieval should find memories"
|
||
);
|
||
|
||
assert!(
|
||
result.preferences.len() > 0,
|
||
"should find preference memories"
|
||
);
|
||
}
|
||
|
||
/// EM-08: Embedding configuration hot update — no panic, no disruption.
|
||
#[tokio::test]
|
||
async fn em08_embedding_hot_update() {
|
||
let storage = Arc::new(SqliteStorage::in_memory().await);
|
||
let adapter = Arc::new(VikingAdapter::new(storage));
|
||
|
||
let agent_id = AgentId::new();
|
||
|
||
// Store a memory
|
||
let entry = MemoryEntry::new(
|
||
&agent_id.to_string(),
|
||
MemoryType::Knowledge,
|
||
"rust-async",
|
||
"Tokio runtime uses work-stealing scheduler".to_string(),
|
||
);
|
||
adapter.store(&entry).await.unwrap();
|
||
|
||
// Start without embedding
|
||
let retriever = MemoryRetriever::new(adapter);
|
||
|
||
// Retrieve without embedding — should not panic
|
||
let _result_before = retriever
|
||
.retrieve(&agent_id, "async runtime")
|
||
.await
|
||
.unwrap();
|
||
|
||
// Hot-update with embedding — should not disrupt ongoing operations
|
||
retriever.set_embedding_client(Arc::new(MockEmbeddingClient::new()));
|
||
|
||
// Retrieve with embedding — should not panic
|
||
let _result_after = retriever
|
||
.retrieve(&agent_id, "async runtime")
|
||
.await
|
||
.unwrap();
|
||
|
||
// Key assertion: hot-update does not panic or disrupt
|
||
}
|