mirror of
https://gitcode.com/JianFeeeee/TrulyMEM-TrueHumanMEM.git
synced 2026-10-15 12:15:32 +00:00
fix: OpenClaw compliance audit fixes - refactor tool interface to AnyAgentTool, restructure skill directories
This commit is contained in:
29
.sisyphus/fix-tracker.md
Normal file
29
.sisyphus/fix-tracker.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# 修复追踪文件
|
||||||
|
|
||||||
|
**分支**: openclaw
|
||||||
|
**开始时间**: 2026-04-16
|
||||||
|
**状态**: 重新执行
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 修复计划
|
||||||
|
|
||||||
|
### P0 - 阻断性问题
|
||||||
|
|
||||||
|
#### 1. 删除自定义 tool_interface.ts
|
||||||
|
#### 2. 重构 graph_memory_tool.ts
|
||||||
|
#### 3. 更新 plugin-entry.ts
|
||||||
|
|
||||||
|
### P2 - 最佳实践
|
||||||
|
|
||||||
|
#### 4. 重组 Skill 目录结构
|
||||||
|
#### 5. 更新 bundled-skills 目录结构
|
||||||
|
#### 6. 更新 openclaw.plugin.json
|
||||||
|
#### 7. 更新 README.md / README_EN.md
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 执行记录
|
||||||
|
|
||||||
|
### [进行中] P0: 核心修复
|
||||||
|
|
||||||
32
README.md
32
README.md
@ -35,13 +35,13 @@ npm run build
|
|||||||
```bash
|
```bash
|
||||||
# 方式 A:工作空间级(推荐,仅当前项目可用)
|
# 方式 A:工作空间级(推荐,仅当前项目可用)
|
||||||
cp -r skills/graph-memory <你的项目目录>/skills/graph-memory
|
cp -r skills/graph-memory <你的项目目录>/skills/graph-memory
|
||||||
cp -r skills/graph-memory/persona <你的项目目录>/skills/graph-memory-persona
|
cp -r skills/graph-memory-persona <你的项目目录>/skills/graph-memory-persona
|
||||||
cp -r skills/graph-memory/task <你的项目目录>/skills/graph-memory-task
|
cp -r skills/graph-memory-task <你的项目目录>/skills/graph-memory-task
|
||||||
|
|
||||||
# 方式 B:用户级(所有项目可用)
|
# 方式 B:用户级(所有项目可用)
|
||||||
cp -r skills/graph-memory ~/.openclaw/skills/graph-memory
|
cp -r skills/graph-memory ~/.openclaw/skills/graph-memory
|
||||||
cp -r skills/graph-memory/persona ~/.openclaw/skills/graph-memory-persona
|
cp -r skills/graph-memory-persona ~/.openclaw/skills/graph-memory-persona
|
||||||
cp -r skills/graph-memory/task ~/.openclaw/skills/graph-memory-task
|
cp -r skills/graph-memory-task ~/.openclaw/skills/graph-memory-task
|
||||||
```
|
```
|
||||||
|
|
||||||
### 步骤 4:安装 Plugin(让 OpenClaw 运行时加载 Tool)
|
### 步骤 4:安装 Plugin(让 OpenClaw 运行时加载 Tool)
|
||||||
@ -130,8 +130,8 @@ npm run build
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp -r skills/graph-memory ~/.agents/skills/graph-memory
|
cp -r skills/graph-memory ~/.agents/skills/graph-memory
|
||||||
cp -r skills/graph-memory/persona ~/.agents/skills/graph-memory-persona
|
cp -r skills/graph-memory-persona ~/.agents/skills/graph-memory-persona
|
||||||
cp -r skills/graph-memory/task ~/.agents/skills/graph-memory-task
|
cp -r skills/graph-memory-task ~/.agents/skills/graph-memory-task
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -153,19 +153,23 @@ ts/
|
|||||||
│ │ └── graph_memory_tool.ts # Tool 实现
|
│ │ └── graph_memory_tool.ts # Tool 实现
|
||||||
│ └── tool_limiter.ts # 调用限制器
|
│ └── tool_limiter.ts # 调用限制器
|
||||||
├── bundled-skills/
|
├── bundled-skills/
|
||||||
│ └── graph-memory/ # 内置 Skill 定义
|
│ ├── graph-memory/ # 内置 Skill 定义
|
||||||
│ ├── SKILL.md
|
│ │ └── SKILL.md
|
||||||
│ ├── persona/SKILL.md
|
│ ├── graph-memory-persona/
|
||||||
│ └── task/SKILL.md
|
│ │ └── SKILL.md
|
||||||
|
│ └── graph-memory-task/
|
||||||
|
│ └── SKILL.md
|
||||||
├── package.json
|
├── package.json
|
||||||
├── tsconfig.json
|
├── tsconfig.json
|
||||||
└── openclaw.plugin.json # Plugin Manifest
|
└── openclaw.plugin.json # Plugin Manifest
|
||||||
|
|
||||||
skills/ # 独立 Skill 定义
|
skills/ # 独立 Skill 定义
|
||||||
└── graph-memory/
|
├── graph-memory/
|
||||||
├── SKILL.md
|
│ └── SKILL.md
|
||||||
├── persona/SKILL.md
|
├── graph-memory-persona/
|
||||||
└── task/SKILL.md
|
│ └── SKILL.md
|
||||||
|
└── graph-memory-task/
|
||||||
|
└── SKILL.md
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
32
README_EN.md
32
README_EN.md
@ -35,13 +35,13 @@ Copy the 3 Skills from `skills/` to OpenClaw's skill directory. Priority: `<work
|
|||||||
```bash
|
```bash
|
||||||
# Option A: Workspace-level (recommended, current project only)
|
# Option A: Workspace-level (recommended, current project only)
|
||||||
cp -r skills/graph-memory <your-project-dir>/skills/graph-memory
|
cp -r skills/graph-memory <your-project-dir>/skills/graph-memory
|
||||||
cp -r skills/graph-memory/persona <your-project-dir>/skills/graph-memory-persona
|
cp -r skills/graph-memory-persona <your-project-dir>/skills/graph-memory-persona
|
||||||
cp -r skills/graph-memory/task <your-project-dir>/skills/graph-memory-task
|
cp -r skills/graph-memory-task <your-project-dir>/skills/graph-memory-task
|
||||||
|
|
||||||
# Option B: User-level (available for all projects)
|
# Option B: User-level (available for all projects)
|
||||||
cp -r skills/graph-memory ~/.openclaw/skills/graph-memory
|
cp -r skills/graph-memory ~/.openclaw/skills/graph-memory
|
||||||
cp -r skills/graph-memory/persona ~/.openclaw/skills/graph-memory-persona
|
cp -r skills/graph-memory-persona ~/.openclaw/skills/graph-memory-persona
|
||||||
cp -r skills/graph-memory/task ~/.openclaw/skills/graph-memory-task
|
cp -r skills/graph-memory-task ~/.openclaw/skills/graph-memory-task
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 4: Install Plugin (enables OpenClaw runtime to load the Tool)
|
### Step 4: Install Plugin (enables OpenClaw runtime to load the Tool)
|
||||||
@ -129,8 +129,8 @@ Copy the `skills/` directory to OpenClaw's skill directory:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp -r skills/graph-memory ~/.agents/skills/graph-memory
|
cp -r skills/graph-memory ~/.agents/skills/graph-memory
|
||||||
cp -r skills/graph-memory/persona ~/.agents/skills/graph-memory-persona
|
cp -r skills/graph-memory-persona ~/.agents/skills/graph-memory-persona
|
||||||
cp -r skills/graph-memory/task ~/.agents/skills/graph-memory-task
|
cp -r skills/graph-memory-task ~/.agents/skills/graph-memory-task
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -152,19 +152,23 @@ ts/
|
|||||||
│ │ └── graph_memory_tool.ts # Tool implementation
|
│ │ └── graph_memory_tool.ts # Tool implementation
|
||||||
│ └── tool_limiter.ts # Call rate limiter
|
│ └── tool_limiter.ts # Call rate limiter
|
||||||
├── bundled-skills/
|
├── bundled-skills/
|
||||||
│ └── graph-memory/ # Bundled Skill definitions
|
│ ├── graph-memory/ # Bundled Skill definitions
|
||||||
│ ├── SKILL.md
|
│ │ └── SKILL.md
|
||||||
│ ├── persona/SKILL.md
|
│ ├── graph-memory-persona/
|
||||||
│ └── task/SKILL.md
|
│ │ └── SKILL.md
|
||||||
|
│ └── graph-memory-task/
|
||||||
|
│ └── SKILL.md
|
||||||
├── package.json
|
├── package.json
|
||||||
├── tsconfig.json
|
├── tsconfig.json
|
||||||
└── openclaw.plugin.json # Plugin Manifest
|
└── openclaw.plugin.json # Plugin Manifest
|
||||||
|
|
||||||
skills/ # Standalone Skill definitions
|
skills/ # Standalone Skill definitions
|
||||||
└── graph-memory/
|
├── graph-memory/
|
||||||
├── SKILL.md
|
│ └── SKILL.md
|
||||||
├── persona/SKILL.md
|
├── graph-memory-persona/
|
||||||
└── task/SKILL.md
|
│ └── SKILL.md
|
||||||
|
└── graph-memory-task/
|
||||||
|
└── SKILL.md
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@ -1,7 +1,13 @@
|
|||||||
{
|
{
|
||||||
"id": "graph-memory",
|
"id": "graph-memory",
|
||||||
"name": "Graph Memory",
|
"name": "Graph Memory",
|
||||||
|
"kind": "memory",
|
||||||
"description": "让 AI 拥有真正的长期记忆能力 - 基于图数据库的记忆系统",
|
"description": "让 AI 拥有真正的长期记忆能力 - 基于图数据库的记忆系统",
|
||||||
|
"skills": [
|
||||||
|
"bundled-skills/graph-memory",
|
||||||
|
"bundled-skills/graph-memory-persona",
|
||||||
|
"bundled-skills/graph-memory-task"
|
||||||
|
],
|
||||||
"configSchema": {
|
"configSchema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|||||||
@ -1,76 +1,5 @@
|
|||||||
/**
|
import { GraphMemoryToolSchema, createGraphMemoryTool } from './runtime/core/tools/builtin/graph_memory_tool.js';
|
||||||
* OpenClaw Plugin Entry Point
|
|
||||||
*
|
|
||||||
* Registers the GraphMemory tool with OpenClaw's plugin system.
|
|
||||||
* Uses @sinclair/typebox for parameter schema definition.
|
|
||||||
*/
|
|
||||||
import { Type } from '@sinclair/typebox';
|
|
||||||
import { GraphMemoryTool } from './runtime/core/tools/builtin/graph_memory_tool.js';
|
|
||||||
|
|
||||||
// OpenClaw Tool Schema using TypeBox
|
|
||||||
const GraphMemoryToolSchema = Type.Object({
|
|
||||||
action: Type.String({
|
|
||||||
description: '记忆操作类型',
|
|
||||||
enum: [
|
|
||||||
'recall', 'commit', 'purge', 'introspect',
|
|
||||||
'persona_update', 'persona_clear',
|
|
||||||
'task_create', 'task_set_state', 'task_delete', 'task_link_info'
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
params: Type.Object({
|
|
||||||
queryIntent: Type.Optional(Type.String({ description: '搜索意图' })),
|
|
||||||
seedEntities: Type.Optional(Type.Array(Type.String({ description: '实体' }), { description: '种子实体' })),
|
|
||||||
depth: Type.Optional(Type.Number({ description: '检索深度' })),
|
|
||||||
sessionFilter: Type.Optional(Type.String({ description: '会话ID过滤' })),
|
|
||||||
triplets: Type.Optional(Type.Array(
|
|
||||||
Type.Object({
|
|
||||||
subject: Type.String({ description: '主体' }),
|
|
||||||
relation: Type.String({ description: '关系' }),
|
|
||||||
object: Type.String({ description: '客体' }),
|
|
||||||
confidence: Type.Optional(Type.Number({ description: '置信度' }))
|
|
||||||
}, { description: '三元组' }),
|
|
||||||
{ description: '三元组数组' }
|
|
||||||
)),
|
|
||||||
sessionId: Type.Optional(Type.String({ description: '会话ID' })),
|
|
||||||
turnId: Type.Optional(Type.Number({ description: '轮次ID' })),
|
|
||||||
criteria: Type.Optional(Type.Object({
|
|
||||||
subject: Type.Optional(Type.String({ description: '主体' })),
|
|
||||||
target: Type.Optional(Type.String({ description: '客体' })),
|
|
||||||
relation: Type.Optional(Type.String({ description: '关系' })),
|
|
||||||
sessionId: Type.Optional(Type.String({ description: '会话ID' }))
|
|
||||||
}, { description: '删除条件' })),
|
|
||||||
mode: Type.Optional(Type.String({
|
|
||||||
enum: ['soft', 'hard', 'supersede'],
|
|
||||||
description: '删除模式'
|
|
||||||
})),
|
|
||||||
newRelation: Type.Optional(Type.Object({
|
|
||||||
relation: Type.String({ description: '关系' }),
|
|
||||||
target: Type.String({ description: '客体' })
|
|
||||||
}, { description: '新关系(supersede模式)' })),
|
|
||||||
attributes: Type.Optional(Type.Array(
|
|
||||||
Type.Object({
|
|
||||||
attribute: Type.String({ description: '属性名' }),
|
|
||||||
value: Type.String({ description: '属性值' })
|
|
||||||
}, { description: '属性' }),
|
|
||||||
{ description: '属性数组' }
|
|
||||||
)),
|
|
||||||
confirm: Type.Optional(Type.Boolean({ description: '确认清除' })),
|
|
||||||
task_id: Type.Optional(Type.String({ description: '任务ID' })),
|
|
||||||
description: Type.Optional(Type.String({ description: '任务描述' })),
|
|
||||||
state: Type.Optional(Type.String({ description: '任务状态' })),
|
|
||||||
info_nodes: Type.Optional(Type.Array(Type.String({ description: '节点' }), { description: '信息节点' })),
|
|
||||||
info_node: Type.Optional(Type.String({ description: '信息节点' })),
|
|
||||||
days: Type.Optional(Type.Number({ description: '归档天数' })),
|
|
||||||
dry_run: Type.Optional(Type.Boolean({ description: '仅预览不删除' }))
|
|
||||||
}, { description: '操作参数' })
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Plugin entry point for OpenClaw.
|
|
||||||
*
|
|
||||||
* When loaded as an OpenClaw plugin, this function registers the GraphMemory tool.
|
|
||||||
* For standalone usage, import GraphMemoryTool directly.
|
|
||||||
*/
|
|
||||||
export default function registerGraphMemoryPlugin(api: {
|
export default function registerGraphMemoryPlugin(api: {
|
||||||
registerTool: (tool: {
|
registerTool: (tool: {
|
||||||
name: string;
|
name: string;
|
||||||
@ -81,34 +10,6 @@ export default function registerGraphMemoryPlugin(api: {
|
|||||||
}>;
|
}>;
|
||||||
}) => void;
|
}) => void;
|
||||||
}): void {
|
}): void {
|
||||||
const tool = new GraphMemoryTool();
|
const tool = createGraphMemoryTool();
|
||||||
|
api.registerTool(tool);
|
||||||
api.registerTool({
|
}
|
||||||
name: 'graph_memory',
|
|
||||||
description: '图记忆工具 - 让 AI 拥有真正的长期记忆能力。支持 recall(检索)、commit(写入)、purge(删除)、introspect(状态)、人设管理、任务管理',
|
|
||||||
parameters: GraphMemoryToolSchema,
|
|
||||||
async execute(_id: string, params: Record<string, unknown>): Promise<{
|
|
||||||
content: Array<{ type: 'text'; text: string }>;
|
|
||||||
}> {
|
|
||||||
const result = await tool.handler(params, {
|
|
||||||
toolCallId: _id,
|
|
||||||
workingDirectory: process.cwd(),
|
|
||||||
abortController: { signal: new AbortController().signal },
|
|
||||||
config: {},
|
|
||||||
logger: {
|
|
||||||
info: () => {},
|
|
||||||
warn: () => {},
|
|
||||||
error: () => {},
|
|
||||||
debug: () => {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
content: [{
|
|
||||||
type: 'text',
|
|
||||||
text: typeof result === 'string' ? result : JSON.stringify(result)
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -1,167 +1,95 @@
|
|||||||
import type { Tool, ToolCategory, PermissionLevel, ToolInputSchema, ToolExecutionContext, ToolOutput } from '../tool_interface';
|
import { Type } from '@sinclair/typebox';
|
||||||
import { GraphDatabase } from '../../graph_memory/graph_database';
|
import type { Static } from '@sinclair/typebox';
|
||||||
import { MemoryService } from '../../graph_memory/memory_service';
|
import { GraphDatabase } from '../../graph_memory/graph_database.js';
|
||||||
import { ToolLimiter } from '../tool_limiter';
|
import { MemoryService } from '../../graph_memory/memory_service.js';
|
||||||
|
import { ToolLimiter } from '../tool_limiter.js';
|
||||||
|
|
||||||
const GRAPH_MEMORY_TOOL_ID = 'builtin:graph_memory';
|
export const GraphMemoryToolSchema = Type.Object({
|
||||||
|
action: Type.String({
|
||||||
|
description: '记忆操作类型',
|
||||||
|
enum: [
|
||||||
|
'recall', 'commit', 'purge', 'introspect', 'archive', 'cleanup',
|
||||||
|
'persona_update', 'persona_clear',
|
||||||
|
'task_create', 'task_set_state', 'task_delete', 'task_link_info'
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
params: Type.Object({
|
||||||
|
queryIntent: Type.Optional(Type.String({ description: '搜索意图' })),
|
||||||
|
seedEntities: Type.Optional(Type.Array(Type.String({ description: '实体' }), { description: '种子实体' })),
|
||||||
|
depth: Type.Optional(Type.Number({ description: '检索深度' })),
|
||||||
|
sessionFilter: Type.Optional(Type.String({ description: '会话ID过滤' })),
|
||||||
|
triplets: Type.Optional(Type.Array(
|
||||||
|
Type.Object({
|
||||||
|
subject: Type.String({ description: '主体' }),
|
||||||
|
relation: Type.String({ description: '关系' }),
|
||||||
|
object: Type.String({ description: '客体' }),
|
||||||
|
confidence: Type.Optional(Type.Number({ description: '置信度' }))
|
||||||
|
}, { description: '三元组' }),
|
||||||
|
{ description: '三元组数组' }
|
||||||
|
)),
|
||||||
|
sessionId: Type.Optional(Type.String({ description: '会话ID' })),
|
||||||
|
turnId: Type.Optional(Type.Number({ description: '轮次ID' })),
|
||||||
|
criteria: Type.Optional(Type.Object({
|
||||||
|
subject: Type.Optional(Type.String({ description: '主体' })),
|
||||||
|
target: Type.Optional(Type.String({ description: '客体' })),
|
||||||
|
relation: Type.Optional(Type.String({ description: '关系' })),
|
||||||
|
sessionId: Type.Optional(Type.String({ description: '会话ID' }))
|
||||||
|
}, { description: '删除条件' })),
|
||||||
|
mode: Type.Optional(Type.String({
|
||||||
|
enum: ['soft', 'hard', 'supersede'],
|
||||||
|
description: '删除模式'
|
||||||
|
})),
|
||||||
|
newRelation: Type.Optional(Type.Object({
|
||||||
|
relation: Type.String({ description: '关系' }),
|
||||||
|
target: Type.String({ description: '客体' })
|
||||||
|
}, { description: '新关系(supersede模式)' })),
|
||||||
|
attributes: Type.Optional(Type.Array(
|
||||||
|
Type.Object({
|
||||||
|
attribute: Type.String({ description: '属性名' }),
|
||||||
|
value: Type.String({ description: '属性值' })
|
||||||
|
}, { description: '属性' }),
|
||||||
|
{ description: '属性数组' }
|
||||||
|
)),
|
||||||
|
confirm: Type.Optional(Type.Boolean({ description: '确认清除' })),
|
||||||
|
task_id: Type.Optional(Type.String({ description: '任务ID' })),
|
||||||
|
description: Type.Optional(Type.String({ description: '任务描述' })),
|
||||||
|
state: Type.Optional(Type.String({ description: '任务状态' })),
|
||||||
|
info_nodes: Type.Optional(Type.Array(Type.String({ description: '节点' }), { description: '信息节点' })),
|
||||||
|
info_node: Type.Optional(Type.String({ description: '信息节点' })),
|
||||||
|
days: Type.Optional(Type.Number({ description: '归档天数' })),
|
||||||
|
dry_run: Type.Optional(Type.Boolean({ description: '仅预览不删除' }))
|
||||||
|
}, { description: '操作参数' })
|
||||||
|
});
|
||||||
|
|
||||||
|
export type GraphMemoryToolParams = Static<typeof GraphMemoryToolSchema>;
|
||||||
|
|
||||||
const GRAPH_MEMORY_TOOL_DESCRIPTION = `图记忆工具 - 让 AI 拥有真正的长期记忆能力
|
const GRAPH_MEMORY_TOOL_DESCRIPTION = `图记忆工具 - 让 AI 拥有真正的长期记忆能力
|
||||||
|
|
||||||
操作:
|
操作:
|
||||||
- recall: 检索记忆
|
- recall: 检索记忆
|
||||||
- commit: 写入记忆
|
- commit: 写入记忆
|
||||||
- purge: 删除记忆
|
- purge: 删除记忆
|
||||||
- introspect: 查看状态
|
- introspect: 查看状态
|
||||||
|
- archive: 归档旧记忆
|
||||||
|
- cleanup: 清理无效数据
|
||||||
- persona_update/clear: 人设管理
|
- persona_update/clear: 人设管理
|
||||||
- task_create/set_state/delete: 任务管理`;
|
- task_create/set_state/delete/link_info: 任务管理`;
|
||||||
|
|
||||||
export interface OpenClawToolResult {
|
export function createGraphMemoryTool(dbPath?: string, sessionId?: string) {
|
||||||
content: Array<{ type: 'text'; text: string }>;
|
const db = new GraphDatabase(dbPath, sessionId);
|
||||||
}
|
const service = new MemoryService(db);
|
||||||
|
const limiter = new ToolLimiter();
|
||||||
|
|
||||||
export class GraphMemoryTool implements Tool {
|
async function executeAction(action: string, params: Record<string, unknown>): Promise<unknown> {
|
||||||
readonly id = GRAPH_MEMORY_TOOL_ID;
|
const [allowed, reason] = limiter.canCall(action);
|
||||||
readonly name = 'GraphMemory';
|
|
||||||
readonly description = GRAPH_MEMORY_TOOL_DESCRIPTION;
|
|
||||||
readonly category: ToolCategory = 'analysis';
|
|
||||||
readonly permissionLevel: PermissionLevel = 'safe';
|
|
||||||
|
|
||||||
readonly inputSchema: ToolInputSchema = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
action: {
|
|
||||||
type: 'string',
|
|
||||||
enum: [
|
|
||||||
'recall', 'commit', 'purge', 'introspect', 'archive', 'cleanup',
|
|
||||||
'persona_update', 'persona_clear',
|
|
||||||
'task_create', 'task_set_state', 'task_delete', 'task_link_info'
|
|
||||||
],
|
|
||||||
description: '记忆操作类型'
|
|
||||||
},
|
|
||||||
params: {
|
|
||||||
type: 'object',
|
|
||||||
description: '操作参数',
|
|
||||||
properties: {
|
|
||||||
queryIntent: { type: 'string', description: '搜索意图' },
|
|
||||||
seedEntities: { type: 'array', items: { type: 'string', description: '实体' }, description: '种子实体' },
|
|
||||||
depth: { type: 'number', description: '检索深度' },
|
|
||||||
sessionFilter: { type: 'string', description: '会话ID过滤' },
|
|
||||||
triplets: {
|
|
||||||
type: 'array',
|
|
||||||
items: {
|
|
||||||
type: 'object',
|
|
||||||
description: '三元组',
|
|
||||||
properties: {
|
|
||||||
subject: { type: 'string', description: '主体' },
|
|
||||||
relation: { type: 'string', description: '关系' },
|
|
||||||
object: { type: 'string', description: '客体' },
|
|
||||||
confidence: { type: 'number', description: '置信度' }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
description: '三元组数组'
|
|
||||||
},
|
|
||||||
sessionId: { type: 'string', description: '会话ID' },
|
|
||||||
turnId: { type: 'number', description: '轮次ID' },
|
|
||||||
criteria: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
subject: { type: 'string', description: '主体' },
|
|
||||||
target: { type: 'string', description: '客体' },
|
|
||||||
relation: { type: 'string', description: '关系' },
|
|
||||||
sessionId: { type: 'string', description: '会话ID' }
|
|
||||||
},
|
|
||||||
description: '删除条件'
|
|
||||||
},
|
|
||||||
mode: { type: 'string', enum: ['soft', 'hard', 'supersede'], description: '删除模式' },
|
|
||||||
newRelation: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
relation: { type: 'string', description: '关系' },
|
|
||||||
target: { type: 'string', description: '客体' }
|
|
||||||
},
|
|
||||||
description: '新关系(supersede模式)'
|
|
||||||
},
|
|
||||||
attributes: {
|
|
||||||
type: 'array',
|
|
||||||
items: {
|
|
||||||
type: 'object',
|
|
||||||
description: '属性',
|
|
||||||
properties: {
|
|
||||||
attribute: { type: 'string', description: '属性名' },
|
|
||||||
value: { type: 'string', description: '属性值' }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
description: '属性数组'
|
|
||||||
},
|
|
||||||
confirm: { type: 'boolean', description: '确认清除' },
|
|
||||||
task_id: { type: 'string', description: '任务ID' },
|
|
||||||
description: { type: 'string', description: '任务描述' },
|
|
||||||
state: { type: 'string', description: '任务状态' },
|
|
||||||
info_nodes: { type: 'array', items: { type: 'string', description: '节点' }, description: '信息节点' },
|
|
||||||
info_node: { type: 'string', description: '信息节点' },
|
|
||||||
days: { type: 'number', description: '归档天数' },
|
|
||||||
dry_run: { type: 'boolean', description: '仅预览不删除' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
required: ['action', 'params']
|
|
||||||
};
|
|
||||||
|
|
||||||
private db: GraphDatabase;
|
|
||||||
private service: MemoryService;
|
|
||||||
private limiter: ToolLimiter;
|
|
||||||
|
|
||||||
constructor(dbPath?: string, sessionId?: string) {
|
|
||||||
this.db = new GraphDatabase(dbPath, sessionId);
|
|
||||||
this.service = new MemoryService(this.db);
|
|
||||||
this.limiter = new ToolLimiter();
|
|
||||||
}
|
|
||||||
|
|
||||||
async handler(params: Record<string, unknown>, _context: ToolExecutionContext): Promise<ToolOutput> {
|
|
||||||
const action = params.action as string;
|
|
||||||
const actionParams = params.params as Record<string, unknown>;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await this.executeAction(action, actionParams);
|
|
||||||
return JSON.stringify({ success: true, data: result }, null, 2);
|
|
||||||
} catch (error) {
|
|
||||||
return JSON.stringify({
|
|
||||||
success: false,
|
|
||||||
error: {
|
|
||||||
type: 'execution_error',
|
|
||||||
message: error instanceof Error ? error.message : String(error)
|
|
||||||
}
|
|
||||||
}, null, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async execute(_toolCallId: string, params: Record<string, unknown>): Promise<OpenClawToolResult> {
|
|
||||||
const result = await this.handler(params, {
|
|
||||||
toolCallId: _toolCallId,
|
|
||||||
workingDirectory: process.cwd(),
|
|
||||||
abortController: { signal: new AbortController().signal },
|
|
||||||
config: {},
|
|
||||||
logger: { info: () => {}, warn: () => {}, error: () => {}, debug: () => {} }
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
content: [{
|
|
||||||
type: 'text',
|
|
||||||
text: typeof result === 'string' ? result : JSON.stringify(result)
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private async executeAction(action: string, params: Record<string, unknown>): Promise<unknown> {
|
|
||||||
const [allowed, reason] = this.limiter.canCall(action);
|
|
||||||
if (!allowed) {
|
if (!allowed) {
|
||||||
throw new Error(reason);
|
throw new Error(reason);
|
||||||
}
|
}
|
||||||
this.limiter.recordCall(action);
|
limiter.recordCall(action);
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'recall':
|
case 'recall':
|
||||||
return this.service.recall({
|
return service.recall({
|
||||||
queryIntent: params.queryIntent as string || '',
|
queryIntent: params.queryIntent as string || '',
|
||||||
seedEntities: params.seedEntities as string[] | undefined,
|
seedEntities: params.seedEntities as string[] | undefined,
|
||||||
depth: params.depth as number | undefined,
|
depth: params.depth as number | undefined,
|
||||||
@ -169,79 +97,100 @@ export class GraphMemoryTool implements Tool {
|
|||||||
});
|
});
|
||||||
|
|
||||||
case 'commit':
|
case 'commit':
|
||||||
return this.service.commit({
|
return service.commit({
|
||||||
triplets: params.triplets as Array<{ subject: string; relation: string; object: string; confidence?: number }>,
|
triplets: params.triplets as Array<{ subject: string; relation: string; object: string; confidence?: number }>,
|
||||||
sessionId: params.sessionId as string | undefined,
|
sessionId: params.sessionId as string | undefined,
|
||||||
turnId: params.turnId as number | undefined
|
turnId: params.turnId as number | undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
case 'purge':
|
case 'purge':
|
||||||
return this.service.purge({
|
return service.purge({
|
||||||
criteria: params.criteria as { subject?: string | undefined; target?: string | undefined; relation?: string | undefined; sessionId?: string | undefined } | undefined,
|
criteria: params.criteria as { subject?: string; target?: string; relation?: string; sessionId?: string } | undefined,
|
||||||
mode: params.mode as 'soft' | 'hard' | 'supersede' | undefined,
|
mode: params.mode as 'soft' | 'hard' | 'supersede' | undefined,
|
||||||
newRelation: params.newRelation as { relation: string; target: string } | undefined
|
newRelation: params.newRelation as { relation: string; target: string } | undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
case 'introspect':
|
case 'introspect':
|
||||||
return this.service.introspect();
|
return service.introspect();
|
||||||
|
|
||||||
case 'persona_update':
|
case 'persona_update':
|
||||||
return this.service.updatePersona({
|
return service.updatePersona({
|
||||||
attributes: params.attributes as Array<{ attribute: string; value: string }>,
|
attributes: params.attributes as Array<{ attribute: string; value: string }>,
|
||||||
mode: params.mode as 'merge' | 'replace'
|
mode: params.mode as 'merge' | 'replace'
|
||||||
});
|
});
|
||||||
|
|
||||||
case 'persona_clear':
|
case 'persona_clear':
|
||||||
return this.service.clearPersona({
|
return service.clearPersona({
|
||||||
confirm: params.confirm as boolean
|
confirm: params.confirm as boolean
|
||||||
});
|
});
|
||||||
|
|
||||||
case 'task_create':
|
case 'task_create':
|
||||||
return this.service.createTask({
|
return service.createTask({
|
||||||
task_id: params.task_id as string,
|
task_id: params.task_id as string,
|
||||||
description: params.description as string,
|
description: params.description as string,
|
||||||
info_nodes: params.info_nodes as string[] | undefined
|
info_nodes: params.info_nodes as string[] | undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
case 'task_set_state':
|
case 'task_set_state':
|
||||||
return this.service.setTaskState({
|
return service.setTaskState({
|
||||||
task_id: params.task_id as string,
|
task_id: params.task_id as string,
|
||||||
state: params.state as string
|
state: params.state as string
|
||||||
});
|
});
|
||||||
|
|
||||||
case 'task_delete':
|
case 'task_delete':
|
||||||
return this.service.deleteTask({
|
return service.deleteTask({
|
||||||
task_id: params.task_id as string
|
task_id: params.task_id as string
|
||||||
});
|
});
|
||||||
|
|
||||||
case 'task_link_info':
|
case 'task_link_info':
|
||||||
return this.service.linkInfoToTask({
|
return service.linkInfoToTask({
|
||||||
task_id: params.task_id as string,
|
task_id: params.task_id as string,
|
||||||
info_node: params.info_node as string
|
info_node: params.info_node as string
|
||||||
});
|
});
|
||||||
|
|
||||||
case 'archive':
|
case 'archive':
|
||||||
return this.service.archive(params.days as number | undefined);
|
return service.archive(params.days as number | undefined);
|
||||||
|
|
||||||
case 'cleanup':
|
case 'cleanup':
|
||||||
return this.service.cleanup(params.dry_run as boolean | undefined);
|
return service.cleanup(params.dry_run as boolean | undefined);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown action: ${action}`);
|
throw new Error(`Unknown action: ${action}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resetLimiter(): void {
|
return {
|
||||||
this.limiter.reset();
|
name: 'graph_memory',
|
||||||
}
|
description: GRAPH_MEMORY_TOOL_DESCRIPTION,
|
||||||
|
parameters: GraphMemoryToolSchema,
|
||||||
|
|
||||||
getLimiterSummary(): string {
|
async execute(_toolCallId: string, params: Record<string, unknown>): Promise<{
|
||||||
return this.limiter.getSummary();
|
content: Array<{ type: 'text'; text: string }>;
|
||||||
}
|
}> {
|
||||||
|
const action = params.action as string;
|
||||||
|
const actionParams = params.params as Record<string, unknown>;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await executeAction(action, actionParams);
|
||||||
|
return {
|
||||||
|
content: [{ type: 'text', text: JSON.stringify({ success: true, data: result }) }]
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
content: [{
|
||||||
|
type: 'text',
|
||||||
|
text: JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
type: 'execution_error',
|
||||||
|
message: error instanceof Error ? error.message : String(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createGraphMemoryTool(dbPath?: string, sessionId?: string): GraphMemoryTool {
|
export { ToolLimiter } from '../tool_limiter.js';
|
||||||
return new GraphMemoryTool(dbPath, sessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
export { ToolLimiter } from '../tool_limiter';
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
export type ToolCategory = 'file' | 'code' | 'search' | 'execute' | 'network' | 'analysis' | 'generation' | 'communication' | 'mcp' | 'custom';
|
|
||||||
|
|
||||||
export type PermissionLevel = 'safe' | 'moderate' | 'dangerous' | 'restricted';
|
|
||||||
|
|
||||||
export type SchemaType = 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'object';
|
|
||||||
|
|
||||||
export interface SchemaProperty {
|
|
||||||
type: SchemaType;
|
|
||||||
description: string;
|
|
||||||
enum?: string[];
|
|
||||||
minimum?: number;
|
|
||||||
maximum?: number;
|
|
||||||
minLength?: number;
|
|
||||||
maxLength?: number;
|
|
||||||
pattern?: string;
|
|
||||||
default?: unknown;
|
|
||||||
examples?: unknown[];
|
|
||||||
items?: SchemaProperty;
|
|
||||||
properties?: Record<string, SchemaProperty>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ToolInputSchema {
|
|
||||||
type: 'object';
|
|
||||||
properties: Record<string, SchemaProperty>;
|
|
||||||
required?: string[];
|
|
||||||
additionalProperties?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ToolOutputSchema {
|
|
||||||
type: 'object';
|
|
||||||
properties: Record<string, SchemaProperty>;
|
|
||||||
format?: 'json' | 'text' | 'markdown' | 'binary';
|
|
||||||
maxSize?: number;
|
|
||||||
maxLines?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ToolInput = Record<string, unknown>;
|
|
||||||
export type ToolOutput = string | Record<string, unknown> | void;
|
|
||||||
|
|
||||||
export interface ToolExecutionContext {
|
|
||||||
toolCallId: string;
|
|
||||||
workingDirectory: string;
|
|
||||||
abortController: { signal: AbortSignal };
|
|
||||||
config: { timeout?: number };
|
|
||||||
logger: { info: (...args: unknown[]) => void; warn: (...args: unknown[]) => void; error: (...args: unknown[]) => void; debug: (...args: unknown[]) => void };
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ToolHandler = (params: ToolInput, context: ToolExecutionContext) => Promise<ToolOutput>;
|
|
||||||
|
|
||||||
export interface Tool {
|
|
||||||
readonly id: string;
|
|
||||||
readonly name: string;
|
|
||||||
readonly description: string;
|
|
||||||
readonly category: ToolCategory;
|
|
||||||
readonly inputSchema: ToolInputSchema;
|
|
||||||
readonly outputSchema?: ToolOutputSchema;
|
|
||||||
readonly handler: ToolHandler;
|
|
||||||
readonly permissionLevel: PermissionLevel;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user