在上週五,ChatGPT Plus 用戶終於可以使用 OpenAI 新推出了 GPTs 的客製化功能,提供我們以定制化和優化 GPT 模型以滿足特定行業和應用場景的需求,而且還可以和其他人分享這些創造。今天我就讓 GPTs 搭配我常用的 Gitea 整合一下合併請求功能,順便記錄一下我是如何創建 GPTs。那麼讓我們開始吧!
💡 目前 GPTs 只有 Plus 以上的訂閱用戶才可以創建和使用(包括別人建立好的)。




建立 GPTs

在 ChatGPT 的頁面中找到 Explore,使用 Create a GPT 來建立客製化 GPTs

接著會看到 Create 和 Configure 的頁簽,以及右方一個 Preview 的頁面

我們可以使用對話的方式(Create)一步一步和 GPT Builder 溝通,他會一步一步的引導你如何製作客製化的 GPTs,包括你的客製化 GPTs 圖像、描述、以及起始對話建議框

或是直接使用配置的方式(Configure)快速建置你要的客製化 GPTs

  • Instruction:這個欄位就是填入 Prompt,Prompt 越精準客製化的 GPTs 功能就會越強大。如果使用對話的方式,GPT Builder 會自動幫填入你和他討論並整理過後的 Prompt。
  • Conversation starters:起始對話建議互動框。
  • Knowledge:有點類似知識庫的東西,你可以提供一些 GPT 不知道的知識提供它當作解答的參考。譬如本篇的範例,我就會把 PR 時的一些規範寫進去,然後上傳到 Knowledge 後,請 GPTs 依照這些規範去審核和評估 PR。
    💡 目前我只知道可以上傳 TXT 檔案,其餘的格式大家可以自行嘗試看看。
    💡 2023/11/16 補充:關於 Knowledge 的限制和測試,可以參考 OpenAI 社群的討論【點我前往】。
  • Capabilities:是否啟用如網路搜尋、DALL-E 圖像建置、運行程式碼、資料分析、檔案上傳等等的功能。
  • Actions:運行 GPTs 的同時串接你個人化的應用,如串接 ERP、串接 Google 服務等等。

接著我們使用對話的方式(Create)和 GPT Builder 溝通並建立我希望的 Gitea 合併請求助手的客製化功能


另外我們在對話的方式(Create)的過程 GPT Builder 也會統整 Prompt 並且幫我自動填入配置的方式(Configure)的 Instruction 和 Conversation Starters




設定Actions

接下來需要設定Actions,讓 GPTs 可以與 Gitea 進行溝通。Actions 使用的是 OpenAPI Specification 來定義整個數據結構。

💡 關於 OpenAPI Specification 的文件定義可以【點我前往】。目前為:v3.1.0版本。

  • Schema:定義 OpenAPI Specification 的位置。
  • Authentication:驗證 API Token。你也可以在 OpenAPI Specification 內個別針對 API 進行 Token 的設定。
    💡 個別設定請使用「Security Scheme Object 」。
  • Privacy Policy:隱私政策。
    💡 如果要開放出去給所有人用,則需要加入隱私政策宣告的網址列(其實隨便打一個網址也會通過,GPTs 不會特意檢查這個)。

Schema 可以透過選擇 Import from URL 旁的下拉選單,選擇 OpenAI  Profile,就會有一個定義好 OpenAPI Specification 的基本範例提供你參考



接著我們需要知道 Gitea 有什麼 API 可以使用,關於 Gitea 的 API 文檔可以【點我前往】。我們依照Gitea官方的文檔來建立第一個 API:「列出指定儲存庫的合併請求清單」

{
  "openapi": "3.1.0",
  "info": {
    "title": "Gitea PR Assistant",
    "description": "Gitea合併請求小助手",
    "version": "v1.0.0"
  },
  "servers": [
    {
      "url": "https://你的GiteaURL/api/v1"
    }
  ],
  "paths": {
    "/repos/TestGit/{repo}/pulls": {
      "get": {
        "description": "查詢指定儲存庫的合併請求清單",
        "operationId": "ListReposPullRequest",
        "parameters": [
          {
            "name": "repo",
            "in": "path",
            "required": true,
            "description": "儲存庫名稱",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "用於限制返回的合併請求數量,最高是10",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 5
            }
          },
          {
            "name": "state",
            "in": "query",
            "description": "篩選合併請求的狀態,例如'open'或'closed'",
            "required": true,
            "schema": {
              "type": "string",
              "default": "open"
            }
          }
        ],
        "deprecated": false
      }
    }
  },
  "components": {
    "schemas": {
    }
  }
}

  • servers:API 服務的 URL。
  • paths:API 的具體路徑或端點。
  • components:請求和回應中使用的數據結構。
