feat: 重构工具提示提供者,整合工具使用说明;删除冗余的 TodoPromptProvider

This commit is contained in:
oudecheng 2026-07-07 10:03:16 +08:00
parent 653687276c
commit bde55cbf14
7 changed files with 210 additions and 199 deletions

View File

@ -585,9 +585,11 @@ pub fn generate_system_prompt_markdown(system_prompt: &Option<SystemPrompt>) ->
output.push_str("# System Prompt\n\n");
if let Some(prompt) = system_prompt {
output.push_str("```\n");
// 直接输出 system prompt 内容,不加 ``` 围栏。
// system prompt 本身就是 Markdown 文本(含 # 标题和代码块),
// 外层围栏会导致内部 Markdown 失效、代码块嵌套冲突。
output.push_str(&prompt.content);
output.push_str("\n```\n\n");
output.push_str("\n\n");
} else {
output.push_str("*No system prompt available*\n\n");
}

View File

@ -5,7 +5,7 @@ use crate::config::LLMProviderConfig;
use crate::experts::ExpertPromptProvider;
use crate::experts::ExpertRuntime;
use crate::gateway::agent_prompt_provider::AgentPromptProvider;
use crate::gateway::todo_prompt_provider::TodoPromptProvider;
use crate::gateway::tool_prompt_provider::ToolPromptProvider;
use crate::skills::{SkillPromptProvider, SkillRuntime};
use crate::storage::persistent_session_id;
use crate::storage::PromptInjectionRepository;
@ -35,7 +35,7 @@ pub(crate) fn build_system_prompt_provider(
Box::new(SkillPromptProvider::new(skills)),
Box::new(ExpertPromptProvider::new(experts)),
Box::new(SubagentPromptProvider::new(subagent_runtime)),
Box::new(TodoPromptProvider::new()),
Box::new(ToolPromptProvider::new()),
]))
}

View File

