Semantic Kernelのサンプルを動かしてみた

# 概要
MicrosoftのSemantic Kernelのリポジトリを落として、そこに入っているサンプルアプリケーション複数をローカル環境で動かしてみます。
※５つのうち、２つのアプリケーション（GitHub Repo Q and AとCopilot Chat Sample）は正しく動作させることが出来ておりません。その状態についても記載しておりますが、未解決状態なのでご了承ください。

# Semantic Kernelとは
これは @nohanaga さんがまとめて下さっているのでそのまま引用↓
> Semantic Kernel (SK) は Microsoft が OSS として発表した、大規模言語モデル (LLM) をアプリにすばやく簡単に統合できる SDK です。SK は従来のプログラミング言語と最新のLLM AI "プロンプト" を簡単に組み合わせることができ、テンプレート化、チェーン化、埋め込みベースのメモリー、およびプランニング機能を備えています。

詳しくは@nohanaga さんのこちらの記事をご確認ください。
[Microsoft が LLM をアプリ開発に統合するための OSS「Semantic Kernel」を発表](https://qiita.com/nohanaga/items/430b59209b02c298ef2a)

# 動かす
## GitHubのリポジトリから落とす
[microsoft/semantic-karnel](https://github.com/microsoft/semantic-kernel) という公式ライブラリがあるのでそれをクローンします。

落としてみると下記の様にdotnet、python、あとsamplesのディレクトリなどがありました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/10111d14-4fa5-f64d-c20c-841fe2dee854.png)

samplesには、下記のキャプチャ内容の様にSemantic Kernelを使ったサービスのサンプルアプリケーションがいくつか入っており、これを動かすことでSemantic Kernalではどんなことが出来るかより具体的にイメージしてみることが出来そうです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/f19a6ec9-2457-57b1-9ae7-ff4c0e2d8066.png)
上記では英語で書いてますが、各アプリケーションで学べることがまとめられていますね。以下に、僕のざっくり翻訳（突っ込まないで）。
<table>
  <tr>
    <td> チャット </td> <td>スキルの準備</td>
  </tr>
  <tr>
    <td> 本生成 </td> <td>複雑なゴールの分解と想定をプランナーを利用して分解</td>
  </tr>
<tr>
    <td> 認証とAPI </td> <td>外部データをインデグレートする為の認証とAPIへの接続（コネクター利用）</td>
  </tr>
  <tr>
    <td> GitHub Repo QA </td> <td>埋め込みとメモリを利用したデータ蓄積とクエリ</td>
  </tr>
  <tr>
    <td> Copilotチャットサンプル</td> <td>Semantic Kernelに基づいたチャット体験の作成</td>
  </tr>
</table>

## APIを動かす
上記のサンプルはすべてローカルでSemantic Kernel Service APIというのを動かす前提になっていて、これも当該リポジトリに入っています。
[※公式の動作マニュアル](https://github.com/microsoft/semantic-kernel/blob/main/samples/dotnet/KernelHttpServer/README.md)
APIの実態は、Azure Functionsで、ここからOpenAIにプロンプトや設定を投げて、結果を返しているのだと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/77e4183e-b1db-bfb5-d501-84d6ee2a7cdc.png)


### 必要なもの
・Open AI API Key or Azure Open AI service key
・Azure Functions Core Tools
・.NET 6（.NET 7があってもダメ）
・Yarn

動かし方としては、semantic-kernel/samples/dotnet/KernelHttpServer にまでターミナルでいって、下記のコマンドを打つだけです。
```
func start --csharp
```
エラーなく進んだらそれでAPI実行は完了です。
ちなみに僕は.NET6を入れろと、一回怒られて、インストールしました。
上手くいっているときは、VS Codeのターミナルだとこんな感じになります（Azure Functionsのおなじみの画面）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/76721266-86a4-4f9a-9f84-2b638d4c0931.png)

