太陽がまぶしかったから

C'etait a cause du soleil.

SlackのAgents & AI Apps API でオリジナルのAIアシスタントをSplit Viewに常駐させて楽しいおしゃべり

Slack 用の常駐 AI エージェント

これまでのいくつかのエントリで AI を活用した Slack Bot の話をしてきたが、Slack はプロダクトとしての AI 対応の過程で、Agents & AI Apps API を公開しており、右側のペインに AI アシスタントが常駐できるようになった。

こんな感じ。右上の小さいアイコンで呼び出せばすぐに起動して挨拶をしたり、小話を話したり、ひとりごとも聞いてくれる。つまり仮想オフィス内における“AI 同僚”や“AI 秘書"が作れるということ。往年のデスクトップマスコットアプリの感覚に近い。

本記事では、そんな「しゃべるだけの AI アシスタント」を Slack に常駐させる手順を解説する。おしゃべりするだけと言っても Slack の情報を読み取れるし、「知識」をさまざまなところから持ってきて創発的な会話をしてもらうこともできるので、楽しいながらも発見がある。こともある。

Slack Agents & AI Apps 対応 AI アシスタント

When you add the Agents & AI Apps feature your app, you can build AI-powered, conversational apps integrated with your favorite large language model (LLM), and people can access them from the top bar in Slack. AI apps themselves are not an LLM; rather, they give you the tools and interface to best integrate an LLM for use in Slack.

API 設定の公式記事はこちら。基本的にはここを読んで実行していけば良いのだが、個人的にハマったところや工夫したところを中心に紹介していきたい。

ソースコード

シーケンス図

sequenceDiagram
    participant Slack
    participant PubBot
    participant SubBot
    participant OpenAI

    Slack->> PubBot: 起動通知
    PubBot->> Slack: ack して挨拶やプロンプトサジェスト
    Slack->> PubBot: メッセージ通知
    PubBot->> Slack: ack してプレースホルダメッセージを投稿
    PubBot ->> SubBot : メッセージ履歴等を topic に詰め込んで通知
    SubBot ->> SubBot : 処理内容をメッセージ履歴から判断
    subBot ->> SubBot : 処理内容に応じた前処理(Slack検索等)
    SubBot ->> SubBot : 前処理内容を埋め込んだプロンプト生成
    SubBot ->>OpenAI: プロンプト実行
    OpenAI->> SubBot:  プロンプト結果返却
    SubBot ->>Slack: chat.updateMessageで投稿

実装方針としては上記のシーケンス図の通りとする。メール要約ボットの際にも説明したが、Bot が Pub と Sub に分かれているのは、Slack API の 3 秒ルール によるもので、Slack 相手の責務としては即座に ack を返しつつ後続のサーバーに後から返信したり、情報を書き換えるための情報とともに topic 通知して遅延書き換えを行う方法を取っている。

今回も Cloud Functions で Bolt for Python での実装を前提としているというか、同じ BOT をそのまま拡張している。Slack Bot そのものの作り方については、上記を参照のこと。

Slack Agents & AI Apps 設定

Slack Agents & AI App を常駐させるための設定内容としては以下の通り。

  • 上記を ON にすると権限設定として、 assistant:write が勝手につくが、追加で im:historychat:write が必要となる。
  • そしてサブスクリプション設定として assistant_thread_started eventassistant_thread_context_changed eventmessage.im eventが必要となる。
  • 追加設定後にアプリをワークスペースに再インストールすると slack bot からワークスペースでアシスタントエクスペリエンスの設定が依頼される

  • ボタンを押下して出てくる設定画面でトップバーへの表示を設定

以上まで Slack 用の常駐 AI エージェントを起動することができるようになったが、はじめて設定する際にはちょっと迷ってしまったのでスクリーンショットなどを用意した。

asistant を bolt から利用する

プログラムする内容としては通常の BOT とあまり変わらないが、アシスタント向けのイベントを処理を行うために、slack_bolt.Assistant() インスタンスを生成して app.use に入れ込む必要がある。

app: slack_bolt.App = slack_bolt.App(
    token=SECRETS.get("SLACK_BOT_TOKEN"),
    signing_secret=SECRETS.get("SLACK_SIGNING_SECRET"),
    request_verification_enabled=True,
)
assistant: slack_bolt.Assistant = slack_bolt.Assistant()
app.use(assistant)

動的な挨拶やプロンプト提案

挨拶内容やプロンプト提案について固定的な設定もできるが、せっかくなので色々なパターンを用意したい。動的なプロンプト提案をするためには Agents & AI Apps 設定画面で Suggested Prompts を Dynamic とする。

@assistant.thread_started
def handle_assistant_start(say, set_suggested_prompts):
    greeting, prompts = slack_assistant.get_assistant_greeting_and_prompts()
    say(greeting)
    set_suggested_prompts(prompts=prompts)

