OmniAICreator/Qiita-1.07M huggingface dataset:
Power Apps と AI Builder の GPT-4o で画像判定アプリを構築する方法


# はじめに

以下の記事で紹介した通り、AI Builer のプロンプトにて、直接ファイルをアップロード出来るようになりました。

https://qiita.com/Takashi_Masumori/items/4abf6e330edde0a3f580

そのため、以下で紹介したようなアプリについて、直接 Azure OpenAI にリクエストしなくても実現できるようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/f20099d0-e2e7-e602-e91a-b3a9355df24b.png)

https://qiita.com/Takashi_Masumori/items/f6461c2dea1d1a7e4cde

今回は、こちらを置き換えるにあたって、Power Apps から Power Automate へのファイルの受け渡しについて、少し癖があるため、簡単ですが、アプローチを紹介します。

# アプローチ

まず、用意するプロンプトはこのような感じで超シンプルです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/51e9b7a6-49df-019f-a336-c11a952c9296.png)

作成した Power Automate 側も超シンプルです。作成アクションは確認のために入れているだけですので、実質トリガーと 2 つのアクションだけでいけます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/4777f898-7413-8174-54d5-c9ac5de43e10.png)

Power Apps 側でアップロードしたファイルを Power Automate に複数送る処理は以下のような感じです。複数ファイルの送信、少し癖がありますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/53908d95-81a5-a98f-af1c-81b64705d087.png)


```
Set(
    output,
    'CallGPT-4oAIBuilder'.Run(
        TextInput1.Text,
        {
            file: {
                name: Concatenate(
                    "正しい画像_",
                    Text(
                        Now(),
                        "yyyymmddhhmmss"
                    ),
                    ".png"
                ),
                contentBytes: Image2.Image
            },
            file_1: {
                
                name: Concatenate(
                    "調査対象画像_",
                    Text(
                        Now(),
                        "yyyymmddhhmmss"
                    ),
                    ".png"
                ),
                contentBytes: UploadedImage1.Image
            
               
            }
        }
    ).result
)


```

# 実行結果

実行してみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/c6168bf2-0e20-dfbd-298e-dfdc165aea4d.png)

ガイドラインに従っていない旨、しっかり指摘してくれています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/e9bc2c5e-a03c-303e-137d-1c5bffcdd0a8.png)

なお、今回、Power Automate 経由にしましたが、Power Apps から直接でも行けます。引数の順番がずれていることがあるので、確認した上で渡してあげましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/56726f4e-04ed-31bf-a9b3-1d16fe03b7f0.png)

```
Set(
    output,
    画像比較プロンプト.Predict(
        Image2.Image,
        UploadedImage1.Image,
        TextInput1.Text
    )
)

```

# まとめ

今回は、AI Builder のプロンプトで直接ファイルをアップロードできる機能が追加されたことを踏まえ、これまで Azure OpenAI に直接リクエストを送信して画像判定を行っていたアプリやフローを、AI Builder のプロンプトベースに置き換える方法をご紹介しました。

この変更により、ローコードやノーコードの範囲内で (Azure OpenAI と連携せずに) GPT-4o の技術を活用したアプリやフローを作成できるようになり、市民開発で実現可能な範囲がさらに広がったと感じています。
見切り発車で平仮名の学習アプリを作る【8日目】

**2024年のアドベントカレンダー8日目の記事です。**

https://qiita.com/advent-calendar/2024/yaikawa_memory_2024


### Googleのスプレッドシートにアクセスしたい！#1

問題文をスプレッドシートで管理できるようにしたいので[Google Sheets api](https://developers.google.com/sheets/api/reference/rest?hl=ja)を利用してスプレッドシートへのアクセスができるように準備していきます。
今回はGoogleCloud側の設定を行っていきます。

まずGoogleCloudConsleにアクセスし、Google sheets apiで検索を行い有効化を行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519878/afcac0e1-b973-50c1-4773-dcdd9b8f3724.png)

APIを有効化したらAPIのページに移動し、「認証情報」メニューの上部より「認証情報を作成 > サービスアカウント」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519878/488547fa-90e5-6442-db7f-5a9b56527483.png)

サービスアカウント名に好きな名前を入力し「作成して続行」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519878/fbd9ca14-6a89-10fb-5b43-fa40da209ba5.png)


「ロール」にオーナーを付与します。（必要に応じてロールは選択してください。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519878/54b2303a-25d0-9327-e9cb-2c29eba902d2.png)

③の画面については何も設定せずに完了しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519878/7f9ffe0e-3eb4-dfec-7133-59157fae1052.png)

作成後サービスアカウントの詳細より上部メニュー「鍵」を選択し、「キーを追加」より新しい鍵を作成をクリックし、JSON形式で鍵の作成を行いました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519878/51f2b778-b75f-7293-5fa8-e172725c3bba.png)

明日はアプリケーション側の設定を行っていきます。
見切り発車で平仮名の学習アプリを作る【6日目】

**2024年のアドベントカレンダー6日目の記事です。**

https://qiita.com/advent-calendar/2024/yaikawa_memory_2024

### 音声入力を実施したい#2

5日目に引き続き音声入力処理を作成します。
音声入力をするにあたってリアルタイムで発声内容を描画していくことも考えましたが、テストと同じように回答した後に採点したほうが(~~つくりやすくて~~)いいかなと思ったのでリアルタイムでの描画は行わないこととしました。

