Semantic Kernel 入门系列:🥑Memory内存
了解的运作原理之后,就可以开始使用Semantic Kernel来制作应用了。
Semantic Kernel将embedding的功能封装到了Memory中,用来存储上下文信息,就好像电脑的内存一样,而LLM就像是CPU一样,我们所需要做的就是从内存中取出相关的信息交给CPU处理就好了。
内存配置
使用Memory需要注册 embedding
模型,目前使用的就是 text-embedding-ada-002
。同时需要为Kernel添加MemoryStore,用于存储更多的信息,这里Semantic Kernel提供了一个 VolatileMemoryStore
,就是一个普通的内存存储的MemoryStore。
var kernel = Kernel.Builder.Configure(c =>
{
c.AddOpenAITextCompletionService("openai", "text-davinci-003", Environment.GetEnvironmentVariable("MY_OPEN_AI_API_KEY"));
c.AddOpenAIEmbeddingGenerationService("openai", "text-embedding-ada-002", Environment.GetEnvironmentVariable("MY_OPEN_AI_API_KEY"));
})
.WithMemoryStorage(new VolatileMemoryStore())
.Build();
信息存储
完成了基础信息的注册后,就可以往Memroy中存储信息了。
const string MemoryCollectionName = "aboutMe";
await kernel.Memory.SaveInformationAsync(MemoryCollectionName, id: "info1", text: "My name is Andrea");
await kernel.Memory.SaveInformationAsync(MemoryCollectionName, id: "info2", text: "I currently work as a tourist operator");
await kernel.Memory.SaveInformationAsync(MemoryCollectionName, id: "info3", text: "I currently live in Seattle and have been living there since 2005");
await kernel.Memory.SaveInformationAsync(MemoryCollectionName, id: "info4", text: "I visited France and Italy five times since 2015");
await kernel.Memory.SaveInformationAsync(MemoryCollectionName, id: "info5", text: "My family is from New York");
SaveInformationAsync
会将text的内容通过 embedding
模型转化为对应的文本向量,存放在的MemoryStore中。其中CollectionName如同数据库的表名,Id就是Id。
语义搜索
完成信息的存储之后,就可以用来语义搜索了。
直接使用Memory.SearchAsync
方法,指定对应的Collection,同时提供相应的查询问题,查询问题也会被转化为embedding,再在MemoryStore中计算查找最相似的信息。
var questions = new[]
{
"what is my name?",
"where do I live?",
"where is my family from?",
"where have I travelled?",
"what do I do for work?",
};
foreach (var q in questions)
{
var response = await kernel.Memory.SearchAsync(MemoryCollectionName, q).FirstOrDefaultAsync();
Console.WriteLine(q + " " + response?.Metadata.Text);
}
// output
/*
what is my name? My name is Andrea
where do I live? I currently live in Seattle and have been living there since 2005
where is my family from? My family is from New York
where have I travelled? I visited France and Italy five times since 2015
what do I do for work? I currently work as a tourist operator
*/
到这个时候,即便不需要进行总结归纳,光是这样的语义查找,都会很有价值。
引用存储
除了添加信息以外,还可以添加引用,像是非常有用的参考链接之类的。
const string memoryCollectionName = "SKGitHub";
var githubFiles = new Dictionary<string, string>()
{
["https://github.com/microsoft/semantic-kernel/blob/main/README.md"]
= "README: Installation, getting started, and how to contribute",
["https://github.com/microsoft/semantic-kernel/blob/main/samples/notebooks/dotnet/2-running-prompts-from-file.ipynb"]
= "Jupyter notebook describing how to pass prompts from a file to a semantic skill or function",
["https://github.com/microsoft/semantic-kernel/blob/main/samples/notebooks/dotnet/Getting-Started-Notebook.ipynb"]
= "Jupyter notebook describing how to get started with the Semantic Kernel",
["https://github.com/microsoft/semantic-kernel/tree/main/samples/skills/ChatSkill/ChatGPT"]
= "Sample demonstrating how to create a chat skill interfacing with ChatGPT",
["https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/SemanticKernel/Memory/Volatile/VolatileMemoryStore.cs"]
= "C# class that defines a volatile embedding store",
["https://github.com/microsoft/semantic-kernel/tree/main/samples/dotnet/KernelHttpServer/README.md"]
= "README: How to set up a Semantic Kernel Service API using Azure Function Runtime v4",
["https://github.com/microsoft/semantic-kernel/tree/main/samples/apps/chat-summary-webapp-react/README.md"]
= "README: README associated with a sample starter react-based chat summary webapp",
};
foreach (var entry in githubFiles)
{
await kernel.Memory.SaveReferenceAsync(
collection: memoryCollectionName,
description: entry.Value,
text: entry.Value,
externalId: entry.Key,
externalSourceName: "GitHub"
);
}
同样的,使用SearchAsync搜索就行。
string ask = "I love Jupyter notebooks, how should I get started?";
Console.WriteLine("===========================\n" +
"Query: " + ask + "\n");
var memories = kernel.Memory.SearchAsync(memoryCollectionName, ask, limit: 5, minRelevanceScore: 0.77);
var i = 0;
await foreach (MemoryQueryResult memory in memories)
{
Console.WriteLine($"Result {++i}:");
Console.WriteLine(" URL: : " + memory.Metadata.Id);
Console.WriteLine(" Title : " + memory.Metadata.Description);
Console.WriteLine(" ExternalSource: " + memory.Metadata.ExternalSourceName);
Console.WriteLine(" Relevance: " + memory.Relevance);
Console.WriteLine();
}
//output
/*
===========================
Query: I love Jupyter notebooks, how should I get started?
Result 1:
URL: : https://github.com/microsoft/semantic-kernel/blob/main/samples/notebooks/dotnet/Getting-Started-Notebook.ipynb
Title : Jupyter notebook describing how to get started with the Semantic Kernel
ExternalSource: GitHub
Relevance: 0.8677381632778319
Result 2:
URL: : https://github.com/microsoft/semantic-kernel/blob/main/samples/notebooks/dotnet/2-running-prompts-from-file.ipynb
Title : Jupyter notebook describing how to pass prompts from a file to a semantic skill or function
ExternalSource: GitHub
Relevance: 0.8162989178955157
Result 3:
URL: : https://github.com/microsoft/semantic-kernel/blob/main/README.md
Title : README: Installation, getting started, and how to contribute
ExternalSource: GitHub
Relevance: 0.8083238591883483
*/
这里多使用了两个参数,一个是limit,用于限制返回信息的条数,只返回最相似的前几条数据,另外一个是minRelevanceScore,限制最小的相关度分数,这个取值范围在0.0 ~ 1.0 之间,1.0意味着完全匹配。
语义问答
将Memory的存储、搜索功能和语义技能相结合,就可以快速的打造一个实用的语义问答的应用了。
只需要将搜索到的相关信息内容填充到 prompt中,然后将内容和问题都抛给LLM,就可以等着得到一个满意的答案了。
const string MemoryCollectionName = "aboutMe";
await kernel.Memory.SaveInformationAsync(MemoryCollectionName, id: "info1", text: "My name is Andrea");
await kernel.Memory.SaveInformationAsync(MemoryCollectionName, id: "info2", text: "I currently work as a tourist operator");
await kernel.Memory.SaveInformationAsync(MemoryCollectionName, id: "info3", text: "I currently live in Seattle and have been living there since 2005");
await kernel.Memory.SaveInformationAsync(MemoryCollectionName, id: "info4", text: "I visited France and Italy five times since 2015");
await kernel.Memory.SaveInformationAsync(MemoryCollectionName, id: "info5", text: "My family is from New York");
var prompt =
"""
It can give explicit instructions or say 'I don't know' if it does not have an answer.
Information about me, from previous conversations:
{{ $fact }}
User: {{ $ask }}
ChatBot:
""";
var skill = kernel.CreateSemanticFunction(prompt);
var ask = "Hello, I think we've met before, remember? my name is...";
var fact = await kernel.Memory.SearchAsync(MemoryCollectionName,ask).FirstOrDefaultAsync();
var context = kernel.CreateNewContext();
context["fact"] = fact?.Metadata?.Text;
context["ask"] = ask;
var resultContext =await skill.InvokeAsync(context);
resultContext.Result.Dump();
//output
/*
Hi there! Yes, I remember you. Your name is Andrea, right?
*/
优化搜索过程
由于这种场景太常见了,所以Semantic Kernel中直接提供了一个技能TextMemorySkill,通过Function调用的方式简化了搜索的过程。
// .. SaveInformations
// TextMemorySkill provides the "recall" function
kernel.ImportSkill(new TextMemorySkill());
var prompt =
"""
It can give explicit instructions or say 'I don't know' if it does not have an answer.
Information about me, from previous conversations:
{{ recall $ask }}
User: {{ $ask }}
ChatBot:
""";
var skill = kernel.CreateSemanticFunction(prompt);
var ask = "Hello, I think we've met before, remember? my name is...";
var context = kernel.CreateNewContext();
context["ask"] = ask;
context[TextMemorySkill.CollectionParam] = MemoryCollectionName;
var resultContext =await skill.InvokeAsync(context);
resultContext.Result.Dump();
// output
/*
Hi there! Yes, I remember you. Your name is Andrea, right?
*/
这里直接使用 recall 方法,将问题传给了 TextMemorySkill,搜索对应得到结果,免去了手动搜索注入得过程。
内存的持久化
VolatileMemoryStore
本身也是易丢失的,往往使用到内存的场景,其中的信息都是有可能长期存储的,起码并不会即刻过期。那么将这些信息的 embedding
能够长期存储起来,也是比较划算的事情。毕竟每一次做 embedding的转化也是需要调接口,需要花钱的。
Semantic Kernel库中包含了SQLite、Qdrant和CosmosDB的实现,自行扩展的话,也只需要实现 IMemoryStore
这个接口就可以了。
至于未来,可能就是专用的 Vector Database
了。
参考资料:
- https://learn.microsoft.com/en-us/semantic-kernel/concepts-sk/memories
- https://github.com/microsoft/semantic-kernel/blob/main/samples/notebooks/dotnet/6-memory-and-embeddings.ipynb
- https://github.com/johnmaeda/SK-Recipes/blob/main/e4-memories/notebook.ipynb
- https://learn.microsoft.com/en-us/semantic-kernel/concepts-ai/vectordb
Semantic Kernel 入门系列:🥑Memory内存的更多相关文章
- [转]oracle学习入门系列之五内存结构、数据库结构、进程
原文地址:http://www.2cto.com/database/201505/399285.html 1 Oracle数据库结构 关于这个话题,网上一搜绝对一大把,更别提书籍上出现的了,还有很多大 ...
- How The Kernel Manages Your Memory.内核是如何管理内存的
原文标题:How The Kernel Manages Your Memory 原文地址:http://duartes.org/gustavo/blog/ [注:本人水平有限,只好挑一些国外高手的精彩 ...
- 【ABAP系列】SAP ABAP模块-memory内存数据传输的例子
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP模块-memor ...
- linux入门系列12--磁盘管理之分区、格式化与挂载
前面系列文章讲解了VI编辑器.常用命令.防火墙及网络服务管理,本篇将讲解磁盘管理相关知识. 本文将会介绍大量的Linux命令,其中有一部分在"linux入门系列5--新手必会的linux命令 ...
- Docker入门系列(一):目标和安排
Docker入门系列(一) 这个系列的教程来源于docker的官方文档,此文档的目的在于一步一步学习docker的使用方法. 这一系列的教程有如下几篇文档: docker安装启动 构建第一个docke ...
- Go语言入门系列(五)之指针和结构体的使用
Go语言入门系列前面的文章: Go语言入门系列(二)之基础语法总结 Go语言入门系列(三)之数组和切片 Go语言入门系列(四)之map的使用 1. 指针 如果你使用过C或C++,那你肯定对指针这个概念 ...
- ABP入门系列(1)——学习Abp框架之实操演练
作为.Net工地搬砖长工一名,一直致力于挖坑(Bug)填坑(Debug),但技术却不见长进.也曾热情于新技术的学习,憧憬过成为技术大拿.从前端到后端,从bootstrap到javascript,从py ...
- Jenkins入门系列之——02第二章 Jenkins安装与配置
2014-12-08:已不再担任SCM和CI的职位,Jenkins的文章如无必要不会再维护. 写的我想吐血,累死了. 网页看着不爽的,自己去下载PDF.有问题请留言! Jenkins入门系列之——03 ...
- 快速入门系列--WebAPI--03框架你值得拥有
接下来进入的是俺在ASP.NET学习中最重要的WebAPI部分,在现在流行的互联网场景下,WebAPI可以和HTML5.单页应用程序SPA等技术和理念很好的结合在一起.所谓ASP.NET WebAPI ...
- Maven入门系列(二)--设置中央仓库的方法
原文地址:http://www.codeweblog.com/maven入门系列-二-设置中央仓库的方法/ Maven仓库放在我的文档里好吗?当然不好,重装一次电脑,意味着一切jar都要重新下载和发布 ...
随机推荐
- jsp第九周作业
1.做一个图书类Book id,name,price ,get,set访问器,构造方法2个,1个无参,1个有参做一个测试类,在main中创建3个图书对象,放到list集合中.做一个菜单,可以添加,删除 ...
- dos的使用
打开cmd的方式 开始+系统+命令提示符 win+r,输入cmd 在任意的文件夹下面,按住shift键+鼠标右键点击,在此处打开命令运行窗口 打开我的电脑 在资源管理器的地址栏前面加上cmd路径 常用 ...
- vue移动端登录与登录保持
成品效果 首先进入首页点击右下角个人中心,若状态为登录中则进入个人中心页面,否则进入登录页 实现步骤 首先完成静态的登录页与个人中心页面 登录页 <template> <div cl ...
- 3-1 熟悉Hadoop及其操作
Hadoop最早起源于Nutch.Nutch的设计目标是构建一个大型的全网搜索引擎,包括网页抓取.索引.查询等功能,但随着抓取网页数量的增加,遇到了严重的可扩展性问题--如何解决数十亿网页的存储和索引 ...
- 自学JavaDay02_class02
注释 单行注释: //单行注释 多行注释 /** 多行注释* 多行注释* */ 文档注释 /** * 文档注释 * 文档注释 */ 标识符 关键字 标识符 所有的标识符都应该以字母(A-Z 或者 a- ...
- Cookie会话跟踪技术
cookie是什么 cookie 也叫 HTTPCookie,是客户端与服务器端进行会话(session)使用的一个能够在浏览器本地化存储的技术. cookie就是为了存储 sessionID而诞生. ...
- 修改word文档中已有的批注者名称
前言 https://blog.csdn.net/hyh19962008/article/details/89430548 word中可以通过修改用户的信息实现新建的批注者显示不同的名称,但是对于文档 ...
- weblogic session timed out
How to Configure Session Timeout in Weblogic Server (WLS) ?
- vue路由守卫并向后台发送token
vue代码 //全局路由首位:当路由发生异常首先执行的方法 router.beforeEach((to, from, next) => { //是否被认证 var isAuthenticated ...
- python selenium 操作文件上传,并发操作时,文件选择窗口混乱解决方案
上传文件 使用的是 python + autoit 模块,这种方式有一个问题,当出现多条任务同时选择文件上传的时候,无法判断那个文件选择窗口的归属,从而出现上传了错误的文件! 解决方法: 要上载文件而 ...