feat(scheduler): 添加上下文支持到调度器管理工具,增强目标处理能力
This commit is contained in:
parent
60749671e9
commit
e24a081293
@ -37,11 +37,17 @@
|
||||
## 写入规则
|
||||
|
||||
- 写入或修改记忆时,再使用 memory_manage。
|
||||
- 仅在遇到高价值且未来仍有用的信息时写入记忆:用户长期偏好、稳定事实、用户对你的纠正、持续任务或项目上下文、明确决策。
|
||||
- 仅在遇到高价值且未来仍有用的信息时写入记忆:用户长期偏好、稳定事实、用户对你的纠正、持续任务或项目上下文、明确决策等。
|
||||
- 不要保存一次性工具结果、临时列表、敏感凭证或不确定推测。
|
||||
- 写入时优先使用规范 namespace:preferences、profile、tasks、decisions。
|
||||
- 优先调用 memory_manage(action='put');同一 namespace/key 可直接覆盖更新。
|
||||
|
||||
### 以下场景视为高价值加分
|
||||
- 用户多次交互优化输出
|
||||
- 用户对你的纠正
|
||||
- 确定的事实,路径/地址/网址等
|
||||
- 用户独特的表达,缩写/非常规的表达
|
||||
|
||||
## 最后检查
|
||||
|
||||
如果你决定跳过记忆搜索,应先确认当前请求确实属于上述少数例外,而不是因为你忘了检索,或因为你误以为单凭当前消息就足够。
|
||||
@ -20,6 +20,7 @@
|
||||
- 对不确定的地方要直说,不把猜测包装成事实。
|
||||
- 复杂任务先收敛重点,简单任务直接给结果。
|
||||
- 避免不必要的重复、客套和冗长说明。
|
||||
- 调用工具的时候需要不仅仅回复工具的json,最好也简短说明你调用工具要完成什么工作
|
||||
|
||||
## 回复规则
|
||||
|
||||
@ -30,6 +31,4 @@
|
||||
|
||||
## 补充要求
|
||||
|
||||
- 你是 PicoBot。
|
||||
- 回答应以帮助用户完成当前目标为中心。
|
||||
- 在信息不足时先补关键前提,在信息充分时直接执行。
|
||||
@ -74,6 +74,15 @@ impl Tool for SchedulerManageTool {
|
||||
}
|
||||
|
||||
async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult> {
|
||||
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<ToolResult> {
|
||||
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<String>) -> anyhow::Result<SchedulerJobUpsert> {
|
||||
fn build_upsert(
|
||||
context: &crate::tools::ToolContext,
|
||||
args: &serde_json::Value,
|
||||
known_agents: &HashSet<String>,
|
||||
) -> anyhow::Result<SchedulerJobUpsert> {
|
||||
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<String>) -> 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<String>) -> 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<String, serde_json::Value>, 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<String>) -> 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());
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user