效果

使用gpt-3.5-turbo的效果

什么是向量数据库?

向量数据库是一种专为处理高维向量数据而设计的数据库系统。与传统的关系型数据库不同,向量数据库专注于存储和查询向量数据,例如图像、音频、文本等。其核心思想是将数据表示为向量形式,并通过向量之间的相似度来进行查询和分析。

向量数据库的特点包括高效的相似度搜索和复杂的查询操作。通过利用向量之间的相似度,可以快速检索与查询向量相似的数据,适用于大规模数据集和高维度数据。此外,向量数据库还支持复杂的查询操作,如范围查询、聚类和聚合等。

在实际应用中,向量数据库被广泛用于图像搜索、推荐系统、自然语言处理等领域。例如,在图像搜索中,可以利用向量数据库来存储图像的特征向量,并通过计算相似度来实现快速的图像检索。在推荐系统中,可以利用用户的特征向量来实现个性化推荐。

总之,向量数据库是一种适用于处理高维向量数据的数据库系统,具有高效的相似度搜索和复杂的查询操作,广泛应用于图像搜索、推荐系统等领域。

什么是嵌入?

嵌入是一种在高维空间中将单词或其他数据表示为向量的方法。向量就像有方向和长度的箭头。高维意味着空间有很多维度,比我们能看到或想象的要多。这个想法是,相似的单词或数据将具有相似的向量,而不同的单词或数据将具有不同的向量。这有助于我们衡量它们的相关或不相关程度,并对它们进行操作,例如加、减、乘等。嵌入对 AI 模型很有用,因为它们可以以计算机可以理解和处理的方式捕获单词或数据的含义和上下文。

所以基本上你拿一个句子、段落或整页文本,然后生成相应的嵌入向量。当执行查询时,查询将转换为其嵌入表示形式,然后通过所有现有的嵌入向量执行搜索以找到最相似的嵌入向量。这类似于在必应上进行搜索查询时,它会为您提供与查询近似的多个结果。语义记忆不太可能给你一个完全匹配,但它总是会给你一组匹配,根据你的查询与其他文本的相似程度进行排名。

Chroma向量数据库

在本示例中,我使用的是Chroma向量数据库。

在docker中运行chroma:

docker pull chromadb/chroma
docker run -p 8000:8000 chromadb/chroma

成功启动如下所示:

创建ISemanticTextMemory

因为向量数据库使用的是Chroma,因此需要下载这个包:

#pragma warning disable SKEXP0003
ISemanticTextMemory? memory;
#pragma warning disable SKEXP0003
var memoryBuilder = new MemoryBuilder();
#pragma warning disable SKEXP0011
memoryBuilder.WithOpenAITextEmbeddingGeneration("text-embedding-ada-002", envVars["OpenAIAPIKey"]); #pragma warning disable SKEXP0022
var chromaMemoryStore = new ChromaMemoryStore("http://127.0.0.1:8000"); memoryBuilder.WithMemoryStore(chromaMemoryStore); memory = memoryBuilder.Build();

ISemanticTextMemory

语义记忆的接口,用于创建和调用与文本相关的记忆。

WithOpenAITextEmbeddingGeneration

添加 OpenAI 文本嵌入服务。

ChromaMemoryStore

用于 Chroma 的 IMemoryStore 的实现。

本地需要开启Chroma服务。

保存自有数据

 // 创建 OpenFileDialog 对象
