feat: 添加定时任务执行系统提示,优化agent_task作业的提示描述,避免重复调度时间

This commit is contained in:
ooodc 2026-04-26 23:31:21 +08:00
parent c83d697f93
commit 60cc8e507c
2 changed files with 98 additions and 11 deletions

View File

@ -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<String, LLMProviderConfig>,
@ -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;

View File

@ -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("每小时"));
}
}