Qiitaの記事が100本超えたのでGPT-4oとPower Automateで現状把握する

# はじめに

私の*Qiita*の記事が100本を超えました。読んでくださる方々に、心から感謝します。
ただ自分の中でも今どのような記事を書いたか、現状把握の必要性を感じています。

以前書いた記事である下記の記事から、内容の洗い出しを実施していこうかと思います。

https://qiita.com/DEmodoriGatsuO/items/4d7d2a41477ddc24dc6a

# SharePoint ListsにQiitaの情報を列挙する
まずは`SharePoint Lists`を用意します。

■ 記載内容

|項目|キー|説明|
|---|---|---|
|タイトル|1 行テキスト|既定で存在するタイトル列|
|QiitaID|1 行テキスト|
|HyperLink|ハイパーリンクまたは画像||
|作成日時|日付と時刻||
|タグ|選択肢|`複数選択` ON、`値を手動で追加` ON|
|要約HTML|複数行テキスト|`複数選択` ON、`拡張リッチ テキストを使用` ON|
|要約Markdown|複数行テキスト||

コチラにQiitaの記事情報を集約していくことにしましょう。

# Power Automateの全体像
1. `Qiita API`より記事情報を取得
    1. 並列で`Dataverse`より`Azure OpenAI`のシークレットを取得
1. 繰り返し処理を実行 - コンカレンシー制御を最大値に
    1. `Qiita API`より取得した記事の記載内容よりプロンプトを設定
    1. `Azure OpenAI GPT-4o`を実行
    1. `GPT-4o`から`JSON の解析`
    1. [SharePoint 項目の作成](https://learn.microsoft.com/ja-jp/sharepoint/dev/business-apps/power-automate/sharepoint-connector-actions-triggers#create-item)用に`Qiita`の記事タグ情報を選択
    1. [SharePoint 項目の作成](https://learn.microsoft.com/ja-jp/sharepoint/dev/business-apps/power-automate/sharepoint-connector-actions-triggers#create-item)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/3794119a-d49f-c5e4-bf6a-a0530f342925.png)

# Qiita API V2のおさらい
`Qiita API V2`は、下記のURLにHTTP要求を送ることで記事情報を取得できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/6970660a-3bbb-1b39-5a92-be1b0326787d.png)

```
// 出戻りガツオ🐟の場合 - https://qiita.com/api/v2/users/DEmodoriGatsuO/items

https://qiita.com/api/v2/users/@{ユーザーID}/items
```

https://qiita.com/api/v2/docs

こちらに`HTTP要求`を送ります。
記事数が多い方は
 - page
 - per_page(1ページあたりに含まれる要素数)

上記の設定が必要です。
今回は`per_page`を100に設定しています。

こちらでQiitaの記事情報がまとめて取得できます。
`JSON の解析`で戻り値を評価。

```json:schema
{
    "type": "array",
    "items": {
        "type": "object",
        "properties": {
            "rendered_body": {
                "type": "string"
            },
            "body": {
                "type": "string"
            },
            "created_at": {
                "type": "string"
            },
            "id": {
                "type": "string"
            },
            "likes_count": {
                "type": "integer"
            },
            "private": {
                "type": "boolean"
            },
            "reactions_count": {
                "type": "integer"
            },
            "stocks_count": {
                "type": "integer"
            },
            "tags": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "name": {
                            "type": "string"
                        },
                        "versions": {
                            "type": "array"
                        }
                    },
                    "required": [
                        "name",
                        "versions"
                    ]
                }
            },
            "title": {
                "type": "string"
            },
            "updated_at": {
                "type": "string"
            },
            "url": {
                "type": "string"
            },
            "user": {
                "type": "object",
                "properties": {
                    "description": {
                        "type": "string"
                    },
                    "facebook_id": {
                        "type": "string"
                    },
                    "followees_count": {
                        "type": "integer"
                    },
                    "followers_count": {
                        "type": "integer"
                    },
                    "github_login_name": {
                        "type": "string"
                    },
                    "id": {
                        "type": "string"
                    },
                    "items_count": {
                        "type": "integer"
                    },
                    "linkedin_id": {
                        "type": "string"
                    },
                    "location": {
                        "type": "string"
                    },
                    "name": {
                        "type": "string"
                    },
                    "organization": {
                        "type": "string"
                    },
                    "permanent_id": {
                        "type": "integer"
                    },
                    "profile_image_url": {
                        "type": "string"
                    },
                    "team_only": {
                        "type": "boolean"
                    },
                    "twitter_screen_name": {
                        "type": "string"
                    },
                    "website_url": {
                        "type": "string"
                    }
                }
            },
            "page_views_count": {},
            "team_membership": {},
            "organization_url_name": {},
            "slide": {
                "type": "boolean"
            }
        }
    }
}
```

並列で`Dataverse`を経由し、`Azure Key Vault`に格納された`Azure OpenAI`のAPI情報を呼び出します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/11d38f68-eff9-4872-c489-1fcf8fbf0ee0.png)

`Dataverse` & `Azure Key Vault`の[活用法はこちら](https://learn.microsoft.com/ja-jp/power-apps/maker/data-platform/environmentvariables-azure-key-vault-secrets)

https://learn.microsoft.com/ja-jp/power-apps/maker/data-platform/environmentvariables-azure-key-vault-secrets

何回か使っていますが、コチラの呼び出しに数秒の時間を要する印象です。

## Azure OpenAI GPT-4o

さて、ここから`GPT-4o`に渡すプロンプトを設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/6c298496-0edf-497d-c3f5-00a11e37c4ab.png)

