Microsoft MVP성태의 닷넷 이야기
.NET Framework: 2125. C# - Semantic Kernel의 Semantic Memory 사용 예제 [링크 복사], [링크+제목 복사]
조회: 3588
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

(시리즈 글이 8개 있습니다.)
.NET Framework: 2117. C# - (OpenAI 기반의) Microsoft Semantic Kernel을 이용한 자연어 처리
; https://www.sysnet.pe.kr/2/0/13345

.NET Framework: 2118. C# - Semantic Kernel의 Prompt chaining 예제
; https://www.sysnet.pe.kr/2/0/13347

.NET Framework: 2119. C# - Semantic Kernel의 "Basic Loading of the Kernel" 예제
; https://www.sysnet.pe.kr/2/0/13348

.NET Framework: 2120. C# - Semantic Kernel의 Skill과 Function 사용 예제
; https://www.sysnet.pe.kr/2/0/13349

.NET Framework: 2121. C# - Semantic Kernel의 대화 문맥 유지
; https://www.sysnet.pe.kr/2/0/13352

.NET Framework: 2123. C# - Semantic Kernel의 ChatGPT 대화 구현
; https://www.sysnet.pe.kr/2/0/13355

.NET Framework: 2124. C# - Semantic Kernel의 Planner 사용 예제
; https://www.sysnet.pe.kr/2/0/13357

.NET Framework: 2125. C# - Semantic Kernel의 Semantic Memory 사용 예제
; https://www.sysnet.pe.kr/2/0/13358




C# - Semantic Kernel의 Semantic Memory 사용 예제

ASK, Kernel, Planner, Skill을 지나 이제 Memory 예제까지 왔습니다. ^^

samples/notebooks/dotnet/06-memory-and-embeddings.ipynb
; https://github.com/microsoft/semantic-kernel/blob/main/samples/notebooks/dotnet/06-memory-and-embeddings.ipynb

Memory는 간단하게 설명하면 전체적인 문맥을 기억하기 위한 수단입니다. 문맥이라고 하니, 지난 Context 예제가 있었는데요,

C# - Semantic Kernel의 대화 문맥 유지
; https://www.sysnet.pe.kr/2/0/13352

사실 위에서 구현한 ContextVariables는 그다지 특별한 것이 아니고, 지금까지의 대화 내용을 전부 누적시켜 설정하는 것에 지나지 않습니다. 일례로, 이전 예제의 코드를 보면,

var context = new ContextVariables();
var history = "";
context.Set("history", history);

...[생략]...

var bot_answer = await kernel.RunAsync(context, skill["Chat"]);
Console.WriteLine(bot_answer);

history += $"\nHuman: {human_input}\nAssistant: {bot_answer}\n";
context.Update(history);

history 문자열에 대화의 내용을 전부 합쳐 Update 시킨 후 RunAsync에 전달하는 식입니다. 여기서 문제는, 이런 식으로 누적시켜 대화를 이어나가면 결국 Model에서 정한 Token 제약에 금방 걸리고 맙니다.

바로 이런 문제를 해결하기 위해, 단기/장기로 대화 상태를 저장할 수 있는 메모리가 필요한 것입니다. 이 외에도, 메모리를 이용하면 또 다른 활용이 가능합니다. 예를 들어, 사내에서 개발한 제품의 매뉴얼이 있다고 가정해 보겠습니다. 물론, OpenAI가 크롤링한 웹 데이터에 해당 매뉴얼이 있다면 상관없겠지만 그렇지 않을 수도 있습니다. 그런 상황이라면 그 매뉴얼 전체를 Chat 내에서 인식시키려 할 때 또다시 Token 제약에 걸립니다.

이런 문제를 Memory를 이용해 해결할 수 있는데요, 이를 위해서는 2가지 구성요소(Memory Storage와 Embedding backend)를 Kernel에 추가해야 합니다. 방법은 예제 코드에 잘 나옵니다. ^^

우선 Embedding backend는 KernelConfig을 이용해 새롭게 "text-embedding-ada-002" 모델을 추가합니다.

