openclaw-sc 5.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +13 -0
- package/INSTALL.md +13 -0
- package/LICENSE +233 -0
- package/README.md +123 -0
- package/SECURITY.md +27 -0
- package/index.js +3479 -0
- package/lib/code-review-shared.js +164 -0
- package/lib/config.js +164 -0
- package/lib/constants.js +438 -0
- package/lib/decomposer.js +896 -0
- package/lib/dialog-recall.js +389 -0
- package/lib/env.js +59 -0
- package/lib/level-rules.js +72 -0
- package/lib/log-manager.js +258 -0
- package/lib/preemption.js +278 -0
- package/lib/priority-calc.js +134 -0
- package/lib/prompt-injection.js +748 -0
- package/lib/route-evidence.js +701 -0
- package/lib/shared-fs.js +244 -0
- package/lib/steward-rules.js +648 -0
- package/lib/task-center.js +602 -0
- package/lib/task-chain.js +713 -0
- package/lib/task-checker.js +317 -0
- package/lib/task-profiles.js +255 -0
- package/lib/tcell.js +411 -0
- package/lib/tool-auto-discover.js +522 -0
- package/lib/tool-enrich-subagent.js +375 -0
- package/lib/tool-handlers.js +178 -0
- package/lib/tool-selector.js +459 -0
- package/openclaw.plugin.json +55 -0
- package/package.json +63 -0
- package/security.js +141 -0
- package/tools/bridge.js +3288 -0
- package/tools/dashboard/tk-dashboard.py +262 -0
- package/tools/hippocampus-multi-search.js +1038 -0
- package/tools/hippocampus-sqlite.js +738 -0
- package/tools/hippocampus-store.js +262 -0
- package/tools/mcp-tools.config.json +653 -0
- package/tools/pipeline-engine.js +667 -0
- package/tools/sidecar/_check_tools.py +34 -0
- package/tools/sidecar/sidecar-server.cjs +1360 -0
- package/tools/sidecar/subagent-runner.cjs +1471 -0
- package/tools/system-tools.js +619 -0
- package/vector/README.md +100 -0
- package/vector/usearch-bridge.js +639 -0
- package/vector/usearch-http.js +74 -0
- package/vector/usearch-serve.js +156 -0
- package/workers/worker.js +1419 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🦞 sc — RAG-MCP 动态工具选择器 (Tool Selector)
|
|
3
|
+
*
|
|
4
|
+
* 基于用户任务意图,从全部工具中动态选出最相关的 3-5 个工具。
|
|
5
|
+
* 准确率: 语义检索 RAG-MCP 方案可提升工具选择准确率 3倍 (13.62% → 43.13%)
|
|
6
|
+
* 设计参考: RAG-MCP arXiv 论文 + meta-tool 三级金字塔架构
|
|
7
|
+
*
|
|
8
|
+
* 架构: 三级选择链
|
|
9
|
+
* Level 1: 关键词 → 域 (keyword-to-domain mapping)
|
|
10
|
+
* Level 2: 域 → 工具组 (domain-to-tools mapping)
|
|
11
|
+
* Level 3: 优先级排序 (relevance scoring & ranking)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// ====== 域定义(8个功能域)======
|
|
15
|
+
const DOMAINS = {
|
|
16
|
+
SEARCH: 'search', // 搜索/查找/调研/研究
|
|
17
|
+
CODE: 'code', // 代码编辑/审查/Bug修复/文件操作
|
|
18
|
+
SYSTEM: 'system', // 系统诊断/安全/网络/状态
|
|
19
|
+
TASK: 'task', // 任务调度/编排/并行/子agent
|
|
20
|
+
MEMORY: 'memory', // 记忆/知识/历史/对话日记
|
|
21
|
+
BRAIN: 'brain', // 脑域管理/进化/内化/associative cache
|
|
22
|
+
OPS: 'ops', // 运维/同步/管道/快速执行
|
|
23
|
+
TOOL: 'tool', // 工具管理/管家/通行证
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// ====== 域工具映射(动态生成)======
|
|
27
|
+
// 不再硬编码!在 cacheAllTools() 时根据工具命名规则自动分配到各域。
|
|
28
|
+
// 命名规则:cpu_domain_XXX → 对应域;cpu_XXX → 描述/关键词匹配
|
|
29
|
+
let DOMAIN_TOOLS = {}; // 初始为空,buildDynamicDomainTools() 填充
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 🧬 工具名 → 域的映射规则
|
|
33
|
+
* key=工具名正则, value=子匹配组索引 → 目标域列表
|
|
34
|
+
*/
|
|
35
|
+
const TOOL_DOMAIN_RULES = [
|
|
36
|
+
// cpu_domain_XXX → XXX 决定域
|
|
37
|
+
{ re: /^core_memorySearch$/i, domains: ['search', 'memory'] },
|
|
38
|
+
{ re: /^core_webSearch$/i, domains: ['search'] },
|
|
39
|
+
{ re: /^core_codeEditor$/i, domains: ['code'] },
|
|
40
|
+
{ re: /^core_localScheduler$/i, domains: ['task'] },
|
|
41
|
+
{ re: /^core_systemDiag$/i, domains: ['system', 'ops'] },
|
|
42
|
+
{ re: /^core_toolRouter$/i, domains: ['tool'] },
|
|
43
|
+
// 其他工具 - 按名匹配
|
|
44
|
+
{ re: /^core_memoryHub$/i, domains: ['brain', 'memory'] },
|
|
45
|
+
{ re: /^core_taskCenter$/i, domains: ['task'] },
|
|
46
|
+
{ re: /^core_netDiag$/i, domains: ['system'] },
|
|
47
|
+
{ re: /^core_stats$/i, domains: ['system'] },
|
|
48
|
+
{ re: /^core_sysStatus$/i, domains: ['system'] },
|
|
49
|
+
{ re: /^core_batchVision$/i, domains: ['search', 'code'] }, // 🔧 修正域归类:批量看图更适合search而非system
|
|
50
|
+
{ re: /^core_bgService$/i, domains: ['task', 'ops', 'system'] }, // 🔧 合并重复条目
|
|
51
|
+
{ re: /^core_directRun$/i, domains: ['ops'] },
|
|
52
|
+
{ re: /^core_backup$/i, domains: ['ops'] },
|
|
53
|
+
{ re: /^core_emergencyStop$/i, domains: ['tool'] },
|
|
54
|
+
{ re: /^core_auditLog$/i, domains: ['tool'] },
|
|
55
|
+
{ re: /^core_fileManager$/i, domains: ['code'] },
|
|
56
|
+
{ re: /^core_about$/i, domains: ['tool'] },
|
|
57
|
+
{ re: /^core_pickTools$/i, domains: ['tool'] },
|
|
58
|
+
// 默认兜底:cpu_xxx 按描述关键词匹配
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 从工具描述中提取域关键词
|
|
63
|
+
*/
|
|
64
|
+
const DESC_DOMAIN_KEYWORDS = [
|
|
65
|
+
{ keywords: ['搜索','查找','调研','研究','检索','search','query','find'], domain: 'search' },
|
|
66
|
+
{ keywords: ['代码','编辑','审','bug','文件','code','edit','review','file'], domain: 'code' },
|
|
67
|
+
{ keywords: ['系统','诊断','网络','安全','状态','资源','system','diagnose','security'], domain: 'system' },
|
|
68
|
+
{ keywords: ['任务','调度','编排','并行','子agent','spawn','task','orchestrate'], domain: 'task' },
|
|
69
|
+
{ keywords: ['记忆','日记','历史','知识','memory','dialog','history'], domain: 'memory' },
|
|
70
|
+
{ keywords: ['脑域','联想','内化','进化','缓存','brain','cache','evolve'], domain: 'brain' },
|
|
71
|
+
{ keywords: ['运维','同步','清理','备份','执行','sync','backup','exec'], domain: 'ops' },
|
|
72
|
+
{ keywords: ['工具','管家','通行证','管理','tool','steward','abort'], domain: 'tool' },
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 从缓存的工具列表动态构建 DOMAIN_TOOLS
|
|
77
|
+
* 在 cacheAllTools() 后被调用
|
|
78
|
+
*/
|
|
79
|
+
export function buildDynamicDomainTools() {
|
|
80
|
+
if (!_allToolsMetaCache || _allToolsMetaCache.length === 0) {
|
|
81
|
+
DOMAIN_TOOLS = {};
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 初始化所有域
|
|
86
|
+
const allDomains = Object.values(DOMAINS);
|
|
87
|
+
const domainMap = {};
|
|
88
|
+
for (const d of allDomains) domainMap[d] = [];
|
|
89
|
+
|
|
90
|
+
for (const tool of _allToolsMetaCache) {
|
|
91
|
+
const name = tool.name;
|
|
92
|
+
const desc = (tool.description || '').toLowerCase();
|
|
93
|
+
let matchedDomains = new Set();
|
|
94
|
+
|
|
95
|
+
// 1. 按命名规则匹配
|
|
96
|
+
for (const rule of TOOL_DOMAIN_RULES) {
|
|
97
|
+
if (rule.re.test(name)) {
|
|
98
|
+
for (const d of rule.domains) matchedDomains.add(d);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 2. 命名规则没命中时,按描述关键词兜底
|
|
103
|
+
if (matchedDomains.size === 0) {
|
|
104
|
+
for (const rule of DESC_DOMAIN_KEYWORDS) {
|
|
105
|
+
if (rule.keywords.some(kw => name.toLowerCase().includes(kw) || desc.includes(kw))) {
|
|
106
|
+
matchedDomains.add(rule.domain);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 3. 还是没命中 → 按 name 中包含的域关键词再扫一遍
|
|
112
|
+
if (matchedDomains.size === 0) {
|
|
113
|
+
// 直接从名字提取:cpu_xxx → xxx 和域名做包含匹配
|
|
114
|
+
const nameLower = name.toLowerCase();
|
|
115
|
+
for (const [domainKey, domainVal] of Object.entries(DOMAINS)) {
|
|
116
|
+
if (nameLower.includes(domainVal)) {
|
|
117
|
+
matchedDomains.add(domainVal);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 加入各域
|
|
123
|
+
for (const d of matchedDomains) {
|
|
124
|
+
if (domainMap[d]) {
|
|
125
|
+
domainMap[d].push({
|
|
126
|
+
name,
|
|
127
|
+
priority: domainMap[d].length + 1,
|
|
128
|
+
description: tool.description || name,
|
|
129
|
+
matchedBy: 'auto',
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
DOMAIN_TOOLS = domainMap;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ====== 全量工具元信息缓存(供 MCP tools/list 返回使用)======
|
|
139
|
+
let _allToolsMetaCache = null;
|
|
140
|
+
let _allToolsMap = new Map();
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* 缓存全量工具元信息(由 initSansanCpuTools 填充)
|
|
144
|
+
* @param {Array} toolsMeta - MCP tools/list 返回的工具数组
|
|
145
|
+
*/
|
|
146
|
+
export function cacheAllTools(toolsMeta) {
|
|
147
|
+
if (!Array.isArray(toolsMeta)) return;
|
|
148
|
+
_allToolsMetaCache = toolsMeta;
|
|
149
|
+
_allToolsMap.clear();
|
|
150
|
+
for (const t of toolsMeta) {
|
|
151
|
+
_allToolsMap.set(t.name, t);
|
|
152
|
+
}
|
|
153
|
+
// 动态构建域工具映射
|
|
154
|
+
buildDynamicDomainTools();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* 获取所有工具元信息
|
|
159
|
+
* @returns {Array}
|
|
160
|
+
*/
|
|
161
|
+
export function getAllCachedTools() {
|
|
162
|
+
return _allToolsMetaCache || [];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 获取单个工具元信息
|
|
167
|
+
* @param {string} name
|
|
168
|
+
* @returns {object|null}
|
|
169
|
+
*/
|
|
170
|
+
export function getToolMeta(name) {
|
|
171
|
+
return _allToolsMap.get(name) || null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ====== 关键词 → 域映射 ======
|
|
175
|
+
|
|
176
|
+
/** 域 - 关键词映射表(优先级由高到低) */
|
|
177
|
+
const DOMAIN_KEYWORDS = {
|
|
178
|
+
[DOMAINS.SEARCH]: [
|
|
179
|
+
// 高优先级精确匹配
|
|
180
|
+
{ pattern: /搜索|查找|查一查|搜一搜|找一找|搜(?![索码])/, weight: 1.0 },
|
|
181
|
+
{ pattern: /调研|研究|调查|分析.*问题/, weight: 1.0 },
|
|
182
|
+
// 中优先级
|
|
183
|
+
{ pattern: /查|找|搜|看看.*有.*吗|有没有/, weight: 0.7 },
|
|
184
|
+
{ pattern: /内容|信息|资料|文件.*找/, weight: 0.5 },
|
|
185
|
+
],
|
|
186
|
+
[DOMAINS.CODE]: [
|
|
187
|
+
{ pattern: /改代码|写代码|修bug|改bug|代码.*审|审.*代码/, weight: 1.0 },
|
|
188
|
+
{ pattern: /创建文件|修改文件|编辑文件|重命名|移动文件|删.*文件/, weight: 0.9 },
|
|
189
|
+
{ pattern: /代码|文件.*改|改.*文件|git|提交|推送|分支/, weight: 0.8 },
|
|
190
|
+
{ pattern: /审查|review|bugfix|修复|优化.*代码/, weight: 0.8 },
|
|
191
|
+
],
|
|
192
|
+
[DOMAINS.SYSTEM]: [
|
|
193
|
+
{ pattern: /系统.*诊断|系统.*状态|系统.*体检/, weight: 1.0 },
|
|
194
|
+
{ pattern: /安全|网络|端口|ping|trace|dns/, weight: 0.9 },
|
|
195
|
+
{ pattern: /状态|诊断|体检|资源|监控|告警/, weight: 0.7 },
|
|
196
|
+
{ pattern: /CPU|内存|磁盘|进程|负载/, weight: 0.6 },
|
|
197
|
+
],
|
|
198
|
+
[DOMAINS.TASK]: [
|
|
199
|
+
{ pattern: /调度|并行|并发|分批|批处理/, weight: 1.0 },
|
|
200
|
+
{ pattern: /跑任务|派任务|分配|编排|流水线/, weight: 0.9 },
|
|
201
|
+
{ pattern: /子agent|spawn|派子|监控.*子/, weight: 0.8 },
|
|
202
|
+
{ pattern: /同时.*做|多个.*任务|批量|队列/, weight: 0.7 },
|
|
203
|
+
],
|
|
204
|
+
[DOMAINS.MEMORY]: [
|
|
205
|
+
{ pattern: /记忆|回忆|记得|之前.*说|之前.*聊/, weight: 1.0 },
|
|
206
|
+
{ pattern: /历史|对话.*记录|日记|知识/, weight: 0.9 },
|
|
207
|
+
{ pattern: /还记得|上次|学到|学习.*到|记住/, weight: 0.8 },
|
|
208
|
+
],
|
|
209
|
+
[DOMAINS.BRAIN]: [
|
|
210
|
+
{ pattern: /进化|学习.*改进|自我.*优化|内化/, weight: 1.0 },
|
|
211
|
+
{ pattern: /脑域|联想|缓存.*查询|模型.*推荐/, weight: 0.8 },
|
|
212
|
+
],
|
|
213
|
+
[DOMAINS.OPS]: [
|
|
214
|
+
{ pattern: /运维|清理|同步|备份|磁盘.*检查/, weight: 1.0 },
|
|
215
|
+
{ pattern: /刷新|重置|恢复|重启|维护/, weight: 0.8 },
|
|
216
|
+
],
|
|
217
|
+
[DOMAINS.TOOL]: [
|
|
218
|
+
{ pattern: /工具.*管理|管家|通行证|紧急/, weight: 1.0 },
|
|
219
|
+
{ pattern: /暂停|恢复.*主脑|终止.*Worker|abort/, weight: 0.8 },
|
|
220
|
+
{ pattern: /API队列|限流|白名单/, weight: 0.6 },
|
|
221
|
+
],
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* 基于任务文本检测匹配的域
|
|
226
|
+
* @param {string} taskText - 用户任务描述
|
|
227
|
+
* @returns {Array<{domain: string, score: number, keywords: string[]}>} 匹配的域列表(按分数降序)
|
|
228
|
+
*/
|
|
229
|
+
function detectDomains(taskText) {
|
|
230
|
+
if (!taskText || typeof taskText !== 'string') return [];
|
|
231
|
+
|
|
232
|
+
const text = taskText.toLowerCase().trim();
|
|
233
|
+
const matched = [];
|
|
234
|
+
|
|
235
|
+
for (const [domain, patterns] of Object.entries(DOMAIN_KEYWORDS)) {
|
|
236
|
+
let maxWeight = 0;
|
|
237
|
+
for (const { pattern, weight } of patterns) {
|
|
238
|
+
if (pattern.test(text)) {
|
|
239
|
+
maxWeight = Math.max(maxWeight, weight);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (maxWeight > 0) {
|
|
243
|
+
matched.push({ domain, score: maxWeight });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// 按权重降序
|
|
248
|
+
matched.sort((a, b) => b.score - a.score);
|
|
249
|
+
return matched;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* 从域列表获取推荐工具集(去重+优先级排序)
|
|
254
|
+
* @param {Array<{domain: string, score: number}>} domains
|
|
255
|
+
* @returns {Array<{name: string, priority: number, description: string, domains: string[]}>}
|
|
256
|
+
*/
|
|
257
|
+
function getToolsFromDomains(domains) {
|
|
258
|
+
const toolMap = new Map(); // name -> { tool, domains: [] }
|
|
259
|
+
|
|
260
|
+
for (const { domain } of domains) {
|
|
261
|
+
const domainTools = DOMAIN_TOOLS[domain] || [];
|
|
262
|
+
for (const tool of domainTools) {
|
|
263
|
+
if (toolMap.has(tool.name)) {
|
|
264
|
+
const existing = toolMap.get(tool.name);
|
|
265
|
+
existing.domains.push(domain);
|
|
266
|
+
existing.priority = Math.min(existing.priority, tool.priority);
|
|
267
|
+
} else {
|
|
268
|
+
toolMap.set(tool.name, {
|
|
269
|
+
...tool,
|
|
270
|
+
domains: [domain],
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// 按优先级排序(priority 越小越靠前)
|
|
277
|
+
const sorted = [...toolMap.values()].sort((a, b) => {
|
|
278
|
+
// 出现在多个域的工具优先
|
|
279
|
+
if (a.domains.length !== b.domains.length) {
|
|
280
|
+
return b.domains.length - a.domains.length;
|
|
281
|
+
}
|
|
282
|
+
return a.priority - b.priority;
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
return sorted;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* 主入口:基于任务文本选择最相关的 3-5 个工具
|
|
290
|
+
*
|
|
291
|
+
* @param {string} taskText - 用户任务文本
|
|
292
|
+
* @param {Array} [allTools] - 全量工具数组(可选,未传时使用缓存)
|
|
293
|
+
* @param {number} [maxTools=5] - 最多返回工具数(默认 5)
|
|
294
|
+
* @returns {object} { tools, domains, reasoning, raw }
|
|
295
|
+
* tools: 筛选后的工具列表(含全量元信息)
|
|
296
|
+
* domains: 匹配的域
|
|
297
|
+
* reasoning: 选择理由
|
|
298
|
+
* raw: 原始全量工具(兜底时包含所有)
|
|
299
|
+
*/
|
|
300
|
+
export function selectTools(taskText, allTools = null, maxTools = 5) {
|
|
301
|
+
const startTime = Date.now();
|
|
302
|
+
|
|
303
|
+
// 使用传入的工具列表或缓存
|
|
304
|
+
const sourceTools = allTools || _allToolsMetaCache || [];
|
|
305
|
+
const toolMetaAvailable = sourceTools.length > 0;
|
|
306
|
+
|
|
307
|
+
// 检测意图域
|
|
308
|
+
const domains = detectDomains(taskText);
|
|
309
|
+
|
|
310
|
+
// 如果没有任何域匹配 → 返回所有工具(兜底)
|
|
311
|
+
if (domains.length === 0) {
|
|
312
|
+
return {
|
|
313
|
+
tools: sourceTools.slice(0, maxTools),
|
|
314
|
+
domains: [],
|
|
315
|
+
reasoning: '未能识别任务意图,返回默认工具集',
|
|
316
|
+
raw: sourceTools,
|
|
317
|
+
elapsedMs: Date.now() - startTime,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// 从匹配域获取工具推荐
|
|
322
|
+
const recommended = getToolsFromDomains(domains);
|
|
323
|
+
|
|
324
|
+
// 如果有全量元信息,用完整元信息替换推荐工具
|
|
325
|
+
let resultTools;
|
|
326
|
+
if (toolMetaAvailable) {
|
|
327
|
+
const sourceMap = new Map(sourceTools.map(t => [t.name, t]));
|
|
328
|
+
resultTools = recommended
|
|
329
|
+
.map(t => sourceMap.get(t.name) || t)
|
|
330
|
+
.filter(Boolean)
|
|
331
|
+
.slice(0, maxTools);
|
|
332
|
+
} else {
|
|
333
|
+
resultTools = recommended.slice(0, maxTools);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// 生成选择理由
|
|
337
|
+
const reasoning = domains
|
|
338
|
+
.map(d => `${d.domain}(置信度:${d.score.toFixed(1)})`)
|
|
339
|
+
.join(' → ');
|
|
340
|
+
|
|
341
|
+
return {
|
|
342
|
+
tools: resultTools,
|
|
343
|
+
domains: domains.map(d => d.domain),
|
|
344
|
+
reasoning: `检测到意图域: ${reasoning}`,
|
|
345
|
+
raw: sourceTools,
|
|
346
|
+
elapsedMs: Date.now() - startTime,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* 注册为一个 MCP 可调用工具(在主文件中调用)
|
|
352
|
+
* 返回 tool 定义和 handler
|
|
353
|
+
*/
|
|
354
|
+
export function createSelectToolsToolDef() {
|
|
355
|
+
return {
|
|
356
|
+
name: 'core_pickTools',
|
|
357
|
+
description: '🎯 动态工具选择 — 根据任务描述从sc 30+工具中智能选出最相关的3-5个。调用此工具后,优先使用返回的工具来完成后续任务,不要使用未返回的工具。这是RAG-MCP智能选择机制。',
|
|
358
|
+
inputSchema: {
|
|
359
|
+
type: 'object',
|
|
360
|
+
required: ['task'],
|
|
361
|
+
properties: {
|
|
362
|
+
task: { type: 'string', description: '任务描述文本(如"帮我搜索配置文件中的日志配置")' },
|
|
363
|
+
maxTools: { type: 'number', description: '最多返回工具数(默认 5,范围 1-8)' },
|
|
364
|
+
},
|
|
365
|
+
additionalProperties: false,
|
|
366
|
+
},
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* core_pickTools 的执行函数
|
|
372
|
+
*/
|
|
373
|
+
export async function handleSelectTools(params) {
|
|
374
|
+
const { task, maxTools } = params || {};
|
|
375
|
+
if (!task) {
|
|
376
|
+
return { status: 'error', message: 'missing task 参数' };
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const max = Math.min(Math.max(maxTools || 5, 1), 8);
|
|
380
|
+
const result = selectTools(task, null, max);
|
|
381
|
+
|
|
382
|
+
// 如果没有匹配域但原始工具有 → 返回前 maxTools 个
|
|
383
|
+
if (!result.tools || result.tools.length === 0) {
|
|
384
|
+
const fallbackTools = (_allToolsMetaCache || []).slice(0, max);
|
|
385
|
+
return {
|
|
386
|
+
status: 'info',
|
|
387
|
+
selected: fallbackTools,
|
|
388
|
+
reasoning: '未识别到明确意图域,返回默认工具集',
|
|
389
|
+
hint: '请使用上述工具来完成当前任务',
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return {
|
|
394
|
+
status: 'success',
|
|
395
|
+
intentDomains: result.domains,
|
|
396
|
+
reasoning: result.reasoning,
|
|
397
|
+
selected: result.tools,
|
|
398
|
+
totalAvailable: (_allToolsMetaCache || []).length,
|
|
399
|
+
hint: '请使用上述选中的工具来完成任务,未列出的工具当前场景不适用',
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* 生成给 LLM 的系统提示指令
|
|
405
|
+
* @param {string} taskText
|
|
406
|
+
* @returns {string} 提示文本
|
|
407
|
+
*/
|
|
408
|
+
export function buildToolSelectionPrompt(taskText) {
|
|
409
|
+
if (!taskText) return '';
|
|
410
|
+
|
|
411
|
+
const result = selectTools(taskText, null, 5);
|
|
412
|
+
if (!result.tools || result.tools.length === 0) {
|
|
413
|
+
return '';
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const toolList = result.tools
|
|
417
|
+
.map((t, i) => ` ${i + 1}. \`${t.name}\` — ${t.description || t.name}`)
|
|
418
|
+
.join('\n');
|
|
419
|
+
|
|
420
|
+
return [
|
|
421
|
+
`🎯 【RAG-MCP 动态工具选择】`,
|
|
422
|
+
`根据当前任务 "${taskText.substring(0, 80)}",系统建议优先使用以下工具:`,
|
|
423
|
+
'',
|
|
424
|
+
toolList,
|
|
425
|
+
'',
|
|
426
|
+
`📋 理由: ${result.reasoning}`,
|
|
427
|
+
`未在此列表中的工具当前场景不适用,请优先使用上述工具。`,
|
|
428
|
+
`如需要其他工具,请先调用 \`core_pickTools\` 获取推荐。`,
|
|
429
|
+
].join('\n');
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// ====== 调试/自测 ======
|
|
433
|
+
export function testSelectors() {
|
|
434
|
+
const testCases = [
|
|
435
|
+
'帮我搜索一下配置文件中的数据库连接信息',
|
|
436
|
+
'帮我改一下这个文件名',
|
|
437
|
+
'系统状态怎么样?内存使用率高吗?',
|
|
438
|
+
'同时派三个子agent去搜索不同的关键词',
|
|
439
|
+
'之前我们讨论过关于API网关的配置,你还记得吗?',
|
|
440
|
+
'检查一下代码有没有bug',
|
|
441
|
+
'帮我清理一下日志文件',
|
|
442
|
+
'看看网络能不能ping通百度',
|
|
443
|
+
'你好,今天有什么新功能?',
|
|
444
|
+
'派任务去调研一下市场上的竞品',
|
|
445
|
+
];
|
|
446
|
+
|
|
447
|
+
for (const tc of testCases) {
|
|
448
|
+
const result = selectTools(tc, null, 5);
|
|
449
|
+
// TODO: 移除调试日志 console.log(`\n📝 "${tc.substring(0, 40)}..."`);
|
|
450
|
+
// TODO: 移除调试日志 console.log(` 域: ${result.domains.join(', ') || '无匹配'}`);
|
|
451
|
+
// TODO: 移除调试日志 console.log(` 工具: ${result.tools.map(t => t.name).join(', ')}`);
|
|
452
|
+
// TODO: 移除调试日志 console.log(` 耗时: ${result.elapsedMs}ms`);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// 直接在命令行运行测试:node lib/tool-selector.js
|
|
457
|
+
if (process.argv[1]?.endsWith('tool-selector.js')) {
|
|
458
|
+
testSelectors();
|
|
459
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "sc",
|
|
3
|
+
"name": "sc",
|
|
4
|
+
"version": "5.38.0",
|
|
5
|
+
"description": "sc v5.38.0",
|
|
6
|
+
"activation": {
|
|
7
|
+
"onStartup": true
|
|
8
|
+
},
|
|
9
|
+
"contracts": {
|
|
10
|
+
"tools": [
|
|
11
|
+
"stats",
|
|
12
|
+
"memorySearch",
|
|
13
|
+
"webSearch",
|
|
14
|
+
"glob",
|
|
15
|
+
"grep",
|
|
16
|
+
"codeEditor",
|
|
17
|
+
"batchVision",
|
|
18
|
+
"emergencyStop",
|
|
19
|
+
"fileManager",
|
|
20
|
+
"spawnWorker",
|
|
21
|
+
"spawnAgent",
|
|
22
|
+
"taskPipeline"
|
|
23
|
+
],
|
|
24
|
+
"subagentTools": [
|
|
25
|
+
"stats",
|
|
26
|
+
"memorySearch",
|
|
27
|
+
"webSearch",
|
|
28
|
+
"glob",
|
|
29
|
+
"grep",
|
|
30
|
+
"codeEditor",
|
|
31
|
+
"fileManager",
|
|
32
|
+
"spawnWorker",
|
|
33
|
+
"validate"
|
|
34
|
+
],
|
|
35
|
+
"mainDeny": [
|
|
36
|
+
"validate"
|
|
37
|
+
],
|
|
38
|
+
"subagentBlocked": [
|
|
39
|
+
"emergencyStop",
|
|
40
|
+
"spawnAgent",
|
|
41
|
+
"taskPipeline",
|
|
42
|
+
"batchVision"
|
|
43
|
+
],
|
|
44
|
+
"comment": "同步自tools/mcp-tools.config.json:mainTools=12;validate 为 subagent/runtime validation 工具,main deny;子Agent blocked 见 subagentBlocked"
|
|
45
|
+
},
|
|
46
|
+
"configSchema": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"additionalProperties": false,
|
|
49
|
+
"properties": {
|
|
50
|
+
"enabled": {
|
|
51
|
+
"type": "boolean"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "openclaw-sc",
|
|
3
|
+
"version": "5.38.0",
|
|
4
|
+
"description": "OpenClaw sc plugin for sub-agent orchestration, MCP tool routing, local worker tasks, and memory retrieval.",
|
|
5
|
+
"changelog": "v5.5.0 → v5.7.1: 司令铁律补丁(patch-before-tool-call.ps1) / 内置工具走before_tool_call / 路由守卫全覆盖 / 备份加时间戳防回滚旧版 / 紧急模式恢复防抖60秒",
|
|
6
|
+
"changelogDetail": "v5.7.1: 路由修复: routeQuick集成L0-L7分类 / 代码去重 / 文档同步",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "index.js",
|
|
9
|
+
"license": "BUSL-1.1",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/haoyun18881-beep/openclaw-sc.git"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/haoyun18881-beep/openclaw-sc/issues"
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://github.com/haoyun18881-beep/openclaw-sc#readme",
|
|
18
|
+
"files": [
|
|
19
|
+
"index.js",
|
|
20
|
+
"lib",
|
|
21
|
+
"tools",
|
|
22
|
+
"vector",
|
|
23
|
+
"workers",
|
|
24
|
+
"security.js",
|
|
25
|
+
"openclaw.plugin.json",
|
|
26
|
+
"INSTALL.md",
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE",
|
|
29
|
+
"SECURITY.md",
|
|
30
|
+
".env.example"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"start": "node index.js",
|
|
34
|
+
"lint": "node --check index.js && node --check security.js && node --check tools/bridge.js && node --check tools/sidecar/sidecar-server.cjs && node --check tools/sidecar/subagent-runner.cjs",
|
|
35
|
+
"pack:dry": "npm pack --dry-run"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=22"
|
|
39
|
+
},
|
|
40
|
+
"keywords": [
|
|
41
|
+
"openclaw",
|
|
42
|
+
"agent",
|
|
43
|
+
"subagent",
|
|
44
|
+
"mcp",
|
|
45
|
+
"memory",
|
|
46
|
+
"worker"
|
|
47
|
+
],
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"acorn": "^8.16.0",
|
|
50
|
+
"archiver": "^8.0.0"
|
|
51
|
+
},
|
|
52
|
+
"optionalDependencies": {
|
|
53
|
+
"usearch": "^2.25.3"
|
|
54
|
+
},
|
|
55
|
+
"openclaw": {
|
|
56
|
+
"compat": {
|
|
57
|
+
"pluginApi": "^2026.5.28"
|
|
58
|
+
},
|
|
59
|
+
"build": {
|
|
60
|
+
"openclawVersion": "2026.6.1"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|