OmniAICreator/Qiita-1.07M huggingface dataset:
Home Assistantの移行　～Virtual BoxからRaspberry Pi 4へ～

# Home Assistantの移行

以前まではWindowsマシン上でVirtual Boxを用いてHAOSを起動していたが、VMの再起動時にUSBのBluetoothドングルを手動で再認識させる必要があり、構成を変えたいと考えていた。
しかし、下手にSSL接続やらAlexaやらを設定していたためあまり気が進まず放置していた…。
今回ようやく重い腰を上げ、余っていたRaspberry Pi 4へ移行してみた。

## VM側からバックアップの作成

現Home Assistant側にて丸々バックアップを作成する。
システム > バックアップよりバックアップを作成。

![Screenshot_7.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3363316/7f90e504-4148-aada-052d-87144a2671af.jpeg)
![Screenshot_8.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3363316/c042279a-8a7f-1aad-27c7-28fbab1f9095.jpeg)
適当な名前を付けてわかりやすい場所へ保存。
![Screenshot_9.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3363316/e0005651-dccf-fe49-0598-eb2534bde168.jpeg)


## ラズパイセットアップ

まずはラズパイへHAOSのインストールを行う。
[公式のガイド](https://www.home-assistant.io/installation/raspberrypi/)に従ってラズパイへHAOSをインストールしていく。
![Screenshot_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3363316/b3af356e-a30e-ab44-d09b-3f22971e06fe.jpeg)
OSを選ぶ > Other specific-purpose OS > Home assistants and home automation > Home Assistant > Home Assistant OS 10.5 (RPi 4/400)を選択。
![Screenshot_6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3363316/c923a1f7-2edc-c06f-cf0d-fc42c588f425.jpeg)

MicroSDへの書き込みが完了したら、MicroSDをラズパイへ差し込んで起動。
ラズパイの画面を見ると無事Home Assistantが起動した。

### (参考)CLIからWifiへの接続を行う

今回は有線で接続しているが、Wi-Fiへの接続方法も示しておく。

Wifiドングルを使う場合は`network info`からのWifiドングルのIDを探す。
`interface: wlp1s0u1u4`という形でIDが出てくる。

`network update <interface id (この場合wlp1s0u1u4)> --ipv4-method auto –-ipv6-method auto --wifi-auth wpa-psk --wifi-mode infrastructure --wifi-ssid <Wi-FiのSSID> --wifi-psk <Wi-Fiパスワード>`
接続が終わると `Command completed successfully.`と出るはず。

---

再起動して[http://homeassistant:8123](http://homeassistant:8123)へアクセスすると無事接続できたので、しばらく待つ。
![Screenshot_10.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3363316/8e66e2e6-fdc2-3373-e0fb-4c63a019cc51.jpeg)

## バックアップからの復元

準備が完了すると初期セットアップ画面になる。
![Screenshot_11.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3363316/584e9c08-2446-05af-9ac2-fd224dfc7ead.jpeg)
今回はバックアップからの復元を選択
![Screenshot_12.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3363316/d099b48e-d547-1c18-8947-9c84bdbda46b.jpeg)
するとなぜかUnsupported file formatだと怒られる。
![Screenshot_13.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3363316/d5d86723-9df0-ea0f-1aad-c4a0d4c8ea57.jpeg)
どうやらFirefoxだとうまくいかないようなので、Chromeからトライすると無事ファイルを読み込んだ。良かった。
![Screenshot_14.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3363316/0d0c30d7-810b-a4b6-e4f0-2073d73153f1.jpeg)
![Screenshot_15.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3363316/0143e15e-5d9d-2eb4-b009-f45054b64ce4.jpeg)
RESTOREを選択し、またしばらく待つ。

### 待ってる間にルーター側の設定

Home AssistantのIPが変更になったため、ポートフォワーディングの設定も変更する。
ラズパイの新IPをコマンドプロンプトから`nslookup homeassistant`を行うことで確認し、
新IPの443番ポートを開放した。

## 移行完了

30分ほど待ったが復元中のまま進まないので、ブラウザでリロードした結果アクセスできなくなっていた。
元々の構成ではHTTPSでアクセスする形だったのを思い出し、`https://自分のグローバルIP`へ接続したところ、今まで通りにアクセスできるようになっていた。どうやら無事移行が完了したようだ。

Bluetoothの再セットアップをしたら完了。

SSLやAlexaの設定がおかしくならないか不安だったが、無事移行できてよかった。また問題が出たら追記する。
SpringBootで学ぶSQLインジェクション

# はじめに
## 想定読者
- SQLインジェクションという言葉は知ってるけど、どういうものなのか知らない人

## 検証環境
```
Mac
Java17
SpringBoot3.1.0
```

# SQLインジェクション
## 概要

SQLの呼び出し方に不備によって起こる脆弱性です。

攻撃者が任意のSQLを実行することができるため、以下のようなことが可能となります。

- データの更新や削除
- 取得できないはずのデータの取得 

ピンと来ていない方は以下の画像を見てください。
検索ボックスのユーザ名と一致するユーザしか取得できないのですが、` ' OR '1' = '1 `と入力することで全件のデータが取得できるようになります。
![タイトルなし.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169545/ade9d82b-acb1-f29b-420c-fed61b0dd20e.gif)



## 脆弱性の原因
リテラルに原因があります。
SQLの標準機能で文字列はシングルクォーテーションで囲むことになっています。
入力値にシングルクォーテーションが含まれる場合、入力値をそのまま使いSQL文を組み立てることで予期しないSQLを作ることができます。

例えば、入力値を基に商品を検索する機能があるとします。
入力値が「靴」と入力された場合に以下のようなSQLが呼び出せれます。

```
SELECT * FROM item WHERE category = '靴';
```


不正な入力値`' OR  '1'='1`を入力した場合は以下のSQLが呼び出されます。

```
SELECT * FROM item WHERE category = '' OR '1'='1'
```

シングルクォーテーションを重ねることにより、文字列リテラルが終了し、後続に任意の処理を追加することができます。

SQLの実装すると、SQLインジェクションが成立します。
`name`に`' OR '1'='1`が渡されると、変数queryは `SELECT * FROM User WHERE name =' OR '1'='1'`という文字列になります。
queryを実行するため不正なSQLが実行されることになります。
```java
public List<User> selectUsersByName(String name) {
	String query = String.format("SELECT * FROM User WHERE name = '%s'", name);
	RowMapper<User> rowMapper = new UserRowMapper();
	return this.jdbcTemplate.query(query, rowMapper);
}
```

## 対策方法
- エスケープ処理を行う
- プレースホルダを利用する

エスケープ処理は実装漏れなどの可能性があり、完全な対応が難しい為、プレースホルダを利用することが推奨されています。

### プレースホルダによる組み立て
可変な値（パラメータ部）に`?`を使ってSQL文に埋め込むやり方です。
SpringBoot（jdbcTemplateを利用）の場合は、以下のように書くことができます。

```java
public List<User> selectUsersByName(String name) {
	String sql = "SELECT * FROM user WHERE name = ?";
	RowMapper<User> rowMapper = new UserRowMapper();
	return this.jdbcTemplate.query(sql, rowMapper, name);
}
```
プレースホルダによる組み立ては、以下の2種類があります。
2つの違いとしてはバインド（パラメータに値を埋め込むこと）するタイミングが異なります。

#### 静的プレースホルダ
プレースホルダのままSQLをDBエンジンに渡し、SQLを実行する時にパラメータの値をDBに渡します。
バインド処理はDBエンジンで行う為、安全とされています。

#### 動的プレースホルダ
バインド処理をアプリケーション側で行います。
ライブラリでエスケープ処理が行われるので、自前でエスケープ処理を行うよりは安全とされていますが、ライブラリによっては、SQL構文を変化させてしまう可能性があるため静的プレースホルダが推奨されています。


# 参考リンク
- [安全なSQLの呼び出し方](https://www.ipa.go.jp/security/vuln/websecurity/ug65p900000196e2-att/000017320.pdf)
Kindleの技術書セールがヤバすぎる【Amazonプライム感謝祭】

半額以下で買えます。明日辺り終了なのでお早めに。

# Good Code, Bad Code ～持続可能な開発のためのソフトウェアエンジニア的思考
https://www.amazon.co.jp/gp/product/B0BSW72QKZ/
現代のリーダブルコードとも言われている良書。
Googleのソフトウェアエンジニア(テックリード)がまとめる良いコードとは何か？という解説。
変数/関数の命名、ネストの深さなど初歩的なことから、エラー設計やnullの扱い、ユニットテストなど上級者でもためになることが体系的に学べる。

# ドメイン駆動設計入門 ボトムアップでわかる！ドメイン駆動設計の基本
https://www.amazon.co.jp/dp/B082WXZVPC/
ドメイン（要件への知識）をコードに落とし込む手法について書かれている。

# プログラマー脳 ～優れたプログラマーになるための認知科学に基づくアプローチ
https://www.amazon.co.jp/dp/B0BVDQM5H1/
プログラミングを学ぶ、コードを読むということを脳科学の観点から解説。

# プリンシプル オブ プログラミング 3年目までに身につけたい 一生役立つ101の原理原則
https://www.amazon.co.jp/dp/B071V7MY82/

# セキュアなソフトウェアの設計と開発
https://www.amazon.co.jp/dp/B0CF48662T/

# 図解入門TCP/IP　仕組み・動作が見てわかる
https://www.amazon.co.jp/dp/B08MVXPW3V/

# インフラ/ネットワークエンジニアのためのネットワーク技術&設計入門 第2版
https://www.amazon.co.jp/dp/4797396806/


----
リンク化できてるページできてないページがあるのなぜ
基本情報技術者に受かった話！

この度、基本情報技術者に受かったので自分が行った学習方法を記載したいと思います。

プログラミング未経験という事でITパスポートを取得し、次はワンランク上の基本情報技術者を取ろうと、2022年9月より勉強を開始しました。

試験日は年に2回、期間内に一度だけ受験可能とのこと、2023年5月ぐらいに標準を絞り勉強を開始
、、、、、の最中に形式が変更になることが発覚、、、、、、
年2回の縛りが無くなり好きなときにいつでも受けれるようにITパスポートみたいになりました。（受験後1ヶ月のインターバルあり）
まぁ、そもそも勉強を始めたばかりなのでそんなに影響はなかったのですが科目B（旧午後試験）の例題がなく取り敢えず受けてみようと特に対策もせず挑むも盛大に撃沈しました笑
ちなみにこの時の点数が
科目A 約550点
科目B 約450点
平均がどのくらいか分かりませんが科目Bがチンプンカンプン
ITパスポートに毛が生えたものだと思い油断していたと言い訳をさせてもらいます( ；∀；)

ここからが本題です！

勉強に使用した教材
参考書
キタミ式基本情報技術者
出るとこだけ！基本情報技術者　科目B　第３版

youtubeでの動画（しらみ潰しに）

過去問道場の過去問


合格までの勉強時間
約200時間　

初めに動画で勉強方法の動画を回覧しまくりました。

午後試験は選択式から20問に変更、個人的にはかなり簡単になった気がしましたがどのような感じかわからなかった為初見の時は混乱しました。

動画の情報を自分なりに整理した結果
1.参考書の読破（1周）
2.科目Aの過去問道場をほぼ毎日６０問
3.平均して正解率が80％になるまで繰り返す。
4.科目Aを４０問と科目Bの参考問題と公式過去問、言語の参考書にてプログラムを読む

簡単にまとめるとこんな感じです。

運よく参考問題と少しですが公式の過去問があったのが幸いでした。

科目Aは過去問から５割ぐらい出題されるみたいなのでひたすら過去問を解くことが大事

科目Bはプログラミングに慣れていれば苦労しないとの事なので参考問題をしつつ、プログラミングの勉強も兼ねて言語の参考書を読み続けました。

合格の点数は、
科目A 660点
科目B 650点
かなりギリギリの点数でしたが合格は合格なので参考程度に勉強方法を投稿させて頂きます。

私と同じように周りに頼れる人がおらず、未経験から独学で資格取得を目指している方の参考になれば幸いです。


PS.挫折しそうな時は,資格取得について後押しをされている動画を見てモチベーションを上げることをお勧めします
回転式LiDARのスペック一覧

### Velodyne
| | Range[m] | V FoV[°] | V Res[°] | H Res[°] | Rate[Hz] |
|--|--:|--:|--:|--:|--:|
| VLP-16 | 100 | +15 to -15 | 2 | 0.1 - 0.4 | 5 - 20 |
| VLP-32C | 200 | +15 to -25 | min 0.33 | 0.1 - 0.4 | 5 - 20 |
| VLS-128 | 300 | +15 to -25 | min 0.11 | 0.1 - 0.4 | 5 - 20 |

### Hesai

| | Range[m] | V FoV[°] | V Res[°] | H Res[°] | Rate[Hz] |
|--|--:|--:|--:|--:|--:|
| XT16 | 0.05 ~ 120 | +15 to -15 | 2 | 0.18 | 10 |
| XT32 | 0.05 ~ 120 | +15 to -15 | 1 | 0.18 | 10 |
| QT128 | 0.05 ~ 50 | +52.6 to -52.6 | 0.4 | 0.4 | 10 |
| QT64 | 20 | +52.1 to -52.1 | 1.45 | 0.6 | 10 |
| Pandar 128 | 0.3 ~ 200 | +15 to -25 | 0.125 ~ 1 | 0.1 ~ 0.4 | 10,20 |
| Pandar 64 | 0.3 ~ 200 | +15 to -25 | 0.167 ~ 6 | 0.2 ~ 0.4 | 10,20 |
| Pandar 40 | 0.3 ~ 200 | +15 to -25 | 0.33 ~ 6 | 0.2 ~ 0.4 | 10,20 |


### Ouster
全て128 Channel.
| | Range[m] | V FoV[°] | V Res[°] | H Res[°] | Rate[Hz] |
|--|--:|--:|--:|--:|--:|
| OS0 | 0.5 ~ 35 | +45 to -45 | 0.7 | 0.175 ~ 0.7 | 10,20 |
| OS1 | 0.5 ~ 90 | +22.5 to -22.5 | 0.35 | 0.175 ~ 0.7 | 10,20 |
| OS2 | 0.5 ~ 200 | +11.5 to -11.5 | 0.175 | 0.175 ~ 0.7 | 10,20 |

### Robosense
Ruby Plusは128 Channel.
| | Range[m] | V FoV[°] | V Res[°] | H Res[°] | Rate[Hz] |
|--|--:|--:|--:|--:|--:|
| Ruby Plus | 0.4 ~ 240 | +15 to -25 | Up-to 0.1 | 0.1 ~ 0.4 | 5/10/20 |
| Helios 32 FoV70 | 0.2 ~ 150 | +15 to -55 | Up-to 1.33 | 0.1/0.2/0.4 | 5/10/20 |
| Helios 32 FoV31 | 0.2 ~ 150 | +15 to -16 | 1 | 0.1/0.2/0.4 | 5/10/20 |
| Helios 32 FoV26 | 0.2 ~ 150 | +10 to -16 | Up-to 0.5 | 0.1/0.2/0.4 | 5/10/20 |
| Helios 16 | 0.2 ~ 150 | +15 to -15 | 2 | 0.1/0.2/0.4 | 5/10/20 |
Android Studio: スライドショー

3秒毎に、画面が変わるスライドショーです。

## プログラム

```xml:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="こんにちは"
        android:textSize="60sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_gravity="center"
        android:background="#CCCCCC"/>

</androidx.constraintlayout.widget.ConstraintLayout>
```

```kt:MainActivity.kt
package com.example.disp02

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

import android.widget.ImageView
 import coil.load
import android.os.CountDownTimer
import android.widget.TextView

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val imageUrl = arrayOf(
            "https://www.city.fuji.shizuoka.jp/page/gazou/fmervo000001dsro-att/rn2ola000000lk6n.JPG"
,"https://www.city.fuji.shizuoka.jp/page/gazou/fmervo000001dsro-att/5000-21.jpg",
            "https://www.city.fuji.shizuoka.jp/page/gazou/fmervo000001dsro-att/fujikawa01b.jpg",
            "https://www.city.fuji.shizuoka.jp/page/gazou/fmervo000001dsro-att/rn2ola000000lk9d.JPG",
            "https://www.city.fuji.shizuoka.jp/page/gazou/fmervo000001dsro-att/rn2ola000000lko8.JPG")
        val timageView = findViewById<ImageView>(R.id.image_view)

        var textMessage = findViewById<TextView>(R.id.text_view)
        var count = 0
        var jcount = 0
        val days = arrayOf("日","月","火","水","木","金","土")
        object : CountDownTimer(21000, 3000) {

            override fun onTick(millisUntilFinished: Long) {
                textMessage.setText(days[count])
                timageView.load(imageUrl[jcount])

                count+=1
                count = count % 7
                jcount+=1
                jcount = jcount % 5

            }

            override fun onFinish() {
                textMessage.setText("完了!")
            }
        }.start()

    }

}
```

## 実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/b3bf03bd-13a3-46a4-39e4-74e4df112862.png)

## 参考情報

[Android Studio: Coil の使い方](https://qiita.com/ekzemplaro/items/1f52eca64d2339ea11f5)

[Android Studio: CountDownTimer の使い方](https://qiita.com/ekzemplaro/items/4163a9afdca68af2954f#%E6%94%B9%E9%80%A0%E3%81%97%E3%81%9F%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0)
【AWS】SNSからショートメール機能を使ってみました

# SNSからショートメールを送信
AWSのSNS送信機能を使って、ショートメールを送信してみました。

# ステップ１
```Amazon SNS```→```モバイルテキストメッセージ（SMS）```→```サンドボックスの送信先電話番号```→```電話番号を追加```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983478/7a96851f-4814-ce07-91d4-3f63ba3b34a9.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983478/12e31f29-188c-c30c-c48c-e382d22bfe87.png)

* 送信先国別を選択する
* 電話番号を入力する 
* 検証メッセージの言語を選択する

![截屏2023-10-14 18.52.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983478/65b08696-081b-287a-340a-030f9fd5a9e5.png)

* 上記に入力した電話番号にワンタイムパスワードが届くので、それを入力すること
* ```電話番号を認証```をクリックする 

![截屏2023-10-14 19.00.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983478/ad6f1305-e4b9-95b2-d241-f90e7a5402c6.png)

# ステップ2
```テキストメッセージの発行```をクリックする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983478/7a96851f-4814-ce07-91d4-3f63ba3b34a9.png)

* メッセージのタイプ: ```プロモーション```
* 上記ステップ１で登録した電話番号を選択する 
* ```メッセージ```に送信したい内容を入力する

![截屏2023-10-14 19.11.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983478/27a0c627-36f7-4bc4-4a6c-cabb5ef6c1b7.png)

* 他の設定はデフォルトのまま
* ```メッセージの発行```をクリックする

![截屏2023-10-14 19.16.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983478/c0f2b5f7-6a8a-985c-e4cf-099cdf42758b.png)

# ステップ3
上記ステップ１で指定していた電話番号にショートメールが届いたことを確認する

![IMG_6526.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983478/1f815547-1630-e9da-c5bc-f9a22e8a975e.jpeg)

# その他
上記手順はあくまでSMSサンドボックスにて、ショートメールを送信したケースの手順であり、サンドボックスを解除すると、事前に電話番号の認証をせずに送信できるので、サンドボックス解除手順は詳細AWSサポートに問い合わせする。

参考URL：

https://docs.aws.amazon.com/ja_jp/sns/latest/dg/sns-sms-sandbox.html
GitHub CodespacesでXdebugを使う

1. PHP Debugをインストール
1. launch.jsonを作成
1. Xdebugをリッスン
1. ブレイクポイントを設置
1. Webページを開く

![ezgif.com-video-to-gif (5).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435735/03356646-3120-7a8d-2d6d-f619ed4a4b1e.gif)

ターミナルからウェブサーバーを起動する場合
![2023-10-14-19-04-31.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435735/233a2f55-b3c7-7ab3-fb82-efea737b058f.gif)

よろしくお願いします。
【学習メモ】AWS X-Ray SDKにおける各プログラミング言語の環境変数について調べた

# X-Ray SDK for Java
（ほかの言語とは同じ）

AWS_XRAY_TRACING_NAME：SDK がセグメントに使用するサービス名を設定

AWS_XRAY_DAEMON_ADDRESS ：X-Ray デーモン リスナーのホストとポートを設定

AWS_XRAY_CONTEXT_MISSING ：コンテキスト欠落エラーが発生するとき、どのように処理するかを設定
　-RUNTIME_ERROR：デフォルトでは、SDK はランタイムの例外をスローするように設定
　-LOG_ERROR：エラーを記録して続行
　-IGNORE_ERROR：エラーを無視して続行

# X-Ray SDK for .NET
（ほかの言語とは同じ）

AWS_XRAY_TRACING_NAME：SDK がセグメントに使用するサービス名を設定

AWS_XRAY_DAEMON_ADDRESS ：X-Ray デーモン リスナーのホストとポートを設定

AWS_XRAY_CONTEXT_MISSING ：コンテキスト欠落エラーが発生するとき、どのように処理するかを設定
　-RUNTIME_ERROR：デフォルトでは、SDK はランタイムの例外をスローするように設定
　-LOG_ERROR：エラーを記録して続行
　-IGNORE_ERROR：エラーを無視して続行

# X-Ray SDK for Ruby
（ほかの言語とは同じ）

AWS_XRAY_TRACING_NAME：SDK がセグメントに使用するサービス名を設定

AWS_XRAY_DAEMON_ADDRESS ：X-Ray デーモン リスナーのホストとポートを設定

AWS_XRAY_CONTEXT_MISSING ：コンテキスト欠落エラーが発生するとき、どのように処理するかを設定
　-RUNTIME_ERROR：デフォルトでは、SDK はランタイムの例外をスローするように設定
　-LOG_ERROR：エラーを記録して続行
　-IGNORE_ERROR：エラーを無視して続行

# X-Ray SDK for Go
（基本ほかの言語とは同じだけど、AWS_XRAY_CONTEXT_MISSINGにはIGNORE_ERRORがない）

AWS_XRAY_TRACING_NAME：SDK がセグメントに使用するサービス名を設定

AWS_XRAY_DAEMON_ADDRESS ：X-Ray デーモン リスナーのホストとポートを設定

AWS_XRAY_CONTEXT_MISSING ：コンテキスト欠落エラーが発生するとき、どのように処理するかを設定
　-RUNTIME_ERROR：デフォルトでは、SDK はランタイムの例外をスローするように設定
　-LOG_ERROR：エラーを記録して続行

# X-Ray SDK for Node.js
AWS_XRAY_TRACING_NAME：SDK がセグメントに使用するサービス名を設定

AWS_XRAY_DAEMON_ADDRESS ：X-Ray デーモン リスナーのホストとポートを設定

AWS_XRAY_CONTEXT_MISSING ：コンテキスト欠落エラーが発生するとき、どのように処理するかを設定
　-RUNTIME_ERROR：デフォルトでは、SDK はランタイムの例外をスローするように設定
　-LOG_ERROR：エラーを記録して続行
　-IGNORE_ERROR：エラーを無視して続行

ーー以上がほかの言語と同じーー

AWS_XRAY_DEBUG_MODE：debugレベルでコンソールにログを出力するように SDK を設定するには、TRUEに設定

AWS_XRAY_LOG_LEVEL：デフォルトロガーのログレベルを設定、有効な値は、debug、info、warn、error、silent 。AWS_XRAY_DEBUG_MODE が TRUE に設定されている場合、AWS_XRAY_LOG_LEVELが無視される

# X-Ray SDK for Python
AWS_XRAY_TRACING_NAME：SDK がセグメントに使用するサービス名を設定

AWS_XRAY_DAEMON_ADDRESS ：X-Ray デーモン リスナーのホストとポートを設定

AWS_XRAY_CONTEXT_MISSING ：コンテキスト欠落エラーが発生するとき、どのように処理するかを設定
　-RUNTIME_ERROR：デフォルトでは、SDK はランタイムの例外をスローするように設定
　-LOG_ERROR：エラーを記録して続行
　-IGNORE_ERROR：エラーを無視して続行

ーー以上がほかの言語と同じーー

AWS_XRAY_SDK_ENABLED：falseに設定されている場合、SDK は無効になる。デフォルトでは、環境変数が false に設定されている場合を除き、SDK は有効

# 参考サイト
https://docs.aws.amazon.com/ja_jp/xray/latest/devguide/xray-daemon.html
AWS料金ざっくりメモ【個人用】

・1ドル=150円の場合のAWS料金の雰囲気をつかみたい
・料金計算ツールでの計算結果のチェック用の個人メモ
・データサイズが2倍なら金額2倍程度、コア数、メモリも同様に2倍なら金額も2倍程度
・金額は間違えてるかもしれないので注意！！ 詳細は各章の料金ツールURLから確認してください。

## データサイズ(TB)による課金
| サービス | 課金の単位 | 何千円？ |
|:--:|:--|--:|
| EBS  | サーバのSSDディスクサイズ(1TB) | 1万4千円 |
| RDS  | DBのSSDディスクサイズ(1TB) | 4万2千円 |
| S3  | 安い！ストレージ(1TB) | 4千円 |
|  AWS Backup(EBS) |  SSD(1TB)のバックアップ | 8千円 |
| EFS | NFSマウントできる！ファイルサーバ(1TB) | 1万4千円 |
| CloudWatch | 便利で高い！ログ保管(1TB) | 11万8千円 |

※ssdはgp2で計算
※2023年10月頃の料金計算ツールの結果を基に算出
※データ転送にかかる料金は含みませんが、AWS料金ツールのデフォルトで
データ転送が含まれているのであれば、デフォルトの条件のままかもしれません。
※上記の詳細条件(料金ツールURL)
https://calculator.aws/#/estimate?id=c33800a51f9954333cc6395ca71d12d2b53b08a0


## CPUとメモリによる課金
| サービス | インスタンスサイズ | vCPU | メモリGB | その他条件 |何千円？ |
|:--:|:--|--:|--:|:-:|--:|
| EC2  | m5.2xlargeインスタンス | 8 | 32 | Linux | 5万4千円 |
| EC2  | m5.2xlargeインスタンス | 8 | 32 | Windows | 6万5千円 |
| EC2  | m5.2xlargeインスタンス | 8 | 32 | SQLServerEnterpriseとWindows | 42万3千円 |
| RDS for MySQL  | db.r5.xlargeインスタンス | 4 | 32 | マルチAZ | 13万2千円 |
| RDS for Oracle  | db.r5.xlargeインスタンス | 4 | 32 | マルチAZ、Standard Two版ライセンス料金を含む | 24万4千円 |
| RDS for SQL server  | db.r5.xlargeインスタンス | 4 | 32 | マルチAZ、Enterprise版ライセンス料金を含む | 42万5千円 |

※2023年10月頃の料金計算ツールの結果を基に算出
※上記の詳細条件(料金ツールURL)
https://calculator.aws/#/estimate?id=b92ad683fc26027e33cc2b80e7495e318d53a2f4



## 所感
・データサイズに対してCloudWatchは高いので、直近のログ以外はS3に移動したほうが良い
・DBも全般的に高い気がするので、データベースの集約やインスタンスサイズの見直し、
　保管するデータサイズを減らす工夫は大事なのだと思う。




## 注意事項
・個人用なので、本記事のメンテをする予定はありません。
・間違いがあっても、多分直しません。
・とはいえ、クリティカルな間違い（桁が違う、何十パーセントオーダーで違う）は指摘して頂けるのは有難いです。
・気付いた人はこのページの内容をコピーしてメンテして頂くのは大歓迎

## 参考文献など
https://aws-rough.cc/
japanese-stablelm-instruct-alpha-7b-v2をtext-generation-webuiで動かした備忘録

なんかそのままだと動かなかったのでメモ

私はこの手の専門家ではないので、本当に治し方が合っているは分かりません。
あと将来的には直っているかも（執筆日:2023/10/14）

text-generation-webuiとjapanese-stablelm-instruct-alpha-7b-v2はダウンロード済みの想定です。

https://github.com/oobabooga/text-generation-webui

https://huggingface.co/stabilityai/japanese-stablelm-instruct-alpha-7b-v2

# 1.modules/models.pyの102行目辺りにtokenizerの処理を入れる

novelai-tokenizer？で初期化するようにします。
（huggingfaceに置いてあるサンプルコードと同じものです）
これがないとロード中にエラーが発生します。

```py
def load_tokenizer(model_name, model):
    tokenizer = None
    path_to_model = Path(f"{shared.args.model_dir}/{model_name}/")
    if any(s in model_name.lower() for s in ['gpt-4chan', 'gpt4chan']) and Path(f"{shared.args.model_dir}/gpt-j-6B/").exists():
        tokenizer = AutoTokenizer.from_pretrained(Path(f"{shared.args.model_dir}/gpt-j-6B/"))
    #以下の3行
    elif model_name.find("stablelm-instruct-alpha-7b-v2") != -1:
        from transformers import LlamaTokenizer
        tokenizer = LlamaTokenizer.from_pretrained("novelai/nerdstash-tokenizer-v1", additional_special_tokens=['▁▁'])
    elif path_to_model.exists():
        if shared.args.use_fast:
            logger.info('Loading the tokenizer with use_fast=True.')

        tokenizer = AutoTokenizer.from_pretrained(
            path_to_model,
            trust_remote_code=shared.args.trust_remote_code,
            use_fast=shared.args.use_fast
        )

    return tokenizer
```

# 2.trust-remote-codeをチェックしロード

**trust-remote-code**にチェックを入れ、モデルをロードします。
（私のマシンはVRAM12GBの非力マンなので、加えてload-in-8bitも指定しています。）
![スクリーンショット 2023-10-14 17.48.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277386/b2a826fb-dec3-e7db-2522-6f79f6e38dce.png)
これでモデルがロードできれば成功です

# 3.動作風景

「メロスはあり得ないぐらいにめっちゃ激怒した」と入れてみる。
![スクリーンショット 2023-10-14 17.55.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277386/92c92d3d-840a-3b56-b045-468e49640660.png)
メロスがやべーやつになってますが、なんかいい感じに書いてくれてますね。

次に質問っぽいのも
![スクリーンショット 2023-10-14 18.24.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277386/e5bcdf3f-fb62-2985-95f2-c09aaf796e6a.png)
質問に対する回答はあっていますね。それ以外の情報は分かりませんが…
ただ毎回合っているわけではなく、的外れな回答も返ってくるようです。
![スクリーンショット 2023-10-14 18.23.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277386/f5449851-e9f3-5c9b-4039-4bab4d8a3c0b.png)
次に、公式のフォーマットに極力沿ってやってみます。

![スクリーンショット 2023-10-14 18.20.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277386/d4bf27a5-ab75-e01d-df8d-14342c6ecc6a.png)
「にゃん」は付けてくれなかったけど、いい感じで返答してくれましたね。 

次にコード生成を試してみる。
![スクリーンショット 2023-10-14 18.32.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277386/e992721e-0ad5-d47a-18a8-bd33e2e9dc74.png)
書いてはくれるもののでたらめですね…
うーむ。load-in-8bitの弊害なのかな？
あるいはちゃんと直せていないのか…

ともあれ、OSSでこのクオリティは将来が楽しみです
【AWS】CLIでCloudWatchのカスタムダッシュボードを作成する

## はじめに
CLIでCloudWatchのカスタムダッシュボードを作成する手順。CLI操作用のIAMユーザ作成とか認証情報の設定は割愛。
1はカスタムダッシュボードはどんな感じかっていう話なので本題は2。

## 1. CloudWatchカスタムダッシュボードはこんな感じ(GUIで作ってみる)
CloudWatch -> ダッシュボード -> ダッシュボードの作成で画面右上の`+`か、`+ 最初のウィジェットを追加`をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/e1182efc-fe2f-25ab-6006-f25cfa0d628b.png)

好きな形式を選ぶ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/2987986d-9033-59cc-bd1b-12c0fa51451e.png)

Lambdaの実行時間のグラフ。他に確認したいメトリクスがあればさらに追加する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/878f25df-25fe-3f73-380d-6509d7b6d8be.png)


