2024年10月30日

【やってみた共有】GPTsでBacklogにコメント登録しよう

Contents

 

GPTsに翻訳させたコメントをBacklogに登録させたい

こんにちは、熊本事業所 Y・Mです。

ChatGPT初心者の筆者が、社内で利用している既存ツールと連携させて何かアウトプットする処理を開発するシリーズ 3回目の記事です。
第1回:ChatGPTと既存ツール連携 させたい・検討編
第2回:GPTsにBacklogのコメントを翻訳させたい

前回の記事にて、「GPTsにBacklogスペースの最近の更新から外国語のコメントを探させ、翻訳させる」処理の開発に取り組みました。前回はGPTsに翻訳結果を答えてもらうところまででしたが、今回はもう一歩発展して、その翻訳結果をBacklogにコメント登録させる処理の開発を行います。
他の方の事業部ブログとは少々趣が異なる記事となりますが、どなたかの参考になれば幸いです。

※なお、本記事で扱う事柄は筆者が個人的な興味・関心で取り組んだ内容の共有となります。弊社がビジネスとして行っているものではございません。諸々至らない点はご容赦ください。
※記載している内容は、2024年7月時点のものになります。

 

注意事項

作成したGPTsを一般に公開する前提では作成しておりません。
自分ならびに社内の関係者で利用する程度の範囲を想定して作成しておりますので、参考にしていただく場合は、利用範囲を明確にした上で必要なセキュリティ上の措置やルールの策定を講じていただく必要があります。
また、GPTsに取得させるBacklogのデータには情報セキュリティ上の問題を生じさせる内容を含まないことを事前にご確認ください。

 

準備

前回の記事をご参照ください。
前回記事はこちら

 

開発手順:GPTsからBacklogコメント登録APIを呼び出す


グレー背景部分が前回開発したGPTsの処理です。今回はオレンジ背景部分の処理を追加していきます。

1.Backlog課題コメント追加APIを呼び出すschema定義を作成する
前回は、最近の更新の取得API(https://developer.nulab.com/ja/docs/backlog/api/2/get-recent-updates/)を呼び出すためのCustom Actions用schemaを定義しました。
今回も同様に、まずは課題コメント追加API
(https://developer.nulab.com/ja/docs/backlog/api/2/add-comment/)を呼び出すschemaを定義します。
Custom Actions用のschemaは1ドメインに対して1つの構成となり、その中に複数のAPI呼び出し定義を含むことができます。したがって、作成済みのBacklogドメインschemaを修正する手順となります。

GPTs「構成」タブからアクションの修正ボタンを押下します。

Chat GPTに、修正前のschema定義と追加したいAPIの仕様解説ページのURLを提示して、schemaを修正するように依頼します。
このようにして修正してもらったschemaをテストして、コメント追加処理に成功しました。(当初のテストでは401エラー:Authentification failureが発生してしまいました。対応手順については後述の「エラー対応」にて説明致します)

完成したschema定義はこちらになります。最近の更新取得APIと課題コメント追加APIの2つの定義を含んでいます。


{
  "openapi": "3.1.0",
  "info": {
    "title": "Backlog API",
    "description": "Backlogと連携するためのAPI",
    "version": "v1.0.0"
  },
  "servers": [
    {
      "url": "https://your_backlog_domain.jp"
    }
  ],
  "paths": {
    "/api/v2/issues/{issueIdOrKey}/comments": {
      "post": {
        "operationId": "addComment",
        "summary": "課題にコメントを追加",
        "description": "課題に新しいコメントを追加します。",
        "parameters": [
          {
            "name": "issueIdOrKey",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "課題のIDまたは課題キー"
          },
          {
            "name": "apiKey",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "default": "***************"
            },
            "description": "APIキー"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string",
                    "description": "コメントの本文",
                    "example": "この課題に関する最新の進捗を報告します。"
                  },
                  "notifiedUserId[]": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    },
                    "description": "通知を受け取るユーザーID(複数指定可)"
                  },
                  "attachmentId[]": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    },
                    "description": "添付ファイルのID(複数指定可)"
                  }
                },
                "required": ["content"]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "コメントが正常に追加された場合のレスポンス",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "projectId": {
                      "type": "integer"
                    },
                    "issueId": {
                      "type": "integer"
                    },
                    "content": {
                      "type": "string"
                    },
                    "createdUser": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "userId": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "roleType": {
                          "type": "integer"
                        },
                        "lang": {
                          "type": "string"
                        },
                        "mailAddress": {
                          "type": "string"
                        }
                      }
                    },
                    "created": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "stars": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "notifications": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "無効なリクエスト",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "サーバーエラー",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/space/activities": {
      "get": {
        "operationId": "getRecentActivities",
        "summary": "最近の更新の取得",
        "description": "Backlogスペースで最近の更新を取得し、特定のテキストを含むコメントのIDを返します。",
        "parameters": [
          {
            "name": "apiKey",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "default": "***************"
            },
            "description": "APIキー"
          }
        ],
        "responses": {
          "200": {
            "description": "正常なレスポンス",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "project": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "projectKey": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          }
                        }
                      },
                      "type": {
                        "type": "integer"
                      },
                      "content": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "key_id": {
                            "type": "integer"
                          },
                          "summary": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          },
                          "comment": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "integer"
                              },
                              "content": {
                                "type": "string",
                                "description": "コメントの内容"
                              }
                            }
                          },
                          "changes": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "field": {
                                  "type": "string"
                                },
                                "new_value": {
                                  "type": "string"
                                },
                                "old_value": {
                                  "type": "string"
                                },
                                "type": {
                                  "type": "string"
                                }
                              }
                            }
                          }
                        }
                      },
                      "notifications": {
                        "type": "array",
                        "items": {
                          "type": "object"
                        }
                      },
                      "createdUser": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "userId": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "roleType": {
                            "type": "integer"
                          },
                          "lang": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "無効なリクエスト",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "サーバーエラー",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "エラーメッセージ"
          },
          "code": {
            "type": "integer",
            "description": "エラーコード"
          }
        }
      }
    }
  }
}