## Simple chat summaryを動かす
### 動作準備
さて、ではAPIが動いたので、早速サンプルアプリケーション第一弾のチャットアプリを動かします。
システム概要としては、ReactとTypeScriptのチャットアプリで上記のAPIを実際にコールしたアプリケーションです。
公式ドキュメントは[こちら](https://github.com/microsoft/semantic-kernel/blob/main/samples/apps/chat-summary-webapp-react/README.md)。

#### .envの作成
同リポジトリの\samples\apps\chat-summary-webapp-react がその内容となります。
ここに.envを新規作成して、内容は.env.exampleをコピー。
下記の様な内容になっているので、OpenAIの鍵を持っている場合は下記の様になります。

```
REACT_APP_FUNCTION_URI=http://localhost:7071
REACT_APP_OPEN_AI_KEY={あなたの鍵}
REACT_APP_OPEN_AI_MODEL=text-davinci-003
REACT_APP_AZURE_OPEN_AI_KEY=
REACT_APP_AZURE_OPEN_AI_DEPLOYMENT=
REACT_APP_AZURE_OPEN_AI_ENDPOINT=
```
#### yarn
ターミナルで
```
yarn install
```
```
yarn start
```
するだけ。

するとlocalhost:3000でブラウザからチャットアプリケーションが開きます。

### Webアプリケーションの動作
アプリケーションでは、既に10往復くらいの英語によるチャットのやり取りが既に画面に表示されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/58d08a2f-e826-0046-1ad4-afc239677f5d.png)
車のお勧めを聞いているところから会話がスタート
→　スポーツカーのポルシェやシビックを進める
→　もっとエコな車がいい
→　プリウスとかリーフはどう？
→　そしたら今週末、日産のディーラーに行くよ
→　ところで電気自動車どう思う？
みたいなやりとりがあります。

ページの下部にあるボタン「Get AI Summary」を押下すると、下記の様に要約などが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/db7faa7a-b47c-341d-4762-e257d06379db.png)
具体的には、要約とトピックと会話の中で決められた予定っぽいものがまとめられています。

このアプリケーションでは、Conversation Summary Skilの下記の３つのネイティブ関数が利用されているそうです。
・SummarizeConversation,
・GetConversationActionItems
・GetConversationTopics

なるほど、先ほどのボタンを押下したときに、これらのネイティブ関数をコールすることで、チャット履歴のなかから要約、トピック、アクションを抽出したということですね。
こういう仕組みそのものがSemantic Kernelの一つということか。

公式ページにも書いていますが、AISummary.tsxでこれらの関数をコールしてるとこあって、ここを見るとなんだかイメージ出来ます。
``` AISummary.tsx
const getSummary = async (ask: any) => {
        try {
            var result = await sk.invokeAsync(keyConfig, ask, 'ConversationSummarySkill', 'SummarizeConversation');
            setSummary(result.value);
        } catch (e) {
            alert('Something went wrong.\n\nDetails:\n' + e);
        }
    };

    const getActionItems = async (ask: any) => {
        try {
            var result = await sk.invokeAsync(keyConfig, ask, 'ConversationSummarySkill', 'GetConversationActionItems');
            setActionItems(result.value);
        } catch (e) {
            alert('Something went wrong.\n\nDetails:\n' + e);
        }
    };

    const formatActionItems = (actionItems: string): JSX.Element => {
        var actionItemsJson = JSON.parse(
            '[' +
                actionItems
                    .split('}\n\n{')
                    .map((item, index, array) => {
                        if (array.length === 1) {
                            return item;
                        } else if (index === 0) {
                            return item + '}';
                        } else if (index === array.length - 1) {
                            return '{' + item;
                        } else {
                            return '{' + item + '}';
                        }
                    })
                    .join(',') +
                ']',
        );

        var actionItemsList = actionItemsJson.reduce((acc: any, cur: any) => {
            return acc.concat(cur.actionItems);
        }, []);

        var actionItemsFormatted = actionItemsList.map((actionItem: any) => {
            return (
                <Card>
                    <CardHeader header={<Subtitle2>{actionItem.actionItem}</Subtitle2>} />
                    <Body1>
                        <b>Owner:</b> {actionItem.owner}
                    </Body1>
                    <Body1>
                        <b>Due Date:</b> {actionItem.dueDate}
                    </Body1>
                </Card>
            );
        });

        return (
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 20 }}>{actionItemsFormatted}</div>
        );
    };

    const getTopics = async (ask: any) => {
        try {
            var result = await sk.invokeAsync(keyConfig, ask, 'ConversationSummarySkill', 'GetConversationTopics');
            setTopics(result.value);
        } catch (e) {
            alert('Something went wrong.\n\nDetails:\n' + e);
        }
    };
``` 