それに伴い、web speech apiの制御は[useState](https://ja.react.dev/reference/react/useState)ではなく[useRef](https://ja.react.dev/reference/react/useRef)を利用することにしました。
(useStateとuseRefの違いは再レンダリングの有無くらいのざっくり知識しかないのでこの選択が正しいかは不明です)

[ドキュメント](https://developer.mozilla.org/ja/docs/Web/API/SpeechRecognition)を見た感じだと以下のイベントがあれば動かせそうだったため、useRefを利用してそのあたりの制御を行っていきます。

1.start
2.error
3.result
4.end

また、結果を格納するstateも追加して最終的に以下のようなコードで落ち着きました。

```javascript:App.js
import './App.css';
import { Provider } from './components/ui/provider';
import { Box, AbsoluteCenter, Heading, VStack, HStack } from '@chakra-ui/react';
import { useState, useRef } from 'react';
import { Button } from './components/ui/button';

function App() {
  const [question, setQuestion] = useState('あとですぷしからしゅとくするようにします');
  const [answer, setAnswer] = useState('ここがおんせいにゅうりょくでかわるところ');
  const [isRecording, setIsRecording] = useState(false);
  const recognitionRef = useRef(null);

  const startRecording = () => {
    if (!('webkitSpeechRecognition' in window)) {
      alert('なんかだめみたい。おじさんにきいてみて');
      return;
    }

    recognitionRef.current = new window.webkitSpeechRecognition();
    recognitionRef.current.lang = 'ja-JP';
    recognitionRef.current.continuous = false;
    recognitionRef.current.interimResults = false;

    recognitionRef.current.onstart = () => {
      setIsRecording(true);
    };

    recognitionRef.current.onresult = (event) => {
      const transcript = event.results[0][0].transcript;
      setAnswer(transcript);
    };

    recognitionRef.current.onerror = (event) => {
      console.error(event.error);
    };

    recognitionRef.current.onend = () => {
      setIsRecording(false);
    };

    recognitionRef.current.start();
  };

  const stopRecording = () => {
    if (recognitionRef.current) {
      recognitionRef.current.stop();
    }
  };

  return (
    <Provider>
      <Box bg="blue.100" w="100vw" h="100vh">
        <AbsoluteCenter>
          <VStack gap="20">
            <Heading size="5xl">
              {question}
            </Heading>
            <HStack gap="20">
              <Button size="2xl" variant="outline" borderColor="green.600" color="green.600"onClick={isRecording ? stopRecording : startRecording}>
                {isRecording ? "こたえた" : "こたえる"}
              </Button>
              <Button size="2xl" variant="outline" borderColor="orange.600" color="orange.600">
                わからない
              </Button>
            </HStack>
            <Heading size="5xl">
              {answer}
            </Heading>
          </VStack>
        </AbsoluteCenter>
      </Box>
    </Provider>
  );
}

export default App;

```

結果として音声入力自体は無事に動きました。

![2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519878/b6b30692-1f71-e025-bef0-6aacd412ddaf.gif)

ただし回答が自動的に漢字になってしまったりするので、ここは最終的にすべて平仮名で保持されるように変換する必要があり、今後どこかで改修しようかとおもいます。
見切り発車で平仮名の学習アプリを作る【5日目】

**2024年のアドベントカレンダー5日目の記事です。**

https://qiita.com/advent-calendar/2024/yaikawa_memory_2024

### 音声入力を実施したい#1

STTを行うにあたり、[Google](https://cloud.google.com/speech-to-text?hl=ja)だったり[IBM](https://cloud.ibm.com/docs/speech-to-text?topic=speech-to-text-gettingStarted&locale=ja)だったり[Microsoft](https://learn.microsoft.com/ja-jp/azure/ai-services/speech-service/speech-to-text)だったりのAPIを軽く見てみたものの無料枠はあるものの基本は有料~~かつ導入が地味にめんどくさそう~~だったりでどうしたものかとなりました。

他にお手軽なSTTあったかなぁと思いつつ記憶をたどってみると昔[web speech api](https://developer.mozilla.org/ja/docs/Web/API/Web_Speech_API)なるものを触ったことがあったことを思い出しました。

ブラウザの対応状況を見たところchromeだけではなくios safariにも対応していたので、甥っ子が触る環境（私のPCもしくはタブレット）での動作も問題なさそうなため、STTの処理についてはweb speech apiを利用することにしました。

音声入力のスタート、ストップのタイミングでボタンの表示を変えるため、状態管理のstateの追加と状態の変更を行うメソッドを追加します。

```javascript:App.js
import './App.css';
import { Provider } from './components/ui/provider';
import { Box, AbsoluteCenter, Heading, VStack, HStack } from '@chakra-ui/react';
import { useState } from 'react';
import { Button } from './components/ui/button';

function App() {
  const [question, setQuestion] = useState('あとですぷしからしゅとくするようにします');
  const [isRecording, setIsRecording] = useState(false);

  const startRecording = () => {
    setIsRecording(true);
  }

  const stopRecording = () => {
    setIsRecording(false);
  }

  return (
    <Provider>
      <Box bg="blue.100" w="100vw" h="100vh">
        <AbsoluteCenter>
          <VStack gap="20">
            <Heading size="5xl">
              {question}
            </Heading>
            <HStack gap="20">
              <Button size="2xl" variant="outline" borderColor="green.600" color="green.600"onClick={isRecording ? stopRecording : startRecording}>
                {isRecording ? "こたえた" : "こたえる"}
              </Button>
              <Button size="2xl" variant="outline" borderColor="orange.600" color="orange.600">
                わからない
              </Button>
            </HStack>
          </VStack>
        </AbsoluteCenter>
      </Box>
    </Provider>
  );
}

export default App;

```

これによりボタンクリックで`isRecording`のステートにより呼び出しメソッドが変わり、呼ばれたメソッド内でステートを切り替えることができました。

![chrome-capture-2024-12-22.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519878/6f5bcba2-a4c5-5340-c52d-afea220795d3.gif)

音声入力部分を作るのはまた次回頑張ります
見切り発車で平仮名の学習アプリを作る【4日目】

**2024年のアドベントカレンダー4日目の記事です。**

https://qiita.com/advent-calendar/2024/yaikawa_memory_2024

### ざっくりのデザインを作る

1日目の記事で書いた通りかなり簡素化したデザインで作成を行います。
要素として必要な項目は以下の3つでいい（と現在は思ってる）はず。

1.問題文
2.回答ボタン
3.スキップボタン

とりあえずコードを以下のように作成してみた。

```javascript:App.js
import './App.css';
import { Provider } from './components/ui/provider';
import { Box, AbsoluteCenter, Heading, VStack, HStack } from '@chakra-ui/react';
import { useState } from 'react';
import { Button } from './components/ui/button';

function App() {
  const [question, setQuestion] = useState('あとですぷしからしゅとくするようにします');

  return (
    <Provider>
      <Box bg="blue.100" w="100vw" h="100vh">
        <AbsoluteCenter>
          <VStack gap="20">
            <Heading size="5xl">
              {question}
            </Heading>
            <HStack gap="20">
              <Button size="2xl" variant="outline" borderColor="green.600" color="green.600">
                こたえる
              </Button>
              <Button size="2xl" variant="outline" borderColor="orange.600" color="orange.600">
                わからない
              </Button>
            </HStack>
          </VStack>
        </AbsoluteCenter>
      </Box>
    </Provider>
  );
}

export default App;
```

要素の縦並び、横並びについては`VStack`、`HStack`
中央寄せについては`AbsoluteCenter`
表示する要素については`Heading`、`Button`
それぞれimportを行い色やサイズ、要素間の距離などを簡単に記載するだけで以下のような画面が作成できた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519878/8d10c525-c756-5fc5-22c2-db95e01fa8ff.png)

記載が楽にできるのでUIライブラリ入れていてよかったなと思いました。

5日目以降ではSTT,TTSやスプシ連携などGoogle系のサービスで実装できそうなのでそのあたりを調べつつ実装していこうかと思います。
見切り発車で平仮名の学習アプリを作る【3日目】

**2024年のアドベントカレンダー3日目の記事です。**

https://qiita.com/advent-calendar/2024/yaikawa_memory_2024


### UIライブラリの導入を行う

今回作成する平仮名学習アプリではあまりUIにこだわるところはないかもしれませんが、もしかしたら何かしらで利用するかもしれないことを考えてUIライブラリの導入を行います。

導入するライブラリは[ChakraUI](https://www.chakra-ui.com/docs/get-started/installation)の導入を行います。選定した理由としてはひとえに触ったことがあるからです。

基本的にリンク先にある通りのコマンドを入力していくだけで導入は可能です。

```
npm i @chakra-ui/react @emotion/react
npx @chakra-ui/cli snippet add
```

上記のコマンドを実行したあと、App.jsを以下のように修正してみます。

```javascript:App.js
import logo from './logo.svg';
import './App.css';
import { Provider } from './components/ui/provider';
import { Box } from '@chakra-ui/react';

function App() {
  return (
    <Provider>
      <Box className="hoge">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
      </Box>
    </Provider>
  );
}

export default App;
```

導入ページに従い、全体を`Provider`で囲った後に、`div`で定義されていたタグを`Box`に変更します。
またBoxを利用するにあたりimportの記載も行います。(確認しやすいようにクラス名も変えておきます)

変更後開発者ツールで画面を見てみるとBoxで定義した内容がdivとして描画されていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519878/0381a1f7-a095-2adc-3c9d-d80d710a0cf1.png)

ChakraUIの導入としては以上になります。
簡単に導入できて利用も簡単なので軽く何かを作るときには便利だと思います。（他のライブラリも導入はすぐできるかとおもいますが）
見切り発車で平仮名の学習アプリを作る【2日目】

**2024年のアドベントカレンダー2日目の記事です。**

https://qiita.com/advent-calendar/2024/yaikawa_memory_2024

### 開発環境を作成する

開発にあたりReactを動かせる環境を作成します。
さしあたってReactのロゴを表示するところまでを簡単にやってみようかと思います。

まずReactアプリの作成のためターミナルで以下のコマンドを実行します。
(過去にReact触っていたので諸々インストールは終わっています)

```
npx create-react-app hiragana
cd hiragana/
```

無事に作成できたのでDockerfile及びdocker-compose.ymlの作成を行います。

```Dockerfile:Dockerfile
FROM node:14

WORKDIR /app

COPY package.json /app
COPY package-lock.json /app

RUN npm install

COPY . /app

RUN npm run build

CMD ["npm", "start"]

EXPOSE 3000
```

```docker-compose.yml
version: '3'
services:
  hiragana:
    build: .
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      - NODE_ENV=development

```

この状態で以下のコマンドを実行します。

```
docker-compose up --build
```

実行したところ以下のエラーが表示されました。

```
0.460 > hiragana@0.1.0 build /app
0.460 > react-scripts build
0.460 
1.080 Creating an optimized production build...
2.515 Failed to compile.
2.515 
2.515 Module not found: Error: Can't resolve 'web-vitals' in '/app/src'
```

`web-vitals`なるものが足りていないようなので`package.json`の`dependencies`ブロックを以下のように書き換えます。

```package.json
  "dependencies": {
    "cra-template": "1.2.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
```

以下のコマンドを実行し追加した`web-vitals`のインストールとコンテナの起動を行います。

```
npm install web-vitals
docker-compose up --build
```

無事にReactお馴染みのページの表示まで対応できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519878/52eb253d-ceb9-be9c-0270-026004aae598.png)

### おわりに
2日目にして欲望に負け記事投稿が止まってしまいました。
最終的に25記事投稿できるよう、ここから頑張ります。
カスタムアノテーションをグループ化する。

DTOやFORMのフィールドに付けたカスタムアノテーションを、グループ化して、適応する。
## 方法
前提：自分が作ったカスタムvalidatorアノテーションがある。
例えば、Requiredアノテーション
```
package com.example.validation;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;


@Constraint(validatedBy = RequiredValidator.class)
@Target({ElementType.FIELD, ElementType.METHOD}) 
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Required {

    // エラーメッセージ
    String message() default "This field is required";

    // グループ（標準の仕様で必要!これがないとグループ化出来ない）
    Class<?>[] groups() default {};

    // ペイロード
    Class<? extends Payload>[] payload() default {};
}

```

RequiredValidator.classも必要でしょ
```
package com.example.validation;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class RequiredValidator implements ConstraintValidator<Required, Object> {

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        if (value == null) {
            return false;
        }
        if (value instanceof String) {
            return !((String) value).trim().isEmpty();
        }
        return true;
    }
}
```

上のカスタムアノテーションをDTOクラスとかのフィールドに使用。
```
    @Required(message = "Aは必須です。")
    private String myA;

    @Required(message = "Bは必須です。")
    private String myB;
```

SpringのValidatedアノテーションを使用して、リクエストを検証。

```
    @GetMapping
    public String create(@Validated CustomDto myDto, BindingResult bindingResult) {
        if(BindingResult.hasErrors()){
            // バリデーションが失敗した場合
        }
        return "遷移先URL"
    }

```
しかし、同じエンティティまたはDTOを使って、複数のメソッド（例: 作成、更新、削除）に応じた異なるバリデーションを実行する必要がある場合がある。
例えば、以下の二つの機能があると、各機能に適したバリデーションを適用するためにグループ化が必要だ。
１．新規作成（Create）：Aは不要、Bは必須。
２．更新（Update）：Aは必須、Bも必須。

適当な場所に以下のように空っぽのinterfaceを作る。
```
public interface CreateGroup {}
public interface UpdateGroup {}
```

DTOでグループを使用
```
    @Required(message = "Aは必須です。", groups = CreateGroup.class)
    private String myA;

    @Required(message = "Bは必須です。", groups = {CreateGroup.class, UpdateGroup.class}
    private String myB;
```
コントローラで適用
```
    @GetMapping
    public String create(@Validated(CreateGroup.class)  CustomDto myDto, BindingResult bindingResult) {
        if(BindingResult.hasErrors()){
            // バリデーションが失敗した場合
        }
        return "遷移先URL"
    }

        @GetMapping
    public String update(@Validated(UpdateGroup.class)  CustomDto myDto, BindingResult bindingResult) {
        if(BindingResult.hasErrors()){
            // バリデーションが失敗した場合
        }
        return "遷移先URL"
    }

```

### （　・。。・　）🖐

間違っている内容がありましたら、教えて頂ければ喜びます。
AIで文章を生成できる時代にアウトプットは意味があるのか？

# AIで文章を生成できる時代にアウトプットは意味があるのか？

## はじめに

よくアウトプットが大事とか言われますが、ChatGPT使えば文章簡単にできる時代にわざわざ自分で時間かけて文章作成する必要あります？？？

って思い、改めてQiitaにわざわざアウトする理由を整理できればと思って本記事を作成しました！

## 生成AIが普及した現在におけるアウトプットの意義

現在においてアウトプットの意義を考える上で、生成AIが普及する前のアウトプットの意義は以下の感じかなと思います！

- 自分の考えや経験を整理するためのツールとしての役割。
- 他者との知識共有やコミュニケーションの手段。
- 自分のスキルや専門性をアピールする場。

今回は、\
自分の考えや経験を整理するためのツールとしての役割。\
について深堀って調べた内容を本記事にまとめました。

### 自分の考えや経験を整理するためのツールとして効果的なのか？

アウトプットが知識の整理と定着に効果的であることは、複数の研究や実例から裏付けられています。

コロンビア大学の研究によると、学習時間の70%をアウトプットに充てた場合、最も高い学習効果が得られることが示されています。この研究では、小学校3年生から中学校2年生の子どもたちを対象に「覚える時間（インプット）」と「練習する時間（アウトプット）」の比率を変えて効果を比較した結果、アウトプットに70%を費やしたグループが最も高い成績を記録したそうです。

さらに、アウトプット活動が学習者に与える効果として、以下が挙げられます：

1. **気づきの促進**: アウトプットを通じて、学習内容に対する新たな発見や洞察を得ることができる。
2. **記憶の強化**: 記憶を参照して情報を引き出す過程が、記憶の強固化に寄与する。
3. **チャンクの形成**: 情報を適切に整理し、大きなまとまり（チャンク）として扱えるようになる。
4. **予測の効果**: アウトプットを繰り返すことで、次に必要な情報やステップを予測しやすくなる。

これらの効果が示すように、アウトプットは単なる結果としての行動ではなく、学習そのものを深め、支える重要なプロセスです。

#### 記憶の強化

書く・話すといったアウトプット行動は、記憶を長期的に保持する助けになり、自分の理解度を確認する絶好の機会ともなります。

これらのエビデンスから、アウトプットは単なる知識伝達だけでなく、学習プロセスそのものを強化する重要な役割を持っていることがわかります。

**参考リンク**:

- [【練習が最強】最も効果的なインプットとアウトプットの割合！](https://umujapan.co.jp/column/learning-technology-maruyama05/)
- [インプットとアウトプットの黄金比](https://consul-circle.com/bennkyouhouhou-output-input/)
- [知識の定着を促すアウトプット方法](https://omu.repo.nii.ac.jp/record/2000302/files/2022990033a.pdf)

なお、コロンビア大学の研究に関する一次情報は見つけられなかったため、もし一次情報（論文？）をご存知の方は教えて下さいm(\_ \_)m

## おわりに

生成AIが普及した現在におけるアウトプットっているのかな？\
っていう疑問から今回は記事作成をしてみました。\
昔と違いアウトプットすることは自分自身の学習効果を高めるためにも必要ということなので引き続きアウトプットをしていきたいと思います。
米田の補題 #14 圏の例 集合圏

# 米田の補題は、圏論でもっとも重要な定理です（繰り返し）
圏論は、数学の基礎理論です。いままで、[万物の理論](https://amzn.to/3ZLmCJH)
といわれていましたが、[数理科学 11月号](https://amzn.to/4iz6NgM)では、なんと　<font color="Red">万物・万事・万人</font>のための数学理論とまでいわれるようになりました
このAdventCalendar は、体験的に圏論の最も重要な定理である米田の補題を体験的に理解してもらうためにかいています

参考にしているのは、[圏論の道案内](https://amzn.to/4iH2Rus)です

# 集合圏 
まず、集合とはからいきましょう。集合とは要素（元）の集まりで、要素が集合に含まれるかどうかを判定できます。とあるけど、そんなに単純にいいきっていいのでしょうか？　判定はできるのが当たり前なのでしょうか？　例えば、部品製造をしていて、良品のみの集合と、不良品のみの集合を考えてみましょう。なんらかの良品、不良品の判定システムがないと判定できないはずです。実際は判定の精度はだんだんとあがっていくはずです。実際の世の中も不確実を確実にする量子コンピュータとよく似ていますね。だんだんと良くしていくとのを念頭におきつつ、集合の性質をみていきましょう。集合の間の関係には、写像があります。これは、対象との間の射がひとつしかないことです。ようするに写像は、射のひとつの様式です。

```mermaid
---
title: 写像、対応、関数　射は複数可能なのですが、これらはひとつしかない射と考えることができます。
---
graph LR;
a[Aの要素]-.->|写像、対応、関数|b[Bの要素];
a1[域]-.->|f|b1[余域];
a1[域]-.->|g|b1[余域];
a1[域]-.->|h|b1[余域];
```
</br>

**#米田の補題川柳 集合の関係は射がひとつだけ**
集合の要素間の関係は、従来、写像、対応、関数などがあります。これらは、通常の射と異なり、集合間の射ひとつです。この差を意識すれば、集合と圏の関係を見通しよく俯瞰できます。理解はすすんだ気がしますか？
【書籍紹介】『生成AIアプリケーション開発入門』

2022年11月にChatGPTが公開されてから、わずか2か月で1億人のユーザーを獲得し、史上最速で成長したプロダクトとなりました。その成功を皮切りに、生成AIブームが一気に加速し、多くの大規模言語モデルやアプリケーションが次々と登場しています。さまざまな業界で生成AI技術が積極的に活用され、生産性の飛躍的な向上が目指されています。現在、生成AIは最も注目を集める技術分野のひとつであり、ソフトウェアエンジニアにこれまでにないチャンスをもたらす一方で、より高度な専門スキルが求められるようになっています。

生成AIが私たちの生活やビジネスのあり方を大きく変えつつある今、その可能性を最大限に引き出すスキルは、エンジニアにとって必要不可欠なものとなりつつあります。そんな時代のニーズに応えるべく、エンジニアのための実践的なガイド『**生成AIアプリケーション開発入門**』が登場しました。

📘 **書籍情報はこちら** 👉 https://www.amazon.co.jp/gp/product/4991400805

![cover.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103400/969b2660-e590-454d-a77c-44e023b9b40b.png)

本書は、生成AIの基礎から応用までを徹底的に解説し、初心者から中級者まで幅広く対応する内容となっています。本記事では、本書の特徴や注目すべきポイントを詳しくご紹介します！

------

### 本書の特徴と学べる内容

#### **1. 生成AIの基礎知識をしっかり理解**

生成AIを活用するためには、まずその仕組みを理解することが重要です。本書では、以下の内容を丁寧に解説しています：

- 人工知能の歴史と機械学習・ディープラーニングの基本概念
- 大規模言語モデル（LLM）の仕組みと進化
- 最新の生成AIエコシステムや具体的な活用事例

初心者でもスムーズに理解できるよう、専門用語をわかりやすく説明しているのがポイントです。

#### **2. 主要なLLMの特徴と選定方法**

生成AIを活用する際に避けて通れないのが、モデルの選定です。本書では、以下の主要なLLMを徹底比較しています：

- GPT（OpenAI）
- Claude（Anthropic）
- Gemini（Google）
- Llama（Meta）

各モデルの特徴、選定基準、設定方法について、エンジニア目線で分かりやすく解説。計算量やパラメータ数など、技術的なポイントも詳しく学べます。

#### **3. プロンプトエンジニアリングの技術を習得**

生成AIを効果的に活用する鍵となる「プロンプト設計技術」についても、本書では次のような具体的な手法を解説しています：

- Zeroshot-prompting
- Few-shot prompting
- Chain of Thought（CoT）

さらに、ハルシネーション対策やテンプレート活用の実例も紹介しており、即戦力となるスキルが身に付きます。

#### **4. 生成AIサービスとツールの活用法**

生成AIを利用するためのツールやサービスも、本書で詳しく取り上げています：

- ChatGPT
- Hugging Face
- Stable Diffusion
- LangChain
- Streamlit / Chainlit / Gradio

これらのツールを用いたAPI連携やアプリケーション開発の手法を学び、実務に活かせる具体的なノウハウを得られます。

#### **5. AI駆動開発の実務知識を深める**

生成AIを開発プロセス全体にどう取り入れるかを、以下の各工程ごとに解説しています：

- 要件定義
- 設計
- 開発
- テスト

これにより、効率的かつ実践的なプロジェクト運営が可能になります。具体的な事例を通して、生成AIを最大限に活用する方法を学べます。

#### **6. 実践例で次世代の開発手法を学ぶ**

本書では、以下の具体例を実際のコードとともに解説しています：

- LangChainを活用したチャットボット
- RAG（Retrieval-Augmented Generation）
- エージェント（Agent）
- 画像生成・処理アプリケーション

初心者でも実践できる手順で解説されているため、手を動かしながら学べるのが特徴です。

#### **7. 高度応用技術と未来技術を習得**

生成AIの発展を見据えた高度な技術や未来技術についてもカバーしています：

- DifyやLlamaの環境構築
- LLMOps（LLM運用管理）
- AGI（人工汎用知能）の可能性と基礎概念

エンジニアとして、次世代技術に備えた知識を身に付けることができます。

------

### 本書はこんな方におすすめ

- 生成AIの仕組みをゼロから学びたい初心者
- 現場で生成AIを応用したい中級エンジニア
- 生成AIをプロジェクトに導入し、効率化を図りたいマネージャー
- 生成AIツールを使ったアプリケーション開発に挑戦したい方

------

### 最後に

『生成AIアプリケーション開発入門』は、エンジニアが生成AIを活用して新しい価値を生み出すための「実践的な知識」を提供する一冊です。生成AIの未来を切り拓くために、ぜひ本書を手に取ってみてください！
dtsファイルのいじり方

kernelがFDT対応している場合、dtsファイルを用意する必要があります。

FreeBSDのソースツリーでdtsは以下のディレクトリにあります。

sys/gnu/dts
sys/dts

アーキテクチャ毎にディレクトリがきられています。

ZRouterにもdtsディレクトリを作ってあります。

dtsを修正した時にエラーがないかフルビルドでチェックするのは効率が悪いので、以下のようなスクリプトでチェックするとよいです。

```sh
#!/bin/sh

S=$1/sys

MACHINE=$2;export MACHINE

$S/tools/fdt/make_dtb.sh $S $3 /tmp
```

第一引数がfreebsdのソースの絶対パスで、第二引数がアーキテクチャ、第三引数がdtsファイルです。

ZRouterではboard.mkでdtsを指定しています。

```makefile:boards/Fon/FON2601E/board.mk
ZKERNCONF_FDT_DTS_FILE?= "dts/mips/FON2601E.dts"
```

なにか変更したい時はvendor.mkで以下のようにして差し替えます。

```makefile:vendor/Fon/vendor.mk
ZKERNCONF_FDT_DTS_FILE:="vendor/dts/FON2601E_MY.dts"
```

同じボードで複数dtsを用意している場合はフルビルドの前にdtbを消したほうが安全です。依存か何かの処理が壊れているみたいです。
【第1章】要求を仕様化する技術・表現する技術 【要件定義】

## はじめに
ここ最近、仕事で要件を考える、擦り合わせるという機会が増えてきており その重要性を改めて実感しています。

設計が決まっているものをただ、実装する能力は身についている一方 要件定義や仕様書作成の能力が まだ足りないと感じることが多かったので今回は備忘録として「要求を仕様化する技術・表現する技術」で自分が覚えておきたいポイントをまとめていきます。

殴り書きの側面が強いかもしれませんが、ご容赦ください。

## 第１章
大きなシステムでは、5000件を超えるようなバグが発生することもある。
そのバグを修正するためには数万時間にも達するとのこと、時間にすると1人の作業員の10年分にも相当する。

ここで注目したいのは、バグの過半が、**要求仕様に関連するもの**ということ。

### 1.1 どこで問題が発生しているか?
- バグなどの "エラー" の原因プロセス
  - 5:3:2(仕様:設計:実装) の割合でプロセスエラーが発生している
- 設計者テストが十分にできているところでは...
  - 7:2:1(仕様:設計:実装) の割合でプロセスエラーが発生している
- 実装上の仕様を設計の中で決めていくという考え方を変える必要がある
  - 変えていかないと、派生開発での変更モレが起きる

自分のイメージだと、 3:3:4(仕様:設計:実装)の感覚でした。
ここから見るに、要件定義が重要ということがよくわかると思います。

### 1.2 要件仕様のトラブルの実態
- トラブルのケースは以下のようなものがある
  - 要求していた仕様が実現していない
  - 途中で変更を指示した部分が十分に調整されていない
  - 仕様のモレが次々と発見されて、テストがまともに進まない
- 仕様漏れ
  - 仕様として記述するのが漏れてたために、対応するソースコードが書かれなかった状態
  - 例えば...
    - 本来なら受け取ったところで、必要な項目をチェックして問題ないことを確かめてから使用すべきだったがされていなかった(チェックすることが仕様書に書かれていなかった)
    - そのまま数値を計算したところ累積データに異常な値が更新されてしまった
- 解釈に違い
  - 単純な仕様漏れと比べて、複雑である
  - 例えば...
    - 顧客(要件を出す人)と設計者の間での解釈の違い
    - 設計者同士の認識の違い
- 仕様の衝突
  - 個々の機能で必要または有効と考えられる仕様をすべて実現しようとすると、異なる機能のいくつかの仕様間で衝突するという状態
  - 早い段階で全体の仕様化を進めないと防げない

### 1.3 曖昧な状態で作業に入っている
- 構造が傷ついていく
  - 現場では、「納期に間に合わない」という圧力がかかる
  - 強引に送信のタイミングを遅らせたり、データをグローバルで参照したり
    - 結果、プログラムの構造が崩れていく
- 潜在バグの意味
  - 潜在バグとは、今回自分が埋め込んだバグではない、前の人が埋め込んで発見されないままになっていたのを"たまたま" 自分が掘り出してしまった
  - 潜在バグとして、分類した瞬間原因を踏み込んで探すことがなくなってしまう
    - 次の担当者に向けた潜在バグを**仕込む** ことになる

### 1.4 顧客からの仕様変更
- 仕様の変更率が **50%** を超える組織もある
  - 設計者がプログラムのコーディングを進めていく中で仕様な曖昧な箇所に遭遇
  - そこで、依頼者に質問メールや確認のメールを発して仕様変更へ...
- 最後になって機能に制限がつく
 - 担当者も、ソースコードには「具体的な仕様」が必要になり、「具体的な仕様を見つける目」になっている

### 1.5 要求した機能を満たしていない
- 要求を満たさない最大の理由
  - 1.「要件仕様書」が「仕様」といえるレベルに達していない
  - 2.完成品として含まれるべき 「品質要求」も記述されていない
  - 3.関係者が、その不十分さに気づくためのスキルを持ち合わせていないこと

関係者が、仕様書の不十分さに気づかない、仕様と呼ばれるレベルに達していないことはよくある事象ですね。

### 1.6 納期の遅れとなって表面化する
- 要件の仕様化が不十分だと、"予定通り"に設計工程に入っても何をやれば良いかわからない
  - 結果として、**設計作業** が進まなくなる
- 何度も作り直している
- 仕様には設計方法を `誘導`する力がある
  - 仕様によって設計する際のデータ構造の選択や処理方法が決まっていく

### 1.7 品質要求に応えられないSE
- 保守性要求に居直るソフト会社
  - 「品質要求」を加えた顧客に対して、追加で料金をもらおうとする
  - 家を建てる際に、`使いやすさ`を要求して追加料金を請求された
  - それでいてコピペで3000行にも達するプログラムを平気で作ってしまう...
- 品質要求を課さなかった発注側にも責任はある

### 1.8 派生開発での仕様トラブル
- 派生開発はトラブルが多い
  - 逆にいうと、派生開発の中でこそ、**要求の捉え方と仕様化の能力が試される** 
- ソースから仕様を理解できるか
  - 人のソースコードを読んで、要求の具現化の様子や設計の理解には、相当の修練と適切な経験が必要c
- 変更要求(仕様)が表現されていない

### 1.9 設計書が省かれてしまう原因になっている
- 要求仕様書がうまく書かれない原因
  - 「適切な設計書」が書かれていない
- 設計レベルの仕様と混同
  - 「設計仕様書」として書かれている
    - 設計書に仕様が捕捉されている
  - 仕様書と呼ばれるものが複数ある


## まとめ
いかがでしたでしょうか。

「仕様のモレが次々と発見されて、テストがまともに進まない」 というのは、皆さんもあるのではないでしょうか。

自分も直近のプロジェクトで、要件定義が甘かったために、後戻りが多くなり、実装が進まないという状況になってしまいました。
特に、仕様を決めた後に実装を進めてる最中に仕様が曖昧なところを見つける(見つけてもらう)という部分に近いこともありこの本の内容は非常に役立ちそうだと感じました。

単純に実装するスキルもそうですが、自分のためも兼ねて2章以降もまとめていきたいと思います。

## 参照
https://gihyo.jp/book/2010/978-4-7741-4257-9
【React】フォームでsubmitボタンが２つある場合の処理の分け方

## はじめに

今まで１つのフォームにsubmitボタンは１つしか表示したこと無かったのですが、２つ表示したい場面があり、処理の分岐に困ったので備忘録として残します。

## コード

`event.nativeEvent.submitter`でクリックしたボタンの要素を取得して、処理を分岐させてます。

```jsx
import React from 'react';

export const MyForm = () => {
  const handleSubmit = (event) => {
    event.preventDefault();

    // クリックしたボタンの要素
    const clickedButton = event.nativeEvent.submitter;

    if (clickedButton.id === 'button1') {
      // ボタン1がクリックされた時の処理
    }

    if (clickedButton.id === 'button2') {
      // ボタン2がクリックされた時の処理
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* フォーム要素 */}
      <button type="submit" id="button1">
        ボタン１
      </button>
      <button type="submit" id="button2">
        ボタン２
      </button>
    </form>
  );
};
```

## まとめ

そもそもsubmitボタンではなく、片方をクリックイベントにすれば良いだけですが、`submitter`プロパティの存在を初めて知って感動したので記事にしました。

## 参考記事

https://developer.mozilla.org/ja/docs/Web/API/SubmitEvent/submitter

## 最後に

### GoQSystemでは一緒に働いてくれる仲間を募集中です！

ご興味がある方は以下リンクよりご確認ください。

https://goqsystem.recruit.site?qiita=f14cdf1109c2480b51f0
jib - DockerなしでDockerイメージを生成

# 概要
(日記)WindowsでもDockerイメージ(Tarファイル)を作成したい

# 環境
- Widnows 11
- Ubuntu 24.04 (Dockerコンテナとして使用する場合)
- Java 21 (gradlew実行用)
- jib gradle plugin 3.4.4

# 設定
```build.gradle.kts
plugins {
    kotlin("jvm") version "2.0.21"
    id("com.google.cloud.tools.jib") version "3.4.4"
}
// ...
jib {
    from { image = "openjdk:21-jdk-slim" }
    to { image = "docker.io/kyoyap/jibtest:latest" }
}
```

# コンテナイメージ(Tarファイル)生成
```sh
sh gradlew jibBuildTar
```
生成ファイル:`build/jib-image.tar`

# コンテナをレジストリにアップロード
アカウント情報を追加
```build.gradle.kts
//...
jib {
    //...
    to {
        // ...
        auth {
            username = System.getenv("DOCKERHUB_USER")
            password = System.getenv("DOCKERHUB_PASSWORD")
        }
    }
}
```
```sh
export DOCKERHUB_USER=<レジストリUserID>
export DOCKERHUB_PASSWORD=<レジストリPassword>
sh gradlew jib
```
レジストリにコンテナがアップロードされ世界中からアクセスされる

# ソース
https://github.com/kyoya-p/samples/tree/master/2025/jibContainerBuild

# Docker Image生成
以降、実際Dockerが必要なのでUbuntu上で実行

#### Userでdocker 手段1: 自分にdockerの権限を付与する
```sh
sudo usermod -aG docker `id -u -n`
id # 'docker'を含むことを確認。 例: ...,108(docker)
```
#### Userでdocker 手段2: Rootlessモード
https://qiita.com/shokkaa/items/73156905b95270e42a73

#### ビルド
```sh
 sh gradlew jibDockerBuild
```
Docker Image `kyoyap/jibtest:latest` が生成される


# 参照
https://github.com/GoogleContainerTools/jib/blob/master/jib-gradle-plugin/README.md
Workato初心者がIDP by Workatoで物件情報を読み取ってみた

3部作にわたってIDP by Workatoの領収書や履歴書の読み取りをご紹介しました。

https://qiita.com/tabimoba/items/e1cdc12256dfad7e5da0

https://qiita.com/tabimoba/items/54ffc285cd3cddd9e55c

https://qiita.com/tabimoba/items/1bc356821e3ef4daff81

<br>
<br>

今回はWorkatoに携わり始めて6か月弱の私が、物件情報をIDP by Workatoで読み取るということを行ってみます。

※ 読み取り方法等についてはすでにご紹介している上記のものと同様となります。


サンプルはこちら↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3954082/7f88c670-4702-33aa-8bb2-8e73f15bc8fa.png)
それぞれ記載されている情報を読み取る、というところまでをWorkatoで実装します。


<br><br>


今回はBoxにファイルをアップロードするというトリガーにしました。


レシピとしてはこのようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3954082/383c832a-8182-a895-900e-b29cc3a8315b.png)




ドキュメントタイプをDocumentとしているので、フィールドをそれぞれ対応させます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3954082/c4e69ff6-31da-a409-cd97-a9f39f7151a9.png)


3行でレシピが完成しますので、半年しか経験のない、かつコーディング経験のない私でも簡単にレシピ作成することが出来ます。



読み取り結果としてはこのようになりました。
<br>
OCR内容
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3954082/5965b83f-0dcb-3bdb-d17a-d2b760221e18.png)



Confidence Score
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3954082/a7762dda-8e3a-dc6f-52d0-4e0ffe33c75e.png)



<br><br>
いくつかConfidence Scoreが8割台のものがありますが、例えば、間取りが右下に記載されている㎡を拾ってしまったり、諸経費の部分を左側に記載されている共益費から持ってきている等

ここはそれぞれのフィールドのDescriptionを指定してあげると改善してあげられる可能性があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3954082/0230e136-88b0-34ef-0394-75af9d1a8776.png)


例えば上記のように要約せずにそのまま記載してください、とDescriptionで指示してみます。
<br>
すると、1回目では「契約条件：2年間」とAIが解釈していましたが、直接、と指定したことでチラシのとおりの記載になりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3954082/e902a674-3c97-90e2-ac62-aa916ba956ec.png)


前回でもお伝えしたハルシネーションがこのように少しでも解消され、Confidence Scoreを上げられないか、引き続き検証を行っていきます。


<br>

上記のとおり、写真の情報も読み取ることが出来るので、工事情報や地図情報などでも読み取りを行うことを可能にし、現状手書きや別システムへ転載するというような工数がかかっている業務にも活かすことが可能です。
<br>
まだまだ手作業の業務が残っている、OCRのシステムがリプレースを迎える、などのタイミングがある場合はIDP by Workatoの機能がおすすめです。



## まとめ
今回はWorkato初心者がIDP by Workatoでチラシ情報を読み取ることを行ってみました。

非ビジネス職の方でも簡単に触れることを実感していただけていれば幸いです。

是非、本機能の利用もご検討ください。
心理的安全性とは

# はじめに
まだ入社して半年そこらの新入社員ですが、来年度の新人研修を指導することになりました。
関係あるかは分かりませんが、この機会にマネジメント系の学習をしてみたいなと。。

本屋に入り、マネジメントのコーナーでこのタイトルが目に入ったので手に取ってみました。
読み始めはリーダー層向けかな？と思いましたが（この固定概念がまずよくないみたい）、新人の自分にもできることがたくさんあるので自分なりの解釈でアウトプットしていきます。

<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3964610/f5de3186-a4b4-5c0c-eccf-ab2e092c4537.jpeg">

[心理的安全性のつくりかた](https://www.amazon.co.jp/dp/4820728245/ref=emc_bcc_2_i "心理的安全性のつくりかた")
# 心理的安全性とは
__「言いたいことが言える」__
メンバが言いたいことをためらっていたり、上下関係を意識してしまうような状況は、心理的安全性が取れているといえません。
「アットホームな職場」や「言いたいことが言える風通しのいい環境」なんてよく聞きますが、実際にそうなっている会社は多いのでしょうか？
何かを発言・発信しようとする時に勇気が必要というのは誤りで、気兼ねなく発信を受け入れる雰囲気を作ることが大事なのです。
この時点で自分は、「雰囲気つくりはメンバの仕事じゃないし、まだ読むのは早かったな、、」と思いましたが、一人からでもできること（与える影響が大きいもの）があったことをまとめます。

# 感謝しよう
なんだ、精神論か（笑）と思わないでください。
ほんとにもう、これだけだそうです。
感謝を伝えるのは、指摘やアドバイスを行うことに比べて圧倒的にハードルが低いですが、あまり感謝を伝える、伝えてもらう機会って少ないですよね。
何気ない作業、誰にでもできることなんてありません。そこに至るまでの一つ一つの工程、相手の背景を想像してみましょう。そして、恥ずかしがらずに感謝を伝えてみましょう。
その際、「何に感謝しているのか」「どうして感謝しているのか」__理由をつけた感謝__ を伝えることで、心理的安全性はさらに高まります。

# 気にかけていることを伝えよう
「表面上で感謝していればそれでいいの？」と思いますよね、しかし先ほど述べた __理由をつけた感謝__ を続けるには、ほかのメンバをよく観察する必要があります。理由をつけた感謝を続ける事で、｢感謝さえすれば部下は思い通りに動く｣という表面上の心理テクニックではなくなります。続けていくことで、（心理的安全性の）良いリーダーになることが出来るのです。
メンバの心理的安全性を高めるには、(ポジティブな意味で)気にかけていることを伝える必要があります。ここでは、最も簡単で効果的な例としてチャットや投稿に対して👍で反応することが挙げられています。この反応ひとつでメンバに対して気にかけていることを示すことができ、心理的安全性を高めることが出来ます。

# 3割レビューをしよう
作業を振った際に、2割から3割の状態でのレビューを行いましょう。ここで絶対に __やってはいけない__ ことは、誤字脱字などの __細かい指摘__ をすることです。メンバの自由な発想を促進でき、チームの成長に繋がるのはもちろんのこと、レビュアー自身の指摘手戻りの削減にも期待できます。

# メンバーの立場から、リーダーを作ろう
心理的安全性が保てていないチームは、多くの場合上位職が原因となっています。メンバ一人一人が心理的安全性について意識していたとしても、上位職に理解して貰えなければ何も変わらないこともあるでしょう。ここでは、メンバの立場から(無意識的に？)上位職の意識を変える方法が紹介されています。

__1.問題行動を具体化・特定する__
__2.望ましい行動を明確にする__
__3.きっかけ、見返りを分析する__
__4.悪い見返りを無くし、いい見返りを増やす__

具体的な例として以下の事例があります。
いつもは独断で会議を進めていたリーダーが、(珍しく)メンバに意見を求めることがあった。会議が終わったあとメンバから｢先程はおこえがけいただきありがとうございました。本当に話しやすくて助かりました！｣と声掛けをしたことで会議の中でメンバに意見を求めることが増えた。
と言った具合に、 __理由をつけた感謝__ は、リーダーがメンバにするべき事ではなく、リーダーを変えるためのメンバの仕事でもあったのです。

# まとめ
まだ他にも効果的な行動がありましたが、下っ端の自分にも出来そうだなと思ったことを挙げさせてもらいました。
見出しを見ただけでは、｢なんだ、そんなことか｣と思うことが多かったですが、実際にできている場面を見ることや体験することは少なく簡単なことばかりですね。
心理的安全性を高めることで、メンバ同士の意見の衝突を促し、チーム全体の知識・技術が向上します。出来ることから少しずつでも実行していきたいですね。
囚人のジレンマ（アルゴリズム編の2）｜文章題で身に付くプログラミング思考入門️（22/25）



## はじめに

[前回](https://qiita.com/yuki_thewaggle/items/7a09f1f9f61fe65fc209)の続きです。
囚人のジレンマを勝ち抜いた戦略はどのようなアルゴリズムだったのか、紹介していきます。

『文章題で身に付くプログラミング思考入門️』は、日常や文章問題からプログラミング思考を身に付けていく、簡単で身近で、あなたをちょっと輝かせてくれるシリーズです。

## これまでのまとめ

https://qiita.com/advent-calendar/2024/thewaggle

## 対象者
- プログラミングの考え方を学びたい人
- プログラミングに挫折したことのある人
- 論理的思考を鍛えたい人
- 文系の人
- エンジニアの思考を理解したい人


## 振り返り

### 囚人のジレンマとは
```
2人の囚人が別々に隔離されて尋問を受けている。
両者が「黙秘」すれば、2人とも軽い刑罰。
一方が裏切って「自白」、もう一方が「黙秘」すると、裏切った囚人は釈放、黙秘した囚人は重い刑罰。
両者が裏切り合って「自白」なら、中程度の刑罰。
```

### 理論的に優位な選択は
裏切って「自白」した方がお得です。

## 囚人のジレンマをテーマにしたアルゴリズムコンテスト

では本題です。

実施されたコンテストのルールは以下のようなものでした。

:::note warn
囚人のジレンマをテーマにしたアルゴリズムのコンテストは世界各地で複数開催されていて、必ずしもこのルールとは限りません。
「囚人のジレンマ　アルゴリズム」などで検索してみてください。
今回は[こちらの記事](https://mas.kke.co.jp/model/prisoner/)を元に書いていきます。
:::


### ラウンド形式
ゲームは複数のラウンドで構成され、各ラウンドで「協力」か「裏切り」を選択します。

### ポイント制
各ラウンドでの結果に応じて、以下のポイントが与えられます。

- 両者が「協力」：3点ずつ
- 一方が「裏切り」もう一方が「協力」：裏切り側に5点、協力側は0点
- 両者が「裏切り」：1点ずつ

### 総得点の競争
全ラウンドが終了した時点で、得点が最も高い戦略が勝者となります。

### 未知の相手
各戦略は、他の参加者が作成した戦略や主催者が提供する戦略と対戦します。

## 代表的な戦略
囚人のジレンマのアルゴリズムでは、戦略パターンがいくつかにグルーピングできました。

### ①裏切り続ける（Always Defect）
常に「裏切り」を選択するシンプルな戦略。
自分が損をする可能性がなくなりますが、信頼関係の構築は不可能です。

### ②協力し続ける（Always Cooperate）
常に「協力」を選択する戦略。
対戦相手が協力的であれば非常に高いスコアを獲得できますが、裏切りに弱いのが難点です。

### ③しっぺ返し（Tit-for-Tat）
最初は「協力」し、以降は相手の前回の行動をコピーします。
信頼関係を築きつつ、裏切りにはペナルティを課すバランスの取れた戦略です。

### ④ランダム（Random）
ランダムに「協力」と「裏切り」を選択する戦略。
相手にパターンを読まれにくい利点がありますが、結果は運に依存します。


## 一番強かったのは

さて、これらの戦略のうち、どれが一番強かったと思いますか？
実は、**③しっぺ返し（Tit-for-Tat）** が一番強かったそうです。

- まずは友好的関係を構築する
- 相手が裏切ってきたら、こちらも許さないという態度をはっきりとる
- 相手が歩み寄ってきたら、こちらも歩み寄る。根に持たない

**つまり、状況に応じて柔軟に対応できる方が強かったわけです。**

長期的な利益と短期的な利益、どちらを優先するか？
相手の行動を予測するにはどうすればいいか？
不確定な状況で最善の選択をするには？

これらの問いに対する答えを定量的に表現したのが、プログラミングです。
プログラミングは、あなたの思考を助けます。

## ついで
1/fゆらぎ、という言葉を聞いたことがあると思います。
このゆらぎが入った歌声は心地が良いと言われていますよね。

実は、今回題材にした、繰り返す囚人のジレンマゲームにおいて、1/fゆらぎが観測されたのだそうです。
面白いですね。
その論文では生物の進化についても触れられていました。
とても噛み砕いて言うと、生物の絶滅には法則が見られるということらしいのです。
1/fゆらぎも、自然界に存在する、予測できないとも変わらないとも観測できる波のことですね。

デジタルの仕事ばかりしていると、シータの言葉を思い出します。

「土から離れては生きられないのよ」

これから先のテクノロジーは、現実とデジタルの融合を目指して発展を遂げるのでしょう。
物理や生命への理解が、より深く求められる時代になるのかもしれません。

## まとめ
囚人のジレンマ アルゴリズム編では、理論的な戦略、強い戦略について紹介しました。
次回は、囚人のジレンマ 矛盾編をお送りしたいと思います。

## 最後に
ここまで読んでくださってありがとうございます！
よかったら「いいね」をください。続ける励みになります！
react-router-dom（v7）の使い方



## v7からdeferが非推奨
https://react-router-docs-ja.techtalk.jp/upgrading/v6/#%E9%9D%9E%E6%8E%A8%E5%A5%A8%E4%BA%8B%E9%A0%85

```tsx:v7より前
import { Await, useLoaderData, LoaderFunction,defer } from "react-router-dom";

export const flowLoader: LoaderFunction = async ({ params: { flowId } }) => {
  if (!flowId) throw new Error("No id provided");

  return defer({
    comment: fetchComment(),
    article: await fetchArticle(),
  });
};
```

deferの記述が不要になった
```tsx:v7
import { Await, useLoaderData, LoaderFunction } from "react-router-dom";
export const flowLoader: LoaderFunction = async ({ params: { flowId } }) => {
  if (!flowId) throw new Error("No id provided");

  return {
    comment: fetchComment(),
    article: await fetchArticle(),
  };
};
```




```tsx
const fetchArticle = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ name: "article1" });
    }, 1000);
  });
};

const fetchComment = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ title: "test comment" });
    }, 3000);
  });
};

type Article = {
  name: string;
};

type Comment = {
  title: string;
};

type LoaderData = {
  comment: Promise<Comment>;
  article: Article;
};

export const flowLoader: LoaderFunction = async ({ params: { flowId } }) => {
  if (!flowId) throw new Error("No id provided");

  return {
    comment: fetchComment(),
    article: await fetchArticle(),
  };
};

const FlowLayout = () => {
  const { article, comment } = useLoaderData() as LoaderData;
  return (
    <>
      <div className="text-red-800">{article.name}</div>
      <Suspense fallback={<div className="text-red-400">Loading Comments...</div>}>
        <Await resolve={comment} errorElement={<div className="bg-black text-white">Failed to load comments.</div>}>
          {(commentData) => <div className="text-blue-500">{commentData.title}</div>}
        </Await>
      </Suspense>
    </>
  );
};

```
囚人のジレンマ（アルゴリズム編の1）｜文章題で身に付くプログラミング思考入門️（21/25）

## はじめに
『囚人のジレンマ』という言葉を聞いたことがありますか？
これは、経済学や心理学、プログラミングの世界で広く議論されているテーマです。

プログラミングの世界にも競技があるのですが、アルゴリズムコンテストなどでこのテーマを扱われることがあります。
つまり、経済学や心理学の世界で語られるような戦略でも、強い弱いの定量的な判断ができるということですね。

『文章題で身に付くプログラミング思考入門️』は、日常や文章問題からプログラミング思考を身に付けていく、簡単で身近で、あなたをちょっと輝かせてくれるシリーズです。

今回は、アルゴリズムから、囚人のジレンマを考えてみます。

## これまでのまとめ

https://qiita.com/advent-calendar/2024/thewaggle

## 対象者
- プログラミングの考え方を学びたい人
- プログラミングに挫折したことのある人
- 論理的思考を鍛えたい人
- 文系の人
- エンジニアの思考を理解したい人


## 囚人のジレンマとは？

まず始めに、囚人のジレンマとは何か？という話ですが、これはゲーム理論という学問の代表的な例として有名ですね。

### ゲーム理論とは

ではゲーム理論とは何か？をとても簡単に説明しましょう。
世の中にはたくさんの人や団体など、決断を行う主体がたくさんいて、お互いに影響し合いながら生き抜いているわけですが、これを数式として説明しようというのがゲーム理論です。

つまり、難しくて複雑で、答えがないんじゃないかというほどの難問に対して、それでも一定の答えを導けるはずだと信じた先人たちの飽くなき探究心が生み出す、非常に難解で興味深い考え方です。

その中でも、短期的・個人的な利益を優先するか、長期的・全体的な利益を優先するか、結局のところ自分にとってどちらが一体お得なのだろうか？というのが囚人のジレンマです。

### 囚人のジレンマとは

さて、以下のような状況になったら、あなたなら「黙秘」と「自白」のどちらを選びますか？

```
2人の囚人が別々に隔離されて尋問を受けている。
両者が「黙秘」すれば、2人とも軽い刑罰。
一方が裏切って「自白」、もう一方が「黙秘」すると、裏切った囚人は釈放、黙秘した囚人は重い刑罰。
両者が裏切り合って「自白」なら、中程度の刑罰。
```

整理すると、
- 黙秘するなら、軽い刑罰か、重い刑罰
- 自白するなら、釈放か、中程度の刑罰

明らかに裏切って自白した方がお得です。

例えば、数字をつけて算数にしてみましょう。

- 黙秘するなら、軽い刑罰（`1`）か重い刑罰（`3`）
    - 合計4
- 自白するなら、釈放（`0`）か中程度の刑罰（`2`）
    - 合計2

わかりやすくなったと思います。数字は、いくつを選んでも影響がありません。
なぜなら、
釈放 < 軽い刑罰 < 中程度の刑罰 < 重い刑罰
であることは明らかですから。

つまり、比較演算でもできますね。
釈放は軽い刑罰よりお得、中程度の刑罰は重い刑罰よりお得ですから、お得かつお得な自白の方が明らかにお得なわけです。

それなら、理論的には裏切った方が優位となるわけですが、ここから先が面白いところです。

囚人のジレンマを題材にしたアルゴリズムコンテストというのがあり、「一体どういう戦略が、長期的目線で互いに利益を生み出すのか？」というのが競われました。
自分が勝つための強い戦略、というのが決まったわけです。

裏切った方が本当に優位だったのでしょうか？

次回、アルゴリズム編の2で紹介していきたいと思います。

## 最後に
ここまで読んでくださってありがとうございます！
よかったら「いいね」をください。続ける励みになります！
C言語で Quine を書いてみた

# Quine

Quine とは、自分自身のソースコードを出力するプログラムである。

# 今回の戦略

今回は、以下の方針で実装した。

1. ソースコードの前半部分 (エンコードした文字列より前) をデコードして出力する
2. ソースコードの前半部分をエンコードした文字列を出力する
3. ソースコードの中間部分 (前半部分をエンコードした文字列と後半部分をエンコードした文字列の間) を出力する
4. ソースコードの後半部分をエンコードした文字列を出力する
5. ソースコードの後半部分 (エンコードした文字列より後) をデコードして出力する

「エンコード」には、Base64 を用いた。

# 実装

```c
#include <stdio.h>

void decodeb64(const char* b64) {
    int byte = 0, bit = 0;
    while (*b64 != '\0') {
        byte <<= 6;
        bit += 6;
        if ('A' <= *b64 && *b64 <= 'Z') byte += *b64 - 'A';
        else if ('a' <= *b64 && *b64 <= 'z') byte += *b64 - 'a' + 26;
        else if ('0' <= *b64 && *b64 <= '9') byte += *b64 - '0' + 52;
        else if (*b64 == '+') byte += 62;
        else if (*b64 == '/') byte += 63;
        if (bit >= 8) {
            bit -= 8;
            putchar(byte >> bit);
            byte &= (1 << bit) - 1;
        }
        b64++;
    }
}

int main(void) {
    const char* front = "I2luY2x1ZGUgPHN0ZGlvLmg+Cgp2b2lkIGRlY29kZWI2NChjb25zdCBjaGFyKiBiNjQpIHsKICAgIGludCBieXRlID0gMCwgYml0ID0gMDsKICAgIHdoaWxlICgqYjY0ICE9ICdcMCcpIHsKICAgICAgICBieXRlIDw8PSA2OwogICAgICAgIGJpdCArPSA2OwogICAgICAgIGlmICgnQScgPD0gKmI2NCAmJiAqYjY0IDw9ICdaJykgYnl0ZSArPSAqYjY0IC0gJ0EnOwogICAgICAgIGVsc2UgaWYgKCdhJyA8PSAqYjY0ICYmICpiNjQgPD0gJ3onKSBieXRlICs9ICpiNjQgLSAnYScgKyAyNjsKICAgICAgICBlbHNlIGlmICgnMCcgPD0gKmI2NCAmJiAqYjY0IDw9ICc5JykgYnl0ZSArPSAqYjY0IC0gJzAnICsgNTI7CiAgICAgICAgZWxzZSBpZiAoKmI2NCA9PSAnKycpIGJ5dGUgKz0gNjI7CiAgICAgICAgZWxzZSBpZiAoKmI2NCA9PSAnLycpIGJ5dGUgKz0gNjM7CiAgICAgICAgaWYgKGJpdCA+PSA4KSB7CiAgICAgICAgICAgIGJpdCAtPSA4OwogICAgICAgICAgICBwdXRjaGFyKGJ5dGUgPj4gYml0KTsKICAgICAgICAgICAgYnl0ZSAmPSAoMSA8PCBiaXQpIC0gMTsKICAgICAgICB9CiAgICAgICAgYjY0Kys7CiAgICB9Cn0KCmludCBtYWluKHZvaWQpIHsKICAgIGNvbnN0IGNoYXIqIGZyb250ID0gIg";
    const char* back = "IjsKICAgIGRlY29kZWI2NChmcm9udCk7CiAgICBmcHV0cyhmcm9udCwgc3Rkb3V0KTsKICAgIGZwdXRzKCJcIjtcbiAgICBjb25zdCBjaGFyKiBiYWNrID0gXCIiLCBzdGRvdXQpOwogICAgZnB1dHMoYmFjaywgc3Rkb3V0KTsKICAgIGRlY29kZWI2NChiYWNrKTsKICAgIHJldHVybiAwOwp9Cg";
    decodeb64(front);
    fputs(front, stdout);
    fputs("\";\n    const char* back = \"", stdout);
    fputs(back, stdout);
    decodeb64(back);
    return 0;
}
```

# 動作確認

以下のようなコマンドでプログラムを実行し、ソースコードと出力が一致している事を確かめた。

```bash
gcc -Wall -Wextra -O2 -o main main.c
./main > out.c
sha256sum *.c
```

[Coliru Viewer](https://coliru.stacked-crooked.com/a/22a5349794670abe)

:::note info
システムの仕様に合わせるため、実際のコマンドは若干異なる。
:::

以下の出力が得られ、ソースコードと出力が一致している (確率が非常に高い) ことがわかる。

```text
0aeb393850515f49cc475946353b58ec14432eb9bc949cd25355e499b2000f63  main.c
0aeb393850515f49cc475946353b58ec14432eb9bc949cd25355e499b2000f63  out.c
```

# おわりに

今回のプログラムを書いた後に [Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AF%E3%82%A4%E3%83%B3_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0)) を見たら、もっと賢い実装例があるやんけ……orz
【Lottielab】クリスマスプレゼントの中身はなんだろう🤔

:::note
※この記事は後編となっております。
技術的な内容については前編にて紹介しております。
前編をまだ読んでいない方は以下のリンクから前編を一読ください。
https://qiita.com/kou1924/items/8540e3212f02b6fc2619
:::

前編ではサンタさんになりきって記事をお読みいただきましたが、以降はこどもになった気持ちでお読みいただければ幸いです。

本日は12月25日、クリスマス当日🎅
「クリスマスプレゼントは届いているかな？、クリスマスツリーを見てこよう！」

![スクリーンショット 2024-12-22 0.52.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3941610/8f2bc4cb-ace6-aa21-3647-80cb6b61e949.png)

「あ！プレゼント届いてる！！」

よかったですね！
ぜひ皆さんの中にプレゼントが届いた方がいたらコメントに書いていただけるとうれしいです！

さぁ、それではプレゼントを開けてみましょう！何が入っているかな？👀

今回は事前にプレゼントボックスをLottielabを用いて作成させていただきました。
ファイル名は「12_25.json」としました。
そして、前編で用いたコードのfile_pathを書き換え、実行しました。
```python
from streamlit_lottie import st_lottie
import json

# JSONファイルを読み込む関数
def load_lottie(file_path):
        with open(file_path, "r") as f:
                return json.load(f)

# ローカルにあるJSONファイルのパスを指定
file_path = "12_25.json"

# ローカルにあるJSONファイルを読み込む
animation = load_lottie(file_path)

st_lottie(animation, width = 600, height = 400)
```

![presentbox_open.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3941610/031fa17d-2499-9842-5fa2-2360bfe7ee54.gif)

なんと、プレゼントボックスの中にはQiitanが入っていました！🥰
可愛がってあげてね！

Happy Holidays!🎄
【AWS re:Invent 2024】オブザーバビリティのベストプラクティス [COP344]

こんにちは、AWS Jr. Championsの伊藤です！


## 概要
re:Invent 2024にて、オブザーバビリティのベストプラクティスというタイトルのチョークトーク(※)に参加してきましたのでその内容についてご紹介します！

(※チョークトーク：スピーカーと少人数の参加者による対話形式)

普段、インフラエンジニアとして、基盤の構築や運用設計まで携わる部分が多く、今日でもホットなワードであるオブザーバビリティに着目しました！

本セッションでは、発表者と聴講者の対話形式のもと、オブザーバビリティと実現するためにはどのようにすればよいか、また継続的改善を進めるには、という話がありました。

## オブザーバビリティとは？

オブザーバビリティとは、システムの内部状態を外部から得られるデータや情報（メトリクス、ログ、トレースなど）を通じて理解する能力を指します。モニタリングとは異なり、より広範囲のシステム状態を把握し、問題の特定や将来の予測が可能な点が重要となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3735223/b554af58-60a8-7530-b0a1-1438169c3cca.png)

## いきなりですが、6つのベストプラクティス

セッションの序盤でいきなり結論が出ました。

1. ビジネスからの逆算
1. メトリクス、ログ、トレースの一元化と相関化
1. 計測と監視の自動化
1. フルスタックでオブザーバビリティの実現
1. リアルタイムアラートおよびダッシュボードの実現
1. 継続的改善の文化を育む
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3735223/124c6f93-5c07-a55a-9254-383a28b14c23.png)


## スポーツベッティングのシステムがあるとする。クライアントサイドでの監視ポイントは？
- ブラウザの種類、ChromeやFirefoxでエラーが多いのかどうかを見る必要性
    - クライアントサイドで実行されているJavaScriptコードが原因でブラウザエラーが発生する可能性
    - 例えばChromeのアップデートが実行されているために待ち時間が発生することもある
- ベッティングは年齢制限があるためID認証などの機能の必要性
    - ユーザが適切に認証されていることの保証
- 非合法な地域については制限をかけその地域からのアクセスがないか

### 活用できるサービスは、Synthetics Canary、RUMなど


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3735223/33b204f1-8d82-7b8b-7356-ffdf2d50a2a3.png)


## バックエンドでの監視ポイントは？

- バックエンドでは、ログ、メトリクス、トレースを活用して、インフラやアプリケーションの状態を詳細に追跡する
- CloudWatch Logsなどでログ収集をし、フロントエンドーバックエンド間の情報フローを確立
- 「Page Zero Metrics」は最重要のビジネス指標であり、これに基づいてほかのメトリクスを管理する
- 不必要なログやメトリクスは、ストレージや処理コスト削減のため削除する
- ログ・メトリクスの相関性を分析し、不要な部分をクリーンアップするプロセスを定期的に実行する

### 活用できるサービスは、CloudWatch Logs、X-Rayなど


## インフラストラクチャでの監視ポイントは？

- リソースごとのメトリクス
    - Insightsの活用（Lambda Insights / Container Insightsなど）（今回の構成だとLambdaやECSが使われている想定のため）
    - RDSのデータベースメトリクス
- 複数のAWSアカウント間での監視の際には、集中管理アカウントの利用が推奨されるが、すべてのメトリクスを集める必要はない。ビジネスニーズに応じて必要なものを取得する


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3735223/5b1670a3-b30b-e776-f970-f3b39d309556.png)