💡 注意!每個paths內的operationId必須是唯一的命名。

設定完成後,我們可以到 Preview 上測試,讓 GPTs 透過 API 取得合併請求,並且列出清單

接著我們將剩下會用到的 API 都一起加入到 OpenAPI Specification 中:

  1. 列出指定儲存庫的合併請求號碼差異(diff)
  2. 提交對特定儲存庫的合併請求的審核

{
  "openapi": "3.1.0",
  "info": {
    "title": "Gitea PR Assistant",
    "description": "Gitea合併請求小助手",
    "version": "v1.0.0"
  },
  "servers": [
    {
      "url": "https://你的GiteaURL/api/v1"
    }
  ],
  "paths": {
    "/repos/TestGit/{repo}/pulls": {
      "get": {
        "description": "查詢指定儲存庫的合併請求清單",
        "operationId": "ListReposPullRequest",
        "parameters": [
          {
            "name": "repo",
            "in": "path",
            "required": true,
            "description": "儲存庫名稱",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "用於限制返回的合併請求數量,最高是10",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 5
            }
          },
          {
            "name": "state",
            "in": "query",
            "description": "篩選合併請求的狀態,例如'open'或'closed'",
            "required": true,
            "schema": {
              "type": "string",
              "default": "open"
            }
          }
        ],
        "deprecated": false
      }
    },
    "/repos/TestGit/{repo}/pulls/{index}.{diffType}": {
      "get": {
        "description": "查詢指定儲存庫對應的合併請求編號的合併請求差異",
        "operationId": "GetReposPullRequestDiff",
        "parameters": [
          {
            "name": "repo",
            "in": "path",
            "required": true,
            "description": "儲存庫名稱",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "index",
            "in": "path",
            "required": true,
            "description": "合併請求編號",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "diffType",
            "in": "path",
            "description": "指定返回的差異類型,例如'diff'或'patch',用於顯示合併請求中的變更內容",
            "required": true,
            "schema": {
              "type": "string",
              "default": "diff"
            }
          }
        ],
        "deprecated": false
      }
    },
    "/repos/TestGit/{repo}/pulls/{index}/reviews": {
      "post": {
        "description": "審核對特定儲存庫對應的合併請求編號的合併請求",
        "operationId": "SetReposPullRequestCreateReview",
        "parameters": [
          {
            "name": "repo",
            "in": "path",
            "required": true,
            "description": "儲存庫名稱",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "index",
            "in": "path",
            "required": true,
            "description": "合併請求編號",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "description": "審核合併請求的詳細資料",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetReposPullRequestCreateReviewRequest"
              }
            }
          }
        },
        "deprecated": false
      }
    }
  },
  "components": {
    "schemas": {
      "SetReposPullRequestCreateReviewRequest": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": ["APPROVED", "REQUEST_CHANGES", "COMMENT"],
            "description": "指定審查的動作或狀態,例如批准(APPROVED)、請求變更(REQUEST_CHANGES)或僅添加評論(COMMENT)。"
          },
          "body": {
            "type": "string",
            "description": "指定審查的回覆,請依據審查狀態回覆,例如批准(APPROVED)則回覆OK、請求變更(REQUEST_CHANGES)或僅添加評論(COMMENT)請回覆請求變更的問題或是評論。"
          }
        }
      }
    }
  }
}
💡 2023/11/16補充:使用Diff查詢差異時,如果差異查詢 API 回傳的字數過多時(目前查到是4096個 Token),GPTs 是會因為超過字數而無回傳 ResponseTooLargeError 哦!相關的連結可以【點我前往】或是【點我前往】。


💡 2023/11/17補充:另外當如果你的檔案不是 UTF-8 編碼時,在Gitea上查詢出來的資料就會是亂碼,亂碼也就間接導致 GPTs 或 OpenAI 的 API 端在處理回傳時發生錯誤。


最後一起來看看整個 Gitea 合併請求小助手的 Preview 的結果吧!




最後我們可以回到 Gitea 上看到 GPTs 代替我們拒絕了此次的PR,並且回應我們所指定的回覆

以上就是 GPTs 的建立和實際串接後達成的效果,我們利用 GPTs 搭配 Gitea API,讓審核 PR 的過程更簡單輕鬆,還可以請 GPTs 自行產生評語和退回原因,是不是覺得,鋼鐵人中的賈維斯真的離我們越來越近了呢!

💡 不過 PR 時,邏輯的部分當然還是需要人去看哦!只是搭配 GPTs 我認為可以省去理解程式碼的時間(因為你可以取得差異後,請 GPTs 解釋程式碼在做什麼),或是優先讓 GPTs 過濾掉一些常見的基本錯誤,如:命名規則、公司內部的程式碼規範等等。