集成技能功能到系统提示词框架

- 在 AgentLoop 中添加 SkillsLoader 支持\n- 在系统提示词构建中集成技能提示\n- 更新 Session 以传递 SkillsLoader\n- 修复所有编译错误和测试问题
This commit is contained in:
xiaoxixi 2026-04-27 23:04:24 +08:00
parent 8226e8429d
commit 1abac85034
3 changed files with 30 additions and 5 deletions

View File

@ -6,6 +6,7 @@ use crate::observability::{
truncate_args, Observer, ObserverEvent, ToolExecutionOutcome, truncate_args, Observer, ObserverEvent, ToolExecutionOutcome,
}; };
use crate::providers::{create_provider, LLMProvider, ChatCompletionRequest, Message, ToolCall}; use crate::providers::{create_provider, LLMProvider, ChatCompletionRequest, Message, ToolCall};
use crate::skills::SkillsLoader;
use crate::tools::ToolRegistry; use crate::tools::ToolRegistry;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
@ -226,6 +227,7 @@ pub struct AgentLoop {
max_iterations: usize, max_iterations: usize,
workspace_dir: PathBuf, workspace_dir: PathBuf,
model_name: String, model_name: String,
skills_loader: Arc<SkillsLoader>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -236,7 +238,7 @@ pub struct AgentProcessResult {
impl AgentLoop { impl AgentLoop {
/// Create a new AgentLoop with a provider created from config. /// Create a new AgentLoop with a provider created from config.
pub fn new(provider_config: LLMProviderConfig) -> Result<Self, AgentError> { pub fn new(provider_config: LLMProviderConfig, skills_loader: Arc<SkillsLoader>) -> Result<Self, AgentError> {
let max_iterations = provider_config.max_tool_iterations; let max_iterations = provider_config.max_tool_iterations;
let model_name = provider_config.model_id.clone(); let model_name = provider_config.model_id.clone();
let workspace_dir = provider_config.workspace_dir.clone(); let workspace_dir = provider_config.workspace_dir.clone();
@ -250,11 +252,12 @@ impl AgentLoop {
max_iterations, max_iterations,
workspace_dir, workspace_dir,
model_name, model_name,
skills_loader,
}) })
} }
/// Create a new AgentLoop with provider created from config and given tools. /// Create a new AgentLoop with provider created from config and given tools.
pub fn with_tools(provider_config: LLMProviderConfig, tools: Arc<ToolRegistry>) -> Result<Self, AgentError> { pub fn with_tools(provider_config: LLMProviderConfig, tools: Arc<ToolRegistry>, skills_loader: Arc<SkillsLoader>) -> Result<Self, AgentError> {
let max_iterations = provider_config.max_tool_iterations; let max_iterations = provider_config.max_tool_iterations;
let model_name = provider_config.model_id.clone(); let model_name = provider_config.model_id.clone();
let workspace_dir = provider_config.workspace_dir.clone(); let workspace_dir = provider_config.workspace_dir.clone();
@ -268,11 +271,12 @@ impl AgentLoop {
max_iterations, max_iterations,
workspace_dir, workspace_dir,
model_name, model_name,
skills_loader,
}) })
} }
/// Create a new AgentLoop with an existing shared provider. /// Create a new AgentLoop with an existing shared provider.
pub fn with_provider(provider: Arc<dyn LLMProvider>, max_iterations: usize, model_name: String, workspace_dir: PathBuf) -> Self { pub fn with_provider(provider: Arc<dyn LLMProvider>, max_iterations: usize, model_name: String, workspace_dir: PathBuf, skills_loader: Arc<SkillsLoader>) -> Self {
Self { Self {
provider, provider,
tools: Arc::new(ToolRegistry::new()), tools: Arc::new(ToolRegistry::new()),
@ -280,6 +284,7 @@ impl AgentLoop {
max_iterations, max_iterations,
workspace_dir, workspace_dir,
model_name, model_name,
skills_loader,
} }
} }
@ -290,6 +295,7 @@ impl AgentLoop {
max_iterations: usize, max_iterations: usize,
model_name: String, model_name: String,
workspace_dir: PathBuf, workspace_dir: PathBuf,
skills_loader: Arc<SkillsLoader>,
) -> Self { ) -> Self {
Self { Self {
provider, provider,
@ -298,6 +304,7 @@ impl AgentLoop {
max_iterations, max_iterations,
workspace_dir, workspace_dir,
model_name, model_name,
skills_loader,
} }
} }
@ -331,7 +338,17 @@ impl AgentLoop {
// Build and inject system prompt if not present // Build and inject system prompt if not present
let has_system = messages.first().map_or(false, |m| m.role == "system"); let has_system = messages.first().map_or(false, |m| m.role == "system");
if !has_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)] #[cfg(debug_assertions)]
tracing::debug!("System prompt injected:\n{}", system_prompt); tracing::debug!("System prompt injected:\n{}", system_prompt);
messages.insert(0, ChatMessage::system(system_prompt)); messages.insert(0, ChatMessage::system(system_prompt));