KernelConfig kernelConfig = new KernelConfig();
kernelConfig.AddOpenAITextCompletionService("default", "text-davinci-003", apiKey);
kernelConfig.AddOpenAITextEmbeddingGenerationService("ada", "text-embedding-ada-002", apiKey);

그다음 Memory Storage는, Application이 실행 중일 때만 보관이 되도록 VolatileMemoryStore를 사용합니다.

var kernel = Kernel.Builder
    .WithConfiguration(kernelConfig)
    .WithMemoryStorage(new VolatileMemoryStore())
    .Build();

이 외에도, Azure Cosmos DB, PostgreSQL, SQLite 등으로 유지할 수 있다고 합니다. nuget을 찾아보면 관련 타입들이 아마도 다음의 패키지들에 포함된 것 같습니다.

Microsoft.SemanticKernel.Connectors.Memory.Sqlite
; https://www.nuget.org/packages/Microsoft.SemanticKernel.Connectors.Memory.Sqlite

Microsoft.SemanticKernel.Connectors.Memory.Qdrant 
; https://www.nuget.org/packages/Microsoft.SemanticKernel.Connectors.Memory.Qdrant

Microsoft.SemanticKernel.Connectors.Memory.AzureCognitiveSearch
; https://www.nuget.org/packages/Microsoft.SemanticKernel.Connectors.Memory.AzureCognitiveSearch

Microsoft.SemanticKernel.Connectors.Memory.Postgres
; https://github.com/microsoft/semantic-kernel/tree/main/dotnet/src/Connectors/Connectors.Memory.Postgres

당연히 모두 현재(2023-05-17)는 Preview 버전이고 PostgreSQL의 경우에는 찾을 수 없었습니다.

자, 그럼 이제 남은 것은 Memory Store에 문맥 정보를 추가하는 일이 남았는데요, 이를 위해 SaveInformationAsync 메서드를 사용할 수 있습니다.

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

/* 출력 결과
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
*/

오호~~~ 점점 더 예제가 재미있어지는군요. ^^

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




참고로, 이때 문맥으로 저장하는 텍스트는 단순히 문자열로 저장되는 것이 아니고, 우리가 지정한 Embedding backend, 즉 위의 예제에서는 "text-embedding-ada-002" 모델로 벡터값으로 인코딩된 유형으로 저장됩니다. 관련해서는 아래의 문서를 참조하라고 하는군요. ^^

What are Embeddings?
; https://learn.microsoft.com/en-us/semantic-kernel/concepts-ai/embeddings

그러고 보면, 지난 예제에서 "text-embedding-ada-002" 모델을 사용해 "안녕하세요, 책 좀 추천해 주세요." 문자열을 전송했더니 다음과 같은 결과가 나왔는데요,

( 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0, 0 0 0 0 0 1 0 0, 0 0 0, 0 0 0, 0 0, 0 0, 0 0 0, 0 0 0 0,0 0 0, 0 0 0,0,0 0 0 0, 0 0 0 0, 0 0 0,0,0 0, 0,0,0,,0,0,0,0 1 0, 0,0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,0,0, 0,0,0, 0,0,0,0,0,, 0,0,0, 0,0,0,0,0,,0,0,0,0, 1 0,0,0,0,0,0,0,0, 0,0,0,0, 0,0,0, 0,0,0, 0 1 0,0,0,0,0,,, 0,0,0,0, 0,0,0, 0,0, 0,0,0, 0,0, 0,0, 0,0, 0,0,0, 0,0, 0,0,1, 0,0, 0,0, 0,0,0,0, 0,0,0, 0,0, 1,0,0, 0,0, 0,0,0, 0,0,1,0,0,0,0,1, 0,0,0, 0,0,0,1,1,0, 0,0,0,0, 1,0,0,1,1,0, 1,0,0,0,0,0, 1,0,0,0,0,1,0,1,1, 1,0,0,0,1,1, 1,0,1,1,0, 0,0,0,0,0, 0,0, 1, 0,0,1,0,1,1,1, 0,0,0,1,0,1,1, 0,0,0,0, 0,0, 1,0,1,0, 1,1,1,0, 0,0,0, 1,0,0,0,1,1, 1,0,1,1,1, 0,0,0,0, 0,0,0,1,1,1,1,1,1,1,1, 1,0,0,0,0, 1,0,1,1,1,1, 0,0,0,0, 1,1,0,1,0,1,1, 1,0,0,0, 0,0,0,1,,1,0,0,0,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,1,0,0,0,1,0,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,1,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1

아마도 저것이 "vectors or arrays of numbers that represent the meaning and the context of tokens processed by the model."인 듯합니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 6/5/2023]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2023-06-01 08시59분
정성태