### 活用できるサービスは、各種リソースメトリクス、X-Rayなど

## 継続的改善を進めるには？

オブザーバビリティは単なる技術的な問題ではなくプロセスと人々が連携することで機能します。
継続的改善を進めるためには、メカニズムとして定期的なフィードバックループを作り、エラー分析や学びを次回に生かすことが重要です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3735223/85d23512-a02c-9446-9808-d1f70f66fe4f.png)


AWSでは、運用メトリクスを週次で収集し、問題があればその原因を特定し、修正するためのミーティングを行っています。
これにより、改善点を共有し、次回の運用に活かす文化が育まれているようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3735223/508fc9b5-3c7f-ae91-8942-eb666e01bba0.png)

### 運用メトリクスは以下の通り
- 定期的なケイデンスサイクル
- インシデント後の分析
- 非難なく5つのなぜなぜ
- エラー訂正
- 学びの共有

## まとめ
今回オブザーバビリティのベストプラクティスについて学びました。
まずは、オブザーバビリティとは和訳すると可観測性ということで一言だとわかりにくいものなのですが、システムの内部状態を外部から得られるデータや情報を通じて”理解できる能力”のことと学びしっくり感じました。
メトリクスを取ることが目的ではなく、収集したメトリクスからシステムの状態を把握できることが重要だとわかりました。
また、同様に、メトリクスやアラートを収集したり設定したりするだけではなく、それらが本当に必要なのかを判断し、不要な場合にはクリーンアッププロセスを自動実行することも重要だということは、印象に残っています。