## 2. CLIでダッシュボードを作る
put-dashboardコマンドを使う。必須パラメータは`--dashboard-name`(ダッシュボード名)と`--dashboard-body`。(どんなウィジェットを含めるかJSON形式で指定する)
```
> aws cloudwatch put-dashboard --dashboard-name <String> --dashboard-body <String>
```


実際にやるとこんな感じ。(--dashboard-bodyの内容は変数に入れた)
propertiesのendpointで指定しているのはLambda関数。
CloudWatchはカスタムウィジェットとしてLambda関数も組み込める。
```
> $body = ‘"{
                \"widgets\":[
                    {\"type\":\"custom\",
                    \"x\":0,\"y\":0,
                    \"width\":6,
                    \"height\":6,
                    \"properties\":{
                        \"endpoint\":\"arn:aws:lambda:us-east-1:xxxxx:function:GetMetrics\",
                        \"updateOn\":{\"refresh\":true,
                                        \"resize\":true,
                                        \"timeRange\":true},
                        \"title\":\"test\"}
                    }
                ]
            }"’

> aws cloudwatch put-dashboard --dashboard-name test1 --dashboard-body $body
{
    "DashboardValidationMessages": []
}

```

Lambdaを使えばアラートの設定内容を表示させることも可能。(使い道あるかは分からない……)
![alert.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/378d0758-2ead-e057-70c6-aa050f55525d.png)