View File

@ -12,7 +12,7 @@ use tokio::sync::{broadcast, RwLock};
use crate::bus::{MessageBus, MediaItem, OutboundMessage}; use crate::bus::{MessageBus, MediaItem, OutboundMessage};
use crate::channels::base::{Channel, ChannelError}; 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_API_BASE: &str = "https://open.feishu.cn/open-apis";
const FEISHU_WS_BASE: &str = "https://open.feishu.cn"; const FEISHU_WS_BASE: &str = "https://open.feishu.cn";

View File

@ -36,6 +36,7 @@ pub struct Session {
tools: Arc<ToolRegistry>, tools: Arc<ToolRegistry>,
compressor: ContextCompressor, compressor: ContextCompressor,
store: Arc<SessionStore>, store: Arc<SessionStore>,
skills_loader: Arc<SkillsLoader>,
} }
impl Session { impl Session {
@ -45,6 +46,7 @@ impl Session {
user_tx: mpsc::Sender<WsOutbound>, user_tx: mpsc::Sender<WsOutbound>,
tools: Arc<ToolRegistry>, tools: Arc<ToolRegistry>,
store: Arc<SessionStore>, store: Arc<SessionStore>,
skills_loader: Arc<SkillsLoader>,
) -> Result<Self, AgentError> { ) -> Result<Self, AgentError> {
let provider_box = create_provider(provider_config.clone()) let provider_box = create_provider(provider_config.clone())
.map_err(|e| AgentError::Other(format!("provider creation error: {}", e)))?; .map_err(|e| AgentError::Other(format!("provider creation error: {}", e)))?;
@ -64,6 +66,7 @@ impl Session {
tools, tools,
compressor: ContextCompressor::with_config(provider.clone(), provider_config.token_limit, compressor_config), compressor: ContextCompressor::with_config(provider.clone(), provider_config.token_limit, compressor_config),
store, store,
skills_loader,
}) })
} }
@ -178,6 +181,7 @@ impl Session {
self.provider_config.max_tool_iterations, self.provider_config.max_tool_iterations,
self.provider_config.model_id.clone(), self.provider_config.model_id.clone(),
self.provider_config.workspace_dir.clone(), self.provider_config.workspace_dir.clone(),
self.skills_loader.clone(),
)) ))
} }
} }
@ -389,6 +393,7 @@ impl SessionManager {
user_tx, user_tx,
self.tools.clone(), self.tools.clone(),
self.store.clone(), self.store.clone(),
self.skills_loader.clone(),
).await?; ).await?;
let arc = Arc::new(Mutex::new(session)); let arc = Arc::new(Mutex::new(session));
@ -416,6 +421,7 @@ impl SessionManager {
user_tx, user_tx,
self.tools.clone(), self.tools.clone(),
self.store.clone(), self.store.clone(),
self.skills_loader.clone(),
).await?; ).await?;
let arc = Arc::new(Mutex::new(session)); let arc = Arc::new(Mutex::new(session));
@ -431,6 +437,7 @@ impl SessionManager {
user_tx, user_tx,
self.tools.clone(), self.tools.clone(),
self.store.clone(), self.store.clone(),
self.skills_loader.clone(),
).await?; ).await?;
let arc = Arc::new(Mutex::new(session)); let arc = Arc::new(Mutex::new(session));
@ -648,6 +655,7 @@ mod tests {
model_extra: HashMap::new(), model_extra: HashMap::new(),
max_tool_iterations: 1, max_tool_iterations: 1,
token_limit: 4096, token_limit: 4096,
workspace_dir: std::path::PathBuf::from("/tmp/test-workspace"),
} }
} }
} }