説明を受けるだけだと簡単なように思ってしまいますが、いざ実践しようとなるとビジネスニーズから運用設計に落とし込んで、メトリクス要否の定期実行プロセスを回して、インシデントが起こった際には、定例で原因分析と修正をして・・・などやることが盛りだくさんだと思いました・・・これを実践できているＡＷＳはすごい・・・
DocstringAsImage.jl の紹介

# 本日は

https://qiita.com/advent-calendar/2024/julia

の Series $N=3$ の記事です．

自作パッケージ DocstringAsImage.jl の紹介です．

---

## 何ができるの？

1. iTerm2 のように Sixel Graphics に対応しているターミナルを用意します
1. 次に Julia の REPL を開いて次のようにします

```julia
julia> using DocstringAsImage; @imgdoc exp
```

この手順を踏むことで次のような結果を得ます．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/ffd0090d-28e7-6d03-3475-522794b34213.png)

ででーん．ヘルプモードで見るような `@doc exp` で呼び出した結果に比べて読みやすいものができました．`@doc exp` の結果は下記のとおりです．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/e6f62b40-e0f5-845b-fde3-3cd14002c0c1.png)

サンプルコードの部分は読みやすいかもしれませんが

> Compute the matrix exponential of A, defined by

の直後にある数式は読みやすくなったと思います．理系の大学生，大学院生であれば TeX の記法は慣れているので直接ソースコードを読めるように目が訓練されていると思います．

