feat: migrate to TypeScript for WaterFlow framework
- Add TypeScript graph memory module (GraphDatabase, MemoryService) - Add GraphMemoryTool for WaterFlow Tool interface - Add bundled-skills for graph_memory, persona, task - Remove Python code (core/, ui/, tests/, etc.) - Remove redundant docs and build files - Keep only ts/, docs/integration/, .gitignore, LICENSE
This commit is contained in:
102
ts/bundled-skills/graph_memory/SKILL.md
Normal file
102
ts/bundled-skills/graph_memory/SKILL.md
Normal file
@ -0,0 +1,102 @@
|
||||
---
|
||||
name: graph_memory
|
||||
description: 图记忆工具 - 让 AI 拥有真正的长期记忆能力
|
||||
when_to_use: 需要 AI 记住或回忆信息时
|
||||
context: inline
|
||||
allowed_tools:
|
||||
- builtin:graph_memory
|
||||
arguments:
|
||||
- name: action
|
||||
type: string
|
||||
required: true
|
||||
enum: [recall, commit, purge, introspect]
|
||||
description: 记忆操作类型
|
||||
- name: params
|
||||
type: object
|
||||
required: true
|
||||
description: 操作参数
|
||||
user_invocable: true
|
||||
---
|
||||
|
||||
# GraphMemory 图记忆操作
|
||||
|
||||
你可以通过以下操作与图记忆系统交互。
|
||||
|
||||
## 核心操作
|
||||
|
||||
### 1. recall - 检索记忆
|
||||
|
||||
从记忆图中检索相关信息。
|
||||
|
||||
**参数**:
|
||||
- `queryIntent`: 搜索意图/关键词
|
||||
- `seedEntities`: 可选的种子实体名
|
||||
- `depth`: 检索深度
|
||||
- `sessionFilter`: 可选的会话ID过滤
|
||||
|
||||
**示例**:
|
||||
```
|
||||
action: recall
|
||||
params:
|
||||
queryIntent: "用户 喜欢 编程"
|
||||
seedEntities: ["用户"]
|
||||
```
|
||||
|
||||
### 2. commit - 写入记忆
|
||||
|
||||
将信息写入记忆图。
|
||||
|
||||
**参数**:
|
||||
- `triplets`: 三元组数组,每个包含 subject, relation, object
|
||||
- `sessionId`: 会话ID
|
||||
- `turnId`: 轮次ID
|
||||
|
||||
**示例**:
|
||||
```
|
||||
action: commit
|
||||
params:
|
||||
triplets:
|
||||
- subject: "用户"
|
||||
relation: "喜欢"
|
||||
object: "Python"
|
||||
- subject: "用户"
|
||||
relation: "正在学习"
|
||||
object: "TypeScript"
|
||||
```
|
||||
|
||||
### 3. purge - 删除记忆
|
||||
|
||||
从记忆图中删除信息。
|
||||
|
||||
**参数**:
|
||||
- `criteria`: 删除条件 (subject, target, relation, sessionId)
|
||||
- `mode`: 删除模式 (soft/hard/supersede)
|
||||
- `newRelation`: 可选的替代关系
|
||||
|
||||
**示例**:
|
||||
```
|
||||
action: purge
|
||||
params:
|
||||
criteria:
|
||||
subject: "旧信息"
|
||||
mode: "soft"
|
||||
```
|
||||
|
||||
### 4. introspect - 查看状态
|
||||
|
||||
查看当前记忆状态统计。
|
||||
|
||||
**参数**: 无
|
||||
|
||||
**示例**:
|
||||
```
|
||||
action: introspect
|
||||
params: {}
|
||||
```
|
||||
|
||||
## 使用原则
|
||||
|
||||
1. **选择性记忆**: 只记住重要和持久的信息
|
||||
2. **结构化**: 使用三元组 (主体-关系-客体) 格式
|
||||
3. **关联**: 通过关系连接相关实体
|
||||
4. **定期清理**: 删除过时或错误的信息
|
||||
66
ts/bundled-skills/graph_memory/persona/SKILL.md
Normal file
66
ts/bundled-skills/graph_memory/persona/SKILL.md
Normal file
@ -0,0 +1,66 @@
|
||||
---
|
||||
name: graph_memory_persona
|
||||
description: 管理 AI 人设 - 更新或清除 AI 角色特征
|
||||
when_to_use: 需要修改 AI 的角色设定或清除人设时
|
||||
context: inline
|
||||
allowed_tools:
|
||||
- builtin:graph_memory
|
||||
arguments:
|
||||
- name: action
|
||||
type: string
|
||||
required: true
|
||||
enum: [persona_update, persona_clear]
|
||||
description: 操作类型
|
||||
- name: attributes
|
||||
type: array
|
||||
description: 属性数组 (用于 update)
|
||||
- name: mode
|
||||
type: string
|
||||
enum: [merge, replace]
|
||||
default: merge
|
||||
description: 更新模式
|
||||
- name: confirm
|
||||
type: boolean
|
||||
description: 确认清除 (用于 clear)
|
||||
user_invocable: true
|
||||
---
|
||||
|
||||
# GraphMemory Persona 人设管理
|
||||
|
||||
管理 AI 的人设/角色特征。
|
||||
|
||||
## 操作
|
||||
|
||||
### 1. persona_update - 更新人设
|
||||
|
||||
更新 AI 的角色特征。
|
||||
|
||||
**参数**:
|
||||
- `attributes`: 属性数组,每个包含 attribute 和 value
|
||||
- `mode`: 更新模式
|
||||
- `merge`: 合并到现有属性
|
||||
- `replace`: 替换所有现有属性
|
||||
|
||||
**示例**:
|
||||
```
|
||||
action: persona_update
|
||||
attributes:
|
||||
- attribute: "角色"
|
||||
value: "猫娘"
|
||||
- attribute: "性格"
|
||||
value: "活泼"
|
||||
mode: "merge"
|
||||
```
|
||||
|
||||
### 2. persona_clear - 清除人设
|
||||
|
||||
清除 AI 的所有角色特征。
|
||||
|
||||
**参数**:
|
||||
- `confirm`: 确认为 true 才能执行清除
|
||||
|
||||
**示例**:
|
||||
```
|
||||
action: persona_clear
|
||||
confirm: true
|
||||
```
|
||||
98
ts/bundled-skills/graph_memory/task/SKILL.md
Normal file
98
ts/bundled-skills/graph_memory/task/SKILL.md
Normal file
@ -0,0 +1,98 @@
|
||||
---
|
||||
name: graph_memory_task
|
||||
description: 管理连续性任务 - 创建、更新、删除任务节点
|
||||
when_to_use: 需要创建或管理长期任务时
|
||||
context: inline
|
||||
allowed_tools:
|
||||
- builtin:graph_memory
|
||||
arguments:
|
||||
- name: action
|
||||
type: string
|
||||
required: true
|
||||
enum: [task_create, task_set_state, task_delete, task_link_info]
|
||||
description: 操作类型
|
||||
- name: task_id
|
||||
type: string
|
||||
required: true
|
||||
description: 任务ID
|
||||
- name: description
|
||||
type: string
|
||||
description: 任务描述 (用于 create)
|
||||
- name: state
|
||||
type: string
|
||||
enum: [进行中, 已完成, 已暂停, 已取消]
|
||||
description: 任务状态 (用于 set_state)
|
||||
- name: info_nodes
|
||||
type: array
|
||||
description: 信息节点数组 (用于 create)
|
||||
- name: info_node
|
||||
type: string
|
||||
description: 信息节点 (用于 link_info)
|
||||
user_invocable: true
|
||||
---
|
||||
|
||||
# GraphMemory Task 任务管理
|
||||
|
||||
管理长期/连续性任务。
|
||||
|
||||
## 操作
|
||||
|
||||
### 1. task_create - 创建任务
|
||||
|
||||
创建新的任务节点。
|
||||
|
||||
**参数**:
|
||||
- `task_id`: 唯一任务标识
|
||||
- `description`: 任务描述
|
||||
- `info_nodes`: 可选的相关信息节点
|
||||
|
||||
**示例**:
|
||||
```
|
||||
action: task_create
|
||||
task_id: "Task_学习TypeScript"
|
||||
description: "学习 TypeScript 并完成项目"
|
||||
info_nodes: ["TypeScript文档", "教程链接"]
|
||||
```
|
||||
|
||||
### 2. task_set_state - 设置状态
|
||||
|
||||
更新任务状态。
|
||||
|
||||
**参数**:
|
||||
- `task_id`: 任务ID
|
||||
- `state`: 新状态 (进行中/已完成/已暂停/已取消)
|
||||
|
||||
**示例**:
|
||||
```
|
||||
action: task_set_state
|
||||
task_id: "Task_学习TypeScript"
|
||||
state: "已完成"
|
||||
```
|
||||
|
||||
### 3. task_delete - 删除任务
|
||||
|
||||
删除任务节点。
|
||||
|
||||
**参数**:
|
||||
- `task_id`: 任务ID
|
||||
|
||||
**示例**:
|
||||
```
|
||||
action: task_delete
|
||||
task_id: "Task_学习TypeScript"
|
||||
```
|
||||
|
||||
### 4. task_link_info - 关联信息
|
||||
|
||||
将信息节点关联到任务。
|
||||
|
||||
**参数**:
|
||||
- `task_id`: 任务ID
|
||||
- `info_node`: 信息节点
|
||||
|
||||
**示例**:
|
||||
```
|
||||
action: task_link_info
|
||||
task_id: "Task_学习TypeScript"
|
||||
info_node: "新教程链接"
|
||||
```
|
||||
1378
ts/package-lock.json
generated
Normal file
1378
ts/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
17
ts/package.json
Normal file
17
ts/package.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "trulymem-waterflow",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"yaml": "^2.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.5.2",
|
||||
"typescript": "^5.0.0",
|
||||
"vitest": "^2.0.0"
|
||||
}
|
||||
}
|
||||
196
ts/src/runtime/core/graph_memory/graph_database.ts
Normal file
196
ts/src/runtime/core/graph_memory/graph_database.ts
Normal file
@ -0,0 +1,196 @@
|
||||
import type { Entity, Relation, RecallParams, CommitParams, PurgeParams, RecallResult, CommitResult, PurgeResult, MemoryStats } from './types';
|
||||
|
||||
export class GraphDatabase {
|
||||
private entities: Map<string, Entity> = new Map();
|
||||
private relations: Map<string, Relation> = new Map();
|
||||
private sessionId: string;
|
||||
|
||||
constructor(sessionId?: string) {
|
||||
this.sessionId = sessionId || `session-${Date.now()}`;
|
||||
}
|
||||
|
||||
async recall(params: RecallParams): Promise<RecallResult> {
|
||||
const { queryIntent, seedEntities, sessionFilter } = params;
|
||||
const keywords = queryIntent.split(/[,\s]+/).filter(k => k.length > 0);
|
||||
const entities: Entity[] = [];
|
||||
const relations: Relation[] = [];
|
||||
const entityIds = new Set<string>();
|
||||
|
||||
for (const keyword of keywords) {
|
||||
const lowerKeyword = keyword.toLowerCase();
|
||||
for (const [_, entity] of this.entities) {
|
||||
if (entity.name.toLowerCase().includes(lowerKeyword)) {
|
||||
if (!entityIds.has(entity.id)) {
|
||||
entityIds.add(entity.id);
|
||||
entities.push(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (seedEntities && seedEntities.length > 0) {
|
||||
for (const seedName of seedEntities) {
|
||||
for (const [_, entity] of this.entities) {
|
||||
if (entity.name.toLowerCase() === seedName.toLowerCase()) {
|
||||
if (!entityIds.has(entity.id)) {
|
||||
entityIds.add(entity.id);
|
||||
entities.push(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [_, relation] of this.relations) {
|
||||
if (entityIds.has(relation.sourceId) || entityIds.has(relation.targetId)) {
|
||||
if (relation.status === 'active') {
|
||||
if (!sessionFilter || relation.sessionId === sessionFilter) {
|
||||
relations.push(relation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
entities,
|
||||
relations,
|
||||
message: `找到 ${entities.length} 个实体, ${relations.length} 条关系`
|
||||
};
|
||||
}
|
||||
|
||||
async commit(params: CommitParams): Promise<CommitResult> {
|
||||
const { triplets, sessionId, turnId } = params;
|
||||
let createdEntities = 0;
|
||||
let createdRelations = 0;
|
||||
|
||||
for (const triplet of triplets) {
|
||||
const sourceId = this.upsertEntity(triplet.subject);
|
||||
const targetId = this.upsertEntity(triplet.object);
|
||||
|
||||
const relationId = this.generateId();
|
||||
const now = new Date();
|
||||
const relation: Relation = {
|
||||
id: relationId,
|
||||
sourceId,
|
||||
targetId,
|
||||
relationType: triplet.relation,
|
||||
confidence: triplet.confidence || 1.0,
|
||||
status: 'active',
|
||||
sessionId: sessionId || this.sessionId,
|
||||
turnId: turnId || 0,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
dateBucket: this.getDateBucket(now)
|
||||
};
|
||||
|
||||
this.relations.set(relationId, relation);
|
||||
createdEntities += 2;
|
||||
createdRelations++;
|
||||
}
|
||||
|
||||
return { createdEntities, createdRelations };
|
||||
}
|
||||
|
||||
async purge(params: PurgeParams): Promise<PurgeResult> {
|
||||
const { criteria, mode = 'soft' } = params;
|
||||
let deleted = 0;
|
||||
|
||||
for (const [id, relation] of this.relations) {
|
||||
if (relation.status !== 'active') continue;
|
||||
|
||||
if (!criteria) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let matches = true;
|
||||
if (criteria.subject) {
|
||||
const sourceEntity = this.entities.get(relation.sourceId);
|
||||
matches = sourceEntity?.name.toLowerCase() === criteria.subject.toLowerCase();
|
||||
}
|
||||
if (matches && criteria.target) {
|
||||
const targetEntity = this.entities.get(relation.targetId);
|
||||
matches = targetEntity?.name.toLowerCase() === criteria.target.toLowerCase();
|
||||
}
|
||||
if (matches && criteria.relation) {
|
||||
matches = relation.relationType.toLowerCase() === criteria.relation.toLowerCase();
|
||||
}
|
||||
if (matches && criteria.sessionId) {
|
||||
matches = relation.sessionId === criteria.sessionId;
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
if (mode === 'hard') {
|
||||
this.relations.delete(id);
|
||||
} else {
|
||||
relation.status = 'deleted';
|
||||
relation.updatedAt = new Date();
|
||||
}
|
||||
deleted++;
|
||||
}
|
||||
}
|
||||
|
||||
return { deleted, mode };
|
||||
}
|
||||
|
||||
async introspect(): Promise<MemoryStats> {
|
||||
let entityCount = 0;
|
||||
for (const [_, entity] of this.entities) {
|
||||
if (!this.isEntityDeleted(entity.id)) entityCount++;
|
||||
}
|
||||
|
||||
let relationCount = 0;
|
||||
for (const [_, relation] of this.relations) {
|
||||
if (relation.status === 'active') relationCount++;
|
||||
}
|
||||
|
||||
return { entityCount, relationCount, sessionId: this.sessionId };
|
||||
}
|
||||
|
||||
private upsertEntity(name: string): string {
|
||||
for (const [id, entity] of this.entities) {
|
||||
if (entity.name === name && !this.isEntityDeleted(id)) {
|
||||
entity.mentionCount++;
|
||||
entity.updatedAt = new Date();
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
const id = this.generateId();
|
||||
const now = new Date();
|
||||
const entity: Entity = {
|
||||
id,
|
||||
name,
|
||||
type: 'unknown',
|
||||
mentionCount: 1,
|
||||
createdAt: now,
|
||||
updatedAt: now
|
||||
};
|
||||
this.entities.set(id, entity);
|
||||
return id;
|
||||
}
|
||||
|
||||
private isEntityDeleted(entityId: string): boolean {
|
||||
for (const [_, relation] of this.relations) {
|
||||
if ((relation.sourceId === entityId || relation.targetId === entityId) && relation.status === 'deleted') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private generateId(): string {
|
||||
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
|
||||
private getDateBucket(date: Date): string {
|
||||
return date.toISOString().split('T')[0] ?? '';
|
||||
}
|
||||
|
||||
setSessionId(sessionId: string): void {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
getSessionId(): string {
|
||||
return this.sessionId;
|
||||
}
|
||||
}
|
||||
3
ts/src/runtime/core/graph_memory/index.ts
Normal file
3
ts/src/runtime/core/graph_memory/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './types';
|
||||
export * from './graph_database';
|
||||
export * from './memory_service';
|
||||
129
ts/src/runtime/core/graph_memory/memory_service.ts
Normal file
129
ts/src/runtime/core/graph_memory/memory_service.ts
Normal file
@ -0,0 +1,129 @@
|
||||
import { GraphDatabase } from './graph_database';
|
||||
import type { RecallParams, CommitParams, PurgeParams, RecallResult, CommitResult, PurgeResult, MemoryStats } from './types';
|
||||
|
||||
export class MemoryService {
|
||||
private db: GraphDatabase;
|
||||
|
||||
constructor(db: GraphDatabase) {
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
async recall(params: RecallParams): Promise<RecallResult> {
|
||||
return this.db.recall(params);
|
||||
}
|
||||
|
||||
async commit(params: CommitParams): Promise<CommitResult> {
|
||||
return this.db.commit(params);
|
||||
}
|
||||
|
||||
async purge(params: PurgeParams): Promise<PurgeResult> {
|
||||
return this.db.purge(params);
|
||||
}
|
||||
|
||||
async introspect(): Promise<MemoryStats> {
|
||||
return this.db.introspect();
|
||||
}
|
||||
|
||||
async updatePersona(params: { attributes: Array<{ attribute: string; value: string }>; mode?: 'merge' | 'replace' }): Promise<{ status: string; updatedAttributes: number }> {
|
||||
const { attributes, mode = 'merge' } = params;
|
||||
|
||||
if (mode === 'replace') {
|
||||
await this.db.purge({
|
||||
criteria: { subject: 'AI' },
|
||||
mode: 'soft'
|
||||
});
|
||||
}
|
||||
|
||||
const triplets = attributes.map(attr => ({
|
||||
subject: 'AI',
|
||||
relation: attr.attribute,
|
||||
object: attr.value,
|
||||
confidence: 1.0
|
||||
}));
|
||||
|
||||
await this.db.commit({ triplets });
|
||||
|
||||
return { status: 'success', updatedAttributes: attributes.length };
|
||||
}
|
||||
|
||||
async clearPersona(params: { confirm: boolean }): Promise<{ status: string; deletedCount: number }> {
|
||||
if (params.confirm === false) {
|
||||
return { status: 'cancelled', deletedCount: 0 };
|
||||
}
|
||||
|
||||
const result = await this.db.purge({
|
||||
criteria: { subject: 'AI' },
|
||||
mode: 'soft'
|
||||
});
|
||||
|
||||
return { status: 'success', deletedCount: result.deleted };
|
||||
}
|
||||
|
||||
async createTask(params: { task_id: string; description: string; info_nodes?: string[] | undefined }): Promise<{ status: string; taskId: string }> {
|
||||
const { task_id, description, info_nodes = [] } = params;
|
||||
|
||||
await this.db.commit({
|
||||
triplets: [
|
||||
{ subject: task_id, relation: 'is_type', object: 'TaskNode' },
|
||||
{ subject: task_id, relation: 'has_description', object: description },
|
||||
{ subject: task_id, relation: 'HAS_STATE', object: 'State_进行中' }
|
||||
]
|
||||
});
|
||||
|
||||
if (info_nodes.length > 0) {
|
||||
await this.db.commit({
|
||||
triplets: info_nodes.map(node => ({
|
||||
subject: task_id,
|
||||
relation: 'CONTAINS_INFO',
|
||||
object: node
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
return { status: 'success', taskId: task_id };
|
||||
}
|
||||
|
||||
async setTaskState(params: { task_id: string; state: string }): Promise<{ status: string; newState: string }> {
|
||||
const { task_id, state } = params;
|
||||
|
||||
await this.db.purge({
|
||||
criteria: { subject: task_id, relation: 'HAS_STATE' },
|
||||
mode: 'soft'
|
||||
});
|
||||
|
||||
await this.db.commit({
|
||||
triplets: [{ subject: task_id, relation: 'HAS_STATE', object: `State_${state}` }]
|
||||
});
|
||||
|
||||
return { status: 'success', newState: state };
|
||||
}
|
||||
|
||||
async deleteTask(params: { task_id: string }): Promise<{ status: string; taskId: string }> {
|
||||
const { task_id } = params;
|
||||
|
||||
await this.db.purge({
|
||||
criteria: { subject: task_id },
|
||||
mode: 'soft'
|
||||
});
|
||||
|
||||
return { status: 'success', taskId: task_id };
|
||||
}
|
||||
|
||||
async linkInfoToTask(params: { task_id: string; info_node: string }): Promise<{ status: string }> {
|
||||
const { task_id, info_node } = params;
|
||||
|
||||
await this.db.commit({
|
||||
triplets: [{ subject: task_id, relation: 'CONTAINS_INFO', object: info_node }]
|
||||
});
|
||||
|
||||
return { status: 'success' };
|
||||
}
|
||||
|
||||
setSessionId(sessionId: string): void {
|
||||
this.db.setSessionId(sessionId);
|
||||
}
|
||||
|
||||
getSessionId(): string {
|
||||
return this.db.getSessionId();
|
||||
}
|
||||
}
|
||||
125
ts/src/runtime/core/graph_memory/types.ts
Normal file
125
ts/src/runtime/core/graph_memory/types.ts
Normal file
@ -0,0 +1,125 @@
|
||||
export interface Entity {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
mentionCount: number;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export type RelationStatus = 'active' | 'deleted' | 'archived' | 'superseded';
|
||||
|
||||
export interface Relation {
|
||||
id: string;
|
||||
sourceId: string;
|
||||
targetId: string;
|
||||
relationType: string;
|
||||
confidence: number;
|
||||
status: RelationStatus;
|
||||
sessionId: string;
|
||||
turnId: number;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
dateBucket: string;
|
||||
}
|
||||
|
||||
export interface Triplet {
|
||||
subject: string;
|
||||
relation: string;
|
||||
object: string;
|
||||
confidence?: number;
|
||||
}
|
||||
|
||||
export interface RecallParams {
|
||||
queryIntent: string;
|
||||
seedEntities?: string[] | undefined;
|
||||
depth?: number | undefined;
|
||||
timeRange?: { days: number } | undefined;
|
||||
sessionFilter?: string | undefined;
|
||||
}
|
||||
|
||||
export interface CommitParams {
|
||||
triplets: Triplet[];
|
||||
entityTypes?: Record<string, string> | undefined;
|
||||
temporalTag?: string | undefined;
|
||||
sessionId?: string | undefined;
|
||||
turnId?: number | undefined;
|
||||
}
|
||||
|
||||
export interface PurgeParams {
|
||||
criteria?: {
|
||||
subject?: string | undefined;
|
||||
target?: string | undefined;
|
||||
relation?: string | undefined;
|
||||
sessionId?: string | undefined;
|
||||
} | undefined;
|
||||
mode?: 'soft' | 'hard' | 'supersede' | undefined;
|
||||
newRelation?: { relation: string; target: string } | undefined;
|
||||
}
|
||||
|
||||
export interface RecallResult {
|
||||
entities: Entity[];
|
||||
relations: Relation[];
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface CommitResult {
|
||||
createdEntities: number;
|
||||
createdRelations: number;
|
||||
}
|
||||
|
||||
export interface PurgeResult {
|
||||
deleted: number;
|
||||
mode: string;
|
||||
}
|
||||
|
||||
export type TaskState = '进行中' | '已完成' | '已暂停' | '已取消';
|
||||
|
||||
export interface Task {
|
||||
taskId: string;
|
||||
description: string;
|
||||
state: TaskState;
|
||||
infoNodes: string[];
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface MemoryStats {
|
||||
entityCount: number;
|
||||
relationCount: number;
|
||||
sessionId?: string;
|
||||
}
|
||||
|
||||
export interface PersonaAttribute {
|
||||
attribute: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface PersonaUpdateParams {
|
||||
attributes: PersonaAttribute[];
|
||||
mode?: 'merge' | 'replace';
|
||||
}
|
||||
|
||||
export interface PersonaClearParams {
|
||||
confirm: boolean;
|
||||
}
|
||||
|
||||
export interface TaskCreateParams {
|
||||
task_id: string;
|
||||
description: string;
|
||||
info_nodes?: string[] | undefined;
|
||||
}
|
||||
|
||||
export interface TaskSetStateParams {
|
||||
task_id: string;
|
||||
state: string;
|
||||
}
|
||||
|
||||
export interface TaskDeleteParams {
|
||||
task_id: string;
|
||||
}
|
||||
|
||||
export interface TaskLinkInfoParams {
|
||||
task_id: string;
|
||||
info_node: string;
|
||||
}
|
||||
190
ts/src/runtime/core/tools/builtin/graph_memory_tool.ts
Normal file
190
ts/src/runtime/core/tools/builtin/graph_memory_tool.ts
Normal file
@ -0,0 +1,190 @@
|
||||
import type { Tool, ToolCategory, PermissionLevel, ToolInputSchema, ToolExecutionContext, ToolOutput } from '../tool_interface';
|
||||
import { GraphDatabase } from '../../graph_memory/graph_database';
|
||||
import { MemoryService } from '../../graph_memory/memory_service';
|
||||
|
||||
const GRAPH_MEMORY_TOOL_ID = 'builtin:graph_memory';
|
||||
|
||||
const GRAPH_MEMORY_TOOL_DESCRIPTION = `图记忆工具 - 让 AI 拥有真正的长期记忆能力
|
||||
|
||||
操作:
|
||||
- recall: 检索记忆
|
||||
- commit: 写入记忆
|
||||
- purge: 删除记忆
|
||||
- introspect: 查看状态
|
||||
- persona_update/clear: 人设管理
|
||||
- task_create/set_state/delete: 任务管理`;
|
||||
|
||||
export class GraphMemoryTool implements Tool {
|
||||
readonly id = GRAPH_MEMORY_TOOL_ID;
|
||||
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',
|
||||
'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: '删除模式' },
|
||||
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: '信息节点' }
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ['action', 'params']
|
||||
};
|
||||
|
||||
private db: GraphDatabase;
|
||||
private service: MemoryService;
|
||||
|
||||
constructor(sessionId?: string) {
|
||||
this.db = new GraphDatabase(sessionId);
|
||||
this.service = new MemoryService(this.db);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private async executeAction(action: string, params: Record<string, unknown>): Promise<unknown> {
|
||||
switch (action) {
|
||||
case 'recall':
|
||||
return this.service.recall({
|
||||
queryIntent: params.queryIntent as string || '',
|
||||
seedEntities: params.seedEntities as string[] | undefined,
|
||||
depth: params.depth as number | undefined,
|
||||
sessionFilter: params.sessionFilter as string | undefined
|
||||
});
|
||||
|
||||
case 'commit':
|
||||
return this.service.commit({
|
||||
triplets: params.triplets as Array<{ subject: string; relation: string; object: string; confidence?: number }>,
|
||||
sessionId: params.sessionId as string | undefined,
|
||||
turnId: params.turnId as number | undefined
|
||||
});
|
||||
|
||||
case 'purge':
|
||||
return this.service.purge({
|
||||
criteria: params.criteria as { subject?: string | undefined; target?: string | undefined; relation?: string | undefined; sessionId?: string | undefined } | undefined,
|
||||
mode: params.mode as 'soft' | 'hard' | 'supersede' | undefined
|
||||
});
|
||||
|
||||
case 'introspect':
|
||||
return this.service.introspect();
|
||||
|
||||
case 'persona_update':
|
||||
return this.service.updatePersona({
|
||||
attributes: params.attributes as Array<{ attribute: string; value: string }>,
|
||||
mode: params.mode as 'merge' | 'replace'
|
||||
});
|
||||
|
||||
case 'persona_clear':
|
||||
return this.service.clearPersona({
|
||||
confirm: params.confirm as boolean
|
||||
});
|
||||
|
||||
case 'task_create':
|
||||
return this.service.createTask({
|
||||
task_id: params.task_id as string,
|
||||
description: params.description as string,
|
||||
info_nodes: params.info_nodes as string[] | undefined
|
||||
});
|
||||
|
||||
case 'task_set_state':
|
||||
return this.service.setTaskState({
|
||||
task_id: params.task_id as string,
|
||||
state: params.state as string
|
||||
});
|
||||
|
||||
case 'task_delete':
|
||||
return this.service.deleteTask({
|
||||
task_id: params.task_id as string
|
||||
});
|
||||
|
||||
case 'task_link_info':
|
||||
return this.service.linkInfoToTask({
|
||||
task_id: params.task_id as string,
|
||||
info_node: params.info_node as string
|
||||
});
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown action: ${action}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createGraphMemoryTool(sessionId?: string): GraphMemoryTool {
|
||||
return new GraphMemoryTool(sessionId);
|
||||
}
|
||||
59
ts/src/runtime/core/tools/tool_interface.ts
Normal file
59
ts/src/runtime/core/tools/tool_interface.ts
Normal file
@ -0,0 +1,59 @@
|
||||
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;
|
||||
}
|
||||
23
ts/tsconfig.json
Normal file
23
ts/tsconfig.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user