コマンドのドキュメントは以下

https://docs.aws.amazon.com/cli/latest/reference/cloudwatch/put-dashboard.html


## 3. ダッシュボードの情報をゲットする
--dashboard-bodyどうやって書くんだ…っていうときの参考にどうぞ。

```
> aws cloudwatch get-dashboard --dashboard-name test1
{
    "DashboardArn": "arn:aws:cloudwatch::xxxxx:dashboard/test1",
    "DashboardBody": "{\"widgets\":[{\"type\":\"custom\",\"x\":0,\"y\":0,\"width\":6,\"height\":6,\"properties\":{\"endpoint\":\"arn:aws:lambda:us-east-1:xxxxx:function:GetMetrics\",\"updateOn\":{\"refresh\":true,\"resize\":true,\"timeRange\":true},\"title\":\"test\"}}]}",
    "DashboardName": "test1"
}
```

## おわりに
--dashboard-bodyの指定が若干手間だけど、実行したらサクッとダッシュボード作れます。
リーダブルコード　第2章　名前に情報を詰め込む　まとめ

- メソッド名に「get」といった明確ではない単語の使用は避ける
例）インターネットから取ってくる処理のメソッド名は、
`get`よりも
`FetchPage()``DownloadPage()`の方が明確

- 命名に困ったらシソーラス（類語辞典）を用いて単語を探す