OpenFileDialog openFileDialog = new OpenFileDialog(); // 设置文件类型过滤器
openFileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*"; // 显示文件选择对话框
if (openFileDialog.ShowDialog() == true)
{
// 用户选择了一个文件,你可以通过 openFileDialog.FileName 获取文件的路径
string filePath = openFileDialog.FileName;
HandyControl.Controls.MessageBox.Show($"你选中的路径为{filePath}"); var text = File.ReadAllText(filePath); const string MemoryCollectionName = "hello2"; var id = Guid.NewGuid().ToString(); await memory.SaveInformationAsync(MemoryCollectionName, id: id, text: text); HandyControl.Controls.MessageBox.Show($"完成");

这是以选择一个txt文件为例,核心就一行代码:

 await memory.SaveInformationAsync(MemoryCollectionName, id: id, text: text);

将一些信息保存到Semantic Memory中。

查看该方法的定义:

 Task<string> SaveInformationAsync(string collection, string text, string id, string? description = null, string? additionalMetadata = null, Kernel? kernel = null, CancellationToken cancellationToken = default(CancellationToken));

参数及含义:

参数名 类型 含义
collection string 保存数据的集合名
text string 要保存的数据
id string 唯一标识符
description string? 描述
additionalMetadata string? 额外的元数据
kernel Kernel? 包含服务、插件和其他状态的内核,供整个操作使用
cancellationToken CancellationToken 用于监视取消请求的 CancellationToken。默认值为 None

前面三项是必填的,其他项都有默认值。

 const string MemoryCollectionName = "hello2";

经过我的测试,集合名要是英文,中文会报错。

创建TextMemoryPlugin

代码如下所示:

/// <summary>
/// TextMemoryPlugin provides a plugin to save or recall information from the long or short term memory.
/// </summary>
[Experimental("SKEXP0003")]
public sealed class TextMemoryPlugin
{
/// <summary>
/// Name used to specify the input text.
/// </summary>
public const string InputParam = "input";
/// <summary>
/// Name used to specify which memory collection to use.
/// </summary>
public const string CollectionParam = "collection"; /// <summary>
/// Name used to specify memory search relevance score.
/// </summary>
public const string RelevanceParam = "relevance"; /// <summary>
/// Name used to specify a unique key associated with stored information.
/// </summary>
public const string KeyParam = "key"; /// <summary>
/// Name used to specify the number of memories to recall
/// </summary>
public const string LimitParam = "limit"; private const string DefaultCollection = "generic";
private const double DefaultRelevance = 0.0;
private const int DefaultLimit = 1; private readonly ISemanticTextMemory _memory;
private readonly ILogger _logger; /// <summary>
/// Creates a new instance of the TextMemoryPlugin
/// </summary>
public TextMemoryPlugin(
ISemanticTextMemory memory,
ILoggerFactory? loggerFactory = null)
{
this._memory = memory;
this._logger = loggerFactory?.CreateLogger(typeof(TextMemoryPlugin)) ?? NullLogger.Instance;
} /// <summary>
/// Key-based lookup for a specific memory
/// </summary>
/// <param name="key">The key associated with the memory to retrieve.</param>
/// <param name="collection">Memories collection associated with the memory to retrieve</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
[KernelFunction, Description("Key-based lookup for a specific memory")]
public async Task<string> RetrieveAsync(
[Description("The key associated with the memory to retrieve")] string key,
[Description("Memories collection associated with the memory to retrieve")] string? collection = DefaultCollection,
CancellationToken cancellationToken = default)
{ if (this._logger.IsEnabled(LogLevel.Debug))
{
this._logger.LogDebug("Recalling memory with key '{0}' from collection '{1}'", key, collection);
} var memory = await this._memory.GetAsync(collection, key, cancellationToken: cancellationToken).ConfigureAwait(false); return memory?.Metadata.Text ?? string.Empty;
} /// <summary>
/// Semantic search and return up to N memories related to the input text
/// </summary>
/// <param name="input">The input text to find related memories for.</param>
/// <param name="collection">Memories collection to search.</param>
/// <param name="relevance">The relevance score, from 0.0 to 1.0, where 1.0 means perfect match.</param>
/// <param name="limit">The maximum number of relevant memories to recall.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
[KernelFunction, Description("Semantic search and return up to N memories related to the input text")]
public async Task<string> RecallAsync(
[Description("The input text to find related memories for")] string input,
[Description("Memories collection to search")] string collection = DefaultCollection,
[Description("The relevance score, from 0.0 to 1.0, where 1.0 means perfect match")] double? relevance = DefaultRelevance,
[Description("The maximum number of relevant memories to recall")] int? limit = DefaultLimit,
CancellationToken cancellationToken = default)
{ relevance ??= DefaultRelevance;
limit ??= DefaultLimit; if (this._logger.IsEnabled(LogLevel.Debug))
{
this._logger.LogDebug("Searching memories in collection '{0}', relevance '{1}'", collection, relevance);
} // Search memory
List<MemoryQueryResult> memories = await this._memory
.SearchAsync(collection, input, limit.Value, relevance.Value, cancellationToken: cancellationToken)
.ToListAsync(cancellationToken)
.ConfigureAwait(false); if (memories.Count == 0)
{
if (this._logger.IsEnabled(LogLevel.Warning))
{
this._logger.LogWarning("Memories not found in collection: {0}", collection);
}
return string.Empty;
} return limit == 1 ? memories[0].Metadata.Text : JsonSerializer.Serialize(memories.Select(x => x.Metadata.Text));
} /// <summary>
/// Save information to semantic memory
/// </summary>
/// <param name="input">The information to save</param>
/// <param name="key">The key associated with the information to save</param>
/// <param name="collection">Memories collection associated with the information to save</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
[KernelFunction, Description("Save information to semantic memory")]
public async Task SaveAsync(
[Description("The information to save")] string input,
[Description("The key associated with the information to save")] string key,
[Description("Memories collection associated with the information to save")] string collection = DefaultCollection,
CancellationToken cancellationToken = default)
{ if (this._logger.IsEnabled(LogLevel.Debug))
{
this._logger.LogDebug("Saving memory to collection '{0}'", collection);
} await this._memory.SaveInformationAsync(collection, text: input, id: key, cancellationToken: cancellationToken).ConfigureAwait(false);
} /// <summary>
/// Remove specific memory
/// </summary>
/// <param name="key">The key associated with the information to save</param>
/// <param name="collection">Memories collection associated with the information to save</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
[KernelFunction, Description("Remove specific memory")]
public async Task RemoveAsync(
[Description("The key associated with the information to save")] string key,
[Description("Memories collection associated with the information to save")] string collection = DefaultCollection,
CancellationToken cancellationToken = default)
{ if (this._logger.IsEnabled(LogLevel.Debug))
{
this._logger.LogDebug("Removing memory from collection '{0}'", collection);
} await this._memory.RemoveAsync(collection, key, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}

比较长,可以以后用到了什么函数再慢慢看,等一会我们就要接触到的函数如下:

/// <summary>
/// Semantic search and return up to N memories related to the input text
/// </summary>
/// <param name="input">The input text to find related memories for.</param>
/// <param name="collection">Memories collection to search.</param>
/// <param name="relevance">The relevance score, from 0.0 to 1.0, where 1.0 means perfect match.</param>
/// <param name="limit">The maximum number of relevant memories to recall.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
[KernelFunction, Description("Semantic search and return up to N memories related to the input text")]
public async Task<string> RecallAsync(
[Description("The input text to find related memories for")] string input,
[Description("Memories collection to search")] string collection = DefaultCollection,
[Description("The relevance score, from 0.0 to 1.0, where 1.0 means perfect match")] double? relevance = DefaultRelevance,
[Description("The maximum number of relevant memories to recall")] int? limit = DefaultLimit,
CancellationToken cancellationToken = default)
{ relevance ??= DefaultRelevance;
limit ??= DefaultLimit; if (this._logger.IsEnabled(LogLevel.Debug))
{
this._logger.LogDebug("Searching memories in collection '{0}', relevance '{1}'", collection, relevance);
} // Search memory
List<MemoryQueryResult> memories = await this._memory
.SearchAsync(collection, input, limit.Value, relevance.Value, cancellationToken: cancellationToken)
.ToListAsync(cancellationToken)
.ConfigureAwait(false); if (memories.Count == 0)
{
if (this._logger.IsEnabled(LogLevel.Warning))
{
this._logger.LogWarning("Memories not found in collection: {0}", collection);
}
return string.Empty;
} return limit == 1 ? memories[0].Metadata.Text : JsonSerializer.Serialize(memories.Select(x => x.Metadata.Text));
}

一步一步来看:

[KernelFunction, Description("Semantic search and return up to N memories related to the input text")]

KernelFunction是一个特性,可能是用于标记这个方法作为某种内核函数的一部分。具体的含义取决于这个特性是如何在代码中被使用的。

Description 是.NET框架中的一个标准特性,它提供了一个人类可读的描述,这个描述可以在运行时通过反射来访问。在这个例子中,它提供了对 RecallAsync 方法的简单描述:"Semantic search and return up to N memories related to the input text"。

public async Task<string> RecallAsync(
[Description("The input text to find related memories for")] string input,
[Description("Memories collection to search")] string collection = DefaultCollection,
[Description("The relevance score, from 0.0 to 1.0, where 1.0 means perfect match")] double? relevance = DefaultRelevance,
[Description("The maximum number of relevant memories to recall")] int? limit = DefaultLimit,
CancellationToken cancellationToken = default)

RecallAsync方法有input、collection、relevance、limit、cancellationToken参数,它们的含义如下:

参数名 含义
input 用于查看向量数据库中是否有相关数据的文本
collection 向量数据库中的集合名
relevance 相关性,0最低,1最高
limit 相关数据的最大返回数量
cancellationToken .NET中用于协调取消长时间运行的操作的结构

在向量数据库中查找相关数据:

 // Search memory
List<MemoryQueryResult> memories = await this._memory
.SearchAsync(collection, input, limit.Value, relevance.Value, cancellationToken: cancellationToken)
.ToListAsync(cancellationToken)
.ConfigureAwait(false);

向kernel导入插件:

// TextMemoryPlugin provides the "recall" function
kernel.ImportPluginFromObject(new TextMemoryPlugin(memory));

开始测试

用于测试的本地数据:

这些数据大语言模型肯定不会知道的。

导入文件之后,开始测试:

            const string skPrompt = @"
ChatBot can have a conversation with you about any topic.
It can give explicit instructions or say 'I don't know' if it does not have an answer. Information about me, from previous conversations:
- {{$fact1}} {{recall $fact1}}
- {{$fact2}} {{recall $fact2}} Chat:
{{$history}}
User: {{$userInput}}
ChatBot: "; var chatFunction = kernel.CreateFunctionFromPrompt(skPrompt, new OpenAIPromptExecutionSettings { MaxTokens = 200, Temperature = 0.8 }); #pragma warning disable SKEXP0052 var arguments = new KernelArguments(); arguments["fact1"] = "我的名字是什么?";
arguments["fact2"] = "我喜欢什么编程语言?"; arguments[TextMemoryPlugin.CollectionParam] = "hello2";
arguments[TextMemoryPlugin.LimitParam] = "2";
arguments[TextMemoryPlugin.RelevanceParam] = "0.8"; arguments["userInput"] = "我的名字叫什么?"; // Process the user message and get an answer
var answer = await chatFunction.InvokeAsync(kernel, arguments); Debug.WriteLine(answer);

在查找“我的名字是什么?”时,并没有查找到相关内容:

在查看“我喜欢什么编程语言?”时,找到了相关内容:

查看查找到的相关内容:

大语言模型可以根据获取到的数据进行回答:

MemoryChat

经过测试,我们发现是可行的,就可以写成问答的形式:

            loadingMemory2.Visibility = Visibility.Visible;

            string question = textBoxMemory1.Text;

            // Get user input
history.AddUserMessage(question); const string skPrompt = @"
ChatBot can have a conversation with you about any topic.
It can give explicit instructions or say 'I don't know' if it does not have an answer. Information about me, from previous conversations:
- {{$fact1}} {{recall $fact1}} Chat:
{{$history}}
User: {{$userInput}}
ChatBot: "; var chatFunction = kernel.CreateFunctionFromPrompt(skPrompt, new OpenAIPromptExecutionSettings { MaxTokens = 200, Temperature = 0.8 }); #pragma warning disable SKEXP0052 var arguments = new KernelArguments(); arguments["fact1"] = question; arguments[TextMemoryPlugin.CollectionParam] = "hello2";
arguments[TextMemoryPlugin.LimitParam] = "2";
arguments[TextMemoryPlugin.RelevanceParam] = "0.6"; arguments["userInput"] = question; // Process the user message and get an answer
var answer = await chatFunction.InvokeAsync(kernel, arguments); Debug.WriteLine(answer); // Print the results
richTextBoxMemory.AppendText(answer + "\r\n"); // Add the message from the agent to the chat history
history.AddMessage(Microsoft.SemanticKernel.ChatCompletion.AuthorRole.System, answer.ToString()); loadingMemory2.Visibility = Visibility.Hidden;

使用gpt-3.5-turbo的效果

使用gemini-pro的效果

只对了一个最喜欢的编程语言问题。

使用讯飞星火认知大模型的效果

一个都没答对。

参考

1、Understanding AI plugins in Semantic Kernel and beyond | Microsoft Learn

2、Memories in Semantic Kernel | Microsoft Learn

3、LLM AI Embeddings | Microsoft Learn

4、Vector Database | Microsoft Learn

5、semantic-kernel/dotnet/notebooks/09-memory-with-chroma.ipynb at main · microsoft/semantic-kernel (github.com)

SemanticKernel如何基于自有数据聊天的更多相关文章

  1. Android 基于Socket的聊天应用(二)

    很久没写BLOG了,之前在写Android聊天室的时候答应过要写一个客户(好友)之间的聊天demo,Android 基于Socket的聊天室已经实现了通过Socket广播形式的通信功能. 以下是我写的 ...

  2. openfire:基于开源 Openfire 聊天服务器 - 开发Openfire聊天记录插件

    基于开源 Openfire 聊天服务器 - 开发Openfire聊天记录插件 上一篇文章介绍到怎么在自己的Java环境中搭建openfire插件开发的环境,同时介绍到怎样一步步简单的开发openfir ...

  3. 基于开源 Openfire 聊天服务器 - 开发Openfire聊天记录插件

    原文:http://www.cnblogs.com/hoojo/archive/2013/03/29/openfire_plugin_chatlogs_plugin_.html 随笔-150  评论- ...

  4. 基于WebSocket实现聊天室(Node)

    基于WebSocket实现聊天室(Node) WebSocket是基于TCP的长连接通信协议,服务端可以主动向前端传递数据,相比比AJAX轮询服务器,WebSocket采用监听的方式,减轻了服务器压力 ...

  5. IP报文解析及基于IP 数据包的洪水攻击

    版本(4bit) 报头长度(4bit) 优先级和服务类型(8bit) 总长度(16bit) 标识(16bit) 标志(3bit) 分段偏移(13bit) 存活期(8bit) 协议(8bit) 报头校验 ...

  6. H2O是开源基于大数据的机器学习库包

    H2O是开源基于大数据的机器学习库包 H2O能够让Hadoop做数学,H2O是基于大数据的 统计分析 机器学习和数学库包,让用户基于核心的数学积木搭建应用块代码,采取类似R语言 Excel或JSON等 ...

  7. 基于GPS数据建立隐式马尔可夫模型预测目的地

    <Trip destination prediction based on multi-day GPS data>是一篇在2019年,由吉林交通大学团队发表在elsevier期刊上的一篇论 ...

  8. Ext JS学习第十六天 事件机制event(一) DotNet进阶系列(持续更新) 第一节:.Net版基于WebSocket的聊天室样例 第十五节:深入理解async和await的作用及各种适用场景和用法 第十五节:深入理解async和await的作用及各种适用场景和用法 前端自动化准备和详细配置(NVM、NPM/CNPM、NodeJs、NRM、WebPack、Gulp/Grunt、G

    code&monkey   Ext JS学习第十六天 事件机制event(一) 此文用来记录学习笔记: 休息了好几天,从今天开始继续保持更新,鞭策自己学习 今天我们来说一说什么是事件,对于事件 ...

  9. 基于MNIST数据的卷积神经网络CNN

    基于tensorflow使用CNN识别MNIST 参数数量:第一个卷积层5x5x1x32=800个参数,第二个卷积层5x5x32x64=51200个参数,第三个全连接层7x7x64x1024=3211 ...

  10. 基于ifc数据的bim模型吊装模拟-1

    基于ifc数据的bim模型吊装模拟-1 IfcWallStandardCase    IfcColumn

随机推荐

  1. 大规模语言LLaVA:多模态GPT-4智能助手,融合语言与视觉,满足用户复杂需求

    大规模语言LLaVA:多模态GPT-4智能助手,融合语言与视觉,满足用户复杂需求 一个面向多模式GPT-4级别能力构建的助手.它结合了自然语言处理和计算机视觉,为用户提供了强大的多模式交互和理解.LL ...

  2. 从嘉手札<2023-10-30 >

    杂诗 壬戌辛酉日夜,闲看日月,秋风萧瑟,感怀予身期年孑然,岁月难留,故有所感,藉以此诗. 闲来无事,细数春秋. 初月难盈,残烛易收. 未若知人意,夜夜息绝游. 红叶醉天水,星河绕满楼. 竹影戚戚乱,岁 ...

  3. 重写JSON.stringify与JSON.parse使其支持解析function类型

    const JSONStringify = (option) => { return JSON.stringify(option, (key, val) => { // 处理函数丢失问题 ...

  4. Spring一套全通5—SSM整合

    百知教育 - Spring系列课程 - MVC框架整合 第一章.MVC框架整合思想 1. 搭建Web运行环境 <dependency> <groupId>javax.servl ...

  5. KMP 学习笔记

    前言-- \(char\) 与 \(string\) 有的时候 \(char\) 数组确实比 \(string\) 好用,且字符串长度很大时 \(string\) 会被卡掉,所以不要犯懒,老实用 \( ...

  6. Springboot创建项目、docker安装mysql及mybatis-plus调试

    Springboot创建项目及测试 1️⃣ idea构建springboot项目 2️⃣ 测试类 hello 1. 代码 RestController是ResponseBody + controlle ...

  7. PHP常用类

    PHP常用类 一.分页类 <?php /** * 分页类 * 调用方式: * $p=new Page(总条数,显示页码链接数量,当前页码,每页显示条数,[链接]); * print_r($p-& ...

  8. 云计算 - 对象存储服务OSS技术全解

    本文全面深入地探讨了对象存储服务(OSS)的核心技术.基础知识和高级功能.从媒体存储到数据备份,再到数据仓库与数据湖,我们不仅解析了OSS在各种应用场景下的关键角色,还深入讨论了其与机器学习.多媒体处 ...

  9. sshd命令-测试sshd_config配置是否正确

    sshd命令来自于英文词组"SSH daemon"的缩写,其功能是用于openssh服务器守护进程.openssh套件能够为两台主机之间建立加密的.可信任的数据通信服务,是rlog ...

  10. ASP.NET Core 配置 - 创建自定义配置提供程序

    ASP.NET Core 配置 - 创建自定义配置提供程序 在本文中,我们将创建一个自定义配置提供程序,从数据库读取我们的配置.我们已经了解了默认配置提供程序的工作方式,现在我们将实现我们自己的自定义 ...