ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Chat gpt] Plugin 개발하기
    카테고리 없음 2023. 8. 5. 17:42
    Chat gpt Plugin 이란..

    나의 서비스에 ChatGPT를 plugin하는 것이 아니라 ChatGPT에 나의 서비스를 결합하는 기능

     

     

    Plugin Flow

     

    1) manifest file 생성  

     

    - manifest 파일은 내 도메인의 특정 경로에 존재해야함 (domain.com/.well-known/ai-plugin.json)

    - plugin meta에 대한 설명이 있어야함 (name, logo, 등)

    - 인증이 필요한 경우 Oauth URL 등 인증과 관련된 정보를 제공해야함

    - OpenAPI spec 지정

    * 이 때 endpoint 수를 1-2개 정도로 제한하여 토큰 수를 줄이는 것을 권장하는데, plugin 설명과 API 요청, API

    response가 모두 ChatGPT와의 대화 기록에 들어가기 때문이다. 이 말은 즉 모델의 토큰 리밋에 해당된다는 것.

     

    2) ChatGPT UI에서 plugin을 등록

     

    3) 유저가 plugin을 수동으로 activate해주어야함

     

    - OpenAI 측에서 ChatGPT에게 메시지로 plugin에 대한 compact한 설명을 전달, 유저는 이 메시지를 보지 못한다. 이 때 plugin 설명, endpoint, example을 포함하여 GPT가 스스로 plugin을 언제 호출해야 적당할지에 대해 판단할 수 있게 한다.

    - 유저와의 대화 중에 plugin과 연관된 내용이 있다면 ChatGPT가 plugin을 호출할 것인지 판단하게 된다. (단, 만약 POST API라면 plugin 개발자가 직접 user confirmation flow를 짜야함)

    - API 콜에 대한 응답 결과는 rich preview로 보여진다. (이 때 OpenGraph protocol 사용)

     

     

    ChatGPT plugin 만들기 = 위 Plugin Flow 에서 "manifest file 생성"

     

    1) 내 서비스의 API 만들기

     

    2) API를 document화하여 OpenAI yaml파일이나 JSON파일로 담기

     

    3) JSON Manifest 파일을 만들어 plugin 메타에 대한 정보를 담기

    총 3단계를 통해 plugin을 완성할 수 있다.

     

     

     

    chat gpt 에서 제공하는 예제를 통해 알아보자
    예제 (https://github.com/openai/plugins-quickstart)

    폴더 구조

    plugins-quickstart

    .well-known ( 폴더 )

         ㄴ ai-plugin.json

    main.py

    ㄴ openapi.yaml

    ㄴ requirements.txt

     

    소스 작성

    1. main.py 내 간단한 서비스 작성

     

    import json

    import quart
    import quart_cors
    from quart import request

    app = quart_cors.cors(quart.Quart(__name__), allow_origin="https://chat.openai.com")

    # Keep track of todo's. Does not persist if Python session is restarted.
    _TODOS = {}

    @app.post("/todos/<string:username>")
    async def add_todo(username):
        request = await quart.request.get_json(force=True)
        if username not in _TODOS:
            _TODOS[username] = []
        _TODOS[username].append(request["todo"])
        return quart.Response(response='OK', status=200)

    @app.get("/todos/<string:username>")
    async def get_todos(username):
        return quart.Response(response=json.dumps(_TODOS.get(username, [])), status=200)

    @app.delete("/todos/<string:username>")
    async def delete_todo(username):
        request = await quart.request.get_json(force=True)
        todo_idx = request["todo_idx"]
        # fail silently, it's a simple plugin
        if 0 <= todo_idx < len(_TODOS[username]):
            _TODOS[username].pop(todo_idx)
        return quart.Response(response='OK', status=200)

    @app.get("/logo.png")
    async def plugin_logo():
        filename = 'logo.png'
        return await quart.send_file(filename, mimetype='image/png')

    @app.get("/.well-known/ai-plugin.json")
    async def plugin_manifest():
        host = request.headers['Host']
        with open("./.well-known/ai-plugin.json") as f:
            text = f.read()
            return quart.Response(text, mimetype="text/json")

    @app.get("/openapi.yaml")
    async def openapi_spec():
        host = request.headers['Host']
        with open("openapi.yaml") as f:
            text = f.read()
            return quart.Response(text, mimetype="text/yaml")

    def main():
        app.run(debug=True, host="0.0.0.0", port=5003)

    if __name__ == "__main__":
        main()

    python 을 잘 모르나.. 코드만 봐도 자바에 비해선 압도적으로 간단하다.. ㅎㅎ

     

    def main():
        app.run(debug=True, host="0.0.0.0", port=5003)
     

    로컬 서버 run 을 위 한줄로 하다니 .. 비교 차원에서 자바 서버 구축 예시를 첨부한다.

    너무 길어서 링크로... (https://heodolf.tistory.com/133)

     

    언뜻 코드를 보니.. open api 쪽에서 아래 두 파일을 참조하는 듯 싶다.

    @app.get("/.well-known/ai-plugin.json")

    @app.get("/openapi.yaml")

     

    그럼 이제 위의 해당하는 두 파일을 만들어보자.

     

    2. ai-plugin.json 파일 생성

    해당 파일을 생성하여 API 도메인에 호스팅해야한다. 이때 반드시 도메인 아래 /.well-known 폴더에 있어야한다.

    {
        "schema_version": "v1",
        "name_for_human": "TODO List (no auth)", // plugin 이름 
        "name_for_model": "todo", // 모델이 plugin 을 사용할 때 쓸 이름
        "description_for_human": "Manage your TODO list. You can add, remove and view your TODOs.",
        "description_for_model": "Plugin for managing a TODO list, you can add, remove and view your TODOs.", // 모델이 알아야하는 설명
        "auth": {
          "type": "none"
        },
        "api": {
          "type": "openapi",
          "url": "http://localhost:5003/openapi.yaml" // 내가 서비스하고있는 api 도메인.
        },
        "logo_url": "http://localhost:5003/logo.png",
        "contact_email": "legal@example.com",
        "legal_info_url": "http://example.com/legal" // 유저가 plugin 정보를 조회 시, 리다이렉트 될 url
      }

     

    3. openapi.yml 파일 생성 = OpenAPI definition

     

    OpenAPI specification에 정의된 것 외에는 ChatGPT가 API에 대해 아는 바가 전혀 없기 때문에 이를 활용하여 ChatGPT가 접근해야/해도 되는 endpoint만 지정해서 알려주는 것이 좋다. 예를 들어 소셜미디어 API라고 한다면 유저들의 포스트에 코멘트를 남기는 등의 API는 자제하고, content 자체에 대한 access를 할 수 있는 API만을 오픈한다는지 등이다.

    아래는 예제 서비스의 api definition 이다.

    openapi: 3.0.1  // version 정보 : yaml 파일의 가장 위쪽에 기술함
    info:
      title: TODO Plugin // api 제목 ( 필수 작성 )
      description: A plugin that allows the user to create and manage a TODO list using ChatGPT. If you do not know the user's username, ask them first before making queries to the plugin. Otherwise, use the username "global".
      version: 'v1' // api 버전 정보 ( 필수 작성 )
    servers: // target 되는 host server 의 주소를 기록해두는 필드. optional 한 필드이며 없을때 기본 url 은 '/'
      - url: http://localhost:5003 // 위 main.py 를 보면 알겠지만 localhost:5003 으로 서버를 띄우므로 이렇게 작성함.
    paths: // 실제 api 는 다 이 path 에서 작성함
      /todos/{username}:
        get:
          operationId: getTodos
          summary: Get the list of todos
          parameters:
          - in: path
            name: username
            schema:
                type: string
            required: true
            description: The name of the user.
          responses: // get 으로 위 url 로 들어오면... response 는 아래와 같이 보내네
            "200": // 정상적으로 들어왔을땐
              description: OK
              content:
                application/json:
                  schema:
                    $ref: '#/components/schemas/getTodosResponse' // 요 components 들에 대해선 아래에서 기술
        post:
          operationId: addTodo
          summary: Add a todo to the list
          parameters:
          - in: path
            name: username
            schema:
                type: string
            required: true
            description: The name of the user.
          requestBody:
            required: true
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/addTodoRequest'
          responses:
            "200":
              description: OK
        delete:
          operationId: deleteTodo
          summary: Delete a todo from the list
          parameters:
          - in: path
            name: username
            schema:
                type: string
            required: true
            description: The name of the user.
          requestBody:
            required: true
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/deleteTodoRequest'
          responses:
            "200":
              description: OK

    components: // 재사용가능한 object 들을 모아둔 곳이 바로 이 component 임
      schemas:
        getTodosResponse:
          type: object
          properties:
            todos:
              type: array
              items:
                type: string
              description: The list of todos.
        addTodoRequest:
          type: object
          required:
          - todo
          properties:
            todo:
              type: string
              description: The todo to add to the list.
              required: true
        deleteTodoRequest:
          type: object
          required:
          - todo_idx
          properties:
            todo_idx:
              type: integer
              description: The index of the todo to delete.
              required: true

     

    4. 끝 !  의잉..? 생각보다 별거없다.

    이제 chatgpt UI 에서 플러그인만 등록해주면된다.

    ( 단 2023.08 월 기준 플러그인은 chatgpt 4에서만 지원해주므로.. 월 20달러 구독을 하면 사용까진 자유롭게 할 수 있음.

    다만 plugin 등록은 waiting list 에 등록하고 기다리면 openapi 쪽에서 검토하고 plugin 등록 권한을 부여해주는 듯 하다.)

     

    waiting list 에 등록은 해뒀고.. 승인나면 올려봐야지 ㅎㅎ

     

    다음에는 plug in 등록 포스트로 돌아오겠다.

    댓글

Designed by Tistory.