- `i`,`j`,`k`,`iter`　という名前は、イテレータ以外に使うと紛らわしくなるので注意

- イテレータが複数あるときは、i,j,kなどの1文字よりも具体性がある名前を使う
例）
`if(clubs[i].menbers[j] == users[k])`
から、
`if(clubs[club_i].menbers[menbers_i] == users[users_i])`
といった、「変数名+_i」といった名前にする
あるいは
`if(clubs[ci].menbers[mi] == users[ui])`
のように、`clubs`の最初の文字である`c`+`i`→`ci`という名前にするのもよい

- 変数が時間やバイト数など、計測できるものであれば、変数名に単位を入れるとよい

- 名前をつけるとき、プロジェクト固有の省略形を使ってはいけない
例）
  1. evaluation → eval
  2. document → doc
  3. string →　str
  4. BackEndManager → BEManager
  1~3は一般的に知られる省略形だが、4は初めて見た人からすると、「BEManager」とは何の略かが分からないと思われる

- 名前の中にある不要な単語は削除する
例）
「ConvertToString」を「ToString」に変更しても「stringに変換する」と通じる
go getで入れたライブラリが一生使えなかった件

gormのマイグレだと外部キーがうまく作れなかったりしたので、他のライブラリを試そうとしたが、コマンドがうまく実行できなかった、、、

