refactor: 工具简化为「查询 + 调用」二元接口
LLM 工具列表从 10+ 个缩减为 5 个核心: - query_capabilities: 按需查看所有工具说明 - call_capability: 按名称桥接到任意工具 - read_memories / write_memory / read_summaries: 上下文工具 datetime / memos: Rust 原生实现(不走子进程) 系统提示词精简为引导型(先查能力再调用)
This commit is contained in:
@@ -239,5 +239,6 @@ pub const DEFAULT_SYSTEM_PROMPT: &str = "You are a concise and helpful assistant
|
|||||||
Keep replies practical and natural. \
|
Keep replies practical and natural. \
|
||||||
Unless the user asks for detail, keep most replies under 120 Chinese characters. \
|
Unless the user asks for detail, keep most replies under 120 Chinese characters. \
|
||||||
\
|
\
|
||||||
如果需要了解用户的长期记忆或历史对话摘要,使用 read_memories / read_summaries 工具。\
|
可用的核心工具:query_capabilities(查看所有可用工具)、call_capability(按名调用工具)。\
|
||||||
如果用户提到重要的个人信息或约定,使用 write_memory 工具记录下来。";
|
可使用 read_memories / write_memory 管理用户长期记忆,read_summaries 查看历史摘要。\
|
||||||
|
调用工具前先用 query_capabilities 确认工具有哪些。";
|
||||||
|
|||||||
+91
-33
@@ -238,28 +238,43 @@ async fn cmd_listen(
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
// 构建 LLM tools 列表(skills + 上下文工具)
|
// 仅暴露两个元工具:查询能力 + 调用能力
|
||||||
|
// 具体工具描述通过 query_capabilities 返回
|
||||||
let mut tools_list: Vec<serde_json::Value> = Vec::new();
|
let mut tools_list: Vec<serde_json::Value> = Vec::new();
|
||||||
|
|
||||||
if let Some(ref registry) = skill_registry {
|
// query_capabilities:列出所有可用工具及其说明
|
||||||
for spec in registry.list_specs() {
|
tools_list.push(serde_json::json!({
|
||||||
tools_list.push(serde_json::json!({
|
"type": "function",
|
||||||
"type": "function",
|
"function": {
|
||||||
"function": {
|
"name": "query_capabilities",
|
||||||
"name": spec.name,
|
"description": "列出所有可用工具的详细说明、参数格式和调用示例。在需要了解有哪些工具可用时首先调用此工具。",
|
||||||
"description": spec.description,
|
"parameters": { "type": "object", "properties": {} }
|
||||||
"parameters": spec.parameters,
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}));
|
||||||
|
|
||||||
// 上下文工具(长期记忆 + 摘要)
|
// call_capability:按名称调用任意工具
|
||||||
|
tools_list.push(serde_json::json!({
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "call_capability",
|
||||||
|
"description": "按工具名称调用一个已注册的工具。name 为工具名(来自 query_capabilities),prompt 为自然语言需求",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": { "type": "string", "description": "工具名称" },
|
||||||
|
"prompt": { "type": "string", "description": "自然语言需求" }
|
||||||
|
},
|
||||||
|
"required": ["name"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 上下文工具(仍然直接暴露给 LLM)
|
||||||
tools_list.push(serde_json::json!({
|
tools_list.push(serde_json::json!({
|
||||||
"type": "function",
|
"type": "function",
|
||||||
"function": {
|
"function": {
|
||||||
"name": "read_memories",
|
"name": "read_memories",
|
||||||
"description": "读取用户的长期记忆(跨会话持久保存的个人信息、偏好、约定等)",
|
"description": "读取用户的长期记忆(偏好、个人信息、约定)",
|
||||||
"parameters": { "type": "object", "properties": {} }
|
"parameters": { "type": "object", "properties": {} }
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
@@ -267,7 +282,7 @@ async fn cmd_listen(
|
|||||||
"type": "function",
|
"type": "function",
|
||||||
"function": {
|
"function": {
|
||||||
"name": "write_memory",
|
"name": "write_memory",
|
||||||
"description": "写入一条用户的长期记忆(当用户提到重要偏好、个人信息、约定时调用)",
|
"description": "记录用户的重要信息或偏好",
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -281,28 +296,11 @@ async fn cmd_listen(
|
|||||||
"type": "function",
|
"type": "function",
|
||||||
"function": {
|
"function": {
|
||||||
"name": "read_summaries",
|
"name": "read_summaries",
|
||||||
"description": "读取历史会话摘要(包括因上下文压缩或空闲超时产生的摘要)",
|
"description": "读取历史会话摘要",
|
||||||
"parameters": { "type": "object", "properties": {} }
|
"parameters": { "type": "object", "properties": {} }
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// call_capability 桥接:iPet 兼容,按 skill 名调用
|
|
||||||
tools_list.push(serde_json::json!({
|
|
||||||
"type": "function",
|
|
||||||
"function": {
|
|
||||||
"name": "call_capability",
|
|
||||||
"description": "按名称调用一个 skill(等同于直接使用该 skill 的工具名)。name 参数为 skill 的名称,prompt 为自然语言需求",
|
|
||||||
"parameters": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"name": { "type": "string", "description": "要调用的 skill 名称" },
|
|
||||||
"prompt": { "type": "string", "description": "给该 skill 的自然语言需求" }
|
|
||||||
},
|
|
||||||
"required": ["name"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
cfg.tools = Some(tools_list);
|
cfg.tools = Some(tools_list);
|
||||||
|
|
||||||
// 创建 Conversation
|
// 创建 Conversation
|
||||||
@@ -372,6 +370,17 @@ async fn cmd_listen(
|
|||||||
return Ok(memory_store.write_for(&user_id, content).await);
|
return Ok(memory_store.write_for(&user_id, content).await);
|
||||||
}
|
}
|
||||||
"read_summaries" => return Ok(context::tools::read_summaries(&session).await),
|
"read_summaries" => return Ok(context::tools::read_summaries(&session).await),
|
||||||
|
"query_capabilities" => {
|
||||||
|
if let Some(r) = reg.as_ref() {
|
||||||
|
return Ok(build_capability_list(r));
|
||||||
|
}
|
||||||
|
return Ok("暂无工具".to_string());
|
||||||
|
}
|
||||||
|
"get_current_datetime" => {
|
||||||
|
let now = chrono::Local::now();
|
||||||
|
return Ok(serde_json::json!({"datetime": now.format("%Y-%m-%d %H:%M:%S").to_string()}).to_string());
|
||||||
|
}
|
||||||
|
"manage_memos" => return handle_memos(&args).await,
|
||||||
"call_capability" => {
|
"call_capability" => {
|
||||||
let cap_params: serde_json::Value = serde_json::from_str(&args).unwrap_or_default();
|
let cap_params: serde_json::Value = serde_json::from_str(&args).unwrap_or_default();
|
||||||
let cap_name = cap_params.get("name").and_then(|v| v.as_str()).map(|s| s.to_string());
|
let cap_name = cap_params.get("name").and_then(|v| v.as_str()).map(|s| s.to_string());
|
||||||
@@ -800,6 +809,55 @@ fn global_skills_dir() -> std::path::PathBuf {
|
|||||||
.join("skills")
|
.join("skills")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_capability_list(reg: &SkillRegistry) -> String {
|
||||||
|
let mut lines = vec!["📋 可用工具:".to_string()];
|
||||||
|
for spec in reg.list_specs() {
|
||||||
|
let risk = if spec.risk_level == tools::skill::RiskLevel::High { " ⚠️需确认" } else { "" };
|
||||||
|
lines.push(format!(
|
||||||
|
"\n🔹 {} {} — {}", spec.name, risk, spec.description
|
||||||
|
));
|
||||||
|
}
|
||||||
|
lines.join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_memos(args_json: &str) -> Result<String, String> {
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
let params: serde_json::Value = serde_json::from_str(args_json).unwrap_or_default();
|
||||||
|
let action = params.get("action").and_then(|v| v.as_str()).unwrap_or("list");
|
||||||
|
let path = ".data/memos.json";
|
||||||
|
std::fs::create_dir_all(".data").ok();
|
||||||
|
|
||||||
|
let mut data: Vec<serde_json::Value> = if std::path::Path::new(path).exists() {
|
||||||
|
serde_json::from_str(&std::fs::read_to_string(path).unwrap_or_default()).unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
match action {
|
||||||
|
"add" => {
|
||||||
|
let content = params.get("content").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
|
if content.is_empty() { return Ok(r#"{"error":"请提供 content 参数"}"#.to_string()); }
|
||||||
|
let next_id = data.iter().filter_map(|m| m.get("id").and_then(|v| v.as_i64())).max().unwrap_or(0) + 1;
|
||||||
|
data.push(serde_json::json!({"id": next_id, "content": content, "created_at": chrono::Local::now().format("%Y-%m-%d %H:%M").to_string()}));
|
||||||
|
std::fs::write(path, serde_json::to_string_pretty(&data).unwrap()).ok();
|
||||||
|
Ok(format!("已添加备忘录 #{}", next_id))
|
||||||
|
}
|
||||||
|
"list" => {
|
||||||
|
if data.is_empty() { return Ok("暂无备忘录".to_string()); }
|
||||||
|
Ok(data.iter().map(|m| format!("#{} [{}] {}", m["id"], m.get("created_at").and_then(|v| v.as_str()).unwrap_or("?"), m["content"].as_str().unwrap_or("?"))).collect::<Vec<_>>().join("\n"))
|
||||||
|
}
|
||||||
|
"delete" => {
|
||||||
|
let id = params.get("id").and_then(|v| v.as_i64()).unwrap_or(0);
|
||||||
|
let before = data.len();
|
||||||
|
data.retain(|m| m.get("id").and_then(|v| v.as_i64()) != Some(id));
|
||||||
|
if data.len() == before { return Ok(format!("未找到备忘录 #{}", id)); }
|
||||||
|
std::fs::write(path, serde_json::to_string_pretty(&data).unwrap()).ok();
|
||||||
|
Ok(format!("已删除备忘录 #{}", id))
|
||||||
|
}
|
||||||
|
_ => Ok("未知操作,支持: add, list, delete".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn cmd_skills(action: SkillsAction) {
|
async fn cmd_skills(action: SkillsAction) {
|
||||||
use dialoguer::{MultiSelect, theme::ColorfulTheme};
|
use dialoguer::{MultiSelect, theme::ColorfulTheme};
|
||||||
use console::Term;
|
use console::Term;
|
||||||
|
|||||||
Reference in New Issue
Block a user