ChatGPT に Slack のメッセージを送信させてみた

こんにちは!Insight Edgeの小林まさみつです。
以前からChatGPTが様々なところで話題に上がっていて、とても盛り上がっていますね。
弊社でもChatGPTを活用してどのように価値を生み出すことができるかを日々考えており、業務の10%の時間を使って行なっている勉強会の1つのテーマとなっています。
その勉強会の中で「私の代わりにChatGPTがSlackのメッセージを送信する」アプリを作成しましたのでご紹介いたします。
※本プログラムでは、実行される処理の内容がChatGPTの回答に依存するため、意図しない処理を実行する可能性があります。ご利用の際は自己責任でお願いいたします

目次

1. 概要

現在、ChatGPTを活用した文章生成アプリケーションが様々に存在しますが、その大部分は人との対話やチャット用途に焦点を当てています。
一方で、ChatGPTの能力を活かし、生成されたプログラムを実際に動作させるアプリは前述の用途に対するアプリに比べてあまり探求されていない印象です。
そこで今回は、ChatGPTの潜在能力を最大限引き出す新たな試みに取り組みました。具体的には、ChatGTPに送信された文章の回答だけでなく、その回答をSlackに投稿するプログラムも生成させるようにし、文章の送信からそのプログラムの実行までを一括で行うアプリを開発しました。

アプリの動作イメージ

本アプリでは以下のような動作をイメージして作成していきます。

  1. コンソールに、SlackのチャネルIDと投稿したい内容を生成する文章を入力する

     投稿したいチャネルIDを入力してください:D03TDUPPQMP
     投稿したい内容を入力してください:食事に誘って
    
  2. Slackにメッセージが投稿される Slackに投稿されたメッセージ

2. 構成

アプリの構成は以下の通りです。
アーキテクチャ図

2.1. 構成要素

登場する要素は以下の通りです。

  1. コンソール:今回はコンソール上でアプリを動作させます。
  2. GPTアプリ:コンソールから受け取った文章を元に、OpenAI APIへリクエストを送信し、ChatGPTにより生成されたプログラムを実行します。
  3. OpenAI API:OpenAI社 が提供しているAPIです。今回は LangChain を用いてAPIを呼び出します。
  4. Dockerコンテナ:自身の環境を汚さずに実行したいため、Dockerを用います。
  5. Slack API:Slack のAPIです。メッセージを送信するために用います。

2.2. 処理の流れ

処理の流れとしては以下の通りです。

  1. コンソールでSlackへ投稿したいメッセージの内容をGPTアプリへ入力する。
  2. OpenAI APIへ以下の2つのリクエストを送信する。
    1. コンソールに入力された内容を元に、投稿するメッセージを生成する。
    2. Slackにメッセージを投稿するプログラムを生成する。
  3. OpenAI APIから先ほどの2つの回答を受け取る。
  4. 受け取った内容を元に、Dockerコンテナを起動する。
  5. DockerからSlack APIへメッセージ投稿のリクエストを送信する。
  6. Dockerコンテナを削除し、自身の環境を元に戻す。

3. 環境

本システムの環境は以下の通りです。
前述の通り、OpenAI APIを利用するために、LangChainを用います。

  • MacOS 13.3.1
  • Python 3.11.1
  • pip 22.3.1
  • openai 0.27.7
  • langchain 0.0.184
  • Docker 20.10.20

各種APIキーは以下から作成できます。

4. 実装

OpenAIは、APIリクエストの内容を保存しない(学習に用いない)としていますが、念の為SlackのAPIキーは送信しません。そのため、環境変数に格納しておき、その環境変数を用いてプログラムを作成するようにしています。
また、プログラムをシェルスクリプトとして生成してもらうことで、Pythonのsubprocessモジュールで直接実行するようにしました。

import subprocess
from langchain.chat_models import ChatOpenAI
from langchain.schema import (
    HumanMessage,
    SystemMessage
)
from langchain import LLMChain
from langchain.prompts.chat import (
    ChatPromptTemplate,
    HumanMessagePromptTemplate
)
import os
import re

os.environ["OPENAI_API_KEY"] = "**********"
os.environ["SLACK_API_KEY"] = "xoxp-**********"


# Slackに投稿するテキスト生成用のLLMChain
def create_slack_message(question):
    chat = ChatOpenAI(temperature=0)
    human_message_prompt = HumanMessagePromptTemplate.from_template(
        """相手に対して次の内容を送信してください。
        

        Q:今日の挨拶をして
        おはよう!元気?
        
        では、以下の内容でお願いします。
        {text}""")
    question_prompt = ChatPromptTemplate.from_messages([human_message_prompt])
    question_chain = LLMChain(llm=chat, prompt=question_prompt)
    answer = question_chain.run(text=question)
    return answer


