DifyとLLMを用いてgitlabの自動コード・レビューを作成しましょう(3)

前回までの投稿でDIFYの設定は完了しました。

DIFYではDIFY自らが提供するUIも使えますがAPIサーバー形でも使えます。以下はAPI・アクセスの使用方法を見せるページです。

![image-20250225131842185.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55902/b09ac25b-735e-4f3f-b548-aa8bc5243885.png)

このAPIを使うためにはAPIキーが要ります。このキーは右の上の「APIキー」で貰えれます。

ではこのDIFY・APIとgitlabのwebhookを連結する簡単なサーバー・コードを作成してみましょう。

gitlabのWebhookから直接DIFY・APIを直接見させるのもかんがえられますが、パラメーターに制約もあるし安定性にも保証ができないので別のサーバープログラムを作ってnginx unitで駆動します。

DIFY・APIで接続するコードは以下のようです。

```python
# file : difyserver.py
import requests
import json

API_KEY = "****-&&&&&&&**********" # Dify API
BASE_URL = "http://gitlab.example.com/v1"


# Run workflow
def run_workflow(inputs, response_mode, user):
    url = f"{BASE_URL}/workflows/run"
    headers = {
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    }
    data = {
        "inputs": inputs,
        "response_mode": response_mode,
        "user": user
    }
    response = requests.post(url, headers=headers, data=json.dumps(data))

    if response.status_code == 200:
        if response_mode == "blocking":
            result = response.json()
            if "data" in result and "outputs" in result["data"] and "error_message" in result["data"]["outputs"]:
                return result["data"]["outputs"]["error_message"]
            else:
                print("Error: 'error_message' not found in the API response.")
        elif response_mode == "streaming":
            for chunk in response.iter_content(chunk_size=None):
                if chunk:
                    print(chunk.decode())
    else:
        print(f"Request failed with status code {response.status_code}")
```
`inputs`では前の投稿で定義した「**Start**」ノードに伝達されるデータをdictionaryに受け取ってDify・Flow

を動かしてその結果を貰う関数です。それではこれを用いてgitlabでWebhookからデータを貰って処理するコードを作ってみます。

```python
#filename : webhookserver.py

from flask import Flask, request, jsonify
import requests
import threading
import difyserver

app = Flask(__name__)

# GitLab 설정
GITLAB_URL = "https://gitlaburl"

def process_webhook(data):
    response_mode = "blocking"
    user = "sungjoon kim"
    results = difyserver.run_workflow(data, response_mode, user)

@app.route("/gitlab-webhook", methods=["POST"])
def gitlab_webhook():
    data = request.json
    GITLAB_TOKEN = request.headers.get("X-Gitlab-Token")
    # MR 열림 이벤트 확인
    if data.get("object_kind") == "merge_request" and data.get("object_attributes").get("state") == "opened":
        try:
            project_id = data["project"]["id"]

            # run workflow with GITLAB_TOKEN, project_id, GITLAB_URL, "master", "opened"

            inputs = {"PrivateToken": GITLAB_TOKEN,
                      "prjid": str(project_id),
                      "siteurl": GITLAB_URL,
                      "branch": "master",
                      "state": "opened",}

            threading.Thread(target=process_webhook, args=(inputs,)).start()
            return jsonify({"status": "OK"}),200
        except:
            return jsonify({"status": "Error in Dify Server"}),500


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)
```

Flaskを使って簡単に作成してみたサーバーです。気になる部分は`process_webhook()`をスレッドで実行させた部分です。DifyでLLMからの返事を待ってる時間が少し長いので(ほぼ10秒までかかったこともある)成功responseを貰うgitlab・webhooksで実行する時`Internal error occurred while delivering this webhook. Error: Net::ReadTimeout` ログが溜まってしまいました。この問題を解決するためにはWebhookのwaiting時間を少し長く(10秒以上)設定することができますが、これはgitlabでシステム管理者権限がないとできないので、次善策としてまず、スレッドでDify flowを実行した後、すぐに200応答を与えることにしました。

Flaskサーバーの駆動は僕は外部のubuntuサーバーでnginx unitを用いてしました。nginx unitではなくnginxでやってもいいし、別のWSGIサーバーでやってもいいです。ここにはこの部分までは説明しません。

gitlabのWebhookの設定は以下のようです。

![](https://raw.githubusercontent.com/cheuora/cheuora.github.io/master/_posts/2025/images/image-20250222174956432.png)

URLフィールドでは先のFlaskが駆動されてるURLを書いてSecret Tokenフィールドにはgitlab・プロジェクトのAccess tokensの値を入れます。Triggerは「Merge Request events」をチェックします。

下はMRの新規登録後自動で関連レビューをコメントした結果です。

![image-20250225144124196.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55902/651ee88c-7614-4238-b1aa-0b134e5646ed.png)

### この作業をして感じた点

* Dify自体でgitlabプラグインがありますが、ここでやろうとしている作業には足りないのでコードでノードを構成しました。
* このレビュー機能のみ必要であれば、あえてDify・Flowを構成せずにpythonコードだけでやっても良かったと思います。
* Flowをバックボーンにしてこのような作業をしたらさまざまのメリットがあるようです。
  1. 簡潔なコードができる：多くのコードが分離されてフローに配置されるため、変数調整、継承など考慮することがたくさん減りました。
  2. 拡張性：コードだけでやるのとは違って、プラットフォームが提供する拡張性があります。例えばここにはLLMとしてグーグルのGeminiを選択しましたが、簡単にOpenAIのGPTとか他のモデルと切り替えられます。Flowの中で「知識」ノードを追加してわが組織だけのコーディング・ルールを適用し、それに基づいてレビューを要請するのもできます。