## Book Creatorを動かす
名前から察するに、何かテーマを与えたら本を書いてくれるアプリケーションなのなど期待しつつ執筆。
こちらもシステム構造的には先ほどのチャットアプリと同様で、React、TypeScriptでSemantic Karnel Service APIをコールして動かすサンプルです。というか、きっと他のすべてのサンプルもすべて同様なのでしょう。
コードは同リポジトリの\samples\apps\book-creator-webapp-react にあり、インストール手順はチャットアプリと同じなので割愛します。
公式ドキュメントは[こちら](https://github.com/microsoft/semantic-kernel/blob/main/samples/apps/book-creator-webapp-react/README.md)

### Webアプリケーションの動作
#### 本の生成
アプリケーションを起動し、設定を保存したら、下記のページにたどり着きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/6f4eb524-4667-b16f-7a0a-b0658f581989.png)
英語の文面的には、「子供向けの本のトピックやアイデアを羅列して入力し、アイデアを選択していきながら本を作成する」という感じか。
では、英語で適当に下記の様に入力して、Get Ideasを押下してみた。
```
mouse, cheese, apple, cat, dog, surprise
```

すると、なるほどストーリーのアイデアが出てきました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/01e5b3f8-6c0b-e0a9-1e5d-c80588665114.png)
一個目のアイデアがなんか、イメージしてた感じに近かったのでこれ↓を選択してCreate Bookを押下してみた。
```
The Mouse and the Cheese
A story about a mouse who finds a piece of cheese and has to figure out how to get it home without being eaten by the cat or dog.
```

しばらく待つと下記の様に、８ページの子供向けの本の目次が出てきました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/75f2751a-5fa9-599a-d17c-635c71121345.png)

ちなみに、先ほどの出力があったページの右上に「Show Process」というボタンがあり、これを押下すると下記の様に出力されていました。

**プラン作成**
```
[08:07:15 GMT+0900 (Japan Standard Time)]
Invoked function: createplan at URI: /api/skills/plannerskill/invoke/createplanasync with ask:

{
    "value": "Create an 8 page children's book called The Mouse and the Cheese about: A story about a mouse who finds a piece of cheese and has to figure out how to get it home without being eaten by the cat or dog. MUST RETURN A SINGLE RESULT IN THIS FORMAT: {'pages': [{'num':0, content:''}]}",
    "skills": [
        "writerskill",
        "childrensbookskill",
        "summarizeskill"
    ]
}
```
writerskill、childrensbookskill、summarizeskillという３つのスキルの指定と先ほど選択した本のアイデアが渡されています。
勝手な推測ですが、それぞれ本を文章生成スキル、子供の本を書くスキル、要約スキルで、これらのスキルをプランナーに登録して、後はプランナーに8ページの子供向けの絵本書くための計画作成を任せているという感じでしょうか。