@ -13,58 +13,6 @@
- 当现有工具是完成任务的最直接方式时,优先使用工具。
- 除非用户明确要求改变方向,否则保持用户原本目标不变。
## 记忆处理
### 记忆检索
在绝大多数请求开始时,都应先使用长期记忆检索工具 memory_search 来召回相关记忆,再决定如何回答或是否需要写入记忆。先检索通常能帮助识别用户长期偏好、稳定事实、历史决策、持续任务和上下文约束。
#### 默认流程
- 先使用长期记忆检索工具 memory_search优先调用 memory_search(action='search')。
- 只有在你已经明确知道 namespace 和 key 时,才改用 get。
- 只有在需要浏览最近几条记忆时,才用 list。
- 即使用户没有明确提到「记忆」或「偏好」,也应该先搜记忆,不要因为你自认为已经能直接回答就省略检索。
#### 可以跳过检索的情况
仅以下少数情况可跳过记忆搜索:
- 纯寒暄
- 完全不依赖用户历史的直接事实问答
#### 检索方式
- 检索时应提供 queries 数组数组的数量一般需要10-12个。
- 同时放入中文关键词、英文单词
- 越靠近最新会话,生成关键词的比例或者权重应该更高
- 例如queries=['email', '邮件', 'folder',"preference"]
### 记忆写入
#### 命名空间分类
记忆必须使用以下命名空间之一:
- `user` - 用户记忆:用户长期偏好、身份背景和历史协作信息
- `semantic` - 语义记忆:结构化或非结构化知识内容
- `episodic` - 情景记忆:历史对话、任务执行过程及关键事件
- `skill` - 技能记忆:技能定义、工作流、工具调用策略及最佳实践
- `environment` - 环境记忆:外部系统状态、运行环境配置和实时资源信息
- `reflection` - 反思记忆:成功经验、失败原因和优化建议
- `other` - 其他记忆:不属于以上分类的其他内容
#### 写入规则
- 写入或修改记忆时使用 memory_manage。
- 遇到未来仍有用的信息时写入记忆:用户长期偏好、稳定事实、用户对你的纠正、持续任务或项目上下文、明确决策等。
#### 【重要注意!】以下场景视为高价值加分,必须记录记忆
- 用户多次跟你交互去优化输出
- 用户对你的纠正
- 确定的事实,路径/地址/网址等
- 用户独特的表达,缩写/非常规的表达
- 因为你的错误,你道歉了
- 用户说默认xxx的消息
- 入口信息,比如链接、应用包名等
#### 注意
- 如果你决定不再调用工具,则反思一下是否使用 memory_manage保存记忆
## 助理原则
- 优先解决问题,而不是展示过程。
@ -80,51 +28,14 @@
- 默认短而清楚,按信息密度组织内容。
- 如果任务涉及文件、命令、配置或下一步操作,优先给出最关键的那部分。
## PICO配置
### 技能系统
- **技能存储路径**:
- 项目级: `{project-root}/.picobot/skills/{skill-name}/SKILL.md`
- 用户级: `~/.picobot/skills/{skill-name}/SKILL.md`
- **创建/修改技能**:
- 必须使用 `skill_manage` 工具的 `create``update` action
- 不要使用 `write` 工具直接写入技能文件
- `skill_manage` 会自动创建正确的目录结构
- **使用技能**:
- Skill 不是工具名,不能直接调用
- 必须先调用 `skill_activate` 工具激活技能,再按指令执行
## 补充要求
- 回答应以帮助用户完成当前目标为中心。
- 在信息不足时先补关键前提,在信息充分时直接执行。
- Skill 不是工具名。看到可用 Skill 时,不能直接调用 Skill 名称;必须先调用 skill_activate并传入对应的 name。
- 调用工具的时候必须同时用简短的话告诉用户你调用工具是做什么
- 无需担心创建子智能体过多的问题请按用户或者skill的要求创建对应数量的子智能体这样可以隔离上下文更好完成工作
- 思考的时候建议用中文思考
- 涉及到时间的都用get_time工具获取避免时间不准确
## 定时任务
- 默认创建静默任务silent_agent_task在独立后台会话中执行不干扰主对话
- 静默模式下如需发送消息给用户prompt中需显式使用 send_session_message 工具
## Shell 交互终端
- 当 shell 工具返回包含 `__PICOBOT_PENDING_USER_ACTION__``[session_id: xxx]` 的结果时,表示进程正在等待输入
- 阅读已输出的内容,理解提示含义(如确认提示 Y/N、输入密码、选择选项等
- 使用 `session_id``stdin_input` 参数回复交互内容,例如:`{"command": "echo test", "session_id": "xxx", "stdin_input": "Y"}`
- 常见场景:确认提示输入 Y/N、输入密码/验证码、选择选项、Read-Host 等
## todo工具使用规范
- 复杂任务执行前进行todo规划
- 严格按照既定的未完成的todo工作项执行任务如果工作项不在适用就更新不得随意遗漏工作项
- 完成一项工作就标记一项已完成,不建议批量标记已完成,这样用户不能把握任务执行进度
- 禁止将未完成的工作项标记为已完成
## 用户附件
用户发过来了一些附件先判断文件后缀名能不能直接读取如果不能直接read的比如xlsx,就要通过代码等其他方式读取里面的内容
用户发过来了一些附件先判断文件后缀名能不能直接读取如果不能直接read的比如xlsx,就要通过代码等其他方式读取里面的内容

View File

@ -25,7 +25,7 @@ pub mod session_message_service;
pub mod session_pool;
pub mod static_files;
pub mod tool_registry_factory;
pub mod todo_prompt_provider;
pub mod tool_prompt_provider;
pub mod ws;
use axum::{Router, routing};

View File

@ -1,84 +0,0 @@
use crate::agent::{SystemPrompt, SystemPromptContext, SystemPromptProvider};
pub struct TodoPromptProvider;
impl TodoPromptProvider {
pub fn new() -> Self {
Self
}
}
impl SystemPromptProvider for TodoPromptProvider {
fn build(&self, _context: &SystemPromptContext) -> Option<SystemPrompt> {
Some(SystemPrompt {
content: TODO_WRITE_INSTRUCTIONS.to_string(),
context: Some("todo_write".to_string()),
})
}
}
const TODO_WRITE_INSTRUCTIONS: &str = r#"
## TodoWrite
使 `todo_write`
### 使
- 3 使 todo_write
- todo
### merge
- `merge: true` **使**
- `merge: false` todo
###
- `pending`
- `in_progress`
- `completed`
- `cancelled`
###
1. `in_progress`
2. `in_progress`
3. `completed` `cancelled` `in_progress` `pending`
4. `in_progress` 退 `pending` `completed` `cancelled`
5. completed
6. `content`
7. ** `id`** id `"r9Tg8Kq2"`使 idid todo_write `current_todos`
### 使
id
```json
{"merge": true, "todos": [{"id": "aB3kLm9x", "content": "修复登录 bug", "status": "in_progress"}]}
```
```json
{"merge": true, "todos": [{"id": "pQ7nWy2z", "content": "补充测试", "status": "pending"}]}
```
使 id
```json
{"merge": true, "todos": [{"id": "aB3kLm9x", "content": "修复登录 bug", "status": "completed"}]}
```
```json
{"merge": true, "todos": [
{"id": "aB3kLm9x", "content": "修复登录 bug", "status": "completed"},
{"id": "pQ7nWy2z", "content": "补充测试", "status": "in_progress"}
]}
```
###
使 `todo_read`
```json
{}
```
`todo_read`
-
-
-
"#;

View File

@ -0,0 +1,198 @@
use crate::agent::{SystemPrompt, SystemPromptContext, SystemPromptProvider};
/// 工具使用说明 Provider
///
/// 统一收拢所有工具的使用说明memory/skill/todo/shell/scheduler
/// 与 AgentPromptProvider代理身份与行为准则职责分离。
///
/// 设计决策:为什么不合并进 AgentPrompt
/// - AgentPrompt 专注于"你是谁、怎么工作"(身份/原则/回复风格)
/// - ToolPrompt 专注于"工具怎么用"(具体工具的调用流程/参数/规则)
/// - 两者独立演化:新增工具只需在此处加常量,不碰代理身份配置
pub struct ToolPromptProvider;
impl ToolPromptProvider {
pub fn new() -> Self {
Self
}
}
impl SystemPromptProvider for ToolPromptProvider {
fn build(&self, _context: &SystemPromptContext) -> Option<SystemPrompt> {
Some(SystemPrompt {
content: format!(
"{}\n\n{}\n\n{}\n\n{}\n\n{}",
MEMORY_TOOLS_INSTRUCTIONS,
SKILL_TOOLS_INSTRUCTIONS,
TODO_WRITE_INSTRUCTIONS,
SHELL_TOOLS_INSTRUCTIONS,
SCHEDULER_TOOLS_INSTRUCTIONS,
),
context: Some("tools".to_string()),
})
}
}
/// memory_search / memory_manage 工具使用说明
const MEMORY_TOOLS_INSTRUCTIONS: &str = r#"# 记忆工具
##
使 memory_search
###
- 使 memory_search memory_search(action='search')
- namespace key get
- list
- 使
###
-
-
###
- queries 10-12
-
-
- queries=['email', '', 'folder',"preference"]
##
###
使
- `user` -
- `semantic` -
- `episodic` -
- `skill` -
- `environment` -
- `reflection` -
- `other` -
###
- 使 memory_manage
-
###
-
-
- //
- /
-
- xxx的消息
-
###
- 使 memory_manage "#;
/// skill_activate / skill_manage 工具使用说明
const SKILL_TOOLS_INSTRUCTIONS: &str = r#"# 技能工具
##
- : `{project-root}/.picobot/skills/{skill-name}/SKILL.md`
- : `~/.picobot/skills/{skill-name}/SKILL.md`
## /
- 使 `skill_manage` `create` `update` action
- 使 `write`
- `skill_manage`
## 使
-
- `skill_activate`
-
## 使
使
-
-
-
## 使
1. ****: <available_skills>
2. ****:
3. ****: `skill_activate` name
4. ****: skill_activate "#;
/// todo_write / todo_read 工具使用说明
const TODO_WRITE_INSTRUCTIONS: &str = r#"# TodoWrite 工具
使 `todo_write`
## 使
- 3 使 todo_write
- todo
- todo
- todo
-
-
## merge
- `merge: true` **使**
- `merge: false` todo
##
- `pending`
- `in_progress`
- `completed`
- `cancelled`
##
1. `in_progress`
2. `in_progress`
3. `completed` `cancelled` `in_progress` `pending`
4. `in_progress` 退 `pending` `completed` `cancelled`
5. completed
6. `content`
7. ** `id`** id `"r9Tg8Kq2"`使 idid todo_write `current_todos`
## 使
id
```json
{"merge": true, "todos": [{"id": "aB3kLm9x", "content": "修复登录 bug", "status": "in_progress"}]}
```
```json
{"merge": true, "todos": [{"id": "pQ7nWy2z", "content": "补充测试", "status": "pending"}]}
```
使 id
```json
{"merge": true, "todos": [{"id": "aB3kLm9x", "content": "修复登录 bug", "status": "completed"}]}
```
```json
{"merge": true, "todos": [
{"id": "aB3kLm9x", "content": "修复登录 bug", "status": "completed"},
{"id": "pQ7nWy2z", "content": "补充测试", "status": "in_progress"}
]}
```
##
使 `todo_read`
```json
{}
```
`todo_read`
-
-
- "#;
/// shell / bash 工具使用说明
const SHELL_TOOLS_INSTRUCTIONS: &str = r#"# Shell 交互终端
- shell `__PICOBOT_PENDING_USER_ACTION__` `[session_id: xxx]`
- Y/N
- 使 `session_id` `stdin_input` `{"command": "echo test", "session_id": "xxx", "stdin_input": "Y"}`
- Y/N/Read-Host "#;
/// silent_agent_task 工具使用说明
const SCHEDULER_TOOLS_INSTRUCTIONS: &str = r#"# 定时任务
- silent_agent_task
- prompt中需显式使用 send_session_message "#;

View File

@ -466,25 +466,9 @@ impl SkillCatalog {
return None;
}
let mut prompt = String::from(
"# 技能系统Skills\n\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",
);
// 仅输出技能索引列表。
// skill_activate / skill_manage 的使用说明已统一收拢到 ToolPromptProvider。
let mut prompt = String::from("# 可用技能Skills\n\n<available_skills>\n");
for skill in &self.skills {
let entry = format!(
@ -1078,7 +1062,7 @@ mod tests {
let prompt = catalog.system_index_prompt().unwrap();
assert!(prompt.contains("<available_skills>"));
assert!(prompt.contains("技能是预定义的工作流和指令集合,用于处理特定类型的任务。"));
assert!(prompt.contains("# 可用技能"));
assert!(prompt.contains("<name>demo-skill</name>"));
assert!(prompt.contains("<description>demo &lt;skill&gt; &amp; usage</description>"));