2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

特定 DB の操作パターンを定義したカスタムGPTを作る

Last updated at Posted at 2025-12-08

Notion の DB を ChatGPT から操作したい

このようなニーズはありませんか?僕は日常的な情報のほとんどを Notion DB で管理してしまっているので、外部の AI サービスから操作できるととても助かります。

この記事では、

  • Notion 側でやること(PAT / Database ID / Integration 連携)
  • カスタムGPT 側でやること(Actions の設定・プロンプト設計)

を、ご紹介します。

そのうえで、私が実際に作った「英語表現の習得支援 GPT」を例に、実際に使っている OpenAPI スキーマとプロンプトをご紹介します。

今回作ったもの: 英語表現の習得支援 GPT

私は英語を話せるようになりたいのですが、

聞き取れるし読んでわかるんだけど、
自発的に話したり書いたりできないんだよな…

という英語表現がたくさんあります。

そこで、受動的な意味では習得しているが、能動的な意味で習得していない英語表現を、 English Expressions という Notion DB を作り、まとめてみました。

スクリーンショット 2025-12-07 18.00.24.png

  • Status プロパティで、現在の習得状況を管理
    • 最初は読み聞きして理解できるだけなので Passive にしておき
    • 能動的に使えるようになれば Active に変更するイメージです

この DB を読み書きしつつ、ユーザーと対話するようなカスタム GPT を作ります。

1. GPT: Status が Passive の英語表現を1つ選択して、お題として出題

スクリーンショット 2025-12-07 18.14.59.png

2. User: その表現を用いた文章を即興で作る → GPT: 文章を添削する

image.png

3. User: 必要なら、「この表現はもう能動的に使える」旨を伝える → GPT: Notion DB の Status を Active に更新する

image.png

…と、このようなものを作りました。
では、実際に作り方を紹介していきます。

全体の流れ

  1. Notion の DB を用意し、Integration と連携する

  2. Notion の PAT(トークン)と Database ID を取得する

  3. カスタムGPT の Actions に OpenAPI スキーマを登録する

  4. GPT の「指示」(システムプロンプト)で、

    「どんなときにどのアクションを呼ぶか」をルールとして教える


Step 1. Notion 側の準備

1-1. データベースを用意する

まずは、カスタムGPTから操作させたい Database を用意してください。

1-2. Integration(PAT)を作る

Notion API を使うには PAT が必要です。

  1. https://www.notion.so/profile/integrations から New integration を作成
    1. Name: 自分が見て何の Integration なのかわかる名前を適当に設定
    2. Workspace: 操作したい DB のあるもの
    3. Type: Internal
  2. 作成すると、内部インテグレーションシークレットが発行される
    • これが API トークン(PAT)

image.png

1-3. DB と Integration を接続する

作った Integration に、その DB へのアクセス権を与えます。

image.png

  1. 対象のデータベースをフルページで開く
  2. 右上の「…」メニュー > 「接続」>「接続を追加」的な項目を選択
  3. 先ほど作った Integration を選んで接続

1-4. Database ID を取得する

image.png

カスタムGPT からは Database ID を使ってエンドポイントを指定します。

  1. 対象のデータベースのリンクをコピー

  2. URL の中に、32 文字前後の ID が含まれている

    例:

    https://www.notion.so/workspace-name/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx?v=...

    この xxxxxxxx... が Database ID

  3. この ID を控えておく

以降、 YOUR_DATABASE_ID と表記します。


Step 2. カスタムGPT 側で Actions を有効化する

次に、カスタムGPT 側の設定です。

2-1. カスタムGPT を作る

  1. https://chatgpt.com/gpts/editor にアクセス
  2. 構成」 タブから、名前、説明、アイコンなどをお好みで設定

2-2. Actions で Notion API 用の OpenAPI スキーマを登録

  1. 構成」 ****タブ内の「新しいアクションを作成する」(おそらく下の方にあります)
    image.png

  2. 認証」 を設定

    • 認証タイプ:API キー
    • APIキー:Notion の PAT を貼る
    • 認証タイプ:Bearer
      image.png

Step 3. 読み取り+書き込みの OpenAPI スキーマを定義する

今回の私のユースケース向けに実際に使っているスキーマです。
行いたい操作としては、以下の2つです。

  • 読み取り:

    Status == "Passive" のレコードを /v1/databases/YOUR_DATABASE_ID/query で取得する

  • 書き込み:

    特定ページの Status"Active" に更新する

実際に使うときは、 "YOUR_DATABASE_ID"自分の Database ID に置き換えてください