# Dockerfileを生成し、実行するコード生成用のLLMChain
def create_docker_code():
    chat = ChatOpenAI(temperature=0)
    messages = [
        SystemMessage(content="あなたはシェルスクリプト、Docker、Slack APIにとても詳しく親切な人です"),
        HumanMessage(
            content="""
            以下の内容を実行するシェルスクリプトを作成してください。
            1. Slack APIを用いて他者にチャットを送るために必要な記述をしたDockerfileをカレントディレクトリに作成してください。
            2. また、そのDockerfileを利用してコンテナを起動してください。
            3. 2で作成したコンテナを削除してください。
            以下の条件を満たしてください。
            ・SlackAPIキー:$SLACK_API_KEY(環境変数に設定済み)
            ・送信先のチャネルID:$CHANNEL_ID(環境変数に設定済み)
            ・メッセージの内容:$SLACK_MESSAGE(環境変数に設定済み)
            ・SlackAPIキーはリクエストヘッダに、チャネルIDはリクエストボディに含めてください

            回答は、以下の例のように記述してください。
            特に、コメントや説明が無くスクリプトのみ記載されていることに気をつけてください。
            また、改行コードはエスケープしてください。

            ```sh
            cat << EOF > Dockerfile\\nFROM alpine:latest\\nRUN apk add --no-cache curl\\nCMD curl -X GET -d '{"text":"$QUESTION","channel":"$CHANNEL_ID"}' "https://google.com"\\nEOF

            docker build -t google-app .

            docker run -d -p 80:80 google-app

            docker rm -f google-app

            docker rmi google-app
                ```
            """
        ),
    ]
    return chat(messages).content


if __name__ == "__main__":
    # Slackに投稿するチャネルIDを環境変数に設定
    os.environ["CHANNEL_ID"] = input("投稿したいチャネルIDを入力してください:")

    # Slackに投稿するテキストを生成し、環境変数に格納
    message_request = input("投稿したい内容を入力してください:")
    slack_message = create_slack_message(message_request).replace("\n", "\\n")
    os.environ["SLACK_MESSAGE"] = slack_message

    # Slackに投稿するためのスクリプトを生成し、その中のコードのみを抽出する
    ans = create_docker_code()
    code_block = re.findall(r'```[^\n]*\n(.*)```', ans, re.DOTALL)[0]
    codes = code_block.split("\n\n")

    # subprocessを用いてコードを1行ずつ実行する
    for shtxt in codes:
        subprocess.run(shtxt, shell=True)

※本アプリはプログラムの生成がOpenAI APIに依存するため、エンジンが更新されたタイミングなどで動かなくなる可能性があります。受け取る回答のフォーマットに左右されないように改善していく必要があります。

5. 実行結果

実行結果は以下の通りです。
Dockerが起動し、実際にSlackへメッセージが投稿されていることを確認できました。

5.1. コンソールとSlack

% python gpt.py
投稿したいチャネルIDを入力してください:D03TDUPPQMP
投稿したい内容を入力してください:食事に誘って
[+] Building 3.2s (6/6) FINISHED                                                                                                                                                               
 => [internal] load build definition from Dockerfile   0.0s
 => => transferring dockerfile: 434B   0.0s
 => [internal] load .dockerignore   0.0s
 => => transferring context: 2B   0.0s
 => [internal] load metadata for docker.io/library/alpine:latest   3.1s
 => [1/2] FROM docker.io/library/alpine:latest@sha256:02bb6f428431fbc2809c5d1b41eab5a68350194fb508869a33cb1af4444c9b11   0.0s
 => CACHED [2/2] RUN apk add --no-cache curl   0.0s
 => exporting to image   0.0s
 => => exporting layers   0.0s
 => => writing image sha256:71d44499df5f6bea3c9bf89a84b59e0a3ceba1042f0dce52b63f4667749cacee   0.0s
 => => naming to docker.io/library/slack-app   0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
15e9ef691c891cffe18016ba92c106288cc7e0abc7f61b670cbfa4fd71275c23
15e9ef691c89
Untagged: slack-app:latest
Deleted: sha256:71d44499df5f6bea3c9bf89a84b59e0a3ceba1042f0dce52b63f4667749cacee

Slackに投稿されたメッセージ

5.2. 実際に処理されたシェルスクリプト

Slackへメッセージを投稿するためのスクリプトです。
catコマンドでDockerfileを生成し、それを元に起動・削除しています。

cat << EOF > Dockerfile
FROM alpine:latest
RUN apk add --no-cache curl
CMD curl -X POST -H "Authorization: Bearer $SLACK_API_KEY" -H "Content-type: application/json" -d \'{"channel":"$CHANNEL_ID","text":"$SLACK_MESSAGE"}\' "https://slack.com/api/chat.postMessage"
EOF
docker build -t slack-app .
docker run -d slack-app
docker rm -f $(docker ps -aqf "ancestor=slack-app")
docker rmi slack-app

6. まとめ・今後の展望

今回はChatGPTにSlackのメッセージを送信してもらうアプリを紹介しました。本アプリの作成を通してChatGPTの可能性をより実感できました。今後の展望としては、SlackのChannel IDを指定するのではなく、人名やチャネル名などを元に送信先を決定できるようしたいと考えています。そうすることでより使いやすくなりそうです。具体的には、Alexaアプリにすることでより簡単にメッセージを送信できるようになります。他にも、相手のメッセージに対して自動で返信してくれるようにするのも面白そうです。
本アプリではSlackのメッセージ送信を対象としましたが、他にも様々なコードを実行させることができるので色々試してみたいと思います。