|
|
@@ -5,7 +5,7 @@
|
|
|
本文档描述 OpenClaw KnowHub 插件的完整设计,包括工具实现、钩子集成、自动化提醒和消息历史上传功能。
|
|
|
|
|
|
**设计目标:**
|
|
|
-1. 让 OpenClaw Agent 能够搜索和提交 KnowHub 经验
|
|
|
+1. 让 OpenClaw Agent 能够搜索和保存 KnowHub 知识
|
|
|
2. 通过自动化提醒确保 Agent 主动使用 KnowHub
|
|
|
3. 可选的消息历史自动上传功能
|
|
|
4. 保护用户隐私和数据安全
|
|
|
@@ -31,7 +31,7 @@ extensions/knowhub/
|
|
|
|
|
|
| 组件 | 职责 | 实现文件 |
|
|
|
|------|------|---------|
|
|
|
-| 工具注册 | kb_search, kb_submit, kb_content | tools.ts |
|
|
|
+| 工具注册 | kb_search, kb_save, kb_update | tools.ts |
|
|
|
| 钩子处理 | 提醒注入、消息上传 | hooks.ts |
|
|
|
| 配置管理 | 读取和验证配置 | config.ts |
|
|
|
| 安全防护 | 数据脱敏、注入检测 | security.ts |
|
|
|
@@ -93,14 +93,15 @@ extensions/knowhub/
|
|
|
|
|
|
## 工具实现
|
|
|
|
|
|
-### 1. kb_search - 搜索经验
|
|
|
+### 1. kb_search - 搜索知识
|
|
|
|
|
|
-**功能:** 从 KnowHub 搜索相关经验。
|
|
|
+**功能:** 从 KnowHub 搜索相关知识。
|
|
|
|
|
|
**参数:**
|
|
|
- `query` (string, required): 搜索查询
|
|
|
- `top_k` (number, optional): 返回数量,默认 5
|
|
|
- `min_score` (number, optional): 最低评分,默认 3
|
|
|
+- `types` (string[], optional): 知识类型过滤,如 `["tool", "strategy"]`
|
|
|
|
|
|
**实现要点:**
|
|
|
```typescript
|
|
|
@@ -108,14 +109,19 @@ extensions/knowhub/
|
|
|
export async function kb_search(
|
|
|
query: string,
|
|
|
top_k: number = 5,
|
|
|
- min_score: number = 3
|
|
|
+ min_score: number = 3,
|
|
|
+ types?: string[]
|
|
|
): Promise<ToolResult> {
|
|
|
const config = getConfig();
|
|
|
|
|
|
+ // 构建 URL
|
|
|
+ let url = `${config.apiUrl}/api/knowledge/search?q=${encodeURIComponent(query)}&top_k=${top_k}&min_score=${min_score}`;
|
|
|
+ if (types && types.length > 0) {
|
|
|
+ url += `&types=${types.join(',')}`;
|
|
|
+ }
|
|
|
+
|
|
|
// 调用 KnowHub API
|
|
|
- const response = await fetch(
|
|
|
- `${config.apiUrl}/api/search?q=${encodeURIComponent(query)}&top_k=${top_k}&min_score=${min_score}`
|
|
|
- );
|
|
|
+ const response = await fetch(url);
|
|
|
|
|
|
if (!response.ok) {
|
|
|
return {
|
|
|
@@ -127,13 +133,15 @@ export async function kb_search(
|
|
|
const data = await response.json();
|
|
|
|
|
|
// 格式化结果
|
|
|
- const formatted = data.results.map((exp: any, idx: number) =>
|
|
|
- `${idx + 1}. [${exp.task}]\n 资源: ${exp.resource}\n 结果: ${exp.result}\n 评分: ${exp.score}/5`
|
|
|
- ).join('\n\n');
|
|
|
+ const formatted = data.results.map((k: any, idx: number) => {
|
|
|
+ const typesStr = k.types.join(', ');
|
|
|
+ const sourceName = k.source?.name ? ` (来源: ${k.source.name})` : '';
|
|
|
+ return `${idx + 1}. [${k.task}]${sourceName}\n 类型: ${typesStr}\n 内容: ${k.content.substring(0, 150)}...\n 评分: ${k.eval.score}/5 (质量分: ${k.quality_score.toFixed(1)})`;
|
|
|
+ }).join('\n\n');
|
|
|
|
|
|
return {
|
|
|
success: true,
|
|
|
- output: formatted,
|
|
|
+ output: formatted || '未找到相关知识',
|
|
|
metadata: {
|
|
|
count: data.count,
|
|
|
results: data.results
|
|
|
@@ -144,45 +152,57 @@ export async function kb_search(
|
|
|
|
|
|
**返回示例:**
|
|
|
```
|
|
|
-找到 3 条相关经验:
|
|
|
+找到 3 条相关知识:
|
|
|
|
|
|
-1. [使用 Vitest 运行测试]
|
|
|
- 资源: vitest --run
|
|
|
- 结果: 成功运行测试,避免 watch 模式阻塞
|
|
|
- 评分: 4/5
|
|
|
+1. [使用 Vitest 运行测试] (来源: vitest)
|
|
|
+ 类型: tool, best-practice
|
|
|
+ 内容: 使用 vitest --run 执行单次测试,避免 watch 模式阻塞 CI。对于 E2E 测试,建议使用 --no-coverage 加速...
|
|
|
+ 评分: 4/5 (质量分: 6.0)
|
|
|
|
|
|
2. [配置 TypeScript 路径别名]
|
|
|
- 资源: tsconfig.json paths
|
|
|
- 结果: 使用 @/ 别名简化导入
|
|
|
- 评分: 5/5
|
|
|
+ 类型: tool, solution
|
|
|
+ 内容: 在 tsconfig.json 中配置 paths 字段,使用 @/ 别名简化导入。注意需要同步配置 Vite/Webpack 的 resolve.alias...
|
|
|
+ 评分: 5/5 (质量分: 7.0)
|
|
|
```
|
|
|
|
|
|
-### 2. kb_submit - 提交经验
|
|
|
+### 2. kb_save - 保存知识
|
|
|
|
|
|
-**功能:** 提交新经验到 KnowHub。
|
|
|
+**功能:** 保存新知识到 KnowHub。
|
|
|
|
|
|
**参数:**
|
|
|
-- `task` (string, required): 任务描述
|
|
|
-- `resource` (string, required): 使用的资源
|
|
|
-- `result` (string, required): 结果描述
|
|
|
+- `task` (string, required): 任务场景描述
|
|
|
+- `content` (string, required): 核心知识内容
|
|
|
+- `types` (string[], required): 知识类型,如 `["tool", "strategy"]`
|
|
|
- `score` (number, optional): 评分 1-5,默认 3
|
|
|
+- `source_name` (string, optional): 资源名称
|
|
|
+- `source_urls` (string[], optional): 参考链接
|
|
|
+
|
|
|
+**知识类型说明:**
|
|
|
+- `user_profile`: 用户偏好、习惯、背景
|
|
|
+- `strategy`: 执行经验(从反思中获得)
|
|
|
+- `tool`: 工具使用方法、优缺点、代码示例
|
|
|
+- `usecase`: 用户背景、方案、步骤、效果
|
|
|
+- `definition`: 概念定义、技术原理、应用场景
|
|
|
+- `plan`: 流程步骤、决策点、方法论
|
|
|
|
|
|
**实现要点:**
|
|
|
```typescript
|
|
|
// tools.ts
|
|
|
-export async function kb_submit(
|
|
|
+export async function kb_save(
|
|
|
task: string,
|
|
|
- resource: string,
|
|
|
- result: string,
|
|
|
- score: number = 3
|
|
|
+ content: string,
|
|
|
+ types: string[],
|
|
|
+ score: number = 3,
|
|
|
+ source_name?: string,
|
|
|
+ source_urls?: string[]
|
|
|
): Promise<ToolResult> {
|
|
|
const config = getConfig();
|
|
|
|
|
|
// 验证输入
|
|
|
- if (!task || !resource || !result) {
|
|
|
+ if (!task || !content || !types || types.length === 0) {
|
|
|
return {
|
|
|
success: false,
|
|
|
- error: '缺少必需参数'
|
|
|
+ error: '缺少必需参数: task, content, types'
|
|
|
};
|
|
|
}
|
|
|
|
|
|
@@ -193,19 +213,29 @@ export async function kb_submit(
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+ // 构建请求体
|
|
|
+ const body = {
|
|
|
+ task,
|
|
|
+ content,
|
|
|
+ types,
|
|
|
+ score,
|
|
|
+ scopes: ["org:openclaw"], // 默认可见范围
|
|
|
+ owner: `agent:${ctx.agentId}`,
|
|
|
+ source: {
|
|
|
+ name: source_name || "",
|
|
|
+ category: "exp",
|
|
|
+ urls: source_urls || [],
|
|
|
+ agent_id: ctx.agentId,
|
|
|
+ submitted_by: config.submittedBy,
|
|
|
+ message_id: ctx.messageId || ""
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
// 提交到 KnowHub
|
|
|
- const response = await fetch(`${config.apiUrl}/api/submit`, {
|
|
|
+ const response = await fetch(`${config.apiUrl}/api/knowledge`, {
|
|
|
method: 'POST',
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
- body: JSON.stringify({
|
|
|
- task,
|
|
|
- resource,
|
|
|
- result,
|
|
|
- score,
|
|
|
- submitted_by: config.submittedBy,
|
|
|
- agent_id: ctx.agentId,
|
|
|
- timestamp: new Date().toISOString()
|
|
|
- })
|
|
|
+ body: JSON.stringify(body)
|
|
|
});
|
|
|
|
|
|
if (!response.ok) {
|
|
|
@@ -219,47 +249,62 @@ export async function kb_submit(
|
|
|
|
|
|
return {
|
|
|
success: true,
|
|
|
- output: `✅ 经验已提交 (ID: ${data.id})`,
|
|
|
+ output: `✅ 知识已保存 (ID: ${data.id})`,
|
|
|
metadata: { id: data.id }
|
|
|
};
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-### 3. kb_content - 获取详细内容
|
|
|
+### 3. kb_update - 更新知识反馈
|
|
|
|
|
|
-**功能:** 获取经验的详细内容(包括 contents 表数据)。
|
|
|
+**功能:** 更新知识的有效性反馈。
|
|
|
|
|
|
**参数:**
|
|
|
-- `experience_id` (string, required): 经验 ID
|
|
|
+- `knowledge_id` (string, required): 知识 ID
|
|
|
+- `is_helpful` (boolean, required): 是否有用
|
|
|
+- `feedback` (string, optional): 反馈说明
|
|
|
|
|
|
**实现要点:**
|
|
|
```typescript
|
|
|
// tools.ts
|
|
|
-export async function kb_content(
|
|
|
- experience_id: string
|
|
|
+export async function kb_update(
|
|
|
+ knowledge_id: string,
|
|
|
+ is_helpful: boolean,
|
|
|
+ feedback?: string
|
|
|
): Promise<ToolResult> {
|
|
|
const config = getConfig();
|
|
|
|
|
|
- const response = await fetch(
|
|
|
- `${config.apiUrl}/api/content/${experience_id}`
|
|
|
- );
|
|
|
+ const body = is_helpful ? {
|
|
|
+ add_helpful_case: {
|
|
|
+ task: feedback || "使用成功",
|
|
|
+ outcome: "有效",
|
|
|
+ timestamp: new Date().toISOString()
|
|
|
+ }
|
|
|
+ } : {
|
|
|
+ add_harmful_case: {
|
|
|
+ task: feedback || "使用失败",
|
|
|
+ outcome: "无效",
|
|
|
+ reason: feedback || "未说明",
|
|
|
+ timestamp: new Date().toISOString()
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const response = await fetch(`${config.apiUrl}/api/knowledge/${knowledge_id}`, {
|
|
|
+ method: 'PUT',
|
|
|
+ headers: { 'Content-Type': 'application/json' },
|
|
|
+ body: JSON.stringify(body)
|
|
|
+ });
|
|
|
|
|
|
if (!response.ok) {
|
|
|
return {
|
|
|
success: false,
|
|
|
- error: `获取内容失败: ${response.statusText}`
|
|
|
+ error: `更新失败: ${response.statusText}`
|
|
|
};
|
|
|
}
|
|
|
|
|
|
- const data = await response.json();
|
|
|
-
|
|
|
return {
|
|
|
success: true,
|
|
|
- output: data.content,
|
|
|
- metadata: {
|
|
|
- experience_id: data.experience_id,
|
|
|
- content_type: data.content_type
|
|
|
- }
|
|
|
+ output: `✅ 反馈已提交`
|
|
|
};
|
|
|
}
|
|
|
```
|
|
|
@@ -288,11 +333,11 @@ api.on("before_agent_start", async (event) => {
|
|
|
💡 KnowHub 知识库已启用
|
|
|
|
|
|
可用工具:
|
|
|
-- kb_search: 搜索相关经验(任务开始、或在执行中遇到复杂问题时使用)
|
|
|
-- kb_submit: 提交新经验(使用工具或资源后)
|
|
|
-- kb_content: 获取详细内容(需要更多信息时)
|
|
|
+- kb_search: 搜索知识(遇到复杂任务、不确定用什么工具、多次失败时)
|
|
|
+- kb_save: 保存知识(使用资源后、获得用户反馈后、搜索过程有发现时)
|
|
|
+- kb_update: 反馈知识有效性(使用知识后)
|
|
|
|
|
|
-建议:在开始任务前先搜索相关经验,完成后提交新发现。
|
|
|
+建议:开始任务前先搜索相关知识,完成后及时保存新发现。
|
|
|
`.trim()
|
|
|
};
|
|
|
});
|
|
|
@@ -327,7 +372,7 @@ api.on("before_prompt_build", async (event, ctx) => {
|
|
|
if (count % interval !== 0) return;
|
|
|
|
|
|
return {
|
|
|
- prependContext: `💡 提醒:如果使用了工具或资源,或者收到了用户反馈,记得用 kb_submit 提交经验到 KnowHub。`
|
|
|
+ prependContext: `💡 提醒:如果遇到复杂问题、使用了工具/资源、或获得了用户反馈,记得用 kb_save 保存知识到 KnowHub。`
|
|
|
};
|
|
|
});
|
|
|
|
|
|
@@ -662,15 +707,15 @@ export function validateConfig(config: any): KnowHubConfig {
|
|
|
|
|
|
## 使用场景
|
|
|
|
|
|
-### 场景 1: 手动搜索和提交
|
|
|
+### 场景 1: 手动搜索和保存
|
|
|
|
|
|
**用户任务:** 配置 TypeScript 项目
|
|
|
|
|
|
**Agent 行为:**
|
|
|
1. 启动时看到 KnowHub 提示
|
|
|
2. 调用 `kb_search("配置 TypeScript 项目")`
|
|
|
-3. 找到相关经验,参考执行
|
|
|
-4. 完成后调用 `kb_submit(...)`
|
|
|
+3. 找到相关知识,参考执行
|
|
|
+4. 完成后调用 `kb_save(...)`
|
|
|
|
|
|
### 场景 2: 定期提醒
|
|
|
|
|
|
@@ -678,7 +723,7 @@ export function validateConfig(config: any): KnowHubConfig {
|
|
|
|
|
|
**Agent 行为:**
|
|
|
1. 每 3 次 LLM 调用看到提醒
|
|
|
-2. 在使用工具后主动提交经验
|
|
|
+2. 在使用工具后主动保存知识
|
|
|
3. 不会过度干扰任务执行
|
|
|
|
|
|
### 场景 3: 自动提取(可选)
|
|
|
@@ -688,7 +733,7 @@ export function validateConfig(config: any): KnowHubConfig {
|
|
|
**Agent 行为:**
|
|
|
1. 正常执行任务
|
|
|
2. 回合结束时自动上传消息历史
|
|
|
-3. Server 分析并提取经验
|
|
|
+3. Server 分析并提取知识
|
|
|
4. 下次搜索时可以找到
|
|
|
|
|
|
---
|
|
|
@@ -697,7 +742,7 @@ export function validateConfig(config: any): KnowHubConfig {
|
|
|
|
|
|
### Phase 1: 基础功能(MVP)
|
|
|
- [ ] 插件骨架(openclaw.plugin.json + index.ts)
|
|
|
-- [ ] 工具注册(kb_search, kb_submit)
|
|
|
+- [ ] 工具注册(kb_search, kb_save, kb_update)
|
|
|
- [ ] before_agent_start 提醒
|
|
|
- [ ] 配置管理和验证
|
|
|
|
|
|
@@ -718,9 +763,9 @@ export function validateConfig(config: any): KnowHubConfig {
|
|
|
- [ ] 本地/远程服务支持
|
|
|
|
|
|
### Phase 5: 增强功能(可选)
|
|
|
-- [ ] kb_content 工具
|
|
|
-- [ ] 经验去重
|
|
|
+- [ ] 知识去重
|
|
|
- [ ] 离线缓存
|
|
|
+- [ ] 知识进化(evolve_feedback)
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -731,8 +776,9 @@ export function validateConfig(config: any): KnowHubConfig {
|
|
|
**工具测试:**
|
|
|
- kb_search 正常返回
|
|
|
- kb_search 错误处理
|
|
|
-- kb_submit 参数验证
|
|
|
-- kb_submit 成功提交
|
|
|
+- kb_save 参数验证
|
|
|
+- kb_save 成功提交
|
|
|
+- kb_update 反馈提交
|
|
|
|
|
|
**安全测试:**
|
|
|
- redactSensitiveInfo 脱敏效果
|
|
|
@@ -747,9 +793,10 @@ export function validateConfig(config: any): KnowHubConfig {
|
|
|
- agent_end 状态清理
|
|
|
|
|
|
**端到端测试:**
|
|
|
-- 完整任务流程(搜索 → 执行 → 提交)
|
|
|
+- 完整任务流程(搜索 → 执行 → 保存)
|
|
|
- 提醒频率验证
|
|
|
- 消息历史上传(mock server)
|
|
|
+- 知识反馈更新
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -815,10 +862,10 @@ export function validateConfig(config: any): KnowHubConfig {
|
|
|
|
|
|
| 端点 | 方法 | 用途 |
|
|
|
|------|------|------|
|
|
|
-| `/api/search` | GET | 搜索经验 |
|
|
|
-| `/api/submit` | POST | 提交经验 |
|
|
|
-| `/api/content/{id}` | GET | 获取详细内容 |
|
|
|
-| `/api/extract` | POST | 消息历史提取 |
|
|
|
+| `/api/knowledge/search` | GET | 搜索知识 |
|
|
|
+| `/api/knowledge` | POST | 保存知识 |
|
|
|
+| `/api/knowledge/{id}` | PUT | 更新知识反馈 |
|
|
|
+| `/api/extract` | POST | 消息历史提取(可选)|
|
|
|
|
|
|
### OpenClaw 版本
|
|
|
|
|
|
@@ -840,10 +887,10 @@ A: 调整 `reminderMode` 配置:
|
|
|
### Q: 消息历史上传安全吗?
|
|
|
|
|
|
A:
|
|
|
-1. 默认关闭,需要明确启用
|
|
|
-2. 使用 `strict` 模式自动脱敏
|
|
|
-3. 建议只连接本地服务
|
|
|
-4. 远程服务必须使用 HTTPS
|
|
|
+1. 默认启用,但使用 `strict` 模式自动脱敏
|
|
|
+2. 建议只连接本地服务
|
|
|
+3. 远程服务必须使用 HTTPS
|
|
|
+4. 可以设置 `enableServerExtraction: false` 完全关闭
|
|
|
|
|
|
### Q: 如何禁用插件?
|
|
|
|
|
|
@@ -867,6 +914,12 @@ A:
|
|
|
- 提醒注入:异步,几乎无影响
|
|
|
- 消息上传:异步,不阻塞主流程
|
|
|
|
|
|
+### Q: kb_save 和 kb_update 有什么区别?
|
|
|
+
|
|
|
+A:
|
|
|
+- `kb_save`: 保存新知识(首次提交)
|
|
|
+- `kb_update`: 更新已有知识的有效性反馈(使用后反馈)
|
|
|
+
|
|
|
---
|
|
|
|
|
|
## 参考资料
|