[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13596정성태4/14/2024397닷넷: 2238. C# - WAV 기본 파일 포맷파일 다운로드1
13595정성태4/13/2024418닷넷: 2237. C# - Audio 장치 열기 (Windows Multimedia, NAudio)파일 다운로드1
13594정성태4/12/2024477닷넷: 2236. C# - Audio 장치 열람 (Windows Multimedia, NAudio)파일 다운로드1
13593정성태4/8/2024809닷넷: 2235. MSBuild - AccelerateBuildsInVisualStudio 옵션
13592정성태4/2/20241158C/C++: 165. CLion으로 만든 Rust Win32 DLL을 C#과 연동
13591정성태4/2/20241140닷넷: 2234. C# - WPF 응용 프로그램에 Blazor App 통합파일 다운로드1
13590정성태3/31/20241060Linux: 70. Python - uwsgi 응용 프로그램이 k8s 환경에서 OOM 발생하는 문제
13589정성태3/29/20241121닷넷: 2233. C# - 프로세스 CPU 사용량을 나타내는 성능 카운터와 Win32 API파일 다운로드1
13588정성태3/28/20241173닷넷: 2232. C# - Unity + 닷넷 App(WinForms/WPF) 간의 Named Pipe 통신파일 다운로드1
13587정성태3/27/20241115오류 유형: 900. Windows Update 오류 - 8024402C, 80070643
13586정성태3/27/20241235Windows: 263. Windows - 복구 파티션(Recovery Partition) 용량을 늘리는 방법
13585정성태3/26/20241083Windows: 262. PerformanceCounter의 InstanceName에 pid를 추가한 "Process V2"
13584정성태3/26/20241036개발 환경 구성: 708. Unity3D - C# Windows Forms / WPF Application에 통합하는 방법파일 다운로드1
13583정성태3/25/20241139Windows: 261. CPU Utilization이 100% 넘는 경우를 성능 카운터로 확인하는 방법
13582정성태3/19/20241203Windows: 260. CPU 사용률을 나타내는 2가지 수치 - 사용량(Usage)과 활용률(Utilization)파일 다운로드1
13581정성태3/18/20241341개발 환경 구성: 707. 빌드한 Unity3D 프로그램을 C++ Windows Application에 통합하는 방법
13580정성태3/15/2024991닷넷: 2231. C# - ReceiveTimeout, SendTimeout이 적용되지 않는 Socket await 비동기 호출파일 다운로드1
13579정성태3/13/20241473오류 유형: 899. HTTP Error 500.32 - ANCM Failed to Load dll
13578정성태3/11/20241611닷넷: 2230. C# - 덮어쓰기 가능한 환형 큐 (Circular queue)파일 다운로드1
13577정성태3/9/20241834닷넷: 2229. C# - 닷넷을 위한 난독화 도구 소개 (예: ConfuserEx)
13576정성태3/8/20241535닷넷: 2228. .NET Profiler - IMetaDataEmit2::DefineMethodSpec 사용법
13575정성태3/7/20241647닷넷: 2227. 최신 C# 문법을 .NET Framework 프로젝트에 쓸 수 있을까요?
13574정성태3/6/20241549닷넷: 2226. C# - "Docker Desktop for Windows" Container 환경에서의 IPv6 DualMode 소켓
13573정성태3/5/20241555닷넷: 2225. Windbg - dumasync로 분석하는 async/await 호출
13572정성태3/4/20241626닷넷: 2224. C# - WPF의 Dispatcher Queue로 알아보는 await 호출의 hang 현상파일 다운로드1
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...