slack_bolt.Assistant() を利用すると、AI アシスタントの起動時に @assistant.thread_started をつけたメソッドが呼ばれるため、ここで挨拶とプロンプトを生成して返信と設定ができる。いきなり ChatGPT に聞いてしまえば良い思うかもしれないが、起動時のラグが気になるため事前に生成した候補リストからルールベース + ランダムな挨拶 & プロンプト提案をするようにしている。

まだまだブラッシュアップが必要だが、曜日や時間帯に応じて話かける内容が変わるのはなかなか良い。天気やニューストレンドなどの情報も事前にフェッチしておき、ここの処理レイヤーに入れ込みたいと考えているが、起動のたびの外部アクセスするのは UX 的に避けたいところ。

本書は AI キャラクターが YouTubeライブ配信をする「AITuber」の開発をゴールとし、それに必要なシステムについて解説します。生成 AI を利用するプログラミングが必須で、なおかつ個人で楽しむためのツールや環境も整っており、LLM(大規模言語モデル)の機能を組み込んだプログラム開発を学ぶにはぴったりの題材です。 AI キャラクターを作り上げることができれば、YouTube 配信するだけではもったいない。本書では、自分の AI キャラクターが X でつぶやき、日記をブログに書く実装も紹介します。

このあたりは AI Tuber のノウハウを改めて参考にさせてもらっている。無味乾燥なエージェントを作りたいわけではないのだ。

システムプロンプトによるキャラクター設定の入れ込み

あとはプロンプトエンジニアリングの範疇であるが、せっかくなのでキャラクターナイズさせてみたい。最初にちらみせしたが、いつもの猫耳サイバーパーカー少女+スマートフォンアシスタントはラムダとシグマというオリキャラだったりもする。

後続の AI 処理におけるキャラクターナイズについて、今回はシステムプロンプトにキャラクター設定を入れて、個別プロンプトに入れ込まなくても会話ができるようにすることで複数のプロンプトを簡単に増やせるようにした。

また、現実環境の知識について、動的に変更することで時間軸に応じた会話や天気などの会話について適切に返しやすくなっている。メモリー情報を入れたりすることもできるだろう。

Split View で開いているチャンネルの情報取得

Split View で隣に開かれているチャンネルやスレッドを取得して、ツッコミを入れて欲しかったのだけど、これは API の思想的に許可されていない。あくまでチャンネルの側のペインをから明示的に起動した時だけ情報が特定できるようになっている。

公式の Slack AI だとチャンネル側にも AI 用のアイコンを作れるのだが、これが野良でもできるようになってしまうと月 1200 円の Slack AI サブスクリプションの価値が大きく減ってしまうから許可しないだろう。この辺りは、Notion AI などと同じで、営利企業におけるプロダクト設計の舵取りの難しさを感じる。

function calling で起動すべき処理の選択と実行

アシスタントにメッセージを入力したら、単純に GPT の API を呼ぶだけでも良いのだけど、話しかけられた内容に応じて、処理を切り分けて専門的な処理をすることで有用となるため、そのための判定処理を slack-sub-bot/src/function/generative_agent.py at main · bulldra/slack-sub-bot · GitHub のようにルールベース + Chat GPT の function calling の両面で実現している。

ここに特化型のクラスと判定条件を登録していくことで連携先をよしなに増やしていけるため、一気に AI エージェント感が出てくるが、この仕組み自体は 2023 年ごろに作っていたものからの流用なので、時代が追いついてきた感がある。リモート MCP や Dify の API などにも対応できると良い。

「便利」ではなく「楽しいおしゃべり」のための AI

個々のエージェントが何をできるか、などについては作り込んだ上で改めて紹介したいが、これまでの会話内容に応じて Slack に溜めたナレッジを検索してラムダとシグマでブレストを始める DeepResearch もどきがなかなか楽しい。あえて自分がシェアしたことのある情報だけをリサーチすることで、「自分の語彙」から作るためハイコンテキストな話も高速に理解しやすい。

o3 の出力は圧縮された索引であり、意味は展開に宿る。索引とは“見出し語”の羅列だ。本文を読まずに索引だけを追えば、全貌がほのかに浮かぶが詳細は掴めない。o3 の回答には索引と本文が混在しており、索引にこそ価値が宿る。この圧縮は帯域節約のためだけに存在せず「探索空間を狭める指標」という役割もある。

その意味では Kindle ハイライトやこのブログのエクスポート記事を送り込むのも良いのかもしれない。『AI資産化のために14年間分のTwitterログに並列URL展開とMPSによるツイートトピックス分類ラベル付与 - 太陽がまぶしかったから』もある。

その上で、このアシスタントの会話自体はハルシネーションを許容した「与太話」であることを意識している。正しいことを知りたいなら本を読むべきだし、o3 を使えば良い。あくまで突飛だけども、ちょっと発見もあって、パーソナライズされた共通話題としてのあるあるを刺激するぐらいの塩梅が「楽しいおしゃべり」としての中毒性がある部分なのかもしれない。