From 1abac85034f24d58e7c44997a42dc2a3f334bab1 Mon Sep 17 00:00:00 2001 From: xiaoxixi Date: Mon, 27 Apr 2026 23:04:24 +0800 Subject: [PATCH] =?UTF-8?q?=E9=9B=86=E6=88=90=E6=8A=80=E8=83=BD=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=88=B0=E7=B3=BB=E7=BB=9F=E6=8F=90=E7=A4=BA=E8=AF=8D?= =?UTF-8?q?=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 AgentLoop 中添加 SkillsLoader 支持\n- 在系统提示词构建中集成技能提示\n- 更新 Session 以传递 SkillsLoader\n- 修复所有编译错误和测试问题 --- src/agent/agent_loop.rs | 25 +++++++++++++++++++++---- src/channels/feishu.rs | 2 +- src/session/session.rs | 8 ++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/agent/agent_loop.rs b/src/agent/agent_loop.rs index 8bcfa10..53316c3 100644 --- a/src/agent/agent_loop.rs +++ b/src/agent/agent_loop.rs @@ -6,6 +6,7 @@ use crate::observability::{ truncate_args, Observer, ObserverEvent, ToolExecutionOutcome, }; use crate::providers::{create_provider, LLMProvider, ChatCompletionRequest, Message, ToolCall}; +use crate::skills::SkillsLoader; use crate::tools::ToolRegistry; use std::collections::VecDeque; use std::hash::{Hash, Hasher}; @@ -226,6 +227,7 @@ pub struct AgentLoop { max_iterations: usize, workspace_dir: PathBuf, model_name: String, + skills_loader: Arc, } #[derive(Debug, Clone)] @@ -236,7 +238,7 @@ pub struct AgentProcessResult { impl AgentLoop { /// Create a new AgentLoop with a provider created from config. - pub fn new(provider_config: LLMProviderConfig) -> Result { + pub fn new(provider_config: LLMProviderConfig, skills_loader: Arc) -> Result { let max_iterations = provider_config.max_tool_iterations; let model_name = provider_config.model_id.clone(); let workspace_dir = provider_config.workspace_dir.clone(); @@ -250,11 +252,12 @@ impl AgentLoop { max_iterations, workspace_dir, model_name, + skills_loader, }) } /// Create a new AgentLoop with provider created from config and given tools. - pub fn with_tools(provider_config: LLMProviderConfig, tools: Arc) -> Result { + pub fn with_tools(provider_config: LLMProviderConfig, tools: Arc, skills_loader: Arc) -> Result { let max_iterations = provider_config.max_tool_iterations; let model_name = provider_config.model_id.clone(); let workspace_dir = provider_config.workspace_dir.clone(); @@ -268,11 +271,12 @@ impl AgentLoop { max_iterations, workspace_dir, model_name, + skills_loader, }) } /// Create a new AgentLoop with an existing shared provider. - pub fn with_provider(provider: Arc, max_iterations: usize, model_name: String, workspace_dir: PathBuf) -> Self { + pub fn with_provider(provider: Arc, max_iterations: usize, model_name: String, workspace_dir: PathBuf, skills_loader: Arc) -> Self { Self { provider, tools: Arc::new(ToolRegistry::new()), @@ -280,6 +284,7 @@ impl AgentLoop { max_iterations, workspace_dir, model_name, + skills_loader, } } @@ -290,6 +295,7 @@ impl AgentLoop { max_iterations: usize, model_name: String, workspace_dir: PathBuf, + skills_loader: Arc, ) -> Self { Self { provider, @@ -298,6 +304,7 @@ impl AgentLoop { max_iterations, workspace_dir, model_name, + skills_loader, } } @@ -331,7 +338,17 @@ impl AgentLoop { // Build and inject system prompt if not present let has_system = messages.first().map_or(false, |m| m.role == "system"); if !has_system { - let system_prompt = build_system_prompt(&self.workspace_dir, &self.model_name, &self.tools); + let mut system_prompt = build_system_prompt(&self.workspace_dir, &self.model_name, &self.tools); + + // Add skills prompt if there are skills + let skills_prompt = self.skills_loader.build_skills_prompt(); + if !skills_prompt.is_empty() { + if !system_prompt.is_empty() { + system_prompt.push_str("\n\n"); + } + system_prompt.push_str(&skills_prompt); + } + #[cfg(debug_assertions)] tracing::debug!("System prompt injected:\n{}", system_prompt); messages.insert(0, ChatMessage::system(system_prompt)); diff --git a/src/channels/feishu.rs b/src/channels/feishu.rs index c95a883..cc3a9ae 100644 --- a/src/channels/feishu.rs +++ b/src/channels/feishu.rs @@ -12,7 +12,7 @@ use tokio::sync::{broadcast, RwLock}; use crate::bus::{MessageBus, MediaItem, OutboundMessage}; use crate::channels::base::{Channel, ChannelError}; -use crate::config::{FeishuChannelConfig, LLMProviderConfig}; +use crate::config::FeishuChannelConfig; const FEISHU_API_BASE: &str = "https://open.feishu.cn/open-apis"; const FEISHU_WS_BASE: &str = "https://open.feishu.cn"; diff --git a/src/session/session.rs b/src/session/session.rs index cc663b3..14c22b9 100644 --- a/src/session/session.rs +++ b/src/session/session.rs @@ -36,6 +36,7 @@ pub struct Session { tools: Arc, compressor: ContextCompressor, store: Arc, + skills_loader: Arc, } impl Session { @@ -45,6 +46,7 @@ impl Session { user_tx: mpsc::Sender, tools: Arc, store: Arc, + skills_loader: Arc, ) -> Result { let provider_box = create_provider(provider_config.clone()) .map_err(|e| AgentError::Other(format!("provider creation error: {}", e)))?; @@ -64,6 +66,7 @@ impl Session { tools, compressor: ContextCompressor::with_config(provider.clone(), provider_config.token_limit, compressor_config), store, + skills_loader, }) } @@ -178,6 +181,7 @@ impl Session { self.provider_config.max_tool_iterations, self.provider_config.model_id.clone(), self.provider_config.workspace_dir.clone(), + self.skills_loader.clone(), )) } } @@ -389,6 +393,7 @@ impl SessionManager { user_tx, self.tools.clone(), self.store.clone(), + self.skills_loader.clone(), ).await?; let arc = Arc::new(Mutex::new(session)); @@ -416,6 +421,7 @@ impl SessionManager { user_tx, self.tools.clone(), self.store.clone(), + self.skills_loader.clone(), ).await?; let arc = Arc::new(Mutex::new(session)); @@ -431,6 +437,7 @@ impl SessionManager { user_tx, self.tools.clone(), self.store.clone(), + self.skills_loader.clone(), ).await?; let arc = Arc::new(Mutex::new(session)); @@ -648,6 +655,7 @@ mod tests { model_extra: HashMap::new(), max_tool_iterations: 1, token_limit: 4096, + workspace_dir: std::path::PathBuf::from("/tmp/test-workspace"), } } }