Compare commits
No commits in common. "c36650c9aa66c67f4ee0dd5580f49b752dae8671" and "cc3e890ccd9153e3d0028c729d3566c98dc0ed75" have entirely different histories.
c36650c9aa
...
cc3e890ccd
@ -7,7 +7,7 @@ use crate::storage::{MemoryRepository, SchedulerJobRepository, SkillEventReposit
|
|||||||
use crate::tools::{
|
use crate::tools::{
|
||||||
BashTool, CalculatorTool, FileEditTool, FileReadTool, FileWriteTool,
|
BashTool, CalculatorTool, FileEditTool, FileReadTool, FileWriteTool,
|
||||||
HttpRequestTool, MemoryManageTool, MemorySearchTool,
|
HttpRequestTool, MemoryManageTool, MemorySearchTool,
|
||||||
SchedulerManageTool, SessionMessageSender, SessionSendTool, SkillActivateTool,
|
SchedulerManageTool, SessionMessageSender, SessionSendTool, SkillActivateTool, SkillListTool,
|
||||||
SkillManageTool, SubAgentRuntime, TaskTool, TimeTool,
|
SkillManageTool, SubAgentRuntime, TaskTool, TimeTool,
|
||||||
ToolRegistry, WebFetchTool,
|
ToolRegistry, WebFetchTool,
|
||||||
};
|
};
|
||||||
@ -102,6 +102,9 @@ impl ToolRegistryFactory {
|
|||||||
self.skill_events.clone(),
|
self.skill_events.clone(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
if self.is_enabled("skill_list") {
|
||||||
|
registry.register(SkillListTool::new(self.skills.clone()));
|
||||||
|
}
|
||||||
if self.is_enabled("skill_manage") {
|
if self.is_enabled("skill_manage") {
|
||||||
registry.register(SkillManageTool::new(self.skills.clone()));
|
registry.register(SkillManageTool::new(self.skills.clone()));
|
||||||
}
|
}
|
||||||
@ -177,6 +180,9 @@ impl ToolRegistryFactory {
|
|||||||
self.skill_events.clone(),
|
self.skill_events.clone(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
if self.is_enabled("skill_list") {
|
||||||
|
registry.register(SkillListTool::new(self.skills.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
// 进度通知工具
|
// 进度通知工具
|
||||||
if self.is_enabled("session_send") {
|
if self.is_enabled("session_send") {
|
||||||
|
|||||||
@ -434,23 +434,7 @@ impl SkillCatalog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut prompt = String::from(
|
let mut prompt = String::from(
|
||||||
"# 技能系统(Skills)\n\n\
|
"# 技能说明\n技能为特定任务提供专用说明和工作流。\n当任务匹配其描述时,使用 skill_activate 工具加载技能。\n技能不是工具名,即使技能名看起来像工具,也不能直接调用技能名。\n如果需要某个技能,必须先调用 tool skill_activate,并传入 {\"name\": \"<skill-name>\"},再根据返回的技能说明执行。\n\n<available_skills>\n",
|
||||||
技能是预定义的工作流和指令集合,用于处理特定类型的任务。当任务涉及专业化工作流时,使用技能系统获取详细的执行指导。\n\n\
|
|
||||||
## 何时使用技能\n\n\
|
|
||||||
当满足以下条件时,应该使用技能:\n\
|
|
||||||
- 当前任务与某个技能的描述相匹配\n\
|
|
||||||
- 需要执行特定领域的专业化工作流\n\
|
|
||||||
- 任务涉及多步骤操作,且有现成技能可用\n\n\
|
|
||||||
## 如何使用技能\n\n\
|
|
||||||
1. **查看可用技能**: 浏览下方的 <available_skills> 列表,了解可用的技能\n\
|
|
||||||
2. **匹配任务**: 判断是否有技能的描述与当前任务匹配\n\
|
|
||||||
3. **激活技能**: 调用 `skill_activate` 工具,传入技能名称(name 参数)\n\
|
|
||||||
4. **执行指令**: 根据 skill_activate 返回的详细说明执行任务\n\n\
|
|
||||||
## 注意事项\n\n\
|
|
||||||
- 技能名称不是工具名称,不能直接作为工具调用\n\
|
|
||||||
- 必须先调用 skill_activate 获取技能的具体指令,再按照指令执行\n\
|
|
||||||
- 一次只能激活一个技能,激活后会返回该技能的完整说明\n\n\
|
|
||||||
<available_skills>\n",
|
|
||||||
);
|
);
|
||||||
|
|
||||||
for skill in &self.skills {
|
for skill in &self.skills {
|
||||||
|
|||||||
@ -33,7 +33,7 @@ pub use session_send::{
|
|||||||
};
|
};
|
||||||
pub use schema::{CleaningStrategy, SchemaCleanr};
|
pub use schema::{CleaningStrategy, SchemaCleanr};
|
||||||
pub use skill_activate::SkillActivateTool;
|
pub use skill_activate::SkillActivateTool;
|
||||||
pub use skill_manage::SkillManageTool;
|
pub use skill_manage::{SkillListTool, SkillManageTool};
|
||||||
pub use task::{
|
pub use task::{
|
||||||
DefaultSubAgentRuntime, InMemoryTaskRepository, SubAgentRuntime, SubAgentRuntimeConfig,
|
DefaultSubAgentRuntime, InMemoryTaskRepository, SubAgentRuntime, SubAgentRuntimeConfig,
|
||||||
TaskError, TaskRepository, TaskTool,
|
TaskError, TaskRepository, TaskTool,
|
||||||
|
|||||||
@ -10,12 +10,22 @@ pub struct SkillManageTool {
|
|||||||
skills: Arc<SkillRuntime>,
|
skills: Arc<SkillRuntime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SkillListTool {
|
||||||
|
skills: Arc<SkillRuntime>,
|
||||||
|
}
|
||||||
|
|
||||||
impl SkillManageTool {
|
impl SkillManageTool {
|
||||||
pub fn new(skills: Arc<SkillRuntime>) -> Self {
|
pub fn new(skills: Arc<SkillRuntime>) -> Self {
|
||||||
Self { skills }
|
Self { skills }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SkillListTool {
|
||||||
|
pub fn new(skills: Arc<SkillRuntime>) -> Self {
|
||||||
|
Self { skills }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Tool for SkillManageTool {
|
impl Tool for SkillManageTool {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
@ -23,16 +33,7 @@ impl Tool for SkillManageTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
"Manage PicoBot skills. Actions: list, get, create, update, delete, disable, reload.\n\n\
|
"Manage PicoBot skills stored under .picobot/skills or ~/.picobot/skills, while discovery also reads .agents/skills, ~/.agents/skills, .openclaw/skills, and ~/.openclaw/skills. Supports actions: list, get, create, update, delete, disable, reload."
|
||||||
Skill Structure:\n\
|
|
||||||
- Folder name: kebab-case (lowercase with hyphens, e.g., 'my-cool-skill')\n\
|
|
||||||
- Required: SKILL.md with YAML frontmatter + Markdown body\n\
|
|
||||||
- Optional folders: scripts/, references/, assets/\n\
|
|
||||||
- Storage: .picobot/skills/{name}/SKILL.md or ~/.picobot/skills/{name}/SKILL.md\n\n\
|
|
||||||
Installing from Zip:\n\
|
|
||||||
- Extract skill folders to skills/ directory\n\
|
|
||||||
- If zip contains multiple skills, extract each subfolder separately\n\
|
|
||||||
- Final structure: skills/{skill-name}/SKILL.md"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parameters_schema(&self) -> serde_json::Value {
|
fn parameters_schema(&self) -> serde_json::Value {
|
||||||
@ -51,7 +52,7 @@ impl Tool for SkillManageTool {
|
|||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Skill folder name in kebab-case (e.g., 'my-cool-skill', 'code-review'). Must match the folder name under .picobot/skills/ or ~/.picobot/skills/"
|
"description": "Skill name"
|
||||||
},
|
},
|
||||||
"names": {
|
"names": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@ -100,6 +101,10 @@ impl Tool for SkillManageTool {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let name = args.get("name").and_then(|v| v.as_str());
|
let name = args.get("name").and_then(|v| v.as_str());
|
||||||
|
let names = match parse_disable_names(&args) {
|
||||||
|
Ok(names) => names,
|
||||||
|
Err(err) => return Ok(error_result(&err)),
|
||||||
|
};
|
||||||
|
|
||||||
let result = match action {
|
let result = match action {
|
||||||
"list" => list_skills_payload(&self.skills),
|
"list" => list_skills_payload(&self.skills),
|
||||||
@ -202,10 +207,6 @@ impl Tool for SkillManageTool {
|
|||||||
Err(err) => return Ok(error_result(&err)),
|
Err(err) => return Ok(error_result(&err)),
|
||||||
},
|
},
|
||||||
"disable" => {
|
"disable" => {
|
||||||
let names = match parse_disable_names(&args) {
|
|
||||||
Ok(names) => names,
|
|
||||||
Err(err) => return Ok(error_result(&err)),
|
|
||||||
};
|
|
||||||
let targets = &names;
|
let targets = &names;
|
||||||
|
|
||||||
let mut changes = Vec::new();
|
let mut changes = Vec::new();
|
||||||
@ -240,6 +241,37 @@ impl Tool for SkillManageTool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Tool for SkillListTool {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"skill_list"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
"List currently discovered PicoBot skills as a read-only operation. Use this when you only need to inspect available skills without modifying them."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parameters_schema(&self) -> serde_json::Value {
|
||||||
|
json!({
|
||||||
|
"type": "object",
|
||||||
|
"properties": {},
|
||||||
|
"additionalProperties": false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_only(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn execute(&self, _args: serde_json::Value) -> anyhow::Result<ToolResult> {
|
||||||
|
Ok(ToolResult {
|
||||||
|
success: true,
|
||||||
|
output: serde_json::to_string_pretty(&list_skills_payload(&self.skills))?,
|
||||||
|
error: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn error_result(message: &str) -> ToolResult {
|
fn error_result(message: &str) -> ToolResult {
|
||||||
ToolResult {
|
ToolResult {
|
||||||
success: false,
|
success: false,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user