**プラン実行**
```
[08:08:51 GMT+0900 (Japan Standard Time)]
Invoked function: executeplan at URI: /api/planner/execute with ask:

{
    "inputs": [
        {
            "key": "PLAN__PLAN",
            "value": "<goal>\nCreate an 8 page children's book called The Mouse and the Cheese about: A delightful story about a mouse who discovers a cheese and has to find a way to get it back home MUST RETURN A SINGLE RESULT IN THIS FORMAT: {'pages': [{'num':0, content:''}]}\n</goal>\n<plan>\n <function.WriterSkill.NovelOutline input=\"The Mouse and the Cheese\" chapterCount=\"8\" setContextVariable=\"CHAPTER_SYNOPSIS\"/>\n <function.ChildrensBookSkill.CreateBook input=\"$CHAPTER_SYNOPSIS\" numWordsPerPage=\"100\" numPages=\"8\" appendToResult=\"RESULT__PAGES\"/>\n</plan>"
        },
        {
            "key": "PLAN__ISCOMPLETE",
            "value": "False"
        },
        {
            "key": "PLAN__ID",
            "value": "e5f99d8ff0f74754b51ece44fb0cf7ab"
        },
        {
            "key": "INPUT",
            "value": "{\"id\":\"e5f99d8ff0f74754b51ece44fb0cf7ab\",\"goal\":\"Create an 8 page children\\u0027s book called The Mouse and the Cheese about: A delightful story about a mouse who discovers a cheese and has to find a way to get it back home MUST RETURN A SINGLE RESULT IN THIS FORMAT: {\\u0027pages\\u0027: [{\\u0027num\\u0027:0, content:\\u0027\\u0027}]}\",\"plan\":\"\\u003Cgoal\\u003E\\nCreate an 8 page children\\u0027s book called The Mouse and the Cheese about: A delightful story about a mouse who discovers a cheese and has to find a way to get it back home MUST RETURN A SINGLE RESULT IN THIS FORMAT: {\\u0027pages\\u0027: [{\\u0027num\\u0027:0, content:\\u0027\\u0027}]}\\n\\u003C/goal\\u003E\\n\\u003Cplan\\u003E\\n \\u003Cfunction.WriterSkill.NovelOutline input=\\u0022The Mouse and the Cheese\\u0022 chapterCount=\\u00228\\u0022 setContextVariable=\\u0022CHAPTER_SYNOPSIS\\u0022/\\u003E\\n \\u003Cfunction.ChildrensBookSkill.CreateBook input=\\u0022$CHAPTER_SYNOPSIS\\u0022 numWordsPerPage=\\u0022100\\u0022 numPages=\\u00228\\u0022 appendToResult=\\u0022RESULT__PAGES\\u0022/\\u003E\\n\\u003C/plan\\u003E\",\"arguments\":\"\",\"is_complete\":false,\"is_successful\":false,\"result\":\"\"}"
        },
        {
            "key": "PLAN__RESULT",
            "value": ""
        },
        {
            "key": "PLAN__ISSUCCESSFUL",
            "value": "False"
        },
        {
            "key": "available_functions",
            "value": " ChildrensBookSkill.BookIdeas:\n description: Given a topic description generate a number of children's book ideas with short descriptions\n inputs:\n - INPUT: \n - numIdeas: \n\n ChildrensBookSkill.CreateBook:\n description: Creates a children's book from the given input with a suggested number of words per page and a specific total number of pages\n inputs:\n - INPUT: \n - numWordsPerPage: \n - numPages: \n\n PlannerSkill.BucketOutputs:\n description: When the output of a function is too big, parse the output into a number of buckets.\n inputs:\n - input: The output from a function that needs to be parse into buckets.\n - bucketCount: The number of buckets.\n - bucketLabelPrefix: The target label prefix for the resulting buckets. Result will have index appended e.g. bucketLabelPrefix='Result' => Result_1, Result_2, Result_3\n\n SummarizeSkill.MakeAbstractReadable:\n description: Given a scientific white paper abstract, rewrite it to make it more readable\n inputs:\n - input: \n\n SummarizeSkill.Notegen:\n description: Automatically generate compact notes for any text or text document.\n inputs:\n - input: \n\n SummarizeSkill.Summarize:\n description: Summarize given text or any text document\n inputs:\n - input: Text to summarize\n\n SummarizeSkill.Topics:\n description: Analyze given text or document and extract key topics worth remembering\n inputs:\n - input: \n\n WriterSkill.Acronym:\n description: Generate an acronym for the given concept or phrase\n inputs:\n - input: \n\n WriterSkill.AcronymGenerator:\n description: Given a request to generate an acronym from a string, generate an acronym and provide the acronym explanation.\n inputs:\n - INPUT: \n\n WriterSkill.AcronymReverse:\n description: Given a single word or acronym, generate the expanded form matching the acronym letters.\n inputs:\n - INPUT: \n\n WriterSkill.Brainstorm:\n description: Given a goal or topic description generate a list of ideas\n inputs:\n - input: A topic description or goal.\n\n WriterSkill.EmailGen:\n description: Write an email from the given bullet points\n inputs:\n - input: \n\n WriterSkill.EmailTo:\n description: Turn bullet points into an email to someone, using a polite tone\n inputs:\n - to: \n - input: \n - sender: \n\n WriterSkill.NovelChapter:\n description: Write a chapter of a novel.\n inputs:\n - input: A synopsis of what the chapter should be about.\n - theme: The theme or topic of this novel.\n - previousChapter: The synopsis of the previous chapter.\n - chapterIndex: The number of the chapter to write.\n\n WriterSkill.NovelChapterWithNotes:\n description: Write a chapter of a novel using notes about the chapter to write.\n inputs:\n - input: What the novel should be about.\n - theme: The theme of this novel.\n - notes: Notes useful to write this chapter.\n - previousChapter: The previous chapter synopsis.\n - chapterIndex: The number of the chapter to write.\n\n WriterSkill.NovelOutline:\n description: Generate a list of chapter synopsis for a novel or novella\n inputs:\n - input: What the novel should be about.\n - chapterCount: The number of chapters to generate.\n - endMarker: The marker to use to end each chapter.\n\n WriterSkill.Rewrite:\n description: Automatically generate compact notes for any text or text document\n inputs:\n - style: \n - input: \n\n WriterSkill.ShortPoem:\n description: Turn a scenario into a short and entertaining poem.\n inputs:\n - input: \n\n WriterSkill.StoryGen:\n description: Generate a list of synopsis for a novel or novella with sub-chapters\n inputs:\n - input: \n\n WriterSkill.TellMeMore:\n description: Summarize given text or any text document\n inputs:\n - conversationtype: \n - input: \n - focusarea: \n - previousresults: \n\n WriterSkill.Translate:\n description: Translate the input into a language of your choice\n inputs:\n - language: \n - input: \n\n WriterSkill.TranslateV2:\n description: Translate text to English and improve it\n inputs:\n - INPUT: \n\n WriterSkill.TwoSentenceSummary:\n description: Summarize given text in two sentences or less\n inputs:\n - input: "
        },
        {
            "key": "PLAN__GOAL",
            "value": "Create an 8 page children's book called The Mouse and the Cheese about: A delightful story about a mouse who discovers a cheese and has to find a way to get it back home MUST RETURN A SINGLE RESULT IN THIS FORMAT: {'pages': [{'num':0, content:''}]}"
        }
    ],
    "value": "{\"id\":\"e5f99d8ff0f74754b51ece44fb0cf7ab\",\"goal\":\"Create an 8 page children\\u0027s book called The Mouse and the Cheese about: A delightful story about a mouse who discovers a cheese and has to find a way to get it back home MUST RETURN A SINGLE RESULT IN THIS FORMAT: {\\u0027pages\\u0027: [{\\u0027num\\u0027:0, content:\\u0027\\u0027}]}\",\"plan\":\"\\u003Cgoal\\u003E\\nCreate an 8 page children\\u0027s book called The Mouse and the Cheese about: A delightful story about a mouse who discovers a cheese and has to find a way to get it back home MUST RETURN A SINGLE RESULT IN THIS FORMAT: {\\u0027pages\\u0027: [{\\u0027num\\u0027:0, content:\\u0027\\u0027}]}\\n\\u003C/goal\\u003E\\n\\u003Cplan\\u003E\\n \\u003Cfunction.WriterSkill.NovelOutline input=\\u0022The Mouse and the Cheese\\u0022 chapterCount=\\u00228\\u0022 setContextVariable=\\u0022CHAPTER_SYNOPSIS\\u0022/\\u003E\\n \\u003Cfunction.ChildrensBookSkill.CreateBook input=\\u0022$CHAPTER_SYNOPSIS\\u0022 numWordsPerPage=\\u0022100\\u0022 numPages=\\u00228\\u0022 appendToResult=\\u0022RESULT__PAGES\\u0022/\\u003E\\n\\u003C/plan\\u003E\",\"arguments\":\"\",\"is_complete\":false,\"is_successful\":false,\"result\":\"\"}"
}
```
↑内容見てると、PLAN__ISCOMPLETEとPLAN__ISSUCCESSFULでfalseが出ている。。これはどう捉えればいいんだろうか。今回は保留。

