ChatGPTめっちゃ痩せたいです

初めまして。HRBrainでソフトウェアエンジニアをしているビクトルと申します。

最近ChatGPTが人気になって、私はChatGPTのAPIを試して遊びたいと思っていました。私を知ってる人は筋トレマニアという印象を持っているのでChatGPTに栄養指導をしてもらおうと思っています。

最初にどんなことを考えましょ

- ChatGPTのAPI権限周りを研究
- [ChatGPTのライブラリを調べ](https://platform.openai.com/docs/libraries)
- ChatGPTのリスポンスとリクエストを研究
- APIのコスト

![Screenshot 2023-06-16 at 14.04.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2961977/ce4b9a91-165b-5216-9e0f-23d7dec233cb.png)

:cry: 


今回の目標はシンプルなサービスを作ることだけではなく、爆速で開発をすることです（一週間以内に全部終わりたいです）

早速行きましょ！

## ChatGPTのリクエストとリスポンス

最初からChatGPTのリスポンスは気にせずにしたいので2つの方法を考えました。

- [WebSocket](https://qiita.com/tags/websocket)を使う
- ASYNCリクエストを使う

ASYNCでしてみれば実装が複雑になりそう気がしたのと、今回は速さが大事なのでWebSocketを選択しました。

## Show me the code!

ChatGPTをinputに1日の摂取カロリーと体重と持ってる材料で3日以内の栄養指導を説明してくれるというアプリを作りたいと思っています。

### FrontEnd側

スピードに対して[next](https://nextjs.org/)を選択しました。

メインページで`WebSocket`クライアントを使いましょ

```typescript
const ws = new WebSocket("ws://<server>:<port>/<endpoint>")
```

これで四つのイベントをハンドリングしましょう

```typescript
ws.onopen = () => {
  // サーバーと繋がるとき
}

ws.onerror = () => {
  // エラーが起こるとき
}

ws.onmessage = (event) => {
  // メッセージが返すとき
  // eventでデータと他のプロパティを持っている
}

ws.onclose = () => {
  // WebSocketのconnectionを閉まるとき
}
```

### BackEnd側

ここでChatGPTと連携をする。

使っているライブラリは

- [chi](https://github.com/go-chi/chi) ルーター
- [melody](https://github.com/olahol/melody) WebSocketサーバー
  このライブラリはgorillaのwebsocketのラッパーです
- [go-openai](https://github.com/sashabaranov/go-openai)

https://zenn.dev/spiegel/scraps/fa5fcc9f7f1781

コードはこんな感じになっている

```golang
m := melody.New()
r := chi.NewRouter()

// CORSを設定しないといけない
r.Use(cors.Handler(cors.Options{
    AllowedOrigins: []string{"http://*", "ws://*"},
    AllowedMethods: []string{http.MethodPost, http.MethodGet, http.MethodOptions},
    AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
    MaxAge:         300,
}))

// このルーターには最初`/`をつけてWebSocketのルーターを書いている
r.Route("/", func(ws chi.Router) {
    ws.Get("/<endpoint>", func(w http.ResponseWriter, r *http.Request) {
        if err := m.HandleRequest(w, r); err != nil {
            // エラーをハンドリング

            return
        }
    })
})
```

`Melody`ライブラリは`Javascript`の`WebSocket`と似てるメソッドを実装して。。。

```golang

// HandleMessageはクライアントからのイベントを受けるハンドラー
m.HandleMessage(func(s *melody.Session, msg []byte) {
    
    // ここにユースケースでChatGPTライブラリを叩いている
    d, err := advisory.Question(ctx, domain.Advisory{Message: question.Question})
    if err != nil {
        fmt.Println(err)
        _ = m.Broadcast([]byte(`err: ` + err.Error()))
    }

    b, err := json.Marshal(reqDomain.Response{Advise: d.Message})

    // クライアントにリスポンスを返す
    _ = m.Broadcast(b)
})

// WebSocketと連携を切られるとき
m.HandleDisconnect(func(session *melody.Session) {
    _ = session.Write([]byte(`diss`))
})
```

ユースケースで`CreateChatCompletion`のメソッドを使う、このメソッドにはChatGPTのWebでやりとりをするときと一緒です。

```golang
resp, err := a.client.CreateChatCompletion(
    ctx,
    openai.ChatCompletionRequest{
        Model: openai.GPT3Dot5Turbo0301,
        Messages: []openai.ChatCompletionMessage{
            {
                Role:    openai.ChatMessageRoleUser,
                Content: adv.Message,
            },
        },
    },
)
```

`Model`は`ChatGPT`のAIモーデルです。

https://gptforwork.com/guides/openai-gpt3-models

これでサーバーとクライアントを実行して試していきましょーー

![ezgif.com-video-to-gif (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2961977/762ede24-7abd-d471-a2a7-2b4bbe4671b5.gif)


コード管理はこちらです。

https://github.com/ervitis/nutrition-advisory