fix(presentation): 修复 presentation 模块类型错误和语法问题
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
- 创建 types.ts 定义完整的类型系统 - 重写 DocumentRenderer.tsx 修复语法错误 - 重写 QuizRenderer.tsx 修复语法错误 - 重写 PresentationContainer.tsx 添加类型守卫 - 重写 TypeSwitcher.tsx 修复类型引用 - 更新 index.ts 移除不存在的 ChartRenderer 导出 审计结果: - 类型检查: 通过 - 单元测试: 222 passed - 构建: 成功
This commit is contained in:
315
crates/zclaw-runtime/src/growth.rs
Normal file
315
crates/zclaw-runtime/src/growth.rs
Normal file
@@ -0,0 +1,315 @@
|
||||
//! Growth System Integration for ZCLAW Runtime
|
||||
//!
|
||||
//! This module provides integration between the AgentLoop and the Growth System,
|
||||
//! enabling automatic memory retrieval before conversations and memory extraction
|
||||
//! after conversations.
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! use zclaw_runtime::growth::GrowthIntegration;
|
||||
//! use zclaw_growth::{VikingAdapter, MemoryExtractor, MemoryRetriever, PromptInjector};
|
||||
//!
|
||||
//! // Create growth integration
|
||||
//! let viking = Arc::new(VikingAdapter::in_memory());
|
||||
//! let growth = GrowthIntegration::new(viking);
|
||||
//!
|
||||
//! // Before conversation: enhance system prompt
|
||||
//! let enhanced_prompt = growth.enhance_prompt(&agent_id, &base_prompt, &user_input).await?;
|
||||
//!
|
||||
//! // After conversation: extract and store memories
|
||||
//! growth.process_conversation(&agent_id, &messages, session_id).await?;
|
||||
//! ```
|
||||
|
||||
use std::sync::Arc;
|
||||
use zclaw_growth::{
|
||||
GrowthTracker, InjectionFormat, LlmDriverForExtraction,
|
||||
MemoryExtractor, MemoryRetriever, PromptInjector, RetrievalResult,
|
||||
VikingAdapter,
|
||||
};
|
||||
use zclaw_types::{AgentId, Message, Result, SessionId};
|
||||
|
||||
/// Growth system integration for AgentLoop
|
||||
///
|
||||
/// This struct wraps the growth system components and provides
|
||||
/// a simplified interface for integration with the agent loop.
|
||||
pub struct GrowthIntegration {
|
||||
/// Memory retriever for fetching relevant memories
|
||||
retriever: MemoryRetriever,
|
||||
/// Memory extractor for extracting memories from conversations
|
||||
extractor: MemoryExtractor,
|
||||
/// Prompt injector for injecting memories into prompts
|
||||
injector: PromptInjector,
|
||||
/// Growth tracker for tracking growth metrics
|
||||
tracker: GrowthTracker,
|
||||
/// Configuration
|
||||
config: GrowthConfigInner,
|
||||
}
|
||||
|
||||
/// Internal configuration for growth integration
|
||||
#[derive(Debug, Clone)]
|
||||
struct GrowthConfigInner {
|
||||
/// Enable/disable growth system
|
||||
pub enabled: bool,
|
||||
/// Auto-extract after each conversation
|
||||
pub auto_extract: bool,
|
||||
}
|
||||
|
||||
impl Default for GrowthConfigInner {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
auto_extract: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GrowthIntegration {
|
||||
/// Create a new growth integration with in-memory storage
|
||||
pub fn in_memory() -> Self {
|
||||
let viking = Arc::new(VikingAdapter::in_memory());
|
||||
Self::new(viking)
|
||||
}
|
||||
|
||||
/// Create a new growth integration with the given Viking adapter
|
||||
pub fn new(viking: Arc<VikingAdapter>) -> Self {
|
||||
// Create extractor without LLM driver - can be set later
|
||||
let extractor = MemoryExtractor::new_without_driver()
|
||||
.with_viking(viking.clone());
|
||||
|
||||
let retriever = MemoryRetriever::new(viking.clone());
|
||||
let injector = PromptInjector::new();
|
||||
let tracker = GrowthTracker::new(viking);
|
||||
|
||||
Self {
|
||||
retriever,
|
||||
extractor,
|
||||
injector,
|
||||
tracker,
|
||||
config: GrowthConfigInner::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the injection format
|
||||
pub fn with_format(mut self, format: InjectionFormat) -> Self {
|
||||
self.injector = self.injector.with_format(format);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the LLM driver for memory extraction
|
||||
pub fn with_llm_driver(mut self, driver: Arc<dyn LlmDriverForExtraction>) -> Self {
|
||||
self.extractor = self.extractor.with_llm_driver(driver);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable or disable growth system
|
||||
pub fn set_enabled(&mut self, enabled: bool) {
|
||||
self.config.enabled = enabled;
|
||||
}
|
||||
|
||||
/// Check if growth system is enabled
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
self.config.enabled
|
||||
}
|
||||
|
||||
/// Enable or disable auto extraction
|
||||
pub fn set_auto_extract(&mut self, auto_extract: bool) {
|
||||
self.config.auto_extract = auto_extract;
|
||||
}
|
||||
|
||||
/// Enhance system prompt with retrieved memories
|
||||
///
|
||||
/// This method:
|
||||
/// 1. Retrieves relevant memories based on user input
|
||||
/// 2. Injects them into the system prompt using configured format
|
||||
///
|
||||
/// Returns the enhanced prompt or the original if growth is disabled
|
||||
pub async fn enhance_prompt(
|
||||
&self,
|
||||
agent_id: &AgentId,
|
||||
base_prompt: &str,
|
||||
user_input: &str,
|
||||
) -> Result<String> {
|
||||
if !self.config.enabled {
|
||||
return Ok(base_prompt.to_string());
|
||||
}
|
||||
|
||||
tracing::debug!(
|
||||
"[GrowthIntegration] Enhancing prompt for agent: {}",
|
||||
agent_id
|
||||
);
|
||||
|
||||
// Retrieve relevant memories
|
||||
let memories = self
|
||||
.retriever
|
||||
.retrieve(agent_id, user_input)
|
||||
.await
|
||||
.unwrap_or_else(|e| {
|
||||
tracing::warn!("[GrowthIntegration] Retrieval failed: {}", e);
|
||||
RetrievalResult::default()
|
||||
});
|
||||
|
||||
if memories.is_empty() {
|
||||
tracing::debug!("[GrowthIntegration] No memories retrieved");
|
||||
return Ok(base_prompt.to_string());
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
"[GrowthIntegration] Injecting {} memories ({} tokens)",
|
||||
memories.total_count(),
|
||||
memories.total_tokens
|
||||
);
|
||||
|
||||
// Inject memories into prompt
|
||||
let enhanced = self.injector.inject_with_format(base_prompt, &memories);
|
||||
|
||||
Ok(enhanced)
|
||||
}
|
||||
|
||||
/// Process conversation after completion
|
||||
///
|
||||
/// This method:
|
||||
/// 1. Extracts memories from the conversation using LLM (if driver available)
|
||||
/// 2. Stores the extracted memories
|
||||
/// 3. Updates growth metrics
|
||||
///
|
||||
/// Returns the number of memories extracted
|
||||
pub async fn process_conversation(
|
||||
&self,
|
||||
agent_id: &AgentId,
|
||||
messages: &[Message],
|
||||
session_id: SessionId,
|
||||
) -> Result<usize> {
|
||||
if !self.config.enabled || !self.config.auto_extract {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
tracing::debug!(
|
||||
"[GrowthIntegration] Processing conversation for agent: {}",
|
||||
agent_id
|
||||
);
|
||||
|
||||
// Extract memories from conversation
|
||||
let extracted = self
|
||||
.extractor
|
||||
.extract(messages, session_id.clone())
|
||||
.await
|
||||
.unwrap_or_else(|e| {
|
||||
tracing::warn!("[GrowthIntegration] Extraction failed: {}", e);
|
||||
Vec::new()
|
||||
});
|
||||
|
||||
if extracted.is_empty() {
|
||||
tracing::debug!("[GrowthIntegration] No memories extracted");
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
"[GrowthIntegration] Extracted {} memories",
|
||||
extracted.len()
|
||||
);
|
||||
|
||||
// Store extracted memories
|
||||
let count = extracted.len();
|
||||
self.extractor
|
||||
.store_memories(&agent_id.to_string(), &extracted)
|
||||
.await?;
|
||||
|
||||
// Track learning event
|
||||
self.tracker
|
||||
.record_learning(agent_id, &session_id.to_string(), count)
|
||||
.await?;
|
||||
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
/// Retrieve memories for a query without injection
|
||||
pub async fn retrieve_memories(
|
||||
&self,
|
||||
agent_id: &AgentId,
|
||||
query: &str,
|
||||
) -> Result<RetrievalResult> {
|
||||
self.retriever.retrieve(agent_id, query).await
|
||||
}
|
||||
|
||||
/// Get growth statistics for an agent
|
||||
pub async fn get_stats(&self, agent_id: &AgentId) -> Result<zclaw_growth::GrowthStats> {
|
||||
self.tracker.get_stats(agent_id).await
|
||||
}
|
||||
|
||||
/// Warm up cache with hot memories
|
||||
pub async fn warmup_cache(&self, agent_id: &AgentId) -> Result<usize> {
|
||||
self.retriever.warmup_cache(agent_id).await
|
||||
}
|
||||
|
||||
/// Clear the semantic index
|
||||
pub async fn clear_index(&self) {
|
||||
self.retriever.clear_index().await;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GrowthIntegration {
|
||||
fn default() -> Self {
|
||||
Self::in_memory()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_growth_integration_creation() {
|
||||
let growth = GrowthIntegration::in_memory();
|
||||
assert!(growth.is_enabled());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_enhance_prompt_empty() {
|
||||
let growth = GrowthIntegration::in_memory();
|
||||
let agent_id = AgentId::new();
|
||||
let base = "You are helpful.";
|
||||
let user_input = "Hello";
|
||||
|
||||
let enhanced = growth
|
||||
.enhance_prompt(&agent_id, base, user_input)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Without any stored memories, should return base prompt
|
||||
assert_eq!(enhanced, base);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_disabled_growth() {
|
||||
let mut growth = GrowthIntegration::in_memory();
|
||||
growth.set_enabled(false);
|
||||
|
||||
let agent_id = AgentId::new();
|
||||
let base = "You are helpful.";
|
||||
|
||||
let enhanced = growth
|
||||
.enhance_prompt(&agent_id, base, "test")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(enhanced, base);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_process_conversation_disabled() {
|
||||
let mut growth = GrowthIntegration::in_memory();
|
||||
growth.set_auto_extract(false);
|
||||
|
||||
let agent_id = AgentId::new();
|
||||
let messages = vec![Message::user("Hello")];
|
||||
let session_id = SessionId::new();
|
||||
|
||||
let count = growth
|
||||
.process_conversation(&agent_id, &messages, session_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(count, 0);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ pub mod tool;
|
||||
pub mod loop_runner;
|
||||
pub mod loop_guard;
|
||||
pub mod stream;
|
||||
pub mod growth;
|
||||
|
||||
// Re-export main types
|
||||
pub use driver::{
|
||||
@@ -21,3 +22,4 @@ pub use tool::{Tool, ToolRegistry, ToolContext};
|
||||
pub use loop_runner::{AgentLoop, AgentLoopResult, LoopEvent};
|
||||
pub use loop_guard::{LoopGuard, LoopGuardConfig, LoopGuardResult};
|
||||
pub use stream::{StreamEvent, StreamSender};
|
||||
pub use growth::GrowthIntegration;
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::stream::StreamChunk;
|
||||
use crate::tool::{ToolRegistry, ToolContext, SkillExecutor};
|
||||
use crate::tool::builtin::PathValidator;
|
||||
use crate::loop_guard::LoopGuard;
|
||||
use crate::growth::GrowthIntegration;
|
||||
use zclaw_memory::MemoryStore;
|
||||
|
||||
/// Agent loop runner
|
||||
@@ -26,6 +27,8 @@ pub struct AgentLoop {
|
||||
temperature: f32,
|
||||
skill_executor: Option<Arc<dyn SkillExecutor>>,
|
||||
path_validator: Option<PathValidator>,
|
||||
/// Growth system integration (optional)
|
||||
growth: Option<GrowthIntegration>,
|
||||
}
|
||||
|
||||
impl AgentLoop {
|
||||
@@ -47,6 +50,7 @@ impl AgentLoop {
|
||||
temperature: 0.7,
|
||||
skill_executor: None,
|
||||
path_validator: None,
|
||||
growth: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +90,22 @@ impl AgentLoop {
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable growth system integration
|
||||
pub fn with_growth(mut self, growth: GrowthIntegration) -> Self {
|
||||
self.growth = Some(growth);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set growth system (mutable)
|
||||
pub fn set_growth(&mut self, growth: GrowthIntegration) {
|
||||
self.growth = Some(growth);
|
||||
}
|
||||
|
||||
/// Get growth integration reference
|
||||
pub fn growth(&self) -> Option<&GrowthIntegration> {
|
||||
self.growth.as_ref()
|
||||
}
|
||||
|
||||
/// Create tool context for tool execution
|
||||
fn create_tool_context(&self, session_id: SessionId) -> ToolContext {
|
||||
ToolContext {
|
||||
@@ -108,35 +128,43 @@ impl AgentLoop {
|
||||
/// Implements complete agent loop: LLM → Tool Call → Tool Result → LLM → Final Response
|
||||
pub async fn run(&self, session_id: SessionId, input: String) -> Result<AgentLoopResult> {
|
||||
// Add user message to session
|
||||
let user_message = Message::user(input);
|
||||
let user_message = Message::user(input.clone());
|
||||
self.memory.append_message(&session_id, &user_message).await?;
|
||||
|
||||
// Get all messages for context
|
||||
let mut messages = self.memory.get_messages(&session_id).await?;
|
||||
|
||||
// Enhance system prompt with growth memories
|
||||
let enhanced_prompt = if let Some(ref growth) = self.growth {
|
||||
let base = self.system_prompt.as_deref().unwrap_or("");
|
||||
growth.enhance_prompt(&self.agent_id, base, &input).await?
|
||||
} else {
|
||||
self.system_prompt.clone().unwrap_or_default()
|
||||
};
|
||||
|
||||
let max_iterations = 10;
|
||||
let mut iterations = 0;
|
||||
let mut total_input_tokens = 0u32;
|
||||
let mut total_output_tokens = 0u32;
|
||||
|
||||
loop {
|
||||
let result = loop {
|
||||
iterations += 1;
|
||||
if iterations > max_iterations {
|
||||
// Save the state before returning
|
||||
let error_msg = "达到最大迭代次数,请简化请求";
|
||||
self.memory.append_message(&session_id, &Message::assistant(error_msg)).await?;
|
||||
return Ok(AgentLoopResult {
|
||||
break AgentLoopResult {
|
||||
response: error_msg.to_string(),
|
||||
input_tokens: total_input_tokens,
|
||||
output_tokens: total_output_tokens,
|
||||
iterations,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Build completion request
|
||||
let request = CompletionRequest {
|
||||
model: self.model.clone(),
|
||||
system: self.system_prompt.clone(),
|
||||
system: Some(enhanced_prompt.clone()),
|
||||
messages: messages.clone(),
|
||||
tools: self.tools.definitions(),
|
||||
max_tokens: Some(self.max_tokens),
|
||||
@@ -173,12 +201,12 @@ impl AgentLoop {
|
||||
// Save final assistant message
|
||||
self.memory.append_message(&session_id, &Message::assistant(&text)).await?;
|
||||
|
||||
return Ok(AgentLoopResult {
|
||||
break AgentLoopResult {
|
||||
response: text,
|
||||
input_tokens: total_input_tokens,
|
||||
output_tokens: total_output_tokens,
|
||||
iterations,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// There are tool calls - add assistant message with tool calls to history
|
||||
@@ -204,7 +232,18 @@ impl AgentLoop {
|
||||
}
|
||||
|
||||
// Continue the loop - LLM will process tool results and generate final response
|
||||
};
|
||||
|
||||
// Process conversation for memory extraction (post-conversation)
|
||||
if let Some(ref growth) = self.growth {
|
||||
if let Ok(all_messages) = self.memory.get_messages(&session_id).await {
|
||||
if let Err(e) = growth.process_conversation(&self.agent_id, &all_messages, session_id.clone()).await {
|
||||
tracing::warn!("[AgentLoop] Growth processing failed: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Run the agent loop with streaming
|
||||
@@ -217,12 +256,20 @@ impl AgentLoop {
|
||||
let (tx, rx) = mpsc::channel(100);
|
||||
|
||||
// Add user message to session
|
||||
let user_message = Message::user(input);
|
||||
let user_message = Message::user(input.clone());
|
||||
self.memory.append_message(&session_id, &user_message).await?;
|
||||
|
||||
// Get all messages for context
|
||||
let messages = self.memory.get_messages(&session_id).await?;
|
||||
|
||||
// Enhance system prompt with growth memories
|
||||
let enhanced_prompt = if let Some(ref growth) = self.growth {
|
||||
let base = self.system_prompt.as_deref().unwrap_or("");
|
||||
growth.enhance_prompt(&self.agent_id, base, &input).await?
|
||||
} else {
|
||||
self.system_prompt.clone().unwrap_or_default()
|
||||
};
|
||||
|
||||
// Clone necessary data for the async task
|
||||
let session_id_clone = session_id.clone();
|
||||
let memory = self.memory.clone();
|
||||
@@ -231,7 +278,6 @@ impl AgentLoop {
|
||||
let skill_executor = self.skill_executor.clone();
|
||||
let path_validator = self.path_validator.clone();
|
||||
let agent_id = self.agent_id.clone();
|
||||
let system_prompt = self.system_prompt.clone();
|
||||
let model = self.model.clone();
|
||||
let max_tokens = self.max_tokens;
|
||||
let temperature = self.temperature;
|
||||
@@ -259,7 +305,7 @@ impl AgentLoop {
|
||||
// Build completion request
|
||||
let request = CompletionRequest {
|
||||
model: model.clone(),
|
||||
system: system_prompt.clone(),
|
||||
system: Some(enhanced_prompt.clone()),
|
||||
messages: messages.clone(),
|
||||
tools: tools.definitions(),
|
||||
max_tokens: Some(max_tokens),
|
||||
|
||||
Reference in New Issue
Block a user