【Semantic Kernel】SlackのData Access APIで社内AIアシスタントにプライベートチャンネルの情報を参照させてみる

こんにちは。

テックリードのTerukiです。

前回、Semantic Kernelを使って社内向けAIアシスタントを作ったという記事を書きました。

https://qiita.com/omt_teruki/items/4e091282ed6e25735f09

これだけでも結構便利ですが、Slack上にある使用者本人にしか見えない情報を使ってSlack内を検索できると結構捗ることもあると思うので試してみます。

# Data Access API

https://api.slack.com/docs/apps/data-access-api

長々と書いてありますが、ようは下記のAPIを呼べるようにするための仕組みと私は理解しています。

https://api.slack.com/methods/assistant.search.context

このAPIはBOTのアクセストークンとは別に短時間だけ使える専用のトークン(action_token)を必要とします。

APIドキュメントにはOptionalと書かれていますが、実際は必須です。

このAPIはAIアシスタントユーザーが見れるプライベートチャンネルやDMを検索対象にすることができるので、Slack内の情報を使って何かさせるなら便利です。

尚、Data Access APIは記事執筆時点ではクローズドBetaのような状態なので、使うためには申請が必要です。

私は feedback@slack.com にメールでお願いしたら案内してもらえました。

メールをしてから使えるようになるまではだいたい2週間ほどかかりました。

申請が通ると、特定のイベントにaction_tokenが含まれるようになります。

# 検索してみる

まずは普通に検索してみます。

こんなKernelFunctionを書いてみました。

```cs
[KernelFunction]
[Description("""
    Slack内のメッセージを検索する
    """)]
public async Task<List<AssistantSearchContextResultMessage>> SearchSlackAsync([Description("検索クエリ")] string searchQuery) {

    if (Context.ActionToken is null) {
        throw new ArgumentException("action_tokenがないので実行できません");
    }

    var result = await SlackApiService.AssistantSearchContextAsync(searchQuery, Context.ActionToken, "public_channel,private_channel").ConfigureAwait(false);

    return result.Results.Messages;
}
```

シンプルに検索してくるAPIです。

アクショントークンはプラグインのコンストラクタから無理やり渡してきています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192685/f7e5f95c-f7f9-420a-8db9-799887cbc2dc.png)

リンクもいい感じに取ってきてくれています。

尚、プライベートな情報を検索するには各自で権限を付与する必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192685/83e5bfee-f08b-440b-a639-b1fe46ed3891.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192685/7025edcd-fec3-4a2c-b3f2-fa7ee7a0c353.png)

# もうちょっと面白いことをしてみる

これだと検索できただけなのでもうちょっといろいろやってみたいです。

assistant.search.contextにはチャンネルを絞る機能があるのでそれを使ってみます。

times_terukiというチャンネルがあるのでそれを要約してもらいます。

KernelFunctionを少し変更。

```cs
[KernelFunction]
[Description("""
    Slack内のメッセージを検索する
    """)]
public async Task<List<AssistantSearchContextResultMessage>> SearchSlackAsync([Description("検索クエリ")] string searchQuery,
        [Description("検索対象のチャンネル。対象を絞らない場合はnull。正規表現パターン: ^[CGDZ][A-Z0-9]{8,}$")] string? contextChannelId) {

    if (Context.ActionToken is null) {
        throw new ArgumentException("action_tokenがないので実行できません");
    }

    var result = await SlackApiService.AssistantSearchContextAsync(searchQuery, Context.ActionToken, "public_channel,private_channel").ConfigureAwait(false);

    return result.Results.Messages;
}
```

正規表現を入れてどんな形式かを明示してあげます。
SlackのチャンネルリンクはWebhookではチャンネルIDで渡ってくるので良い感じに解釈してくれるはずです。

結果はこちら。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192685/dd60e4a1-ce12-4fe1-90ab-b3114ef2ce54.png)

全く絞られておらずいろいろなチャンネルからデータを取ってきているようです。
ほぼお見せできない内容だったので黒塗りです:pray:

しかし、contextChannelIdはしっかりメッセージから取ってきています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192685/9f32d83a-d464-4c34-aca7-b794768aaad3.png)

SlackのAPIテスターで検証してみたら現時点ではcontext_channel_idとinclude_botsは何を指定してもレスポンスに変化がなかったのでもしかしたらバグっているのかもしれません。

Semantic Kernelはうまく動いていたのでAPIが正しく動作するようになったらより便利になりそうですね。

---

今回はSlackのData Access APIを使ってSemantic KernelにSlack検索をさせてみました。

Data Access APIに言及している記事は探した限りでは全くなかったので、本当にバグが原因なのかは分かりませんが今後に期待です。

また何か面白いことができたら記事にしたいなと思っています。

# Oh my teethについて
[Oh my teeth](https://www.oh-my-teeth.com/?tags=qiita)では未来の歯科体験を創るために日々活動しています。

Techチームではより良いユーザー体験を提供するべく、Webフロントエンドからバックエンド、スマホアプリに機械学習モデルなど、さまざまなプロダクトを開発しています。

一緒に未来の歯科体験を創りませんか？興味がある方は是非こちらを確認してください。

https://oh.careers/

カジュアル面談も可能なので気軽に応募してみてください！

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192685/0138b492-e02b-852e-61fa-fc065b53d285.png" width="450" />