From e24a081293dd2b6c96b95ece013ae911173693ed Mon Sep 17 00:00:00 2001 From: ooodc <549496103@qq.com> Date: Thu, 23 Apr 2026 23:26:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(scheduler):=20=E6=B7=BB=E5=8A=A0=E4=B8=8A?= =?UTF-8?q?=E4=B8=8B=E6=96=87=E6=94=AF=E6=8C=81=E5=88=B0=E8=B0=83=E5=BA=A6?= =?UTF-8?q?=E5=99=A8=E7=AE=A1=E7=90=86=E5=B7=A5=E5=85=B7=EF=BC=8C=E5=A2=9E?= =?UTF-8?q?=E5=BC=BA=E7=9B=AE=E6=A0=87=E5=A4=84=E7=90=86=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agent/memory_tool_usage_system_prompt.md | 8 +- src/gateway/default_agent_prompt.md | 3 +- src/tools/scheduler_manage.rs | 92 +++++++++++++++++++- 3 files changed, 97 insertions(+), 6 deletions(-) diff --git a/src/agent/memory_tool_usage_system_prompt.md b/src/agent/memory_tool_usage_system_prompt.md index c80efd0..58ee04c 100644 --- a/src/agent/memory_tool_usage_system_prompt.md +++ b/src/agent/memory_tool_usage_system_prompt.md @@ -37,11 +37,17 @@ ## 写入规则 - 写入或修改记忆时,再使用 memory_manage。 -- 仅在遇到高价值且未来仍有用的信息时写入记忆:用户长期偏好、稳定事实、用户对你的纠正、持续任务或项目上下文、明确决策。 +- 仅在遇到高价值且未来仍有用的信息时写入记忆:用户长期偏好、稳定事实、用户对你的纠正、持续任务或项目上下文、明确决策等。 - 不要保存一次性工具结果、临时列表、敏感凭证或不确定推测。 - 写入时优先使用规范 namespace:preferences、profile、tasks、decisions。 - 优先调用 memory_manage(action='put');同一 namespace/key 可直接覆盖更新。 +### 以下场景视为高价值加分 +- 用户多次交互优化输出 +- 用户对你的纠正 +- 确定的事实,路径/地址/网址等 +- 用户独特的表达,缩写/非常规的表达 + ## 最后检查 如果你决定跳过记忆搜索,应先确认当前请求确实属于上述少数例外,而不是因为你忘了检索,或因为你误以为单凭当前消息就足够。 \ No newline at end of file diff --git a/src/gateway/default_agent_prompt.md b/src/gateway/default_agent_prompt.md index 42bd732..11f166e 100644 --- a/src/gateway/default_agent_prompt.md +++ b/src/gateway/default_agent_prompt.md @@ -20,6 +20,7 @@ - 对不确定的地方要直说,不把猜测包装成事实。 - 复杂任务先收敛重点,简单任务直接给结果。 - 避免不必要的重复、客套和冗长说明。 +- 调用工具的时候需要不仅仅回复工具的json,最好也简短说明你调用工具要完成什么工作 ## 回复规则 @@ -30,6 +31,4 @@ ## 补充要求 -- 你是 PicoBot。 -- 回答应以帮助用户完成当前目标为中心。 - 在信息不足时先补关键前提,在信息充分时直接执行。 \ No newline at end of file diff --git a/src/tools/scheduler_manage.rs b/src/tools/scheduler_manage.rs index 1e434a3..d769efd 100644 --- a/src/tools/scheduler_manage.rs +++ b/src/tools/scheduler_manage.rs @@ -74,6 +74,15 @@ impl Tool for SchedulerManageTool { } async fn execute(&self, args: serde_json::Value) -> anyhow::Result { + self.execute_with_context(&crate::tools::ToolContext::default(), args) + .await + } + + async fn execute_with_context( + &self, + context: &crate::tools::ToolContext, + args: serde_json::Value, + ) -> anyhow::Result { let action = match args.get("action").and_then(|value| value.as_str()) { Some(action) => action, None => return Ok(error_result("Missing required parameter: action")), @@ -96,7 +105,7 @@ impl Tool for SchedulerManageTool { } } "put" => { - let input = build_upsert(&args, &self.known_agents)?; + let input = build_upsert(context, &args, &self.known_agents)?; let record = self.store.upsert_scheduler_job(&input)?; record_to_json(&record) } @@ -145,7 +154,11 @@ impl Tool for SchedulerManageTool { } } -fn build_upsert(args: &serde_json::Value, known_agents: &HashSet) -> anyhow::Result { +fn build_upsert( + context: &crate::tools::ToolContext, + args: &serde_json::Value, + known_agents: &HashSet, +) -> anyhow::Result { let id = require_str(args, "id")?.to_string(); let kind = require_str(args, "kind")?.to_string(); let schedule_value = args @@ -164,7 +177,10 @@ fn build_upsert(args: &serde_json::Value, known_agents: &HashSet) -> any }; let payload = args.get("payload").cloned().unwrap_or_else(|| json!({})); - let target = args.get("target").cloned().unwrap_or_else(|| json!({})); + let target = enrich_target_from_context( + args.get("target").cloned().unwrap_or_else(|| json!({})), + context, + ); if kind == "agent_task" { validate_agent_task_payload(&payload, known_agents)?; validate_target_fields(&target, &["channel", "chat_id"], "agent_task")?; @@ -198,6 +214,38 @@ fn build_upsert(args: &serde_json::Value, known_agents: &HashSet) -> any }) } +fn enrich_target_from_context( + target: serde_json::Value, + context: &crate::tools::ToolContext, +) -> serde_json::Value { + let mut object = match target { + serde_json::Value::Object(map) => map, + _ => return target, + }; + + if !has_non_empty_string(&object, "channel") { + if let Some(channel_name) = context.channel_name.as_ref().filter(|value| !value.trim().is_empty()) { + object.insert("channel".to_string(), serde_json::Value::String(channel_name.clone())); + } + } + + if !has_non_empty_string(&object, "chat_id") { + if let Some(chat_id) = context.chat_id.as_ref().filter(|value| !value.trim().is_empty()) { + object.insert("chat_id".to_string(), serde_json::Value::String(chat_id.clone())); + } + } + + serde_json::Value::Object(object) +} + +fn has_non_empty_string(object: &serde_json::Map, field: &str) -> bool { + object + .get(field) + .and_then(|value| value.as_str()) + .map(|value| !value.trim().is_empty()) + .unwrap_or(false) +} + fn validate_agent_task_payload(payload: &serde_json::Value, known_agents: &HashSet) -> anyhow::Result<()> { let Some(prompt) = payload.get("prompt").and_then(|value| value.as_str()) else { anyhow::bail!("agent_task payload.prompt is required and must be a string") @@ -411,6 +459,44 @@ mod tests { assert!(error.contains("outbound_message target.channel is required")); } + #[tokio::test] + async fn test_scheduler_manage_put_uses_tool_context_target_defaults() { + let store = Arc::new(SessionStore::in_memory().unwrap()); + let tool = SchedulerManageTool::new(store.clone(), HashSet::new()); + + let put_result = tool + .execute_with_context( + &crate::tools::ToolContext { + channel_name: Some("feishu".to_string()), + sender_id: Some("user-1".to_string()), + chat_id: Some("oc_demo".to_string()), + session_id: Some("feishu:oc_demo".to_string()), + message_id: Some("msg-1".to_string()), + message_seq: Some(1), + }, + json!({ + "action": "put", + "id": "work_reminder", + "kind": "outbound_message", + "schedule": { + "type": "interval", + "seconds": 60 + }, + "payload": { + "content": "ping" + } + }), + ) + .await + .unwrap(); + + assert!(put_result.success); + + let saved = store.get_scheduler_job("work_reminder").unwrap().unwrap(); + assert_eq!(saved.target["channel"], "feishu"); + assert_eq!(saved.target["chat_id"], "oc_demo"); + } + #[tokio::test] async fn test_scheduler_manage_rejects_unknown_agent_task_agent() { let store = Arc::new(SessionStore::in_memory().unwrap());