GitHub Actionsで実現する高度なイシュー管理: 安野たかひろ都知事選マニフェストリポジトリの自動化ワークフロー解説

DevOps

はじめに

安野たかひろ氏の都知事選マニフェストリポジトリで使用されているGitHub Actionsワークフローは、オープンソースプロジェクトの効率的な管理を実現する優れた例です。このワークフローは、新しいイシューが開かれた際に自動的に実行され、コンテンツの適切性チェック、重複イシューの検出、そしてラベル付けを行います。

この記事では、issue-review.ymlファイルの内容を詳細に解説し、初心者の方でも理解できるように丁寧に説明していきます。


自分のリポジトリに組み込んだ例がこちら

I.R.I.S. v0.2.0 アップデートガイド:初心者のための詳細解説
はじめに:I.R.I.S.とは?I.R.I.S.は、GitHubのイシュー(問題や課題の報告)を自動的に分析し、適切なラベル(タグのようなもの)を付けてくれるAIアシスタントです。例えば、バグ報告なのか、新機能の要望なのか、ドキュメントの改...
I.R.I.S:GitHubイシュー管理を革新する人工知能アシスタント(v0.1.0)
はじめに:I.R.I.Sとは何か?皆さん、こんにちは!今日は、「I.R.I.S」(Intelligent Repository Issue Solver)についてご紹介します。I.R.I.Sの概要I.R.I.Sは、GitHubのイシュー(問...

ワークフローの概要

file

このGitHub Actionsワークフローは以下の主要な機能を持っています:

  1. 新しいイシューが開かれたときに自動的に起動
  2. イシューの内容を分析し、不適切なコンテンツをチェック
  3. 既存のイシューとの重複を検出
  4. 必要に応じてラベルを付与

ワークフローの詳細解説

トリガーとパーミッション設定

name: Issue Review

on:
  issues:
    types: [opened]
permissions:
  issues: write
  contents: read

このセクションでは、ワークフローの名前を定義し、トリガー条件とパーミッションを設定しています。

  • on.issues.types: [opened]: 新しいイシューが開かれたときにワークフローが起動します。
  • permissions: ワークフローがイシューの読み書きと、リポジトリコンテンツの読み取りを行うための権限を設定しています。

ジョブの定義

jobs:
  review_issue:
    runs-on: ubuntu-latest

review_issueという名前のジョブを定義し、最新のUbuntuランナーで実行するように指定しています。

ステップの設定

steps:
- name: Checkout repository
  uses: actions/checkout@v2

- name: Set up Python
  uses: actions/setup-python@v2
  with:
    python-version: '3.x'

- name: Install dependencies
  run: |
    python -m pip install --upgrade pip
    pip install openai
    pip install PyGithub
    pip install qdrant-client
    pip install regex

これらのステップでは、以下の準備を行っています:

  1. リポジトリのチェックアウト
  2. Pythonの設定
  3. 必要なPythonパッケージのインストール

メインの処理ステップ

- name: Review Issue with LLM
  env:
    OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    QD_API_KEY: ${{ secrets.QD_API_KEY }}
    QD_URL: ${{ secrets.QD_URL }}
  run: |
    python <<EOF
    # ... (Pythonスクリプト)
    EOF

このステップでは、環境変数を設定し、Pythonスクリプトを実行してイシューのレビューを行います。

Pythonスクリプトの詳細解説

以下、Pythonスクリプトの主要な部分を解説します。

必要なライブラリのインポートと初期設定

# 必要なライブラリをインポート
import github
from github import Github
import os
import openai
import regex as re
from qdrant_client import QdrantClient
from qdrant_client.models import PointStruct

# 環境変数から各種APIキーとURLを取得
token = os.getenv("GITHUB_TOKEN")
qd_api = os.getenv("QD_API_KEY")
qd_url = os.getenv("QD_URL")

# GitHubクライアントの初期化
g = Github(token)
repo = g.get_repo("${{ github.repository }}")
issue = repo.get_issue(${{ github.event.issue.number }})
issue_content = f"{issue.title}\n{issue.body}"

# ラベルの作成(既に存在する場合はエラーをキャッチして無視)
try:
    repo.create_label(name="toxic", color="ff0000")
    repo.create_label(name="duplicated", color="708090")
except:
    pass

# Qdrantクライアントの初期化
qdrant_client = QdrantClient(
    url=qd_url, 
    api_key=qd_api,
)

# OpenAIクライアントの初期化
openai_client = openai.Client()
embedding_model = "text-embedding-3-small"
collection_name = "issue_collection"

この部分では、必要なライブラリをインポートし、各種APIクライアントを初期化しています。また、GitHubリポジトリからイシューの情報を取得し、必要なラベルを作成しています。

画像の不適切性チェック関数

def validate_image(text):
    # GPT-4-visionを使用して画像の不適切性をチェック
    model_name = "gpt-4o"
    prompt = "この画像が暴力的、もしくは性的な画像の場合trueと返してください。"

    # テキストから画像URLを抽出
    image_url = re.search(r"!\[[^\s]+\]\((https://[^\s]+)\)", text)
    if image_url and len(image_url) > 1:
        image_url = image_url[1]
    else:
        return False

    try:
        # OpenAI APIを使用して画像の内容をチェック
        response = openai_client.chat.completions.create(
          model=model_name,
          messages=[
            {
              "role": "user",
              "content": [
                {"type": "text", "text": prompt},
                {
                  "type": "image_url",
                  "image_url": {
                    "url": image_url
                  },
                },
              ],
            }
          ],
          max_tokens=1200,
        )
    except:
        # エラーが発生した場合は、安全のためTrueを返す
        return True

    # レスポンスに"true"が含まれていれば不適切と判断
    v = response.choices[0].message.content.lower()
    return "true" in v

この関数は、イシューに含まれる画像URLを抽出し、GPT-4-visionを使用して画像の不適切性をチェックします。

コンテンツの不適切性チェック関数

def judge_violation(text):
    # OpenAIのモデレーションAPIを使用してテキストの不適切性をチェック
    response = openai_client.moderations.create(input=text)
    flag = response.results[0].flagged

    # 画像の不適切性もチェック
    video_flag = validate_image(text)

    if flag or video_flag:
        print(response)
        issue.add_to_labels("toxic")
        if video_flag:
            warn = "不適切な画像です。アカウントBANの危険性があります。"
        else:
            warn = "不適切な投稿です。アカウントBANの危険性があります。"
        issue.create_comment(warn)
        issue.edit(state="closed")
        return True
    return flag

この関数は、テキストの不適切性をOpenAIのモデレーションAPIでチェックし、画像の不適切性もvalidate_image関数を使用してチェックします。不適切なコンテンツが検出された場合、イシューにラベルを付け、警告コメントを追加してイシューをクローズします。

イシューの追加と重複チェック関数

def add_issue(text:str, iss_num:int):
    # テキストの埋め込みベクトルを作成し、Qdrantに保存
    texts = [text]
    ids = [iss_num]
    result = openai_client.embeddings.create(input=texts, model=embedding_model)
    points = [
        PointStruct(
            id=idx,
            vector=data.embedding,
            payload={"text": t},
        )
        for idx, data, t in zip(ids, result.data, texts)
    ]
    qdrant_client.upsert(collection_name, points)
    return text

def merge_issue(iss:int):
    # 重複イシューにラベルを付け、コメントを追加
    issue.add_to_labels("duplicated")
    print(f"merge to {iss}")
    issue.create_comment(f"#{iss} と重複しているかもしれません")
    return iss

def qd_search(text:str):
    # Qdrantで類似イシューを検索
    results = qdrant_client.search(
        collection_name=collection_name,
        query_vector=openai_client.embeddings.create(
            input=[text],
            model=embedding_model,
        )
        .data[0]
        .embedding,
    )
    return results

def qd_add(text:str, iss_num:int):
    # 新しいイシューをQdrantに追加
    texts = [text]
    ids = [iss_num]
    result = openai_client.embeddings.create(input=texts, model=embedding_model)
    points = [
    PointStruct(
        id=idx,
        vector=data.embedding,
        payload={"text": text},
    )
    for idx, data, text in zip(ids, result.data, texts)
    ]
    qdrant_client.upsert(collection_name, points)

これらの関数は、イシューの追加、重複チェック、そして類似イシューの検索を行います。Qdrantベクトルデータベースを使用して、効率的な類似性検索を実現しています。

メイン処理

# イシューの不適切性をチェック
if judge_violation(issue_content):
    quit()

# 類似イシューを検索
results = qd_search(issue_content)

if len(results) > 2:
    results = results[:3]
else:
    results = results
print(results)

# 検索結果を整形
res = ""
for i in results:
    res+=f'id:{i.id}\n内容:{i.payload["text"]}\n'
res = res.strip()

# GPT-4を使用して重複イシューの判断
prompt = f"""
以下は市民から寄せられた政策提案です。
{issue_content}
この投稿を読み、以下の過去提案の中に重複する提案があるかを判断してください。
{res}
重複する提案があればそのidを出力してください。
もし存在しない場合は0と出力してください。

[出力形式]
id:0
"""
print(prompt)
completion = openai_client.chat.completions.create(
  model="gpt-4o",
  max_tokens= 1024,
  messages=[
  {"role": "system", "content": prompt},
  ]
)
review = completion.choices[0].message.content
if ":" in review:
    review = review.split(":")[-1]
if review.isdecimal():
    if review == "0":
        add_issue(issue_content, issue.number)
    else:
        merge_issue(int(review))
print(review)

メイン処理部分では、以下の手順でイシューの処理を行います:

  1. イシューの不適切性をチェック
  2. 類似イシューを検索
  3. GPT-4を使用して重複イシューの判断
  4. 結果に基づいてイシューを追加または重複としてマーク

まとめ

このGitHub Actionsワークフローは、高度な自然言語処理技術と機械学習を活用して、イシュー管理を自動化しています。主な特徴は以下の通りです:

  1. OpenAIのAPIを使用した不適切コンテンツの検出
  2. Qdrantベクトルデータベースを使用した効率的な類似イシュー検索
  3. GPT-4を活用した重複イシューの判断

このような自動化により、大規模なオープンソースプロジェクトでのイシュー管理が効率化され、コミュニティの健全性維持と建設的な議論の促進が可能になります。

開発者の方々は、このワークフローを参考にして、自身のプロジェクトに合わせたカスタマイズを行うことで、より効率的なプロジェクト管理を実現できるでしょう。

参考サイト

election2024/.github/workflows/issue-review.yml at main · takahiroanno2024/election2024
東京都知事選2024のリポジトリ. Contribute to takahiroanno2024/election2024 development by creating an account on GitHub.

コメント

タイトルとURLをコピーしました