use std::sync::Arc; use crate::agent::{AgentError, AgentLoop, CompositeSystemPromptProvider, SystemPromptProvider}; use crate::config::LLMProviderConfig; use crate::experts::ExpertPromptProvider; use crate::experts::ExpertRuntime; use crate::gateway::agent_prompt_provider::AgentPromptProvider; use crate::gateway::tool_prompt_provider::ToolPromptProvider; use crate::skills::{SkillPromptProvider, SkillRuntime}; use crate::storage::persistent_session_id; use crate::storage::PromptInjectionRepository; use crate::tools::task::runtime::{SubagentPromptProvider, SubagentRuntime}; use crate::tools::{ToolContext, ToolRegistry}; /// 构建与 Agent 实际使用的完全一致的组合系统提示词 Provider。 /// /// 单一来源:AgentFactory::create 与命令侧(/save、/save-session、/current) /// 都调用此函数,确保保存到文件的系统提示词与 LLM 实际接收的提示词一致。 /// /// Provider 顺序:AgentPrompt → SkillPrompt → ExpertPrompt → SubagentPrompt → TodoPrompt pub(crate) fn build_system_prompt_provider( reinject_every: usize, provider_config: LLMProviderConfig, prompt_repository: Arc, skills: Arc, experts: Arc, subagent_runtime: Arc, ) -> Arc { Arc::new(CompositeSystemPromptProvider::new(vec![ Box::new(AgentPromptProvider::new( reinject_every, provider_config, prompt_repository, )), Box::new(SkillPromptProvider::new(skills)), Box::new(ExpertPromptProvider::new(experts)), Box::new(SubagentPromptProvider::new(subagent_runtime)), Box::new(ToolPromptProvider::new()), ])) } #[derive(Clone)] pub(crate) struct AgentFactory { tools: Arc, skills: Arc, experts: Arc, subagent_runtime: Arc, reinject_every: usize, prompt_repository: Arc, /// 实例创建时间戳(用于区分新旧 AgentFactory 实例) instance_id: u64, } pub(crate) struct AgentBuildRequest<'a> { pub(crate) channel_name: &'a str, pub(crate) session_chat_id: &'a str, pub(crate) notification_chat_id: Option<&'a str>, pub(crate) sender_id: Option<&'a str>, pub(crate) message_id: Option<&'a str>, pub(crate) provider_config: LLMProviderConfig, /// 当前话题 ID(可选):用于 todo 等按 topic 隔离的工具 pub(crate) topic_id: Option, /// 取消信号接收端(可选):Agent 在每次迭代时检查是否被取消 pub(crate) cancel_token: Option>, } impl AgentFactory { pub(crate) fn new( tools: Arc, skills: Arc, experts: Arc, subagent_runtime: Arc, reinject_every: usize, prompt_repository: Arc, ) -> Self { // 使用 Arc 指针地址作为实例标识符,用于区分新旧 AgentFactory 实例 let instance_id = Arc::as_ptr(&tools) as u64; tracing::info!( instance_id = instance_id, tool_count = tools.tool_names().len(), "AgentFactory::new created" ); Self { tools, skills, experts, subagent_runtime, reinject_every, prompt_repository, instance_id, } } pub(crate) fn create(&self, request: AgentBuildRequest<'_>) -> Result { let session_id = persistent_session_id(request.channel_name, request.session_chat_id); // 诊断日志:记录 agent 实际使用的配置和实例 ID tracing::info!( instance_id = self.instance_id, channel = %request.channel_name, session_id = %session_id, provider = %request.provider_config.name, model_id = %request.provider_config.model_id, tool_count = self.tools.tool_names().len(), "AgentFactory: creating agent with config" ); // 创建组合的系统提示词提供者(与命令侧 /save 等共享同一构建逻辑) let system_prompt_provider = build_system_prompt_provider( self.reinject_every, request.provider_config.clone(), self.prompt_repository.clone(), self.skills.clone(), self.experts.clone(), self.subagent_runtime.clone(), ); AgentLoop::with_tools_and_system_prompt_provider( request.provider_config, self.tools.clone(), system_prompt_provider, Some(self.skills.clone()), ) .map(|agent| { // notification_chat_id 优先,否则使用 session_chat_id let tool_chat_id = request .notification_chat_id .unwrap_or(request.session_chat_id); let mut agent = agent.with_tool_context(ToolContext { channel_name: Some(request.channel_name.to_string()), sender_id: request.sender_id.map(str::to_string), chat_id: Some(tool_chat_id.to_string()), session_id: Some(session_id), topic_id: request.topic_id.clone(), message_id: request.message_id.map(str::to_string), message_seq: None, subagent_description: None, nesting_depth: 0, task_id: None, parent_task_id: None, tool_call_id: None, }); // 如果有取消信号接收端,注入 Agent if let Some(token) = request.cancel_token { agent = agent.with_cancel_token(token); } agent }) } }