## 内部で行っていること

大雑把にいえば下記のようなコマンドを実行しています．Julia の Docstring はマークダウンで書くことができます．それを適切に前処理をし `sample.qmd` という形でファイルにダンプします．そのあとは Quarto を使って Typst ファイル `sample.typ` に変換します．最後に `sample.typ` をコンパイルして結果を png ファイルとして生成します．

```sh
# 擬似コードです
$ quarto render sample.qmd --to typst 
$ ls
sample.typ sample.qmd ...
$ quarto typst compile sample.typ sample_{n}.png
```

生成された png ファイルを読み込んで Sixel.jl パッケージを使って画像を Sixel Graphics フォーマットとしてエンコードします．それを表示することで画像をターミナルに表示することができています．

---

## 本当はやりたかったこと

Typst のコンパイルで得られた PDF を作成しそれを読み込むアプローチを考えていました．PDF を画像として表示する，しかもページャー付きの便利な Rust ライブラリがあります．

https://github.com/itsjunetime/tdf

最初はこれを使っていましたが，BinaryBuilder でビルドができなかったので諦めました．

DocstringAsImage.jl を公式パッケージにするにあたって tdf_jll を作らないと利便性が損なわれるからです．幸い，Quarto は `quarto_jll` パッケージで利用できるようになっているので Quarto， Quarto と紐づいている Typst を使って色々することができるようになりました．

Sixel Graphics は Windows Terminal でも使えるようになるらしいので多くのプラットフォームで使えるようになるはずです．

https://x.com/windowsdev/status/1828826681265562048

使えるようになったらWindowsユーザもぜひ試してください．