From 60cc8e507cb46cf6b958cb936c41d738883dd738 Mon Sep 17 00:00:00 2001 From: ooodc <549496103@qq.com> Date: Sun, 26 Apr 2026 23:31:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E6=89=A7=E8=A1=8C=E7=B3=BB=E7=BB=9F=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=EF=BC=8C=E4=BC=98=E5=8C=96agent=5Ftask=E4=BD=9C?= =?UTF-8?q?=E4=B8=9A=E7=9A=84=E6=8F=90=E7=A4=BA=E6=8F=8F=E8=BF=B0=EF=BC=8C?= =?UTF-8?q?=E9=81=BF=E5=85=8D=E9=87=8D=E5=A4=8D=E8=B0=83=E5=BA=A6=E6=97=B6?= =?UTF-8?q?=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/gateway/session.rs | 90 +++++++++++++++++++++++++++++++---- src/tools/scheduler_manage.rs | 19 +++++++- 2 files changed, 98 insertions(+), 11 deletions(-) diff --git a/src/gateway/session.rs b/src/gateway/session.rs index 6b24a68..66cba6c 100644 --- a/src/gateway/session.rs +++ b/src/gateway/session.rs @@ -33,6 +33,7 @@ const MANAGED_AGENT_MEMORY_TITLE: &str = "## 用户记忆摘要"; const MEMORY_MAINTENANCE_SYSTEM_PROMPT: &str = include_str!("memory_maintenance_system_prompt.md"); const MEMORY_MAINTENANCE_RETRY_DELAYS_MS: &[u64] = &[1_000, 3_000]; +const SCHEDULED_TASK_EXECUTION_SYSTEM_PROMPT: &str = "系统说明:当前输入来自一次已经触发的定时任务执行。你现在需要执行任务内容本身,而不是创建、修改、恢复、暂停或查询新的定时任务。除非当前任务内容明确要求管理调度器,否则不要调用任何定时任务管理工具;像“每小时”、“每天”、“cron”、“定时”等词,只应视为任务背景,不应再解释为新的建任务请求。"; #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum MemoryMaintenanceCategory { @@ -1481,15 +1482,15 @@ impl SessionManager { session_guard.ensure_chat_loaded(chat_id)?; session_guard.ensure_agent_prompt_before_user_message(chat_id)?; - if let Some(system_prompt) = options.system_prompt.as_deref() { - session_guard.append_persisted_message( - chat_id, - ChatMessage::system_with_context( - system_prompt, - Some(SYSTEM_CONTEXT_SCHEDULED_PROMPT.to_string()), - ), - )?; - } + let scheduled_system_prompt = + compose_scheduled_task_system_prompt(options.system_prompt.as_deref()); + session_guard.append_persisted_message( + chat_id, + ChatMessage::system_with_context( + &scheduled_system_prompt, + Some(SYSTEM_CONTEXT_SCHEDULED_PROMPT.to_string()), + ), + )?; let user_message = session_guard.create_user_message(prompt, Vec::new()); let user_message_id = user_message.id.clone(); @@ -1575,6 +1576,17 @@ fn should_display_message_to_user(show_tool_results: bool, message: &ChatMessage ) } +fn compose_scheduled_task_system_prompt(system_prompt: Option<&str>) -> String { + match system_prompt.map(str::trim).filter(|value| !value.is_empty()) { + Some(system_prompt) => format!( + "{}\n\n任务专属要求:{}", + SCHEDULED_TASK_EXECUTION_SYSTEM_PROMPT, + system_prompt + ), + None => SCHEDULED_TASK_EXECUTION_SYSTEM_PROMPT.to_string(), + } +} + fn select_provider_config( default_provider_config: &LLMProviderConfig, provider_configs: &HashMap, @@ -1936,6 +1948,66 @@ mod tests { assert!(default_outbound[0].content.contains("default-model")); } + #[tokio::test] + async fn test_run_scheduled_agent_task_persists_execution_guard_prompt() { + let base_url = start_mock_openai_server().await; + let provider_config = LLMProviderConfig { + provider_type: "openai".to_string(), + name: "default-provider".to_string(), + base_url, + api_key: "test-key".to_string(), + extra_headers: HashMap::new(), + model_id: "default-model".to_string(), + temperature: Some(0.0), + max_tokens: Some(32), + model_extra: HashMap::new(), + max_tool_iterations: 1, + llm_timeout_secs: 30, + tool_result_max_chars: 20_000, + context_tool_result_trim_chars: 20_000, + }; + + let session_manager = SessionManager::new( + 4, + 100, + false, + provider_config.clone(), + HashMap::from([("default".to_string(), provider_config)]), + Arc::new(SkillRuntime::default()), + ) + .unwrap(); + + session_manager + .run_scheduled_agent_task( + "feishu", + "chat-guard", + "每小时执行以下流程:检查邮箱并同步待办", + ScheduledAgentTaskOptions { + fresh_session: true, + system_prompt: Some("你是邮箱待办同步助手。".to_string()), + ..Default::default() + }, + ) + .await + .unwrap(); + + let session = session_manager.get("feishu").await.unwrap(); + let session_guard = session.lock().await; + let persisted_messages = session_guard + .store + .load_messages(&session_guard.persistent_session_id("chat-guard")) + .unwrap(); + + let scheduled_prompt = persisted_messages + .iter() + .find(|message| message.has_system_context(SYSTEM_CONTEXT_SCHEDULED_PROMPT)) + .expect("missing scheduled system prompt"); + + assert!(scheduled_prompt.content.contains("已经触发的定时任务执行")); + assert!(scheduled_prompt.content.contains("不要调用任何定时任务管理工具")); + assert!(scheduled_prompt.content.contains("你是邮箱待办同步助手。")); + } + #[tokio::test] async fn test_summarize_memory_maintenance_for_scope_uses_model_output() { let base_url = start_mock_openai_server().await; diff --git a/src/tools/scheduler_manage.rs b/src/tools/scheduler_manage.rs index b7fc57f..d9e8dff 100644 --- a/src/tools/scheduler_manage.rs +++ b/src/tools/scheduler_manage.rs @@ -31,7 +31,7 @@ impl Tool for SchedulerManageTool { } fn description(&self) -> &str { - "Manage DB-backed scheduled jobs. Supports actions: list, get, put, delete, pause, resume. Jobs persist in SQLite and are executed by the scheduler runtime." + "Manage DB-backed scheduled jobs. Supports actions: list, get, put, delete, pause, resume. Jobs persist in SQLite and are executed by the scheduler runtime. When creating agent_task jobs, keep prompt/system_prompt focused on the work to perform; do not restate execution times unless the task logic truly depends on them, because the trigger already controls timing." } fn parameters_schema(&self) -> serde_json::Value { @@ -78,7 +78,7 @@ impl Tool for SchedulerManageTool { }, "payload": { "type": "object", - "description": format!("Job payload. agent_task supports prompt, agent, fresh_session, system_prompt, sender_id, metadata. {} outbound_message expects content. internal_event expects event.", agent_hint) + "description": format!("Job payload. agent_task supports prompt, agent, fresh_session, system_prompt, sender_id, metadata. For agent_task, write prompt/system_prompt as execution instructions and avoid repeating schedule phrases or execution times such as 每天9点 or 每小时 unless the task itself must reason about time. {} outbound_message expects content. internal_event expects event.", agent_hint) }, "max_runs": { "type": ["integer", "null"] @@ -619,4 +619,19 @@ mod tests { Some("Missing required parameters: scheduler_manage expects a JSON object like {\"action\":\"list\"}") ); } + + #[test] + fn test_scheduler_manage_schema_advises_against_repeating_schedule_in_agent_task_prompt() { + let store = Arc::new(SessionStore::in_memory().unwrap()); + let tool = SchedulerManageTool::new(store, HashSet::new()); + + let schema = tool.parameters_schema(); + let payload_description = schema["properties"]["payload"]["description"] + .as_str() + .unwrap(); + + assert!(payload_description.contains("avoid repeating schedule phrases or execution times")); + assert!(payload_description.contains("每天9点")); + assert!(payload_description.contains("每小时")); + } } \ No newline at end of file