#### 翻訳
ちなみに本を生成した画面には、下記の様に翻訳するボタンがついています。。
（翻訳先の初期値は元々はSpanishだけど、CreateBook.tsxのtranslateToLanguage をいじることでJapaneseにも変えた状態です。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/6a371057-779e-5f2d-00f5-b4eb931e131a.png)

これを押下してしばらく待つと、日本語翻訳が得られました↓
```
Page 1
物語は、古い廃屋の壁の穴に住む小さなネズミ、マックスという名前のネズミから始まります。彼は好奇心旺盛なネズミで、いつも新しい面白いものを探求しています。ある日、探求していると、部屋の隅にチーズがあるのを見つけました。彼は喜んでチーズを食べてしまいました。彼はまだ気づいていなかったのですが、このチーズが彼の人生を永遠に変えることになるのです。
Page 2
マックスはすぐにチーズが魔法の力を持っていて、人間の言語を話して理解する能力を与えることができることを発見します。彼は驚き、新しい力を使って周りの世界を探検することを決めます。彼は賢い古いネズミ、フレンドリーな猫、そして彼を影から見ている不思議な存在など、様々な面白いキャラクターと出会います。
Page 3
マックスは、チーズの源泉とその魔法の力を見つける間に時間競争に身を置くことになります。途中で、彼は友情、勇気、そして自分自身を信じる力についての貴重な教訓を学びます。
Page 4
マックスはネズミの家を訪れ、優しさと他人を助けることの重要性を学びます。また、勇敢な猫に会い、正しいことを支持することの重要性を教えてもらいます。最後に、彼は謎めいた存在が実際には彼を守るために送られた守護天使だと発見します。
Page 5
マックスは旅を続け、最終的にチーズの源とその魔法の力を見つけます。彼はそれを良いことに使うか悪いことに使うかは自分次第だと発見します。
Page 6
マックスは、自分の力を良いことに使うことを決め、必要な人々を助ける。彼はまた、自分の力を責任を持って使うことが重要であることを学ぶ。友達の助けを借りて、マックスは世界に平和とバランスを取り戻すことができた。
Page 7
マックスは、人生への新しい理解と友情の力をよりよく理解した新しい気持ちで家に帰ってきました。彼は今よりもっと賢く、責任あるネズミになり、魔法のチーズが彼の人生の一部であることを知っています。
Page 8
そして、マックスと彼の友達は、友情の力と自分自身を信じることが世界を変えることができることを知って、幸せな永遠を生きました。
```