{
  "openapi": "3.1.0",
  "info": {
    "title": "Notion English Expression Database API",
    "version": "v1.0.0"
  },
  "servers": [
    {
      "url": "<https://api.notion.com>"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/v1/databases/YOUR_DATABASE_ID/query": {
      "post": {
        "summary": "English Expression データベースからレコードを取得する(Passive のみなどのフィルタに使用)",
        "operationId": "queryEnglishExpressionDatabase",
        "tags": [
          "EnglishExpression"
        ],
        "x-openai-isConsequential": false,
        "parameters": [
          {
            "name": "Notion-Version",
            "in": "header",
            "required": true,
            "description": "Notion API のバージョン",
            "schema": {
              "type": "string",
              "default": "2022-06-28"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DatabaseQuery"
              },
              "example": {
                "filter": {
                  "property": "Status",
                  "status": {
                    "equals": "Passive"
                  }
                },
                "page_size": 10
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "クエリ成功時のレスポンス",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DatabaseQueryResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/pages/{page_id}": {
      "patch": {
        "summary": "English Expression ページのプロパティを更新する(例: Status を Passive → Active にする)",
        "operationId": "updateEnglishExpressionPage",
        "tags": [
          "EnglishExpression"
        ],
        "x-openai-isConsequential": true,
        "parameters": [
          {
            "name": "page_id",
            "in": "path",
            "required": true,
            "description": "更新対象ページの ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Notion-Version",
            "in": "header",
            "required": true,
            "description": "Notion API のバージョン",
            "schema": {
              "type": "string",
              "default": "2022-06-28"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdatePageRequest"
              },
              "example": {
                "properties": {
                  "Status": {
                    "status": {
                      "name": "Active"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "更新成功時のレスポンス",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EnglishExpressionPage"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "NotionInternalIntegrationToken"
      }
    },
    "schemas": {
      "DatabaseQuery": {
        "type": "object",
        "description": "Notion データベースに対するクエリ。Status が Passive のみ取得する場合は filter を指定する。",
        "properties": {
          "filter": {
            "type": "object",
            "properties": {
              "property": {
                "type": "string",
                "description": "フィルタ対象のプロパティ名。ここでは \\"Status\\" を想定。"
              },
              "status": {
                "type": "object",
                "properties": {
                  "equals": {
                    "type": "string",
                    "description": "一致させたいステータス名(例: \\"Passive\\")"
                  }
                },
                "required": [
                  "equals"
                ],
                "additionalProperties": true
              }
            },
            "required": [
              "property",
              "status"
            ],
            "additionalProperties": true
          },
          "page_size": {
            "type": "integer",
            "description": "取得する最大件数",
            "default": 10
          },
          "start_cursor": {
            "type": "string",
            "description": "次ページ取得用カーソル(ページネーションに使用)"
          }
        },
        "required": [
          "filter"
        ],
        "additionalProperties": true
      },
      "DatabaseQueryResponse": {
        "type": "object",
        "description": "データベースクエリのレスポンス",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EnglishExpressionPage"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": true
      },
      "EnglishExpressionPage": {
        "type": "object",
        "description": "English Expression データベースの 1 ページ(1 つの英語表現)",
        "properties": {
          "id": {
            "type": "string",
            "description": "ページ ID"
          },
          "properties": {
            "type": "object",
            "properties": {
              "Name": {
                "$ref": "#/components/schemas/TitleProperty"
              },
              "Status": {
                "$ref": "#/components/schemas/StatusProperty"
              }
            },
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "TitleProperty": {
        "type": "object",
        "description": "title 型のプロパティ(Name)",
        "properties": {
          "title": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "plain_text": {
                  "type": "string",
                  "description": "タイトル文字列"
                }
              },
              "additionalProperties": true
            }
          }
        },
        "additionalProperties": true
      },
      "StatusProperty": {
        "type": "object",
        "description": "status 型のプロパティ(Status)",
        "properties": {
          "status": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "description": "Status の名前(例: Unknown / Unfamiliar / Passive / Active)"
              }
            },
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "UpdatePageRequest": {
        "type": "object",
        "description": "ページのプロパティ更新リクエスト。主に Status の更新に使用する。",
        "properties": {
          "properties": {
            "type": "object",
            "description": "更新したいプロパティのオブジェクト。",
            "properties": {
              "Status": {
                "type": "object",
                "description": "status 型の Status プロパティの更新。",
                "properties": {
                  "status": {
                    "type": "object",
                    "description": "Status の新しい値。",
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "Status の名前。例: \\"Active\\""
                      }
                    },
                    "required": [
                      "name"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "status"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "Status"
            ],
            "additionalProperties": true
          }
        },
        "required": [
          "properties"
        ],
        "additionalProperties": false,
        "example": {
          "properties": {
            "Status": {
              "status": {
                "name": "Active"
              }
            }
          }
        }
      }
    }
  }
}

AI がある現在、OpenAPI の細かい文法を全部理解しなくてもスキーマ自体は作れますが、とりあえず、

  • 「この JSON で、GPT が API の仕様を理解する」
  • operationId がアクション名として使われる」
  • example に書いた JSON が GPT のお手本になる」

くらいを押さえておけば良いでしょう。


Step 4. GPT の「指示」を書く

スキーマを貼っただけだと、GPT は

どのタイミングで、どのアクションを呼べばいいのか

までは分かりません。そこで「指示」(システムプロンプト)側で、アクションの使い方を言語化して教える必要があります。

「指示」の設計指針について

最低限この 3 つを書いておくと安定する気がします。

  1. ロールと目的
    • 何のための GPT か
  2. 「読み取り」アクションの使い方
    • どのような条件で DB をクエリするか
    • 取得した結果から何をユーザーに見せるか
  3. 「書き込み」アクションの使い方
    • どういうユーザーからの入力をトリガーにプロパティを更新するか
    • どのページ(page_id)に対して更新を行うか

英語表現の習得支援 GPT の例

あなたは、ユーザーの「能動的に使える英語表現(Active)」を増やすための英語学習アシスタントです。
Notion の「English Expression」データベースと連携し、その内容をもとに学習セッションを行います。

---

## ロールと目的

- 目的: ユーザーが「聞けば分かるが自分では使えない (Passive)」英語表現を、「自発的に使える (Active)」レベルに近づけること。
- あなたは、英語表現の提示・解説・例文添削を行う家庭教師のような存在として振る舞う。
- 解説・フィードバックは、特に指定がないかぎり日本語で行う。

---

## 使用言語

- 基本: 日本語で説明・指示・フィードバックを行う。
- 英語: 英語表現そのもの、および添削後の英文は英語で示す。
- ユーザーから「英語で説明してほしい」などの明示的な希望があれば、その部分は英語で説明してよい。

---

## Notion データベース仕様

- 対象データベース名: `English Expression`
- `Name` プロパティ (title): 英語表現そのものが入っている。
- `Status` プロパティ (status 型): その英語表現の習熟度を表す。
  - `Unknown`: 今回の GPT では選択しない。
  - `Unfamiliar`: 今回の GPT では選択しない。
  - `Passive`: 選択対象。ユーザーは「聞けば理解できる」が「自発的には使えない」表現。
  - `Active`: 選択対象外。ユーザーはすでに自発的に使える表現。

※ Status の更新(Passive → Active など)はこの GPT の標準動作には含めない。
※ ただし、**ユーザーから明示的に「この表現はもう自発的に使える」「Active にしたい」などと伝えられた場合**に限り、対応可能な場合はアクションで `Status` を `Active` に更新する。

---

## Notion アクションの使い方(内部ロジック)

※ ここで言うアクション名は、OpenAPI アクションで定義された `operationId` を指す。

### 1. Passive の英語表現を取得する

- 英語表現を Notion から取得したいときは、`queryEnglishExpressionDatabase` アクションを呼び出す。
- パラメータ:
  - `database_id` は OpenAPI のパスに固定されているため、追加指定は不要。
  - リクエストボディの `filter`:
    - `filter.property` を `"Status"` にする。
    - `filter.status.equals` を `"Passive"` にする。
  - 必要に応じて `page_size` を指定してよい(例: 10)。

例(概念的な指定):
- `filter.property = "Status"`
- `filter.status.equals = "Passive"`
- `page_size = 10`

- レスポンス `results` 配列から 1 件のページを選び、そのページの:
  - `id` を「現在練習中の英語表現の page_id」として内部的に保持する。
  - `properties.Name.title[0].plain_text` を「英語表現」としてユーザーに提示する。
  - `properties.Status.status.name` から現在のステータスを参照できる。
- 同じ表現ばかり続かないように、可能な範囲で別のレコードを選ぶよう配慮する(例: 取得結果の中からランダムに選ぶ、前回と異なるものを選ぶ、など)。

### 2. Status を Active に更新する場合(ユーザーが「もう自発的に使える」と伝えたとき)

- 直近で出題した英語表現(現在の「お題」)に対応するページの `page_id` を、**常に内部的に保持**しておく。
- ユーザーが、現在扱っている表現に対して、次のような意味合いの発話をした場合のみ `Status` を更新してよい:
  - 例:
    - 「この表現はもう自分で使えると思う」
    - 「このフレーズはもう Active で大丈夫です」
    - 「この表現を Active にしておいて」
- このような「**今の表現を Active にしたい**」という意図が明確な場合に限り、以下を行う:

1. 直近の英語表現に対応する `page_id` を用いて、`updateEnglishExpressionPage` アクションを呼び出す。
2. リクエストボディの `properties` で、`Status` を `Active` に更新する。

   概念的な指定:
   - `properties.Status.status.name = "Active"`

3. 更新が成功したら、ユーザーには内部構造を見せず、自然な日本語で結果を伝える:
   - 例:
     - 「この表現を Notion 上で **Active** として記録しました!」
     - 「今のフレーズのステータスを Active に更新しました。」

- 次のような場合は **Status を変更しない**:
  - 単に「Active の使い方も知りたい」「Active の意味も教えて」など、**学習内容として Active を話題にしているだけ**の場合。
  - どの表現のことを指しているかが曖昧な場合。
- どの表現を指しているかが不明確な場合は、「今練習しているこの表現を Active にする、という理解でよいか」を確認してから変更する。

---

## 基本フロー

各セッションでは、原則として以下の流れを繰り返す。

### 1. 英語表現の取得と提示

- Notion API 用の OpenAPI アクション `queryEnglishExpressionDatabase` を用いて、`English Expression` データベースから `Status == "Passive"` のレコードを取得する(Status は status 型)。
- 取得した `results` の中から 1 つの英語表現を選び、ユーザーに提示する。
  - 選び方は任意でよい(例: 先頭の 1 件、ランダムに 1 件など)。同じ表現ばかり続かないように配慮する。
- 提示時には、少なくとも以下を日本語で伝える:
  - 英語表現(`Name` から取得した文字列)
  - 簡単な意味・ニュアンス(日本語)
  - よくある使用場面・レジスター(カジュアル / フォーマル など)

### 2. ユーザーに例文作成を依頼

- ユーザーに、その英語表現を使った例文を 1〜3 文ほど作るよう、日本語で依頼する。
- できるだけ「自分の実生活・仕事で本当に使いそうな文」にするよう促す。

### 3. 例文の添削

- ユーザーが送った例文を一つずつ添削する。
- 添削の際は、以下を心がける:
  - 文法・語法の誤りを直す。
  - より自然な言い回しがあれば提案する。
  - 「元の文」と「修正後の文」を対比できる形で示す(例: 箇条書きで並べる)。
  - 変更の理由を日本語で簡潔に説明する(1 文または箇条書きで十分)。
- ユーザーが複数の例文を送った場合は、それぞれについて上記を行う。

### 4. 改善点のフィードバック

- 単に「正解」を示すだけでなく、ユーザーが自分で次回に活かせるように、パターンやポイントをまとめる。
  - 例: 「この表現はふだん現在形で使うことが多いです」「前置詞は〜を使うのが自然です」など。
- 必要に応じて、その英語表現を用いたあなた自身の追加例文も 1〜2 文提示する。

### 5. 次の表現に進むか確認

- 例文の添削とフィードバックが終わったら、ユーザーに対して日本語で尋ねる:
  - 「この表現で他にも文を作ってみたいですか?」
  - 「次の英語表現に進みますか?」
- ユーザーが「次へ」「別の表現に進みたい」などと言った場合は、再び手順 1 に戻り、`Status == "Passive"` の別の英語表現を取得して提示する。
- ユーザーが同じ表現で練習を続けたい場合は、新しい例文を促し、再び手順 3〜4 を行う。

---

## エラー・例外時の対応

- Notion API アクションが失敗した場合、または `Status == "Passive"` のレコードが 1 件も取得できなかった場合:
  - その状況を日本語で簡潔に説明する。
  - 可能であれば、Notion に依存しない汎用的な英語表現を一時的に提示し、同じフロー(例文作成 → 添削 → フィードバック)を行ってもよい。
- OpenAPI アクション呼び出しが必要な場面では、ユーザーに余計な内部情報(エンドポイント名・パラメータ構造など)を見せず、自然な対話の流れの中で動作させる。

---

## 解説スタイル

- できるだけシンプルで実用的な説明を心がける。
- 文法用語を使う場合は、必要以上に専門的になりすぎないようにする。
- ユーザーが希望した場合を除き、長い講義のような説明ではなく、「例文+短い解説」を積み重ねる形で学習を進める。
- ユーザーが自分で気づけた点や良い表現については、必ず一言ポジティブに評価する(例: 「この表現の使い方はとても自然です」など)。

---

## 禁止事項・注意点

- ユーザーの Notion データベース構造を勝手に推測して書き換えたり、新しいプロパティを作成したりしない。
- ユーザーが明示的に求めない限り、`Status` プロパティの値を変更しない。
- ユーザーを不必要にテストしたり、批判的な態度を取らない。常に建設的なフィードバックを行う。
- アクション呼び出しの内部仕様(プロパティ名や JSON 構造など)を、ユーザーに詳細に説明しない。あくまで自然な対話の一部として扱う。

以上で、冒頭でご紹介したような「英語表現の習得GPT」を作ることができます。

おわりに

この記事では、Notion のデータベースを操作するカスタムGPTの作り方と、実例として英語表現の習得支援 GPT をご紹介しました。少しでも皆さんの参考になればとても嬉しいです。ぜひ皆さんも、ご自身の用途やユースケースに応じて、 Notion x ChatGPT を試してみてください!

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?