2.GPTsの指示内容を修正する
GPTsの指示内容に追加をしました。
以下が追加後の内容で、下線部分が追加部分になります。


応答は日本語で行ってください。日付や時間は日本標準時で処理してください。

そのユーザとの最初の会話時、ユーザのapiKeyを確認して、そのユーザとのやりとりではそのapiKeyをずっと使用してください。
再度指示された場合は変更してください。
デフォルトでと言われたら以下を使用。
"**************"

ユーザは、以下を都度指示します。日本標準時です。数字は全角半角が混在します。「9時」は「9:00」、「9時半」は「9:30」と解釈してください。「午前中」は「0:00~12:00」、「午後」は「12:00~24:00」と解釈してください。
・日付と時間(例:「今日の9時以降」 「2023年9月1日以降」)

処理の流れは以下です。
---処理開始---
①	getactivitiesを実行。countは固定。
エンドポイントのサンプル:https://your_backlog_domain.jp/api/v2/space/activities?apiKey=ユーザのapiKey&activityTypeId[]=3&count=100
---①のレスポンスリストでループ開始---
②レスポンスの"created"を世界標準時から日本標準時に変換したときにユーザの指示に一致しないデータ、レスポンスの"type"が3(コメント)以外のデータは以下の処理はスキップ
③レスポンスの"content"の"comment"の"content"の50%以上が日本語かそれ以外かを判定する
④"content"の"comment"の"content"の50%以上が日本語以外なら日本語に翻訳。"\r\n"と"\n"は改行に置換する。
⑤issueIdOrKeyを作成。レスポンスの"project"の"projectKey" + "-" + レスポンスの"content"の"key_id"
⑥課題URLを作成。課題URL = "https:// your_backlog_domain.jp/" + 作成したissueIdOrKey
⑦課題タイトルを作成。課題タイトル=レスポンスの”content"の"summary"
---ループ終了---
課題URL、課題タイトル、翻訳結果をセットリストにしてユーザに返答。過去の対話で処理済みのデータも再度教えてください。1件もなければ翻訳の必要性なしと返答してください。「例えば・・・」のような返答は不要です。
---翻訳結果の回答終了---
ユーザが結果をコメント登録するよう指示したら、addCommentを実行。
パラメータのissueIdOrKey=上記⑤で作成したissueIdOrKey
リクエストのcontent="【GPTs投稿】" + 翻訳結果の日本語テキスト
---翻訳結果のコメント追加終了---

実行結果がこちらです。



実際にBacklogにログインして確認した内容がこちらです。
意図通りにコメントが登録できました。

 

エラー対応

・課題コメント追加API呼び出しテストで401エラー:Authentification failure
当初、schema定義の修正作業を課題コメント追加APIの仕様説明ページ提示でChatGPTに依頼したところ、疎通確認で以下のエラーとなりました。

401:Authentification failure

直接的な原因は、URLパラメータとして必要なapiKeyがschema定義に反映されていなかったことでした。

失敗したときのリクエスト内容

さらに元をたどると、ChatGPTに提示した課題コメント追加APIの仕様説明ページにapiKeyについて明記されていなかったためでした。他のサービスとの連携についてもいえることかと思いますが、認証にまつわる事柄はAPIの共通仕様として別ページに記載されていたりするので、結果として個別の仕様解説ページに明記されていない必要事項が発生することがあります。

原因箇所がわかったので、以下のようにschema定義の修正をお願いしました。

修正後のschema定義で再度疎通確認し、エラーが解消しました。

成功したときのリクエスト

前回の記事を書いたときは動くものを作成するのにかなり時間がかかりましたが、今回の修正はかなりスムーズにできました。
GPTsとBacklogの連携に関しては中級者くらいになれたかもしれません。

今回掲載したschema定義やGPTsへの指示内容はコピペOKですので、どなたかの参考になればと思います!

記事 : Y.M

「再春館システム システムインテグレーション」はこちら