APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

Confluence Loaderを使い、Confluenceの内容に基づいて回答するSlack Botを作る

こんにちは!ACSD松崎です。

前回の記事では、Llama HubのConfluence RoaderをGoogle Colab上で動かしましたが、 今回はそこで得た学びを活かしてConfluenceの内容に基づき回答してくれるSlack Botを作ろうと思います。

作成するアプリケーションの概要

  • Slack Botの実装には、Bolt for Pythonを使います
  • Slack Botのホスティングですが、今回はVMの上でDockerコンテナをDaemonとして動かす方式を採用します
    • PWなどの秘匿情報は .env にて保持し、docker composeの environment でDockerコンテナに渡します
  • SlackBot ⇔ Slack Server間の通信はSocket Modeで実行します ※セキュリティ担保のため

アプリのデモ

予め以下のConfluenceページをConfluence Cloud上に用意します。

上記のページの情報をSlack Botに質問すると、ページに記載の情報を元にした回答が得られます。

作成手順

「作成するアプリケーションの概要」に記載の方針を元にSlack Botを作成した際のSlack Appの設定とソースコードを以下に記載します。

Slack Appの設定

  • OAuth & Permissions -> Bot Token Scopes に以下を登録
    • app_mentions:read
    • chat:write
  • Event Subscriptions に app_mention を登録

Slack Botの実装コード

app.py

import json
import os
import openai

from llama_hub.confluence.base import ConfluenceReader
from llama_index import GPTVectorStoreIndex

from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler

# 環境変数から必要なConfig値を取得
# * Confluence Roaderの動作に際し別途CONFLUENCE_USERNAMEと
#   CONFLUENCE_PASSWORD をexportする必要があるので注意
app = App(token=os.environ["SLACK_BOT_TOKEN"])
openai.api_key = os.environ["OPENAI_API_KEY"]
slack_bot_id = os.environ["SLACK_BOT_ID"]
openai.api_key = os.environ["OPENAI_API_KEY"]



@app.event("app_mention")
def handle_mention(event, say):

    # Debug
    print(json.dumps(event,indent=2))

    # event[text]には、メンションが含まれているので、メンションを削除する
    input_message = remove_slack_id_from_text(event["text"], slack_bot_id)
    channel = event["channel"]
    user_slack_id = event["user"]
    print("user_slack_id =>" + user_slack_id)

    thread_ts = event["ts"]
    channel = event["channel"]

    answer = query_engine.query(input_message)

    # say()の第1引数に渡せるよう、
    # llama_index.response.schema.Response -> str にキャスト
    answer_str = str(answer)

    say(text=answer_str, thread_ts=thread_ts, channel=channel)

# slack_idと\nは捨てる
def remove_slack_id_from_text(text, slack_id):
    return text.replace(f"<@{slack_id}>\n", "")

# アプリを起動
if __name__ == "__main__":

    # 読み込ませるConfluence Spaceとページの指定
    base_url = "https://example.com/wiki"
    reader = ConfluenceReader(base_url=base_url)
    page_ids = [xxxxxx]

    # Confluenceページの取得とvector index化
    print("Start to get Cofuence Doc")
    documents = reader.load_data(page_ids=page_ids)

    print("Start indexing")
    index = GPTVectorStoreIndex.from_documents(documents)

    # Query Engineの生成
    print("Genarate Query Engine")
    query_engine = index.as_query_engine()

    # ソケットモードでSlack Appを開始
    SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start()

Dockerコンテナの定義

Dockerfile

FROM python:3.8-slim-buster

WORKDIR /app

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD [ "python", "./app.py" ]

compose.yaml

version: '3.8'
services:
  python-gpt:
    build: .
    environment:
      - SLACK_BOT_TOKEN=${SLACK_BOT_TOKEN}
      - SLACK_APP_TOKEN=${SLACK_APP_TOKEN}
      - SLACK_BOT_ID=${SLACK_BOT_ID}
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - CONFLUENCE_USERNAME=${CONFLUENCE_USERNAME}
      - CONFLUENCE_PASSWORD=${CONFLUENCE_PASSWORD}

.env

SLACK_BOT_TOKEN=xoxb-xxx
SLACK_APP_TOKEN=xapp-xxx
SLACK_BOT_ID=xxx

OPENAI_API_KEY=sk-xxx

CONFLUENCE_USERNAME=matsuzaki@example.com
CONFLUENCE_PASSWORD=xxx

requirements.txt

slack-bolt==1.18.0
slack-sdk==3.21.3
langchain==0.0.284
llama-hub==0.0.27
llama-index==0.8.22
nltk==3.8.1
openai==0.28.0

今後改良したい点

今回は、プロトタイプ目的でとりあえず動くことを目指しましたが、 今後以下の点を改良したいと考えています。

  • GPT3.5ではなくGPT4を使う
  • Confluenceページの取得&インデックス作成処理は、Dockerの外で行い、Dockerには永続化されたindexを起動時に渡すようにする
  • queryの回答の際に、引用元のConfluenceページのURLを回答に含める

終わりに

ドキュメントを元に回答するBotは活用範囲が広くて良さそうですね! プライベートでNotionを使っているので、Notion対応verも別途作ってみたいと思いました。