首页 > 其他分享 >SemanticKernel如何基于自有数据聊天

SemanticKernel如何基于自有数据聊天

时间:2024-03-05 16:57:15浏览次数:38  
标签:cancellationToken string memories collection 自有 memory Description SemanticKerne

效果

使用gpt-3.5-turbo的效果

效果

什么是向量数据库?

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

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

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

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

什么是嵌入?

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

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

Chroma向量数据库

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

image-20240305103835056

在docker中运行chroma:

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

成功启动如下所示:

创建ISemanticTextMemory

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

image-20240305142522438

#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));

开始测试

用于测试的本地数据:

image-20240305150743859

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

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

            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);

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

image-20240305152156869

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

查看查找到的相关内容:

image-20240305152641678

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

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)

标签:cancellationToken,string,memories,collection,自有,memory,Description,SemanticKerne
From: https://www.cnblogs.com/mingupupu/p/18054386

相关文章

  • 智能升级:AntSK教你如何让聊天机器人实现智能联网操作
        随着人工智能技术的飞速发展,智能体已经逐步融入到我们的日常生活中。不过,要想让智能体不仅能聊天,还能接入网络实时获取信息,为我们提供更多便利,所需技术的复杂性不得不让人瞩目。今天,我将和各位分享如何在基于.NET开发的AI知识库/智能体项目AntSK中,利用SemanticKernel......
  • 需要在本地实现一个聊天室,多个tab页相互通信,不能用websocket,你会怎么做?
    可以考虑使用以下方法:使用LocalStorage:这个存储API可在浏览器的不同标签页之间共享数据。当一个标签页发送消息时,将消息存储在LocalStorage中。其他标签页可以监听该存储区的变化,并读取最新的消息内容来实现通信效果。//监听变化2window.addEventListener("storage",(e)=>......
  • 记一次WPF集成SemanticKernel+OneAPI+讯飞星火认知大模型实践
    开启OneAPI服务OneAPI介绍OpenAI接口管理&分发系统,支持Azure、AnthropicClaude、GooglePaLM2&Gemini、智谱ChatGLM、百度文心一言、讯飞星火认知、阿里通义千问、360智脑以及腾讯混元,可用于二次分发管理key,仅单可执行文件,已打包好Docker镜像,一键部署,开箱即用.Ope......
  • 如何杜绝员工上班社交聊天、购物、炒股、看电影等行为
    在当今的办公环境中,员工在工作时间内进行社交聊天、购物、炒股、看电影等非工作相关的行为已经成为一种普遍的现象。这些行为不仅影响了员工的工作效率,而且增加了组织数据泄露的风险。那么,如何杜绝这类行为呢?首要的步骤是制订明确的公司政策。必须清楚地向员工传达哪些行为在工......
  • 提升员工数据安全意识:抵制工作时间社交聊天、购物炒股等行为
    每个企业都有保护其数据资产的重要责任。然而,员工行为习惯及其对数据安全的认识,将直接影响到企业信息安全的状况。在许多情境下,员工本身并无任何恶意,只是因为缺乏必要的安全知识,或者在工作时间分心至社交聊天、炒股等非工作相关活动,可能无意间就导致了数据泄露。因此,及时提升员工......
  • 基于Microsoft SemanticKernel和GPT4实现一个智能翻译服务
    今年.NETConfChina2023技术大会,我给大家分享了.NET应用国际化-AIGC智能翻译+代码生成的议题.NETConfChina2023分享-.NET应用国际化-AIGC智能翻译+代码生成今天将详细的代码实现和大家分享一下。一、前提准备1.新建一个Console类的Project2.引用SK的Nuget包,SK的最新N......
  • 离线AI聊天清华大模型(ChatGLM3)本地搭建指南
    随着人工智能技术的飞速发展,聊天机器人成为了一个热门的研究领域。清华大学研发的ChatGLM3模型,作为其中的佼佼者,为开发者提供了强大的自然语言处理能力。本文将指导您如何在本地搭建ChatGLM3模型,实现离线AI聊天功能。一、前置准备在开始搭建之前,您需要准备以下物品:一台性能良......
  • Netty入门实践-模拟IM聊天
    我们使用的框架几乎都有网络通信的模块,比如常见的Dubbo、RocketMQ、ElasticSearch等。它们的网络通信模块使用Netty实现,之所以选择Netty,有2个主要原因:Netty封装了复杂的JDK的NIO操作,还封装了各种复杂的异常场景,丰富的API使得在使用上也非常方便,几行代码就可以实现高性能的网络......
  • Asp-Net-Core学习笔记:3.使用SignalR实时通信框架开发聊天室
    SignalR牛刀小试在MVP杨老师的博客里看到这么个东西,我还以为是NetCore3才推出的新玩意,原来是已经有很多年的历史了,那看来还是比较成熟的一个技术了。简介SignalR是一个.NETCore/.NETFramework的开源实时框架,SignalR的可使用WebSocket,ServerSentEvents和LongPolling......
  • flutter3+dart3聊天室|Flutter3跨平台仿微信App语音聊天/朋友圈
    全新研发flutter3+dart3+photo_view跨多端仿微信App界面聊天Flutter3-Chat。flutter3-chat基于最新跨全平台技术flutter3+dart3+material-design+shared_preferences+easy_refresh构建的仿微信APP界面聊天实例项目。实现发送图文表情消息/gif大图、长按仿微信语音操作面板、图片......