(5-7)MCP服务器实战参考:持久化记忆MCP服务

5.7 持久化记忆MCP服务
“srcmemory”目录实现了一个名为“memory”的MCP服务器,旨在为大型语言模型(LLMs)提供持久化记忆功能。该模块通过实现一个基于知识图谱的持久记忆系统,使得LLMs能够在多次交互中存储和检索信息。它支持将信息以结构化的方式保存下来,并在需要时检索这些信息,从而允许模型在对话或任务执行过程中保持上下文的连续性,增强模型对用户历史交互的理解和响应能力。简而言之,“memory”服务器为语言模型提供了一种机制,使其能够记住和利用过去的交互信息,从而提供更加连贯和个性化的用户体验。
5.7.1 基于知识图谱的持久记忆介绍
基于知识图谱的持久记忆是一种结合知识图谱技术实现长期、结构化信息存储与复用的智能系统记忆机制,其核心是将信息以“实体-关系-属性”的三元组形式组织成图谱,并通过图谱的动态更新与关联推理,实现对信息的长期保留、高效检索和深度应用。
1. 核心特点
(1)结构化存储
不同于传统记忆的碎片化存储,它将信息拆解为实体(如“人”“地点”)、实体间的关系(如“居住于”“创立”)以及实体的属性(如“出生日期”“面积”),形成清晰的语义网络。例如,“乔布斯”“苹果公司”“创立”可构成三元组(乔布斯,创立,苹果公司),并关联“乔布斯的出生日期为1955年”等属性信息。
(2)持久化与动态更新
信息一旦存入知识图谱,会长期保留,且能根据新数据持续更新。比如,新发现某历史人物的生平细节时,可直接补充到其对应的实体节点中,同时调整相关关系,确保记忆的时效性和完整性。
(3)关联推理能力
借助图谱中实体间的语义关联,系统可进行逻辑推理,挖掘潜在信息。例如,已知(A,父母,B)和(B,父母,C),可推理出(A,祖父母,C),从而实现“记忆的延伸”,让系统不仅能记住显性信息,还能基于已有知识推导隐性内容。
(4)高效检索与复用
由于信息按语义结构化组织,检索时可通过实体、关系或属性快速定位相关内容,避免传统记忆中“模糊搜索”的低效问题。同时,结构化的知识便于跨场景复用,例如在问答系统中调用某实体的关系信息,在推荐系统中利用实体间的关联分析用户偏好。
2. 应用场景
(1)智能问答:如Siri、Alexa等语音助手,通过知识图谱存储常识和专业知识,快速理解用户问题并给出准确答案。
(2)个性化推荐:电商平台利用用户、商品、品类间的关系图谱,分析用户需求,推荐相关商品。
(3)医疗诊断:整合疾病、症状、药物等实体的知识图谱,辅助医生根据患者症状推理可能的病因。
总之,基于知识图谱的持久记忆突破了传统记忆的碎片化和低关联性局限,让智能系统能更接近人类的“结构化记忆与推理”模式,是人工智能实现更高级认知能力的重要基础。
5.7.2 实现MCP服务器
文件srcmemoryindex.ts实现了一个基于MCP协议的知识图谱内存服务器,通过实体、关系和观察构建图结构来管理持久化记忆。它定义了实体(含名称、类型、观察信息)和关系(含起始实体、目标实体、关系类型)的数据结构,提供创建/删除实体、创建/删除关系、添加/ 删除观察信息、读取图谱、搜索节点、获取特定节点等核心操作,并将数据持久化存储在 JSON 文件中(支持通过环境变量配置存储路径)。同时,服务器通过 MCP 协议暴露工具接口,允许客户端调用上述操作与知识图谱交互,实现结构化记忆的管理与查询。
(1)下面代码的功能是定义知识图谱的数据结构及核心管理类的基础方法。原理是通过Entity和Relation接口定义知识图谱的基本组成单元(实体包含名称、类型和观察信息;关系包含起始实体、目标实体和关系类型),并在KnowledgeGraphManager类中实现图谱的加载(从JSON文件读取并解析为实体和关系)和保存(将实体和关系序列化为JSON行写入文件),为后续的图谱操作提供数据基础。
// 定义知识图谱的实体结构
interface Entity {
name: string; // 实体名称
entityType: string; // 实体类型
observations: string[]; // 与实体相关的观察信息列表
}
// 定义知识图谱的关系结构
interface Relation {
from: string; // 关系起始实体名称
to: string; // 关系目标实体名称
relationType: string; // 关系类型
}
// 定义知识图谱整体结构
interface KnowledgeGraph {
entities: Entity[]; // 实体列表
relations: Relation[]; // 关系列表
}
// KnowledgeGraphManager类包含所有与知识图谱交互的操作
class KnowledgeGraphManager {
// 从文件加载知识图谱数据
private async loadGraph(): Promise {
try {
// 读取存储图谱数据的文件内容
const data = await fs.readFile(MEMORY_FILE_PATH, "utf-8");
// 按行分割内容并过滤空行
const lines = data.split("
").filter(line => line.trim() !== "");
// 解析每行数据并构建知识图谱
return lines.reduce((graph: KnowledgeGraph, line) => {
const item = JSON.parse(line);
// 区分实体和关系并添加到对应列表
if (item.type === "entity") graph.entities.push(item as Entity);
if (item.type === "relation") graph.relations.push(item as Relation);
return graph;
}, { entities: [], relations: [] }); // 初始化为空图谱
} catch (error) {
// 如果文件不存在,返回空图谱
if (error instanceof Error && 'code' in error && (error as any).code === "ENOENT") {
return { entities: [], relations: [] };
}
// 抛出其他错误
throw error;
}
}
// 将知识图谱数据保存到文件
private async saveGraph(graph: KnowledgeGraph): Promise {
// 将实体和关系转换为带类型标记的JSON字符串
const lines = [
...graph.entities.map(e => JSON.stringify({ type: "entity", ...e })),
...graph.relations.map(r => JSON.stringify({ type: "relation", ...r })),
];
// 写入文件(每行一个JSON对象)
await fs.writeFile(MEMORY_FILE_PATH, lines.join("
"));
}
}
(2)下面代码的功能是实现知识图谱中实体和关系的创建功能。原理是通过createEntities方法过滤掉已存在的实体(根据名称去重),将新实体添加到图谱并保存;createRelations方法过滤掉已存在的关系(根据起始实体、目标实体和关系类型去重),将新关系添加到图谱并保存,确保图谱数据的唯一性。
// 创建多个新实体
async createEntities(entities: Entity[]): Promise {
// 加载当前图谱
const graph = await this.loadGraph();
// 过滤掉已存在的实体(根据名称判断)
const newEntities = entities.filter(e =>
!graph.entities.some(existingEntity => existingEntity.name === e.name)
);
// 添加新实体到图谱
graph.entities.push(...newEntities);
// 保存更新后的图谱
await this.saveGraph(graph);
// 返回新增的实体
return newEntities;
}
// 创建多个新关系
async createRelations(relations: Relation[]): Promise {
// 加载当前图谱
const graph = await this.loadGraph();
// 过滤掉已存在的关系(根据起始实体、目标实体和关系类型判断)
const newRelations = relations.filter(r =>
!graph.relations.some(existingRelation =>
existingRelation.from === r.from &&
existingRelation.to === r.to &&
existingRelation.relationType === r.relationType
)
);
// 添加新关系到图谱
graph.relations.push(...newRelations);
// 保存更新后的图谱
await this.saveGraph(graph);
// 返回新增的关系
return newRelations;
}
(3)下面代码的功能是实现知识图谱中实体、观察信息和关系的删除功能。原理是通过deleteEntities方法删除指定实体及关联关系;deleteObservations方法删除实体的特定观察信息;deleteRelations方法删除指定关系,确保删除操作后图谱数据的一致性和完整性。
// 删除多个实体及其关联的关系
async deleteEntities(entityNames: string[]): Promise {
// 加载当前图谱
const graph = await this.loadGraph();
// 过滤掉要删除的实体
graph.entities = graph.entities.filter(e => !entityNames.includes(e.name));
// 过滤掉与删除实体相关的关系(无论是起始还是目标实体)
graph.relations = graph.relations.filter(r =>
!entityNames.includes(r.from) && !entityNames.includes(r.to)
);
// 保存更新后的图谱
await this.saveGraph(graph);
}
// 从实体中删除特定观察信息
async deleteObservations(deletions: { entityName: string; observations: string[] }[]): Promise {
// 加载当前图谱
const graph = await this.loadGraph();
// 遍历每个删除请求
deletions.forEach(d => {
// 找到对应的实体
const entity = graph.entities.find(e => e.name === d.entityName);
if (entity) {
// 过滤掉要删除的观察信息
entity.observations = entity.observations.filter(o => !d.observations.includes(o));
}
});
// 保存更新后的图谱
await this.saveGraph(graph);
}
// 删除多个关系
async deleteRelations(relations: Relation[]): Promise {
// 加载当前图谱
const graph = await this.loadGraph();
// 过滤掉要删除的关系
graph.relations = graph.relations.filter(r =>
!relations.some(delRelation =>
r.from === delRelation.from &&
r.to === delRelation.to &&
r.relationType === delRelation.relationType
)
);
// 保存更新后的图谱
await this.saveGraph(graph);
}
(4)下面代码的功能是实现知识图谱的查询功能,包括读取完整图谱、搜索节点和获取特定节点。原理是readGraph返回整个图谱数据;searchNodes根据查询词匹配实体名称、类型或观察信息,返回相关实体及关联关系;openNodes根据实体名称筛选出指定实体及关联关系,满足不同场景的查询需求。
// 读取整个知识图谱
async readGraph(): Promise {
return this.loadGraph();
}
// 基于查询词搜索知识图谱中的节点
async searchNodes(query: string): Promise {
// 加载当前图谱
const graph = await this.loadGraph();
// 过滤包含查询词的实体(匹配名称、类型或观察信息)
const filteredEntities = graph.entities.filter(e =>
e.name.toLowerCase().includes(query.toLowerCase()) ||
e.entityType.toLowerCase().includes(query.toLowerCase()) ||
e.observations.some(o => o.toLowerCase().includes(query.toLowerCase()))
);
// 提取筛选出的实体名称,用于过滤关系
const filteredEntityNames = new Set(filteredEntities.map(e => e.name));
// 过滤出仅涉及筛选后实体的关系
const filteredRelations = graph.relations.filter(r =>
filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to)
);
// 返回筛选后的图谱
return {
entities: filteredEntities,
relations: filteredRelations,
};
}
// 根据名称获取知识图谱中的特定节点
async openNodes(names: string[]): Promise {
// 加载当前图谱
const graph = await this.loadGraph();
// 筛选出名称在指定列表中的实体
const filteredEntities = graph.entities.filter(e => names.includes(e.name));
// 提取筛选出的实体名称,用于过滤关系
const filteredEntityNames = new Set(filteredEntities.map(e => e.name));
// 过滤出仅涉及筛选后实体的关系
const filteredRelations = graph.relations.filter(r =>
filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to)
);
// 返回筛选后的图谱
return {
entities: filteredEntities,
relations: filteredRelations,
};
}








