feat: 统一路径显示格式,确保跨平台兼容性;重构系统提示词提供者构建逻辑
This commit is contained in:
parent
43f6ea7b08
commit
4329cfbbe9
@ -213,9 +213,11 @@ async fn handle_save_session(
|
|||||||
Ok(CommandResponse::success(ctx.request_id)
|
Ok(CommandResponse::success(ctx.request_id)
|
||||||
.with_message(
|
.with_message(
|
||||||
MessageKind::Notification,
|
MessageKind::Notification,
|
||||||
&format!("Session saved to: {}", output_path.display()),
|
// 路径中的反斜杠在 Markdown 渲染时会被当作转义符吃掉,
|
||||||
|
// 统一转换为正斜杠以保证显示完整(跨平台兼容)
|
||||||
|
&format!("Session saved to: {}", output_path.display().to_string().replace('\\', "/")),
|
||||||
)
|
)
|
||||||
.with_metadata("filepath", output_path.to_string_lossy().as_ref())
|
.with_metadata("filepath", &output_path.display().to_string().replace('\\', "/"))
|
||||||
.with_metadata("message_count", &message_count.to_string()))
|
.with_metadata("message_count", &message_count.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -703,7 +705,7 @@ impl InChatCommandHandler for SaveSessionInChatHandler {
|
|||||||
// 返回成功或失败消息
|
// 返回成功或失败消息
|
||||||
match result {
|
match result {
|
||||||
Ok(output_path) => {
|
Ok(output_path) => {
|
||||||
let msg = format!("Session saved to: {}", output_path.display());
|
let msg = format!("Session saved to: {}", output_path.display().to_string().replace('\\', "/"));
|
||||||
tracing::info!("{}", msg);
|
tracing::info!("{}", msg);
|
||||||
Ok(Some(msg))
|
Ok(Some(msg))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -279,8 +279,10 @@ async fn handle_save_topic(
|
|||||||
Ok(CommandResponse::success(ctx.request_id)
|
Ok(CommandResponse::success(ctx.request_id)
|
||||||
.with_message(
|
.with_message(
|
||||||
MessageKind::Notification,
|
MessageKind::Notification,
|
||||||
&format!("Topic saved to: {}", output_path.display()),
|
// 路径中的反斜杠在 Markdown 渲染时会被当作转义符吃掉,
|
||||||
|
// 统一转换为正斜杠以保证显示完整(跨平台兼容)
|
||||||
|
&format!("Topic saved to: {}", output_path.display().to_string().replace('\\', "/")),
|
||||||
)
|
)
|
||||||
.with_metadata("filepath", output_path.to_string_lossy().as_ref())
|
.with_metadata("filepath", &output_path.display().to_string().replace('\\', "/"))
|
||||||
.with_metadata("message_count", &message_count.to_string()))
|
.with_metadata("message_count", &message_count.to_string()))
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::agent::{AgentError, AgentLoop, CompositeSystemPromptProvider};
|
use crate::agent::{AgentError, AgentLoop, CompositeSystemPromptProvider, SystemPromptProvider};
|
||||||
use crate::config::LLMProviderConfig;
|
use crate::config::LLMProviderConfig;
|
||||||
use crate::experts::ExpertPromptProvider;
|
use crate::experts::ExpertPromptProvider;
|
||||||
use crate::experts::ExpertRuntime;
|
use crate::experts::ExpertRuntime;
|
||||||
@ -12,6 +12,33 @@ use crate::storage::PromptInjectionRepository;
|
|||||||
use crate::tools::task::runtime::{SubagentPromptProvider, SubagentRuntime};
|
use crate::tools::task::runtime::{SubagentPromptProvider, SubagentRuntime};
|
||||||
use crate::tools::{ToolContext, ToolRegistry};
|
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<dyn PromptInjectionRepository>,
|
||||||
|
skills: Arc<SkillRuntime>,
|
||||||
|
experts: Arc<ExpertRuntime>,
|
||||||
|
subagent_runtime: Arc<SubagentRuntime>,
|
||||||
|
) -> Arc<dyn SystemPromptProvider> {
|
||||||
|
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(TodoPromptProvider::new()),
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct AgentFactory {
|
pub(crate) struct AgentFactory {
|
||||||
tools: Arc<ToolRegistry>,
|
tools: Arc<ToolRegistry>,
|
||||||
@ -78,19 +105,15 @@ impl AgentFactory {
|
|||||||
"AgentFactory: creating agent with config"
|
"AgentFactory: creating agent with config"
|
||||||
);
|
);
|
||||||
|
|
||||||
// 创建组合的系统提示词提供者
|
// 创建组合的系统提示词提供者(与命令侧 /save 等共享同一构建逻辑)
|
||||||
// 顺序:AgentPrompt → SkillPrompt → ExpertPrompt → SubagentPrompt → TodoPrompt
|
let system_prompt_provider = build_system_prompt_provider(
|
||||||
let system_prompt_provider = Arc::new(CompositeSystemPromptProvider::new(vec![
|
|
||||||
Box::new(AgentPromptProvider::new(
|
|
||||||
self.reinject_every,
|
self.reinject_every,
|
||||||
request.provider_config.clone(),
|
request.provider_config.clone(),
|
||||||
self.prompt_repository.clone(),
|
self.prompt_repository.clone(),
|
||||||
)),
|
self.skills.clone(),
|
||||||
Box::new(SkillPromptProvider::new(self.skills.clone())),
|
self.experts.clone(),
|
||||||
Box::new(ExpertPromptProvider::new(self.experts.clone())),
|
self.subagent_runtime.clone(),
|
||||||
Box::new(SubagentPromptProvider::new(self.subagent_runtime.clone())),
|
);
|
||||||
Box::new(TodoPromptProvider::new()),
|
|
||||||
]));
|
|
||||||
|
|
||||||
AgentLoop::with_tools_and_system_prompt_provider(
|
AgentLoop::with_tools_and_system_prompt_provider(
|
||||||
request.provider_config,
|
request.provider_config,
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex};
|
|||||||
|
|
||||||
use tokio::sync::Semaphore;
|
use tokio::sync::Semaphore;
|
||||||
|
|
||||||
use crate::agent::{AgentError, CompositeSystemPromptProvider, PersistingEmittedMessageHandler};
|
use crate::agent::{AgentError, PersistingEmittedMessageHandler};
|
||||||
use crate::bus::{InboundMessage, MessageBus, OutboundMessage};
|
use crate::bus::{InboundMessage, MessageBus, OutboundMessage};
|
||||||
use crate::command::adapter::InputAdapter;
|
use crate::command::adapter::InputAdapter;
|
||||||
use crate::command::adapters::channel::ChannelInputAdapter;
|
use crate::command::adapters::channel::ChannelInputAdapter;
|
||||||
@ -19,10 +19,9 @@ use crate::command::handlers::session::SessionCommandHandler;
|
|||||||
use crate::command::handlers::stop_execution::StopExecutionCommandHandler;
|
use crate::command::handlers::stop_execution::StopExecutionCommandHandler;
|
||||||
use crate::command::handlers::switch_topic::SwitchTopicCommandHandler;
|
use crate::command::handlers::switch_topic::SwitchTopicCommandHandler;
|
||||||
use crate::config::LLMProviderConfig;
|
use crate::config::LLMProviderConfig;
|
||||||
use crate::gateway::agent_prompt_provider::AgentPromptProvider;
|
use crate::gateway::agent_factory::build_system_prompt_provider;
|
||||||
use crate::gateway::cancel_manager::CancelManager;
|
use crate::gateway::cancel_manager::CancelManager;
|
||||||
use crate::providers::{create_provider, ProviderRuntimeConfig};
|
use crate::providers::{create_provider, ProviderRuntimeConfig};
|
||||||
use crate::skills::SkillPromptProvider;
|
|
||||||
use crate::storage::persistent_session_id;
|
use crate::storage::persistent_session_id;
|
||||||
use crate::topic_description::generate_topic_description;
|
use crate::topic_description::generate_topic_description;
|
||||||
|
|
||||||
@ -65,16 +64,16 @@ impl InboundProcessor {
|
|||||||
command_router.register(Box::new(switch_handler));
|
command_router.register(Box::new(switch_handler));
|
||||||
|
|
||||||
// 创建 system_prompt_provider(用于 save_session, save_topic, get_current)
|
// 创建 system_prompt_provider(用于 save_session, save_topic, get_current)
|
||||||
let skills = session_manager.skills();
|
// 与 AgentFactory::create 共享同一构建逻辑,确保保存到文件的系统提示词
|
||||||
let prompt_repository = session_manager.store().clone();
|
// 与 LLM 实际接收的提示词完全一致(含 Expert/Subagent/Todo)
|
||||||
let system_prompt_provider: Arc<dyn crate::agent::SystemPromptProvider> = Arc::new(CompositeSystemPromptProvider::new(vec![
|
let system_prompt_provider = build_system_prompt_provider(
|
||||||
Box::new(AgentPromptProvider::new(
|
0, // 命令侧不需要 reinject 逻辑
|
||||||
0, // 不需要 reinject 逻辑
|
|
||||||
provider_config.clone(),
|
provider_config.clone(),
|
||||||
prompt_repository,
|
session_manager.store().clone(),
|
||||||
)),
|
session_manager.skills(),
|
||||||
Box::new(SkillPromptProvider::new(skills)),
|
session_manager.experts(),
|
||||||
]));
|
session_manager.subagent_runtime(),
|
||||||
|
);
|
||||||
|
|
||||||
// 注册 get_current 处理器
|
// 注册 get_current 处理器
|
||||||
command_router.register(Box::new(
|
command_router.register(Box::new(
|
||||||
|
|||||||
@ -302,6 +302,8 @@ pub(crate) fn build_session_manager_with_sender(
|
|||||||
Ok((SessionManager::from_services(SessionManagerServices {
|
Ok((SessionManager::from_services(SessionManagerServices {
|
||||||
tools: tools as Arc<ToolRegistry>,
|
tools: tools as Arc<ToolRegistry>,
|
||||||
skills,
|
skills,
|
||||||
|
experts,
|
||||||
|
subagent_runtime: subagent_runtime.clone(),
|
||||||
store,
|
store,
|
||||||
show_tool_results,
|
show_tool_results,
|
||||||
lifecycle,
|
lifecycle,
|
||||||
|
|||||||
@ -621,6 +621,8 @@ impl Session {
|
|||||||
pub struct SessionManager {
|
pub struct SessionManager {
|
||||||
tools: Arc<ToolRegistry>,
|
tools: Arc<ToolRegistry>,
|
||||||
skills: Arc<SkillRuntime>,
|
skills: Arc<SkillRuntime>,
|
||||||
|
experts: Arc<crate::experts::ExpertRuntime>,
|
||||||
|
subagent_runtime: Arc<SubagentRuntime>,
|
||||||
store: Arc<SessionStore>,
|
store: Arc<SessionStore>,
|
||||||
show_tool_results: bool,
|
show_tool_results: bool,
|
||||||
lifecycle: SessionLifecycleService,
|
lifecycle: SessionLifecycleService,
|
||||||
@ -634,6 +636,8 @@ pub struct SessionManager {
|
|||||||
pub(crate) struct SessionManagerServices {
|
pub(crate) struct SessionManagerServices {
|
||||||
pub(crate) tools: Arc<ToolRegistry>,
|
pub(crate) tools: Arc<ToolRegistry>,
|
||||||
pub(crate) skills: Arc<SkillRuntime>,
|
pub(crate) skills: Arc<SkillRuntime>,
|
||||||
|
pub(crate) experts: Arc<crate::experts::ExpertRuntime>,
|
||||||
|
pub(crate) subagent_runtime: Arc<SubagentRuntime>,
|
||||||
pub(crate) store: Arc<SessionStore>,
|
pub(crate) store: Arc<SessionStore>,
|
||||||
pub(crate) show_tool_results: bool,
|
pub(crate) show_tool_results: bool,
|
||||||
pub(crate) lifecycle: SessionLifecycleService,
|
pub(crate) lifecycle: SessionLifecycleService,
|
||||||
@ -649,6 +653,8 @@ impl SessionManager {
|
|||||||
Self {
|
Self {
|
||||||
tools: services.tools,
|
tools: services.tools,
|
||||||
skills: services.skills,
|
skills: services.skills,
|
||||||
|
experts: services.experts,
|
||||||
|
subagent_runtime: services.subagent_runtime,
|
||||||
store: services.store,
|
store: services.store,
|
||||||
show_tool_results: services.show_tool_results,
|
show_tool_results: services.show_tool_results,
|
||||||
lifecycle: services.lifecycle,
|
lifecycle: services.lifecycle,
|
||||||
@ -716,6 +722,16 @@ impl SessionManager {
|
|||||||
self.skills.clone()
|
self.skills.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 获取专家运行时实例(与 AgentFactory、HTTP API 共享同一 Arc 实例)
|
||||||
|
pub fn experts(&self) -> Arc<crate::experts::ExpertRuntime> {
|
||||||
|
self.experts.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取子代理运行时实例(与 AgentFactory 共享同一 Arc 实例)
|
||||||
|
pub fn subagent_runtime(&self) -> Arc<SubagentRuntime> {
|
||||||
|
self.subagent_runtime.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn cli_sessions(&self) -> CliSessionService {
|
pub(crate) fn cli_sessions(&self) -> CliSessionService {
|
||||||
self.cli_sessions.clone()
|
self.cli_sessions.clone()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use super::GatewayState;
|
use super::GatewayState;
|
||||||
use crate::agent::{AgentError, CompositeSystemPromptProvider};
|
use crate::agent::AgentError;
|
||||||
use crate::bus::{InboundMessage, MediaItem};
|
use crate::bus::{InboundMessage, MediaItem};
|
||||||
use crate::command::adapter::{InputAdapter, OutputAdapter};
|
use crate::command::adapter::{InputAdapter, OutputAdapter};
|
||||||
use crate::command::adapters::websocket::{WebSocketInputAdapter, WebSocketOutputAdapter};
|
use crate::command::adapters::websocket::{WebSocketInputAdapter, WebSocketOutputAdapter};
|
||||||
@ -25,9 +25,8 @@ use crate::command::handlers::save_topic::SaveTopicCommandHandler;
|
|||||||
use crate::command::handlers::session::SessionCommandHandler;
|
use crate::command::handlers::session::SessionCommandHandler;
|
||||||
use crate::command::handlers::stop_execution::StopExecutionCommandHandler;
|
use crate::command::handlers::stop_execution::StopExecutionCommandHandler;
|
||||||
use crate::command::handlers::switch_topic::SwitchTopicCommandHandler;
|
use crate::command::handlers::switch_topic::SwitchTopicCommandHandler;
|
||||||
use crate::gateway::agent_prompt_provider::AgentPromptProvider;
|
use crate::gateway::agent_factory::build_system_prompt_provider;
|
||||||
use crate::protocol::{WsInbound, WsOutbound, MediaSummary, parse_inbound, serialize_outbound};
|
use crate::protocol::{WsInbound, WsOutbound, MediaSummary, parse_inbound, serialize_outbound};
|
||||||
use crate::skills::SkillPromptProvider;
|
|
||||||
use crate::storage::persistent_session_id;
|
use crate::storage::persistent_session_id;
|
||||||
use crate::tools::task::repository::TaskRepository;
|
use crate::tools::task::repository::TaskRepository;
|
||||||
use crate::tools::task::types::TaskSessionState;
|
use crate::tools::task::types::TaskSessionState;
|
||||||
@ -399,14 +398,16 @@ async fn handle_inbound(
|
|||||||
.map_err(|e| AgentError::Other(e.to_string()))?;
|
.map_err(|e| AgentError::Other(e.to_string()))?;
|
||||||
let prompt_repository = state.session_manager.store().clone();
|
let prompt_repository = state.session_manager.store().clone();
|
||||||
|
|
||||||
let system_prompt_provider: Arc<dyn crate::agent::SystemPromptProvider> = Arc::new(CompositeSystemPromptProvider::new(vec![
|
// 与 AgentFactory::create 共享同一构建逻辑,确保 /save、/save-session、
|
||||||
Box::new(AgentPromptProvider::new(
|
// /current 保存/展示的系统提示词与 LLM 实际接收的完全一致
|
||||||
0,
|
let system_prompt_provider = build_system_prompt_provider(
|
||||||
|
0, // 命令侧不需要 reinject 逻辑
|
||||||
provider_config.clone(),
|
provider_config.clone(),
|
||||||
prompt_repository.clone(),
|
prompt_repository,
|
||||||
)),
|
skills,
|
||||||
Box::new(SkillPromptProvider::new(skills)),
|
state.session_manager.experts(),
|
||||||
]));
|
state.session_manager.subagent_runtime(),
|
||||||
|
);
|
||||||
|
|
||||||
let mut router = CommandRouter::new();
|
let mut router = CommandRouter::new();
|
||||||
// 注册 Session 处理器
|
// 注册 Session 处理器
|
||||||
@ -426,7 +427,11 @@ async fn handle_inbound(
|
|||||||
.with_session_manager(state.session_manager.clone());
|
.with_session_manager(state.session_manager.clone());
|
||||||
router.register(Box::new(switch_handler));
|
router.register(Box::new(switch_handler));
|
||||||
// 注册 get_current 处理器
|
// 注册 get_current 处理器
|
||||||
router.register(Box::new(GetCurrentSessionCommandHandler::new(store.clone())));
|
router.register(Box::new(
|
||||||
|
GetCurrentSessionCommandHandler::new(store.clone())
|
||||||
|
.with_session_manager(state.session_manager.clone())
|
||||||
|
.with_system_prompt_provider(system_prompt_provider.clone()),
|
||||||
|
));
|
||||||
// 注册 load_topic 处理器
|
// 注册 load_topic 处理器
|
||||||
router.register(Box::new(LoadTopicCommandHandler::new(store.clone())));
|
router.register(Box::new(LoadTopicCommandHandler::new(store.clone())));
|
||||||
// 注册 load_task_messages 处理器
|
// 注册 load_task_messages 处理器
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user