ここでもShow Processを見てみます。
```
[08:31:23 GMT+0900 (Japan Standard Time)]
Invoked function: translate at URI: /api/skills/writerskill/invoke/translate with ask:

 {
    "value": "The story begins with a small mouse, named Max, who lives in a small hole in the wall of an old abandoned house. He is a curious mouse, always looking for something new and exciting to explore. One day, while exploring, he finds a piece of cheese in a corner of the room. He is delighted and quickly devours the cheese. Little does he know that this piece of cheese will change his life forever.",
    "inputs": [
        {
            "key": "language",
            "value": "Japanese"
        }
    ]
}
```
上記の感じで8ページ分のログが残っていました。
これは単純に翻訳のスキルをコールしている様に見えます。

他にもRethinkという、文章再生成のボタンがあったけど今回は割愛します。
まとめると、このアプリケーションでは、Planerを利用して計画策定して実行するというサンプルが見れました。

## auth-api を動かす
外部API連携の方法のサンプルです。
このサンプルでは、Microsoft Graphへの接続と、Outlook、OneDrive、ToDoのAPIを呼び出すことを強調しています。各機能は、Microsoft GraphやOpen AIを呼び出してタスクを実行します。
公式ドキュメントは[こちら](https://github.com/microsoft/semantic-kernel/blob/main/samples/apps/auth-api-webapp-react/README.md)

### 準備
#### AzureのAzure Active Directoryでアプリを登録
Azure PortalでAzure Active Directory > アプリ登録　で下記の様に入力して作成。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/2c812c62-92cf-84b4-19ad-bf1dca87660c.png)

#### .envを作成
.env.sampleを参考にしつつ下記の様に作成します。
```
REACT_APP_GRAPH_CLIENT_ID={先ほど作ったアプリのアプリケーションID}
REACT_APP_GRAPH_SCOPES=User.Read,Files.ReadWrite,Tasks.ReadWrite,Mail.Send
REACT_APP_FUNCTION_URI=http://localhost:7071
REACT_APP_OPEN_AI_KEY={あなたの鍵}
REACT_APP_OPEN_AI_MODEL=text-davinci-003
REACT_APP_AZURE_OPEN_AI_KEY=
REACT_APP_AZURE_OPEN_AI_DEPLOYMENT=
REACT_APP_AZURE_OPEN_AI_ENDPOINT=
```

上記１行目のIDは、先ほどAzure Portalで作成したアプリのところから拾いました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/3f8ccd20-4acf-eb8a-fd5c-9b734749c026.png)

#### yarn
```
yarn install
```
```
yarn start
```

### Webアプリケーションの動作
恒例のReactのアプリケーションが立ち上がり、早速、Microsoftの認証ボタンが現れるので押下してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/7e0c1f06-fc19-0c3a-1709-4d2d859dc8e6.png)