- Qiita記事の文章を渡す（Markdown形式 - `item()?['body']`）
- 箇条書き形式の要約を取得する
    - `HTMLバージョン`と`Markdown記法バージョン`の二つの要約を取得する

コチラを`SharePoint Lists`に最終的に格納します。

繰り返し処理の中で`プロンプト`を設定していきましょう。

```plaintext:プロンプト
下記はMarkdownで表記された技術記事です。解析し、その内容を500文字以内で完結に箇条書きで要約してください。解析した値はJSONの形式でHTMLテキストとMarkdown形式の文字列で返してください。

## 記事
@{item()?['body']}

## JSON Schema {\"type\": \"object\",\"properties\": {\"HTML\": {\"type\":\"string\", \"description\": \"HTMLテキストの要約\"}},{ \"Markdown\": {\"type\": \"string\", \"description\": \"マークダウン形式の要約\"}},\"required\": [\"HTML\",\"Markdown\"],}"
```

何回か記事で取り上げさせていただいていますが、`プロンプト`から`GPT-4o`で取得する値の形式を設定できます。`JSON`形式で文字列だけを返してもらうようにしましょう。

エスケープ文字が入ってしまい、わかりづらいですが想定するスキーマは下記のとおりです。

```json:プロンプトで定義しているJSON スキーマ
{
    "HTML": {
        "type": "string"
    },
    "Markdown": {
        "type": "string"
    }
}
```

さて繰り返し処理で`GPT-4o`を使っていきます。
`max_tokens`を低めに設定して検証しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/c2ecf017-2bf9-5aa3-8aaf-1b6f430623b5.png)

Method - `POST`

```json:Headers
{
    "Content-Type": "application/json",
    "api-key": "@{body('GetSecret_APIKEY')?['EnvironmentVariableSecretValue']}"
}
```

```json:Body
{
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "@{outputs('Prompt')}"
        }
      ]
    }
  ],
  "max_tokens": 1000,
  "temperature": 0.7,
  "frequency_penalty": 0,
  "presence_penalty": 0,
  "top_p": 0.95,
  "response_format": {
    "type": "json_object"
  }
}
```


### 記事のタグの取得
Qiitaにてせっかく記事のタグを取得しているため、コチラも`SharePoint Lists`に設定していきます。
`複数選択を許可`がオンになっている場合、というオブジェクトが格納された配列を渡せば設定できるようです。

- From `item()?['tags']`

```json:Map
{
  "Value": @{item()?['name']}
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/d93ba264-b9db-5c00-2278-721c977c7a50.png)

[選択(Select)](https://learn.microsoft.com/ja-jp/power-automate/data-operations#use-the-select-action)を使って、入力用に加工を実施します。

https://learn.microsoft.com/ja-jp/power-automate/data-operations#use-the-select-action

https://qiita.com/DEmodoriGatsuO/items/aabff4e30bba821c39b7

https://qiita.com/DEmodoriGatsuO/items/80c472e84ece26c8e2c2

## 最後に`項目を作成`
そして[項目を作成](https://learn.microsoft.com/ja-jp/sharepoint/dev/business-apps/power-automate/sharepoint-connector-actions-triggers#create-item)で完了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/54c9b544-8b3f-d569-3b3b-1cc05a8ed611.png)

Qiita API V2で取得できる日時文字列を設定するために[parseDateTime 関数](https://learn.microsoft.com/ja-jp/azure/logic-apps/workflow-definition-language-functions-reference#parsedatetime)を使って、日付列にフィットさせましょう。

https://learn.microsoft.com/ja-jp/azure/logic-apps/workflow-definition-language-functions-reference#parsedatetime

https://learn.microsoft.com/ja-jp/sharepoint/dev/business-apps/power-automate/sharepoint-connector-actions-triggers#create-item

## 繰り返し処理のチューニング
Azure OpenAIを100回呼び出すことから実直に繰り返しを実行すると非常に時間がかかります。
そのため、[それぞれに適用する(Apply to each / For each)](https://mofumofupower.hatenablog.com/entry/2020/03/17/000000_1)のコンカレンシー制御の値を最大まで引き上げます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/b5cb65e7-d04b-527c-1ed7-b24d9e48bc05.png)

https://mofumofupower.hatenablog.com/entry/2020/03/17/000000_1

こちらで実行していることは、繰り返し処理を並列で実行できるようにする設定です。
[項目の作成](https://learn.microsoft.com/ja-jp/sharepoint/dev/business-apps/power-automate/sharepoint-connector-actions-triggers#create-item)アクションの評価されるタイミングは、保証されないことになりますが、並列でAPIの実行ができ、処理が非常に速くなります。

**整いました！**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/62af239b-c2bb-4762-617e-55e4000bec30.png)

# おわりに
Qiita記事がストックされたけど、タイトルだけでは中身わからないな・・・ということで、、GPT-4oを使った内容の可視化を実施しました。

`GPT-4o`を使っているだけの内容になりますが、可視化できることは非常に便利です。
これで散見した自分の記事のまとめなども作れそう🐟

やはり強力な機能ですね。