golang-migrateやgooseを入れてみたが、そもそもバージョンの確認すらできない、、、
GOPATHもGOBINも何度も確認して、bardとも口論したのに一生うまくいかない、、、

ということになり、3日つぶしたので、メモを残しておく

結論としては、go getだけではなくgo installをする必要があった
[Go 1.16でのgo getとgo installの変更点](https://re-engines.com/2021/10/11/go-1-16%E3%81%A7%E3%81%AEgo-get%E3%81%A8go-install%E3%81%AE%E5%A4%89%E6%9B%B4%E7%82%B9/)

使い分けに関してはよくわかってないが、gooseはinstallでバイナリファイルが作成され使えるようになった

golang-migrateはできない（なんで）

dockerが必要なのかとか、みんなwinsdowsじゃないのかとか。環境変数のせいなのかとかあーだこーだ考えて全然進まなかったので、migrateはうまくバイナリをインストールできなかったが、このままgooseでやろうと思っています

疲れた
Reactでchat機能を実装するためのUIライブラリを調査したので、そのメモを共有

React で chat 機能を実装するための UI ライブラリの調査

## 採用

### @chatscope/chat-ui-kit-react

- **機能概要**:
  - Web チャットアプリ開発向けのオープンソース UI ツールキット。
  - 43 個の再利用可能な React コンポーネント。
  - フルレスポンシブとカスタマイズ可能。
  - 400 個の scss 変数とテーマ化のサポート。
- **画面適応**:
  - Web チャット UI の迅速な開発をサポート。
  - スティッキースクロールバー、contenteditable、レスポンシビリティの問題解消機能提供。
- **Github Star**: 817
- **直近の更新**: Aug 22, 2023

### 採用理由

- トレンディ
- 自由度が高い

memo:

- デモ
  - デモには、画像とかバイナリファイル選択の表示なし
    - ※ストーリーブックにはあったので画像とかも送れそう
  - https://chatscope.io/demo/
- 実装簡単そう
  - ストーリーブック
    - https://chatscope.io/storybook/react/?path=/story/components-message--image-content
  - 実装参考
    - [【React で chat 機能を実装するための UI ライブラリ chat-ui-kit-react が好みの UI 過ぎるので使い方を解説する】 - Qiita](https://qiita.com/Ueken3pei/items/a4290883840b23019742)

<hr />

## 却下した候補

### React-Chat-Elements

- **機能概要**:
  - メッセージコンポーネント、システムメッセージコンポーネント、バブルコンポーネント、ユーザーアバターコンポーネント、入力フォームコンポーネントなど、豊富なチャット関連コンポーネントを提供
- **画面適応**: 通常のチャット画面や、システムメッセージを含む高度なチャットインタフェースの作成に適しています。
- **Github Star**: 1K
- **直近の更新**: Jun 5, 2023

memo:

- チュートリアル
  - [Getting Started | React Chat Elements](https://detaysoft.github.io/docs-react-chat-elements/docs/intro)
- 実装参考
  - [Text Message | React Chat Elements](https://detaysoft.github.io/docs-react-chat-elements/docs/message-types/text-message)

### **React-Chat-UI**:

- **機能概要**:
  - シンプルなデザインとカスタマイズ可能なスタイルを提供。
  - メッセージ送信時のアニメーションやスクロール機能を含む基本的なチャット UI 機能を提供
- **画面適応**: シンプルで直接的なチャット画面の作成に適しています。
- **Github Star**: 509
- **直近の更新**: Jun 28, 2023

memo:

- demo サイトあり。
  - [brandonmowat.github.io/react-chat-ui/demo/](https://brandonmowat.github.io/react-chat-ui/demo/)
- ただ、issues の数や PR の数、Github Star の数が他に比べて少ない(メンテナーが少ない)ので却下

### React-Chat-Widget

- **機能概要**:
  - カスタマイズ可能なデザイン、オートスクロール、画像やファイルの添付サポート、メッセージのステータス表示、送信中のメッセージ表示など、ウェブアプリケーションに迅速に追加できるチャットウィジェットを提供
- **画面適応**: レスポンシブデザイン対応のため、異なるデバイスや画面サイズに対応するチャット画面の作成に適しています。
- **Github Star**: ### 3- **直近の更新**: Dec 1, 2021

memo:

- 直近の更新がないため却下

### Chaskiq

- **機能概要**:
  - リッチメッセージコンポーネント、カスタマイズ可能なテーマ、ビデオ・音声チャット対応、キャンペーンやアンケート機能、サードパーティ連携など、カスタマーサポート向けのチャットウィジェットとしての機能を提供
- **画面適応**: カスタマーサポートやビデオ・音声チャットが必要な画面に適しています。
- **Github Star**: 2.6K
- **直近の更新**: 24h 以内

memo:

- 今回の仕様にとっては、オーバーな機能のため却下

### トレンド検索

npm trends: https://npmtrends.com/@chatscope/chat-ui-kit-react-vs-react-chat-elements-vs-react-chat-ui-vs-react-chat-widget

Google trends: https://trends.google.co.jp/trends/explore?q=@chatscope%2Fchat-ui-kit-react,react-chat-elements,react-chat-ui,react-chat-widget&hl=ja

MOIVA: https://moiva.io/?npm=@chatscope/chat-ui-kit-react+react-chat-elements+react-chat-ui+react-chat-widget
Amazon Bedrockをさわってみる

# Bedrockって何？
* [基盤モデル API サービス – Amazon Bedrock – AWS](https://aws.amazon.com/jp/bedrock/)

`Amazon Bedrock は、Amazon や主要な AI スタートアップ企業が提供する基盤モデル (FM) を API を通じて利用できるようにする完全マネージド型サービスです。`

「AWS上でAI基盤モデル（FM）が利用できるようになりました！」といった内容です。

# ざっくりと、料金体系を整理します
* [Amazon Bedrock Pricing](https://aws.amazon.com/jp/bedrock/pricing/)

3つの料金モデルがあるようです。
## オンデマンド
使用した分を支払うモデルです。それぞれ生成させるモデル毎に課金されます。※以下抜粋

* テキスト生成モデルでは、入力トークンの処理および出力トークンの生成ごとに課金されます。
* 埋め込みモデルでは、入力トークンの処理ごとに課金されます。（トークンは数文字で構成されており、ユーザー入力と、結果を生成するためのプロンプトを理解するためにモデルが学習する基本単位です。）
* 画像生成モデルでは、画像の生成ごとに課金されます。

## モデルのカスタマイズ（微調整）
FM：基盤モデルをカスタマイズさせる（追加で学習させる）ことができます。
カスタマイズされた基盤モデルは、「プロビジョンドスループット」で利用することができます。
※オンデマンドでは利用できない仕様です。

## プロビジョンドスループット
大規模で一貫性のある推論ワークロード向けのものです。
期間によって時間単位の課金額が変わります。

# 料金の内訳
プロバイダーやモデルによって異なります。
詳細な料金については、先程の上記リンクから確認できます。

# 試してみた
「Chant playground」で軽く遊んでみようと思います。
オレゴンリージョンからAmazon Bedrockのコンソール画面を開きます。

## 基盤モデルへのアクセス設定
初めに確認するポイントです。
基盤モデル：FM（Foundation Model）は、デフォルトでアクセス権を持っていません。
なので、 **「Model access」** から選択できるモデルをダウンロードする必要があります。

画面左のメニューから、`Model access`をクリックします。
![1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3501605/475749c0-848d-7a68-1a3b-17f6a36bd7e5.png)

Model accessの画面より、右側の`Edit`をクリックします。
![2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3501605/f5c0d9b8-601f-f23a-ca9f-369392b7a698.png)

現在利用できるすべてのモデルにチェック（全選択）します。
`Save changes`をクリックして、アクセスをリクエストします。
![3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3501605/dd504ddb-d3df-c4eb-26e9-5bb4ed218f3f.png)

「Access status」が`Access granted`のステータス表記になることを確認します。（約1分）
![4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3501605/7d18d167-781c-e1f6-4b1d-9f54f145aafc.png)

## チャット機能を試す
プロバイダーは「AI21 Labs」、モデルは「Jurassic-2 Ultra」を選択しました。
画面の下の方から、チャット機能を試します。
![5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3501605/81ad3484-f52b-d682-37e4-9d0fe0549057.png)

日本語に対応していますが、これから更に精度が上がっていくと思われます。
![6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3501605/f5cfc9f8-7d51-07c4-76cd-7d24af1236c8.png)

## 画像生成機能を試す
左のメニューから`Image`をクリックします。
「Prompt」から創り出してほしい画像の名称を入力します。
入力したあとに、下部の「Run」をクリックして実行します。
![7.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3501605/5ef218ad-b644-3f17-629f-71aa8d63d471.png)
おもしろい…！

# 最後に
* SNSでもよく「Bedrock」の話題を見かけていたので、少しですが実際に試すことができてよかったです◎
* それぞれのモデルや詳しい仕様については、時間のある時に少しずつ調べていければと思います。（それぞれのモデルはここから確認できる、、と自分用にメモ）
![8.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3501605/b0f17cd7-d82f-4b68-6a6d-88140d4193b0.png)
モダンWebの特徴

# 概要
- モダンWebとは「バックエンド処理に比重を置いた従来のWeb技術」と一線を画すWeb技術である。
- 機能としては以下挙げられる
    - SPAを利用した高速データ表示機能
    - Service Worker(Webブラウザ内臓)を利用したオフライン表示機能
    - Webストレージ、URLパラメータ等を利用した前回表示画面の復元機能
    - Intersection Observer API等を利用した無限スクロール機能
    - PWA（Progressive Web Apps）の特徴であるネイティブアプリライクなWebアプリ構築
- デザイン面では以下挙げられる
    - レスポンシブデザイン
    - UXデザイン
- はっきりとした定義が有る訳ではないので、上記が全てではない。
- 従来型Webと区別する際に使う対義語と捉えるのが適切と考える

# 機能例
## SPAを利用した高速データ表示機能
- 従来型Webにおいてはユーザーがフロントエンドで操作の度、バックエンドにリクエストを行い画面データを作成しフロントエンドに画面データをレスポンスとして返し、画面表示する仕組みである。
- モダンWebにおいては、極力フロントエンド側で画面データを作成して表示することにより高速データ表示を可能としている。
- 以下の仕組みを持つ事で、高速可能なページ表示を可能としている
    - SPA、UXページデザインにより、1ページ辺り必要最小限のデータを取得する仕組み
    - 画面表示後も、バックグラウンド通信で事前予測したデータをバックエンドから先読み取得する仕組み

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235774/b3af86d9-fd6b-9420-db1b-5fe7cc28a8eb.png)
[Webサイト SPAとMPAの違い](https://www.azusuki.com/spa-mpa/)

## Service Worker(Webブラウザ内臓)を利用したオフライン表示機能
- ネットワーク切断されたオフライン状態でもオンライン時と同様にページ表示、ページ移動、ボタン操作が可能な機能である。
- 以下の仕組みにより、オフライン表示が可能となる
    - Webサイト接続時にWebサイトのデータをローカルにダウンロード（遅延ダウンロード）
    - オフライン時にService WorkerがWebサーバーの代替となりユーザーのリクエスト処理の応答を担う
    - オンライン切替わり時にインターネット上Webサーバーにユーザーから受け取っていたリクエスト処理を渡す

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235774/29d3fc7e-a7dd-b6ad-31f9-be3bd9ff1365.png)
[Medium](https://medium.com/sessionstack-blog/how-javascript-works-service-workers-their-life-cycle-and-use-cases-52b19ad98b58)

## Webストレージ、URLパラメータ等を利用した前回表示画面の復元機能
- 従来型Webでは、Web入力画面で途中まで入力したが、ブラウザを閉じた場合に途中入力データは失われることがデフォルトである。（Webアプリ機能で「一時保存」ボタン等を明示的に実装している例は除く）
- モダンWebにおいては、途中までの入力データ、URLパラメータ等をWebストレージに保存しておく事で再度ログイン時に前回表示画面を復元することが可能となる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235774/1d893b7c-2aab-4591-41a9-e76a6a85d8f8.png)
[ブラウザでストレージ？ Web Storageを使いこなそう](https://atmarkit.itmedia.co.jp/ait/articles/1108/12/news093.html)

## Intersection Observer API等を利用した無限スクロール機能
- 従来型Webにおいては、大量データの一覧表示はページング処理で実現することが多い
- モダンWebにおいては、Google 画像検索ページのように、ページ末尾にスクロールすると自動的に次ページを検索表示する仕組みを持っている
- 無限スクロール機能により、スムーズに検索結果を表示することが可能となる
- 仕組みとしては、バックグラウンド通信と分割ダウンロードを組み合わせて実現している
- 無限スクロール機能については、フッターが見えない等のデメリットも有るので、Webサイトの特徴を考えて、適切に検討する必要が有ると考える

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235774/afd2d7c2-d272-1150-6b8b-c83f5df70f2a.png)

# まとめ
- モダンWebについては明示的に定義されているものではない
- 従来型Webに無い機能を提供することによりUXを向上させる事が出来る
    - 従来より素晴らしい体験をユーザーに提供出来る
- モダンWebを実現するにはjQueryの様なレガシーなフレームワークでは難しいのではないか
- 3大フレームワーク(React, Angular, Vue.js)、HTML5、ES6(2015)以降の知識と幅広くフロントエンド周りを抑えておくことが必要ではないかと考える

# 参考サイト
https://wakka-inc.com/blog/1937/
https://kinsta.com/jp/blog/web-application-architecture/
https://qiita.com/hrkt/items/71c951082c6d90c7cd78
https://laboradian.com/create-offline-site-using-sw/
docker-compose環境のrailsをpry-byebugを使ってデバッグする

## はじめに
docker-composeを使ったrails環境のデバッグ方法のメモです。

## 環境
`rails 6.1.7.6`
`ruby 3.0.6`
`pry-byebug 3.10.1`

## 手順
1. `pry-byebug`インストール
1. docker-compose.yml編集
1. binding.pry挿入
1. docker-compose up
1. docker attach 

### `pry-byebug`インストール
development環境にインストールします。

```ruby:Gemfile
group :development do
  gem 'pry-byebug'
end
```

### docker-compose編集
以下の2行をdocker-compose.ymlに追記します。

```yml:docker-compose.yml
tty: true
stdin_open: true
```

編集したらデバッグしたい場所に`binding.pry`を挿入し、`docker-compose up`です。

### docker attach
`docker-compose up`されたらrailsコンテナにアタッチします。

railsコンテナを探します。
`docker ps`

railsコンテナにアタッチします。
`docker attach xxx`

この状態でアプリを触ることで任意の場所でデバッグできます。
Windows の C ドライブの（安全な）軽量化手順まとめ

## 概要

### 作業の対象
* この文書では Windows 10 での手順を対象にしている
* 本文書では、定期的に以下の3点を実施することで、ドライブの軽量化を図ることを目的とする
  * ディスククリーンアップ
  * temp フォルダ内の一時ファイルの削除
  * ブラウザのキャッシュの削除
* Windows は使い続けるとキャッシュや一時ファイルでドライブが圧迫されるため、社用 PC など、長期（数年以上）にわたって使い続けるマシンは、定期的なメンテナンス作業としてこの作業が必要になる

---

### 注意点
* 基本的に「キャッシュ」と「一時ファイルの削除」につとめる
* Cookie やサイトの情報、フォームの入力履歴など、削除すると再設定が必要になるものは対象としない
* OS やブラウザのバージョンによって、細かい手順が異なる場合があるため、その都度確認して行うこと

---

### 更新履歴

* 初回作成: 2023.10.14

---

## ディスククリーンアップ

### 手順
* C ドライブを右クリック > [プロパティ] > [ディスクのクリーンアップ] を選択
  * [インターネット一時ファイル]、[配信の最適化ファイル]、[一時ファイル]、[縮小表示]にチェック
  * [OK] > [ファイルの削除] > [削除] > [OK]

---

### 効果

* 長期間ブラウザを使用し続けていると、数GB のキャッシュファイルが貯まるのでこれらを削除することで、ドライブの軽量化が図れる
* 上記に Windows Update のキャッシュファイルも含む場合、数GB ～ 10GB の空き容量が増えることもある
  * 前のバージョンの Windows に戻す必要がなければ、必ず対象として削除することを推奨

---

## Tempフォルダ内の一時ファイルを削除する

### 概要
* 半年くらいで 1～2GB のファイルが貯まることもある
* 現在利用中のファイルは削除できないので、消せないものはスキップして OK

---

### 手順

* デスクトップ画面で、[Windows]キーと[R]キーを同時に押す
* テキスト入力欄に、[%temp%］と半角で入力、[Enter］キーを押す
* 「Temp」画面が表示される
* [すべて選択］> [削除］

---

### 効果

* 比較的知られているディスククリーンアップでは消せないファイルがたまっている
* **3年で10GB 以上のファイルが貯まっていることもあり**、削除をまったく実施していない場合の効果は最も高い

---

## ブラウザのキャッシュの削除

### Google Chrome

* [設定] > [プライバシーとセキュリティ] > [閲覧履歴の削除] を選択
  * [閲覧履歴]、[ダウンロード履歴], [キャッシュされた画像とファイル]のみ選択して、[データを削除]をクリック
  * その他の選択事項は、チェックを外しておく
* バージョン: 118.0.5993.71（Official Build） （64 ビット）での確認手順

---

### Microsoft Edge

* [ヘルプとフィードバッグ] > [Microsoft Edge について] > [プライバシー、検索、サービス] を選択
  * [閲覧データをクリア] > [クリアするデータの選択]をクリック
    * [閲覧の履歴]、[ダウンロードの履歴], [キャッシュされた画像とファイル]のみ選択して、[データを削除]をクリック
    * その他の選択事項は、チェックを外しておく
    * バージョン 118.0.2088.46 (公式ビルド) (64 ビット) での確認手順

---

### Firefox

* [設定] > [プライバシーとセキュリティ] を選択
  * [Cookie とサイトデータ] > [データを消去]をクリック
    * [ウェブコンテンツのキャッシュ] のみ選択して、[データを消去]をクリック
    * その他の選択事項は、チェックを外しておく
    * バージョン 118.0.2 (64 ビット) での確認手順

---

### 効果

* ブラウザのキャッシュは、ディスククリーンアップでは削除できない
* 1日の使用で数百MB ～ 1GB のキャッシュが貯まることもある
* キャッシュがあることで、次回以降の表示速度が速くなる効果があるので、 よほど切迫していない限り、頻繁に削除する必要はないが、1日中使い続ける環境なら、最低、1週間に1回程度は削除することを推奨

---

## 最後に

* 特殊なソフトを必要とせず、少ない手順で確実に効果の出る手順をまとめました
* 技術者でない方にすぐ説明するための覚書でもあります
* 使っていないアプリを削ったり、外部ストレージを利用するなどの手順は、環境によって出来ないこともあるので、Windows 標準機能として可能なものを解説しています
* 他にもっとこれを消せば効果があるよ、というものがあればコメントお願いします
* エクスプローラのキャッシュ削除も簡単ではありますが、大きな効果は望めないので記載していません…
* ドライブの容量が逼迫している人の一助となれば幸いです（くれぐれも、自己責任でお願い致します）
BlenderのドープシートにタイムラインのUIを追加するアドオン

Blenderでアニメを作る人の間のトークイベントの
UIをどういう風にしているかの話題の中で
ドープシートを表示してタイムラインを表示していないと自動キー挿入の状態が分からない
という話がありました。

BlenderはアドオンからUIのパーツ追加等もできるので
自動キーの挿入や　現在のフレーム等のUIを追加するアドオンを作成してみました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/29464ad3-58d9-3287-3426-8f8fdbca28eb.png)
（キャプチャでは下側が通常のタイムラインのメニュー　緑色が追加したUIです）
必要に応じて改変してご利用ください
```y_additem_ dopesheet.py
bl_info = {
    "name": "add timeline item on dopesheet",
    "author": "Yukimi",
    "version": (1, 0),
    "blender": (2, 80, 0),
    "location": "View3D ",
    "description": "ドープシートメニューにタイムラインのUIを追加",
    "warning": "",
    "support": "COMMUNITY",
    "wiki_url": "",
    "tracker_url": "",
    "category": "User Interface"
}
import bpy


def draw_keyframe_insert_auto(self, context):
    # 自動キーフレームのUI挿入
    tool_settings = context.tool_settings
    screen = context.screen
    layout = self.layout
    # 区切り線
    layout.separator_spacer()
    #自動キー挿入
    row = layout.row(align=True)
    row.prop(tool_settings, "use_keyframe_insert_auto", text="", toggle=True)
    sub = row.row(align=True)
    sub.active = tool_settings.use_keyframe_insert_auto
    #前後のキーフレームに移動
    row = layout.row(align=True)
    row.operator("screen.keyframe_jump", text="", icon='PREV_KEYFRAME').next = False
    row.operator("screen.keyframe_jump", text="", icon='NEXT_KEYFRAME').next = True
    
def draw_frame_range(self, context):
    # フレーム範囲のUI
    scene = context.scene
    layout = self.layout
    row = layout.row()
    if scene.show_subframe:
        row.scale_x = 1.0
        row.prop(scene, "frame_float", text="")
    else:
        row.scale_x = 0.8
        row.prop(scene, "frame_current", text="")

    row = layout.row(align=True)
    row.prop(scene, "use_preview_range", text="", toggle=True)
    sub = row.row(align=True)
    sub.scale_x = 0.7
    if not scene.use_preview_range:
        sub.prop(scene, "frame_start", text="Start")
        sub.prop(scene, "frame_end", text="End")
    else:
        sub.prop(scene, "frame_preview_start", text="Start")
        sub.prop(scene, "frame_preview_end", text="End")

def register():
    # ドープシートメニューに項目を追加
    bpy.types.DOPESHEET_MT_editor_menus.append(draw_keyframe_insert_auto)
    bpy.types.DOPESHEET_MT_editor_menus.append(draw_frame_range)


def unregister():
    # ドープシートメニューから項目を削除
    bpy.types.DOPESHEET_MT_editor_menus.remove(draw_keyframe_insert_auto)
    bpy.types.DOPESHEET_MT_editor_menus.remove(draw_frame_range)

if __name__ == "__main__":
    register()
```
他のメニュー項目の表示位置を変更する記事も以前に書いてあるので参照してください

https://qiita.com/yukimituki11/items/017a17d6d996adad395e
TableLayoutPanelを持つスーパフォームクラスをサブクラスのデザイナーで編集する方法

# やりたいこと

`TableLayoutPanel`を持つフォームクラス(スーパクラス)を継承した、フォームクラス（サブクラス）のデザイナーで`TableLayoutPanel`を編集したい。

コードは以下のような感じで、SubFormのデザイナー画面で`TableLayoutPanel`を編集することを想定しています。
```cs
public abstract partial class BaseForm
{
	private void InitializeComponent()
	{
		this.tableLyaoutPanel = new System.Windows.Forms.TableLayoutPanel();
		this.tableLyaoutPanel = ColumnCount = 2;
		// 以下省略
	}
	private System.Windows.Forms.TableLayoutPanel tableLyaoutPanel;
}

public abstract partial class BaseForm : Form
{
	public BaseForm()
	{
		InitializeComponent();
	}
}
```

```cs
public partial class SubForm : BaseForm
{
	public SubForm()
	{
		InitializeComponent();
	}
}
```

# 結論
まずは結論から。

**サブクラスで編集することは出来ません**

# なぜ出来ないのか？
[Microsoft](https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/best-practices-for-the-tablelayoutpanel-control?view=netframeworkdesktop-4.8#avoid-visual-inheritance)によると、「`TableLayoutPanelControl`は視覚的継承をデザイナーフォームでサポートしていません。派生クラスではロックされた状態で表示されます。」とのことです。

> Avoid Visual Inheritance
>The TableLayoutPanel control does not support visual inheritance in the Windows Forms Designer in Visual Studio. A TableLayoutPanel control in a derived class appears as "locked" at design time.

# 対応策
`TableLayoutPanel`を使うことはできないので、`Panel`で代用します。
デフォルトの修飾子は`private`になっているので、`protected`または`public`にするのを忘れないでください。


# 参考リンク
- [Add Inherited Controls to a Panel](https://stackoverflow.com/questions/2216577/add-inherited-controls-to-a-panel)
- [Visual Inheritance - Add controls at designer to a Panel hosted in TableLayoutPanel](https://stackoverflow.com/questions/49894180/visual-inheritance-add-controls-at-designer-to-a-panel-hosted-in-tablelayoutpa)
「OSError: mysql_config not found」と言われた時の対処法

# 発生問題
コマンドに`pip install mysqclient`と入力すると、以下のエラーメッセージが出てきた

```
・・・
/bin/sh: 1: mysql_config: not found
/bin/sh: 1: mariadb_config: not found
/bin/sh: 1: mysql_config: not found
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/tmp/pip-install-pnhw9vzq/mysqlclient/setup.py", line 15, in <module>
metadata, options = get_config()
File "/tmp/pip-install-pnhw9vzq/mysqlclient/setup_posix.py", line 70, in get_config
libs = mysql_config("libs")
File "/tmp/pip-install-pnhw9vzq/mysqlclient/setup_posix.py", line 31, in mysql_config
raise OSError("{} not found".format(_mysql_config_path))
OSError: mysql_config not found
mysql_config --version
```

# 開発環境
```
CentOS7
Python3.9
Django3.2.20
```

# 解決策
- 必要なパッケージをインストール
```
sudo yum install python3-dev libmysqlclient-dev mysql-community-server
```
:::note warn
yumリポジトリを追加していない場合は、mysqlをインストールできないので注意してください
:::
Nginx で「onnect() to /hoge.sock failed (13: Permission denied)」の対処法

# 発生問題
Nginxを起動させ、アクセスしたらBad Gatewayと言われた。
![](https://storage.googleapis.com/zenn-user-upload/50fa55584a30-20230828.png)

エラーログを見るとPermissionエラー。

```
*2 connect() to unix:/home/hoge.sock failed (13: Permission denied) while 
connecting to upstream, client:192.168.1.170, server: 192.168.1.180, 
request: "GET / HTTP/1.1", upstream: "http://unix:/home/hoge.sock:/"
```


# 開発環境
```
CentOS7
Nginx
Gunicorn
Django3.2.20
```

# 原因
- `.sock`ファイルがNginxからアクセスできない場所に置かれている
- SELinuxのファイルアクセス制御によってアクセスできない

# 解決策
- SELinuxを一時無効化させる
```
setenforce 0
```
- `.sock`ファイルを`/var/run/`などNignxからアクセスできる場所に作成する

:::note warn
nginx.confのuserをrootにするとアクセス出来ますが、セキュリティ上、危険なのでやめましょう
:::

# 参考文献
- [nginx+unicorn+railsで(13:Permission denied)](https://pebble8888.hatenablog.com/entry/2014/12/28/163359)
- [SELinuxの無効化](https://qiita.com/hanaita0102/items/5d3675e4dc1530b255ba)
- [Nginx cannot access upstream .sock file and public folder](https://serverfault.com/questions/559890/nginx-cannot-access-upstream-sock-file-and-public-folder)
デザイナーで抽象クラスを継承したフォームクラスを開こうとしたら「インスタンスを生成できない」と怒れた時の対処法


# 発生問題

以下のコードのように抽象クラスを継承する。

```SampleControlForm.cs
nameSpace SampleWinApp
{
  public partial class SampleControlForm : AbstractControl
  {
    public AbstractControl()
    {
      IntializeComponet();
    }
  }
}
```

デザイナーを開こうとすると、以下のようなエラーが出てくる

> デザイナーは型 'BaseControl`1[[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=****]]' のインスタンスを作成しなければなりませんが、型が abstract として宣言されているため、作成できませんでした。


# 開発環境
```
c#6.0
.NET Framework4.6.2
```

# 原因
デザイナーを開く時に、抽象クラスのインスタンスを生成しようとしている。

# 解決策
`Debug mode`で中間クラスを作成する。

`AbstractControl`：スーパークラス
`SampleControlForm`：サブクラス（デザイナーで開いたいフォームクラス）
`Middle`：継承するために挟む中間クラス

```SampleControlForm.cs
nameSpace SampleWinApp
{
#if DEBUG
  public partial class SampleControlForm : Middle
#else
  public partial class SampleControlForm : AbstractControl
#endif
  {
    public SampleControlForm()
    {
      IntializeComponet();
    }
}
#if DEBUG
  public class Middle : AbstractControl{}
#endif
}
```

```AbstractControl.cs
nameSpace SampleWinApp
{
  public abstract partial class AbstractControl : UserControl
  {
    public AbstractControl()
    {
      IntializeComponet();
    }
  }
}
```

## 補足
```SampleControlForm.cs
#if DEBUG
  public partial class SampleControlForm : Middle
#else
  public partial class SampleControlForm : AbstractControl
#endif
```
RELEASEモード時には、中間クラスは必要ないので`#else`で分岐しています。



# 参考文献
- [How to fix the errror 'The designer must create an instance of type " " but cant because it marked abstract"](https://stackoverflow.com/questions/55880752/how-to-fix-the-errror-the-designer-must-create-an-instance-of-type-but-cant)
- [Abstract UserControl inheritance in Visual Studio designer](https://stackoverflow.com/questions/6817107/abstract-usercontrol-inheritance-in-visual-studio-designer)
- [Inheriting a Form from an Abstract Class (and Making it Work in the Designer)](https://www.codeproject.com/Articles/22788/Inheriting-a-Form-from-an-Abstract-Class-and-Makin)
【Python】多次元リストのソート

# 一つのキーでソート
## 昇順にソート

0番目の要素で昇順にして並び替えます。

```main.py
score = [[80, 65], [77, 45], [10, 99], [100, 100], [85, 43]]
score = sorted(score, key=lambda x: x[0])
print(score)
>> [[10, 99], [77, 45], [80, 65], [85, 43], [100, 100]]
```

## 降順にソート
0番目の要素で降順にして並び替えます。

```main.py
score = [[80, 65], [77, 45], [10, 99], [100, 100], [85, 43]]
score = sorted(score, key=lambda x: x[0], reverse=True)
print(score)
>> [[100, 100], [85, 43], [80, 65], [77, 45], [10, 99]]
```


# 複数のキーでソート

## 昇順にソート
0番目と2番目の要素で昇順に並び替えます。

```main.py
score = [[80, 65, 75], [77, 45, 95], [77, 99, 65], [100, 100, 0], [85, 43, 20]]
score = sorted(score, key=lambda x: (x[0], x[2]))
print(score)
>> [[77, 99, 65], [77, 45, 95], [80, 65, 75], [85, 43, 20], [100, 100, 0]]
```

## 昇順と降順にソート

0番目の要素を昇順、2番目の要素を降順にして並び替えます。
`reverse`を使ってしまうと、キーの要素で全て降順になってしまうので、降順にしたい要素には`-`をつけます。

```main.py
score = [[80, 65, 75], [77, 45, 95], [77, 99, 65], [100, 100, 0], [85, 43, 20]]
score = sorted(score, key=lambda x: (x[0], -x[2]))
print(score)
>> [[77, 45, 95], [77, 99, 65], [80, 65, 75], [85, 43, 20], [100, 100, 0]]

```

このように、一つの要素を昇順で、もう一つの要素を降順に並び替える方法の記事が中々見つからなくて苦労しました。

# 参考文献
- [How to sort a list with two keys but one in reverse order?](https://stackoverflow.com/questions/37693373/how-to-sort-a-list-with-two-keys-but-one-in-reverse-order)
会話の合間にスクワットを要求してくるマッチョ(bot)との会話サービスを開発した

簡単に言うとChatGPTクラインアントサービスを作りました。
サービスそのもの、技術構成、開発体験記を紹介させてください。

~~使ってみたい方は[こちらのフォーム](https://docs.google.com/forms/d/e/1FAIpQLSeQZZslE1Z-e94LU1oJ6AGOLg51kZYOgrd7M5E3MH-1eE0P6g/viewform?usp=sf_link)に入力頂いたら順次招待させて頂きます。~~

# サービスそのものに関して
## 作ったもの
マッチョなメンター(bot)が会話の合間、会話の最初に筋トレ(スクワット)を要求してくる

- 会話の最初に筋トレ(スクワット)を要求してくる
    - ![スクリーンショット 2023-10-14 15.09.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/9e48d032-466d-68f9-8ae1-d7ab4ea1d0db.png)
- 回答を考えている間にも筋トレ(スクワット)を要求してくる
    - ![スクリーンショット 2023-10-14 15.12.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/31624be6-d6d1-f554-1e91-9a538149dca4.png)
- 質問に対して答えてくれる(GPT-4搭載)
    - ![スクリーンショット 2023-10-14 15.11.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/a0ae4ec4-8c0a-472b-30db-45bce51ebc69.png)
- 音声入力や回答の読み上げができる
 

## なぜ作ったのか
- ChatGPTの登場によって陥った苦しみとそれを緩和した体験を実装したい
    - 仕事ではやることを書き出して整理してChatGPTに作業指示してを繰り返すという事が多くなった
    - 脳がめんどくさい病の私には小ロットの作業を繰り返すのがとにかくしんどい
    - 脳がめんどくさい病の人には体に筋力的な負荷をかけると良いと聞き、実践したところ確かに効く
    - この体験を自然な形で実現する
- ChatGPT的サービスの体験がクライアントによって変わると思った
    - Plugin、Vision、Agent 機能は90パーセントの人は必須ではないと思ったので、変わり種Chatクライアントに可能性を感じた
    - ChatGPTが私にとってはツール群のポータルになっているので、機能を載っけたくなった
- その他
    - このサービスの開発を通じてマッチョになりたい

## サービス紹介も書いた
- 雑に作ってみた
    - <iframe src="https://docs.google.com/presentation/d/e/2PACX-1vSICF-6vnN7l7MAxRmZnNEcpzWn0gA0SwRDLJxQsDV0EzKDPymDz4B4DnAXpfbI-7DlxyyP8UI7K09r/embed?start=false&loop=false&delayms=3000" frameborder="0" width="960" height="569" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe>

- DALL・Eが来たら書き直す

## 想定利用者
- 既にChatGPTを使ったことがあって、ChatGPT Plus までは使っていない人

## サービス料金
- 1200円/月を想定
    - OpenAIAPI料金とサーバー代で消える見込み

# 技術構成
- バックエンド
   - FastAPI(Python)
       - Strem 処理が簡単に実装できた
   - Azure App Service に deploy
- フロント
   - React
- OpenAI (無印)
- 認証・認可
   - AzureADB2C
- 音声入力
   - Azure Speech Service
- 音声読み上げ
   - Azure Speech Service

# 開発体験記
月並みですが、LLMは世の中を変えるくらい便利で学習するに足るサービスだと思っています。
BetterChatGPTを作るべく色々な機能を実装しては、本家が対応してくるを繰り返しています。

- 自前データからの回答を実装
    - Msサービス、AzureOnYourDataで実装
- 音声入力を実装
    - ChatGPTアプリで実装された
- 回答読み上げを実装
    - ChatGPTアプリで実装された
- web検索Agentを実装
    - Bingが実装

途中からそもそも機能で追うべきではないと思い、現在の形になりました。

今後もLLM関連の記事を書いていくので、良かったらフォローといいね👍をお願い致します。