fix(growth,kernel,runtime): 穷尽审计后 7 项修复 — body 持久化 + embedding 死路径 + 安全加固
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
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
CRITICAL 修复: - body_markdown 数据丢失: SkillManifest.body 字段 + serialize_skill_md 使用 body 替代默认内容 - embedding 检索死路径: rerank_entries 使用异步 index_entry_with_embedding + score_similarity_with_embedding (70/30 混合) - try_write 静默丢失: pending_embedding 字段 + apply_pending_embedding() 延迟应用 IMPORTANT 修复: - auto_mode 内存泄漏: add_pending 容量限制 100 + 溢出时丢弃最旧 - name_to_slug 空 ID: uuid fallback for empty/whitespace-only names - compaction embedding 缺失: compaction GrowthIntegration 也接收 embedding - kernel 未初始化警告: viking_configure_embedding warn log 验证: 934+ tests PASS, 0 failures
This commit is contained in:
@@ -19,6 +19,8 @@ pub struct MemoryRetriever {
|
||||
config: RetrievalConfig,
|
||||
/// Semantic scorer for similarity computation
|
||||
scorer: RwLock<SemanticScorer>,
|
||||
/// Pending embedding client (applied on next scorer access if try_write failed)
|
||||
pending_embedding: std::sync::Mutex<Option<Arc<dyn crate::retrieval::semantic::EmbeddingClient>>>,
|
||||
/// Query analyzer
|
||||
analyzer: QueryAnalyzer,
|
||||
/// Memory cache
|
||||
@@ -32,6 +34,7 @@ impl MemoryRetriever {
|
||||
viking,
|
||||
config: RetrievalConfig::default(),
|
||||
scorer: RwLock::new(SemanticScorer::new()),
|
||||
pending_embedding: std::sync::Mutex::new(None),
|
||||
analyzer: QueryAnalyzer::new(),
|
||||
cache: MemoryCache::default_config(),
|
||||
}
|
||||
@@ -244,19 +247,40 @@ impl MemoryRetriever {
|
||||
|
||||
let mut scorer = self.scorer.write().await;
|
||||
|
||||
// Apply any pending embedding client
|
||||
self.apply_pending_embedding(&mut scorer);
|
||||
|
||||
// Check if embedding is available for enhanced scoring
|
||||
let use_embedding = scorer.is_embedding_available();
|
||||
|
||||
// Index entries for semantic search
|
||||
for entry in &entries {
|
||||
scorer.index_entry(entry);
|
||||
if use_embedding {
|
||||
for entry in &entries {
|
||||
scorer.index_entry_with_embedding(entry).await;
|
||||
}
|
||||
} else {
|
||||
for entry in &entries {
|
||||
scorer.index_entry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// Score each entry
|
||||
let mut scored: Vec<(f32, MemoryEntry)> = entries
|
||||
.into_iter()
|
||||
.map(|entry| {
|
||||
let score = scorer.score_similarity(query, &entry);
|
||||
(score, entry)
|
||||
})
|
||||
.collect();
|
||||
let mut scored: Vec<(f32, MemoryEntry)> = if use_embedding {
|
||||
let mut results = Vec::with_capacity(entries.len());
|
||||
for entry in entries {
|
||||
let score = scorer.score_similarity_with_embedding(query, &entry).await;
|
||||
results.push((score, entry));
|
||||
}
|
||||
results
|
||||
} else {
|
||||
entries
|
||||
.into_iter()
|
||||
.map(|entry| {
|
||||
let score = scorer.score_similarity(query, &entry);
|
||||
(score, entry)
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
// Sort by score (descending), then by importance and access count
|
||||
scored.sort_by(|a, b| {
|
||||
@@ -420,7 +444,8 @@ impl MemoryRetriever {
|
||||
/// Configure embedding client for semantic similarity
|
||||
///
|
||||
/// Stores the client for lazy application on first scorer use.
|
||||
/// Safe to call from non-async contexts.
|
||||
/// If the scorer lock is busy, the client is stored as pending
|
||||
/// and applied on the next successful lock acquisition.
|
||||
pub fn set_embedding_client(
|
||||
&self,
|
||||
client: Arc<dyn crate::retrieval::semantic::EmbeddingClient>,
|
||||
@@ -429,7 +454,20 @@ impl MemoryRetriever {
|
||||
*scorer = SemanticScorer::with_embedding(client);
|
||||
tracing::info!("[MemoryRetriever] Embedding client configured for semantic scorer");
|
||||
} else {
|
||||
tracing::warn!("[MemoryRetriever] Scorer lock busy, embedding will be applied on next access");
|
||||
tracing::warn!("[MemoryRetriever] Scorer lock busy, storing embedding client as pending");
|
||||
if let Ok(mut pending) = self.pending_embedding.lock() {
|
||||
*pending = Some(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply any pending embedding client to the scorer.
|
||||
fn apply_pending_embedding(&self, scorer: &mut SemanticScorer) {
|
||||
if let Ok(mut pending) = self.pending_embedding.lock() {
|
||||
if let Some(client) = pending.take() {
|
||||
*scorer = SemanticScorer::with_embedding(client);
|
||||
tracing::info!("[MemoryRetriever] Pending embedding client applied to scorer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user