すると、アクセス許可の確認してきます。プロファイルとかOneDriveとかメールするでとか言ってますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/d028d86d-f552-1e38-8d87-966a62375f02.png)

次は恒例のこの設定画面が現れるのでSaveして進む
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/34561e6d-d4db-d2f4-9279-1114a63c36f6.png)

すると本題の画面となる。
つまり僕のアカウントのデータにアクセスできるからなんか質問してって感じでしょうか。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/ba6fee7b-7637-7d13-dffb-76d8f68a819b.png)

↑真ん中のテキストボックスをDeepLしてみると、
```
氷河は、自重で常に動いている密度の高い氷の持続的な体である。氷河は、何年も、多くは何世紀もかけて、雪の蓄積が雪解けを上回ったところに形成されます。氷河は、その重さによって引き起こされる応力によってゆっくりと流れ、変形するため、クレバスやセラックなどの特徴的な形状を獲得する。氷河が動くと、岩石や岩屑が削られ、圏谷、モレーン、フィヨルドなどの地形が形成されます。氷河は水域に流れ込むこともあるが、陸上にのみ形成され、水面に形成されるはるかに薄い海氷や湖氷とは区別される。
```

おお、通りで英文見てピンとこなかったわけだ。めっちゃ氷河の話してます。

では、まず下部のSuumarize・・・のボタンを押下してみる。上記の氷河について語っているWordドキュメントを要約してOneDriveに保存するらしい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/2c6dd18a-80fb-3005-5429-79647d3e7a4b.png)

そしてその下のGet a shareable link and email the link to myself　を押下
→　するとはい、Semantic Kernelさんからメール来てました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/c7214b10-f015-24be-541e-93167d216cbf.png)

メール本文内のリンク踏むと、オンラインのWordが開いて、氷河の話の要約が来ている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/8485cdea-6fc4-1e5c-3e29-62ba38a520d7.png)

ではアプリケーションに戻って、一番下の下記のボタンを押下する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/3cf865d7-0471-a94f-1cbd-d2def3b173db.png)

↑するとOutlookのタスクに入ってました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/3c7131aa-13fa-19dc-41a8-6ec37fa6c4c4.png)

こんな感じで、コネクタを使うと、外部サービス連携が出来ることが確認できました。

## GitHub Repo Q and Aを動かす
こちらは、GitHub連携してpublicなリポジトリに関するQAが出来るリポジトリ。
Embedding使ったり、Memory使うとこがポイントの様です。

### 準備
#### .envの作成
いつも通り、同ディレクトリ内にある、.env.exampleを参考に.envを作る
```
REACT_APP_FUNCTION_URI=http://localhost:7071

REACT_APP_OPEN_AI_EMBEDDING_KEY={opneAIのAPIキー}
REACT_APP_OPEN_AI_EMBEDDING_MODEL=text-embedding-ada-002

REACT_APP_AZURE_OPEN_AI_EMBEDDING_KEY=
REACT_APP_AZURE_OPEN_AI_EMBEDDING_MODEL=
REACT_APP_AZURE_OPEN_AI_EMBEDDING_DEPLOYMENT=
REACT_APP_AZURE_OPEN_AI_EMBEDDING_ENDPOINT=

REACT_APP_OPEN_AI_COMPLETION_KEY={opneAIのAPIキー}
REACT_APP_OPEN_AI_COMPLETION_MODEL=text-davinci-003

REACT_APP_AZURE_OPEN_AI_COMPLETION_KEY=
REACT_APP_AZURE_OPEN_AI_COMPLETION_MODEL=
REACT_APP_AZURE_OPEN_AI_COMPLETION_DEPLOYMENT=
REACT_APP_AZURE_OPEN_AI_COMPLETION_ENDPOINT=
```

あとは恒例のyarn install, yarn start

### Webアプリケーションの動作
まずはいつもの設定画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/9876a0c7-4c01-c4de-8031-e42e0b923de5.png)

次にEmbedings Modelを選択。
セレクトボックス開くといろいろある。
今回は最近盛り上がってた気がする「text-embedding-ada-002」を利用
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/d83f0cf2-f121-37f9-209a-2b0a3ff8705f.png)

