성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 그냥 RSS Reader 기능과 약간의 UI 편의성 때문에 사용...
[이종효] 오래된 소프트웨어는 보안 위협이 되기도 합니다. 혹시 어떤 기능...
[정성태] @Keystroke IEEE의 문서를 소개해 주시다니... +_...
[손민수 (Keystroke)] 괜히 듀얼채널 구성할 때 한번에 같은 제품 사라고 하는 것이 아...
[정성태] 전각(Full-width)/반각(Half-width) 기능을 토...
[정성태] Vector에 대한 내용은 없습니다. Vector가 닷넷 BCL...
[orion] 글 읽고 찾아보니 디자인 타임에는 InitializeCompon...
[orion] 연휴 전에 재현 프로젝트 올리자 생각해 놓고 여의치 않아서 못 ...
[정성태] 아래의 글에 정리했으니 참고하세요. C# - Typed D...
[정성태] 간단한 재현 프로젝트라도 있을까요? 저런 식으로 설명만 해...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - Semantic Kernel의 Planner 사용 예제</h1> <p> Semantic Kernel의 구성 요소는 다음의 그림에서 잘 확인할 수 있습니다.<br /> <br /> [출처: <a target='tab' href='https://devblogs.microsoft.com/semantic-kernel/hello-world/'>https://devblogs.microsoft.com/semantic-kernel/hello-world/</a>]<br /> <img onclick='toggle_img(this)' class='imgView' alt='sk_planner_1.png' src='/SysWebRes/bbs/sk_planner_1.png' /><br /> <br /> ASK는 Kernel에 보내는 요청이고, Kernel은 이미 그동안 우리가 만든 코드의 시작에 해당하는 타입이었습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var kernel = Kernel.Builder.Build(); // <a target='tab' href='https://www.sysnet.pe.kr/2/0/13348'>C# - Semantic Kernel의 "Basic Loading of the Kernel" 예제</a> </pre> <br /> 그림에서 S는 Skill인데 이것도 "<a target='tab' href='https://www.sysnet.pe.kr/2/0/13349'>C# - Semantic Kernel의 Skill과 Function 사용 예제</a>" 글을 통해 이미 다뤘습니다.<br /> <br /> 나머지는 M(Memory), C(Connector)와 오늘의 주제인 Planner인데요, 이에 대한 예제를 다음의 글에서 다루고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Introduction to the Planner ; <a target='tab' href='https://github.com/microsoft/semantic-kernel/blob/main/samples/notebooks/dotnet/05-using-the-planner.ipynb'>https://github.com/microsoft/semantic-kernel/blob/main/samples/notebooks/dotnet/05-using-the-planner.ipynb</a> </pre> <br /> 코드는 기존 예제와 동일하게 Kernel을 준비하는 것으로 시작하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > KernelConfig kernelConfig = new KernelConfig(); kernelConfig.AddOpenAITextCompletionService("default", "text-davinci-003", apiKey); var kernel = Kernel.Builder .WithConfiguration(kernelConfig) .Build(); </pre> <br /> 그다음 planner를 만들고 Kernel에 Skill/Function을 담고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var planner = new SequentialPlanner(kernel); var skillsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "skills"); <span style='color: blue; font-weight: bold'>kernel.ImportSemanticSkillFromDirectory(skillsDirectory, "SummarizeSkill"); kernel.ImportSemanticSkillFromDirectory(skillsDirectory, "WriterSkill");</span> /* <a target='tab' href='https://github.com/microsoft/semantic-kernel/tree/main/samples/skills/SummarizeSkill'>SummarizeSkill</a> Function: MakeAbstractReadable, Notegen, Summarize, Topics <a target='tab' href='https://github.com/microsoft/semantic-kernel/tree/main/samples/skills/WriterSkill'>WriterSkill</a> Function: Acronym, AcronymGenerator, AcronymReverse, Brainstorm, EmailGen, EmailTo, EnglishImprover, NovelChapter, NovelChapterWithNotes, NovelOutline, Rewrite, ShortPoem, StoryGen, TellMeMore, Translate, TwoSentenceSummary */ </pre> <br /> 해당 스킬들은 <a target='tab' href='https://www.sysnet.pe.kr/2/0/13349#sample_skill'>github에 예제로 제공</a>되고 있는 것들인데, clone 시킨 디렉터리에서 복사할 수 있습니다. <a target='tab' href='https://www.sysnet.pe.kr/2/0/13349#skill_dir'>프로젝트에 파일로 추가하는 경우 각각 CopyToOutputDirectory 설정을 해야 하는데</a>, 일일이 파일 하나씩 하는 것은 번잡하므로 와일드카드 문자열을 사용해 "skills\**\*"로 지정하면 skills 디렉터리 하위에 있는 모든 디렉터리 및 파일들을 복사하게 됩니다. 아래는 csproj에 그 설정을 추가한 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net7.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.SemanticKernel" Version="0.13.277.1-preview" /> </ItemGroup> <span style='color: blue; font-weight: bold'><ItemGroup> <None Update="skills\**\*"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup></span> </Project> </pre> <br /> 그다음 코드에서는 planner를 이용해 사용자의 요청에 기반한 Planner를 생성하는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var ask = "Tomorrow is Valentine's day. I need to come up with a few date ideas and e-mail them to my significant other."; <span style='color: blue; font-weight: bold'>var originalPlan = await planner.CreatePlanAsync(ask);</span> Console.WriteLine("Original plan:\n"); Console.WriteLine(JsonSerializer.Serialize(originalPlan, new JsonSerializerOptions { WriteIndented = true })); </pre> <br /> 재미있는 건, 어떤 스킬을 사용하라고 지정하지도 않았다는 점입니다. 즉, 단순히 현재 Kernel에 포함된 Skill/Function을 이용해 사용자의 ASK를 만족하는 Planner를 구성하는데요, 아래는 originalPlan의 내부 구조를 JSON 포맷으로 보여줍니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Original plan: { "state": [ { "Key": "INPUT", "Value": "" } ], "steps": [ { "state": [ { "Key": "INPUT", "Value": "" } ], "steps": [], "named_parameters": [ { "Key": "INPUT", "Value": "Valentine\u0027s day date ideas" } ], "named_outputs": [ { "Key": "DATE_IDEAS", "Value": "" }, { "Key": "INPUT", "Value": "" } ], "next_step_index": 0, "name": "Brainstorm", "skill_name": "WriterSkill", "description": "Given a goal or topic description generate a list of ideas" }, { "state": [ { "Key": "INPUT", "Value": "" } ], "steps": [], "named_parameters": [ { "Key": "INPUT", "Value": "" } ], "named_outputs": [ { "Key": "INPUT", "Value": "" } ], "next_step_index": 0, "name": "", "skill_name": "Microsoft.SemanticKernel.Orchestration.Plan", "description": "" }, { "state": [ { "Key": "INPUT", "Value": "" } ], "steps": [], "named_parameters": [ { "Key": "sender", "Value": "Me" }, { "Key": "to", "Value": "My Significant Other" }, { "Key": "INPUT", "Value": "$DATE_IDEA_1;$DATE_IDEA_2;$DATE_IDEA_3" } ], "named_outputs": [ { "Key": "INPUT", "Value": "" } ], "next_step_index": 0, "name": "EmailTo", "skill_name": "WriterSkill", "description": "Turn bullet points into an email to someone, using a polite tone" } ], "named_parameters": [ { "Key": "INPUT", "Value": "" } ], "named_outputs": [ { "Key": "INPUT", "Value": "" } ], "next_step_index": 0, "name": "Tomorrow is Valentine\u0027s day. I need to come up with a few date ideas and e-mail them to my significant other.", <span style='color: blue; font-weight: bold'>"skill_name": "Microsoft.SemanticKernel.Orchestration.Plan",</span> "description": "Tomorrow is Valentine\u0027s day. I need to come up with a few date ideas and e-mail them to my significant other." } </pre> <br /> 어찌 보면, 저것 하나가 새롭게 "Skill"이 된 것입니다. 결국 이러한 Planner대로 실행을 하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>var originalPlanResult = await kernel.RunAsync(originalPlan);</span> Console.WriteLine("Original Plan results:\n"); Console.WriteLine(Utils.WordWrap(originalPlanResult.Result, 80)); </pre> <br /> 다음과 같은 출력을 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Original Plan results: Dear My Significant Other, I wanted to take a moment to thank you for being my significant other. You have been a source of joy and comfort in my life, and I am so grateful for your presence. I am so lucky to have you in my life. Thanks, Me </pre> <br /> 그러니까, 사용자의 "Tomorrow is Valentine's day. I need to come up with a few date ideas and e-mail them to my significant other." 입력을 바탕으로, 내부에 구성한 Skill/Function을 활용해 위의 출력을 만들어 낸 것입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그럼, 이 상태에서 Shakespeare 화법으로 말하라는 Skill/Function도 (위에서 만든 Kernel에) 추가해 볼까요? ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > string skPrompt = """ {{$input}} Rewrite the above in the style of Shakespeare. """; var shakespeareFunction = kernel.CreateSemanticFunction(skPrompt, "shakespeare", "ShakespeareSkill", maxTokens: 2000, temperature: 0.2, topP: 0.5); </pre> <br /> 이번 Skill/Function은 ImportSemanticSkillFromDirectory가 아닌 동적으로 CreateSemanticFunction을 이용해 추가했습니다. 이러한 변화를 반영해 Planner를 다시 생성하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var ask = @"Tomorrow is Valentine's day. I need to come up with a few date ideas. She likes Shakespeare so write using his style. E-mail these ideas to my significant other"; <span style='color: blue; font-weight: bold'>var newPlan = await planner.CreatePlanAsync(ask);</span> </pre> <br /> Planner의 내부 변화를 확인합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Console.WriteLine("Updated plan:\n"); Console.WriteLine(JsonSerializer.Serialize(newPlan, new JsonSerializerOptions { WriteIndented = true })); </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Updated plan: { "state": [ { "Key": "INPUT", "Value": "" } ], "steps": [ { "state": [ { "Key": "INPUT", "Value": "" } ], "steps": [], "named_parameters": [ { "Key": "INPUT", "Value": "Date ideas for Valentine\u0027s day" } ], "named_outputs": [ { "Key": "INPUT", "Value": "" }, { "Key": "DATE_IDEAS", "Value": "" } ], "next_step_index": 0, "name": "Brainstorm", "skill_name": "WriterSkill", "description": "Given a goal or topic description generate a list of ideas" }, { "state": [ { "Key": "INPUT", "Value": "" } ], "steps": [], "named_parameters": [ { "Key": "INPUT", "Value": "$DATE_IDEAS" } ], "named_outputs": [ { "Key": "INPUT", "Value": "" }, { "Key": "SHAKESPEARE_IDEAS", "Value": "" } ], "next_step_index": 0, "name": "shakespeare", "skill_name": "ShakespeareSkill", "description": "Generic function, unknown purpose" }, { "state": [ { "Key": "INPUT", "Value": "" } ], "steps": [], "named_parameters": [ { "Key": "sender", "Value": "Myself" }, { "Key": "INPUT", "Value": "$SHAKESPEARE_IDEAS" }, { "Key": "to", "Value": "My Significant Other" } ], "named_outputs": [ { "Key": "INPUT", "Value": "" } ], "next_step_index": 0, "name": "EmailTo", "skill_name": "WriterSkill", "description": "Turn bullet points into an email to someone, using a polite tone" } ], "named_parameters": [ { "Key": "INPUT", "Value": "" } ], "named_outputs": [ { "Key": "INPUT", "Value": "" } ], "next_step_index": 0, "name": "Tomorrow is Valentine\u0027s day. I need to come up with a few date ideas.\r\nShe likes Shakespeare so write using his style. E-mail these ideas to my significant other", "skill_name": "Microsoft.SemanticKernel.Orchestration.Plan", "description": "Tomorrow is Valentine\u0027s day. I need to come up with a few date ideas.\r\nShe likes Shakespeare so write using his style. E-mail these ideas to my significant other" } </pre> <br /> 이에 따라 텍스트를 생성하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>var newPlanResult = await kernel.RunAsync(newPlan);</span> Console.WriteLine("New Plan results:\n"); Console.WriteLine(newPlanResult.Result); </pre> <br /> 이런 출력을 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > New Plan results: Dear My Significant Other, I hope you're doing well. I wanted to suggest some activities that we could do together. We could make a delicious feast together. We could take a romantic walk. We could watch a romantic movie. We could attend a local event. We could take a dancing class. We could visit a museum. We could go to a spa. We could go stargazing. We could go to a concert. We could have a picnic. I look forward to hearing your thoughts. Thanks, Myself </pre> <br /> 음... 잘하면 <a target='tab' href='https://movie.daum.net/moviedb/main?movieId=154521'>시라노</a> 같은 친구도 만들 수 있겠군요. ^^<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=2087&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
2027
(왼쪽의 숫자를 입력해야 합니다.)