次の画面でターゲットにするリポジトリのURLとブランチを入力する。
今回はSemantic Kernelのリポジトリを対象にしてみる。
ちょっとわかりにくかったが、ブランチの右隣りの「↓」を押下するとリポジトリとブランチを読み込む
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/d79c8147-f03d-dac8-0a4b-34cad1053dd1.png)


ただ、このリポジトリの読み込みで失敗して下記の様なアラートが出る。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/df0b81d3-0c73-0331-045e-15109245efef.png)

どのリポジトリでやってもダメだった。
ブラウザのDeveloper Toolで見てみると、GitHubSkill読んだときに400エラーを食らっている。
GitHubの権限の問題だろうか。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/23537680-00d4-c607-f993-0ae040e2bf04.png)

issueに出しておきます。残念ですが、このサンプルは断念します。
解決方法分かる方は教えて頂けると嬉しいです。

## Copilot Chat Sampleを動かす
[公式ドキュメント](https://github.com/microsoft/semantic-kernel/blob/main/samples/apps/copilot-chat-app/README.md)
### 準備
#### アプリ登録
auth-apiでやったとき同様、Azure PortalのAzure Active Directoryでアプリを登録するが、一点だけ異なる点があります。
サポートされているアカウントの種類は、「任意の組織ディレクトリのアカウントと個人のMicrosoftアカウント」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/3515a13e-a20a-637d-5f9f-00425fb58ed1.png)

アプリを登録したら、またアプリケーションIDを取っておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/c41277d2-45d6-ef8d-5de5-e926a7c2ee4f.png)

#### バックエンド
\samples\apps\copilot-chat-app\webapi\appsettings.json を編集する方法が公式ドキュメントにはいろいろ書いてあるが、OpenAIのAPIを叩く場合は、特に編集がいらなそうです。

\samples\apps\copilot-chat-app\webapi にターミナルで移動して下記のコマンドを叩きます。
```
dotnet user-secrets set "CompletionConfig:Key"  "あなたの OPENAI KEY or AZURE OPENAI KEY"
```
```
dotnet user-secrets set "EmbeddingConfig:Key" "あなたの OPENAI KEY or AZURE OPENAI KEY"
```
```
dotnet build
```
```
dotnet run
```

そして、https://localhost:40443/probe にブラウザでアクセスして、「Semantic Kernel service is up and running」というメッセージが確認出来たらOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/c64bf226-dfb0-b35a-8c20-a48ecc4a88cc.png)

これでバックエンドの準備完了。

#### フロントエンド
**envファイルの作成**
\apps\copilot-chat-app\webapp　に移動して、envファイルを作成。

```
REACT_APP_BACKEND_URI=http://localhost:40443/
REACT_APP_AAD_CLIENT_ID={Azure Portalで作成したアプリケーションID}
```

**yarn**
そして恒例のyarn install、yarn start

これでいつも通りWebアプリケーションが立ち上がります。

### Webアプリケーションの動作
最初にこちらの認証画面が現れるので、認証します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/f1b0e139-ca21-ba7a-7919-d49ccd4cbe47.png)


認証進んで、チャット画面を開くと思ったら。。
うーむ。なんかConversationが読み込めていないっぽいですが。
原因探しても、パッと直せそうになかったので今日は断念しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/22f7174b-b40f-9d3c-d740-548d41d20979.png)

## 動かせなかった２つアプリケーションについて
GitHub Repo Q and Aと Copilot Chat Sampleは動作が確認出来なかった。
方法はあると思うので、分かる方いらしたら教えてください。

ただ思ったのは、これらのアプリケーションは未だ不安定なのかもということ。
公式ドキュメントでも、動いた３つのアプリケーションの記事だけあったりするので、そんな気がしました。
↓GitHub Repo Q and AとCopilot Chat Sampleの記述がない。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504562/72e34876-0149-8670-f69f-04fdbd5360b6.png)

## 最後に
動かせなかったアプリケーションもありましたが、実際に触ってみて、Semantic Kernelで実現できるアプリケーションの雰囲気がなんとかく掴めました。
同リポジトリの[notebookes](https://github.com/microsoft/semantic-kernel/tree/main/samples/notebooks)では、Semantic Kernelを使ったコーディングの書き方を順を追って解説してくれている様なので、今度はこちらを触って具体的なコードの理解を進めたいと思います（本来は順番逆かもですが）。