2026/06/23

つないでみよう:#31) Responses API ー 会話

今回から  Responses API を実際に使ってみましょう。まずは、会話から始めます。#2 で紹介した Chat Completion API と比較しながら進めたいと思います。


API のコールの仕方

まず、API の URL であるエンドポイントは以下の通りです。

Responses API Chat Completion API
https://api.openai.com/v1/responses https://api.openai.com/v1/chat/completions


HTTP ヘッダの指定方法には変化はないようです。どちらの API も以下の項目を指定します。

HTTP ヘッダ 設定値 補足
Content-Type application/json 送信するリクエストは JSON
Authorization Bearer (API キー) 別途取得した API キーを送信し認証


リクエストの記述は大きく変わっています。Chat Completion API では messages ノードに記述しましたが、Responses API では input ノードとなっています。

Chat Completion API では、構造がシンプルになっている点がありがたいですね。

Responses API Chat Completion API
{
   "model": "gpt-5-nano",
   "input": "大阪府の県庁所在地は?"
}
{
   "model": "gpt-5-nano",
   "messages": [
      {
         "role": "user",
         "content": "大阪府の県庁所在地は?"
      }
   ]
}

この事例では『大阪府の県庁所在地は?』とテキストで問い合わせるだけなのでシンプルな構造となっています。この input ノードの書き方で、テキストの問い合わせに画像を添えたり、柔軟に指定できます。結果的には、仕組みとしては messages ノードと大差ないと言えますね。


API コール

今回は、API の動作検証を兼ねて Postman でテスト実行してみます。

まず、”POST” を選択し、エンドポイントを入力します。ヘッダーには Authorization を追加し、API キーを指定します(Content-Type はデフォルトでセット済み)。最後に、ボディに送信する JSON を入力して完了です。

準備ができたら[送信]ボタンをクリックします。正しく送信できると、画面下部にレスポンスの JSON が表示されます。

Chat Completion API のレスポンスと比較しながら確認します。赤字の部分がリクエストに対する返答にあたる部分です。

Responses API Chat Completion API
{
   "id": "resp_xxxxx",
   "object": "response",
   "created_at": 1782125043,
   "status": "completed",
   "background": false,
   "billing": {
      "payer": "developer"
   },
   "completed_at": 1782125046,
   "error": null,
   "frequency_penalty": 0.0,
   "incomplete_details": null,
   "instructions": null,
   "max_output_tokens": null,
   "max_tool_calls": null,
   "model": "gpt-5-nano-2025-08-07",
   "moderation": null,
   "output": [
      {
         "id": "rs_xxxxx",
         "type": "reasoning",
         "content": [],
         "summary": []
      },
      {
         "id": "msg_xxxxx",
         "type": "message",
         "status": "completed",
         "content": [
            {
               "type": "output_text",
               "annotations": [],
               "logprobs": [],
               "text": "大阪府の県庁所在地は大阪市です。"
            }
         ],
         "role": "assistant"
      }
   ],
         ・・・(省略)・・・
}
{
   "id": "chatcmpl-xxxxx",
   "object": "chat.completion",
   "created": 1782126352,
   "model": "gpt-5-nano-2025-08-07",
   "choices": [
      {
         "index": 0,
         "message": {
            "role": "assistant",
            "content": "大阪府の県庁所在地は大阪市です。",
            "refusal": null,
            "annotations": []
         },
         "finish_reason": "stop"
      }
   ],
         ・・・(省略)・・・
}

Responses API は、Chat Completions API よりも汎用的かつ拡張性の高い API です。推論やツール実行などの情報を含めて返却するため、レスポンス JSON はより複雑な構造になっています。

output ノード内の要素で type が message となっているものが返答にとなります。output ノードは配列になっていて、リクエストに応じて要素数が可変となります。ですので、各要素の type を判定して返答を探す必要があります。

type にはさまざまな種類があり、抜粋すると次の通りです。

type 役割
message 最終回答
reasoning 内部推論の情報
function_call 関数(ツール)の呼び出し要求
function_call_output 関数実行結果
image_generation_call 画像生成の実行


会話の継続

Responses API では、会話の継続がサポートされています。

使い方は簡単で、previous_response_id に継続元のレスポンスを指定するだけです。具体的には、レスポンスの最初にある id(resp_ で始まる id)を指定するだけです。

Responses API Chat Completion API
{
   "model": "gpt-5-nano",
   "previous_response_id": "resp_xxxxx",
   "input": "そこの面積は?"
}
{
   "model": "gpt-5-nano",
   "messages": [
      {
         "role": "user",
         "content": "大阪府の県庁所在地は?"
      },
      {
         "role": "assistant",
         "content": "大阪府の県庁所在地は大阪市です。"
      }
,
      {
         "role": "user",
         "content": "そこの面積は?"
      }
   ]
}

Chat Completion API では、過去の会話をリクエストの JSON に含める必要がありました。会話が長くなると送信する JSON が膨大になります。

Responses API で会話を継続を使えば、課金額の節約にもなりますね。


前回 連載:つないでみよう


2026/06/19

エージェントと実行対象③ - UnprocessedDocuments の特徴と注意点

前回は、UnprocessedDocuments と UpdateProcessedDoc を紹介しました。

UnprocessedDocuments でエージェントがまだ処理していない文書の取得、UpdateProcessedDoc で処理済みフラグを設定し、次の実行では対象外にできるという使い方でした。処理すべき文書を効率的に取得できる機能なのですが、その陰に Notes/Domino アプリ開発的に意識すべき重要な点が 3 つあります。

  • ”未読/既読”に対する影響
  • 最終更新日の変化
  • エージェントの仕様変更時の影響


フィールドで管理

アプリ開発において、処理したかを記録するためには、フラグやステータスフィールドで管理するのが一般的です。例えば、次のようなプログラムです。

   Dim ns As New NotesSession
   Dim ndb As NotesDatabase
   Dim nv As NotesView
   Dim ndc As NotesDocumentCollection

   Set ndb = ns.CurrentDatabase
   Set nv = ndb.GetView("ByIsProcessed")   '処理済みフラグでソートされたビュー
   Set ndc = nv.GetAllDocumentsByKey("0", True)   '未処理を検索

   '未処理文書を順に処理
   Dim nd As NotesDocument
   Set nd = ndc.GetFirstDocument()

   While Not(nd Is Nothing)
      '(処理をここに記述)

      nd.IsProcessed = "1"   '処理済みフラグをセット
      Call nd.Save(True, True)   'フラグを保存

      Set nd = ndc.GetNextDocument(nd)   '次の文書を取得
   Wend

処理済みフラグ IsProcessed が "0" が未処理で、"1" が処理済みを表し、ビューで "0" の文書を検索して、ヒットした文書を順に処理をするという仕組みです。

ポイントは、Save メソッドをコールしている点です。文書が更新されるので、その文書が未読となってしまいます。


ちなみに、Save メソッドには引数が 3 つあって、最後の引数で未読をコントロールする機能があります。

ただ、ヘルプにも記載がある通り、この引数でコントロールできるのは処理を実行しているユーザだけです。その他ユーザに対してはすべて未読になるので、今回のような要件では効果がありません。


掲示板のように未読情報が重要なアプリでは、システム上の処理で未読になってしまっては、仕様上問題となります。例えば、削除日の1週間前に削除を通知する機能を作ったとしましょう。エージェントで送信済みフラグを書き込むと、未読になります。削除前なのに突然未読になり、逆に目立ってしまうということになってしまいます。

また、保存するということは、最終更新日が変化します。ビューで最終更新日順に表示しているビューではトップに表示されます。また、10 年更新がない文書は削除する機能があった場合、また 10 年間保持されることになります...。

文書が更新されるということは、複製の対象になり、すべての複製先のサーバに反映される点にも注意が必要ですね。


UnprocessedDocuments の注意点

UnprocessedDocuments、UpdateProcessedDoc を使った処理済みフラグの管理では、文書を更新しません。よって、先の「フィールドで管理」で挙げた未読/既読情報や最終更新日は変化しません。

だからといって、この方法が全能で優れているわけではなく、使用時には注意があります。

それは、エージェントを更新するとリセットされるということです。そのため、機能変更などによりエージェントを修正すると、次回実行時には修正前に処理した文書も含めて実行対象となります。

14.5.1 を使って検証したところ、エージェントのコードに変更がなければ処理済みフラグはリセットされないようです。例えば、エージェントを開き再保存したり、「すべての LotusScript をリコンパイル」しただけではクリアされませんでした。ただ、機能は一切変えず、コメントを追記しただけでもフラグはクリアされてしまいました。内部的にどのように管理しているのでしょうね ??

機能強化はもちろん、バグ修正、リファクタリングなどを行う時の制限となりうるのです。「作成または変更されたすべての文書」を使って効率的に文書を取得しているのに、エージェントの最終更新日と比較するのもなんだか変ですよね...


ただ、この問題には回避策があります。

それは、エージェント内のコードをすべてスクリプトライブラリに外だしすることです。例えば、エージェントで行う処理の中身を UnprocessedDocsMain 関数に記述し、lsUnprocessedDocs ライブラリの中に記述します。

そしてエージェントでは、次のようにライブラリの関数を呼ぶだけにします。

Option Declare
Use "lsUnprocessedDocs"

Sub Initialize
   Call UnprocessedDocsMain() 'エージェントの本体
End Sub

将来仕様が変わっても更新するのは、lsUnprocessedDocs ライブラリとなるので、エージェントは更新されません。これにより処理済みフラグを維持することができます。


また、この処理済みフラグはエージェントごとに保持される仕組みなのですが、各文書に対して、どのエージェントで処理済みなのか確認するすべがありません。フィールドを利用したフラグ管理の場合には、文書のプロパティや確認用のビューを作成するなど、効率的に調査ができます。


このように UnprocessedDocuments の利用では、開発終了後、運用フェーズになってからの注意点が多いことに注意しましょう。


まとめ

処理済みフラグの管理方法には 2 種類あることを紹介しました。改めてそれぞれの特徴を整理しましょう。

UnprocessedDocuments フィールドで管理
文書の更新 なし あり(最終更新日が変化)
未読/既読 変化なし 未読になる
調査/確認 対象文書を確認できない 文書のプロパティやビューなどで確認可能
処理時刻 不明
別途処理ログを出力するなどすれば確認可能
最終更新日やフィールドに記録することで確認できる

これまでの経験でいうと、障害や問い合わせ対応は想定外な場合が多いです。その際により詳細に情報収集できる、フィールドで管理する方法が安心だといえます。文書を更新しても構わない(未読/既読にあまりこだわらない)場合は、ステータスを文書に保存する方法をおススメします。

UnprocessedDocuments を使った処理済みフラグの管理は、文書を更新できないというような条件がある場合にとどめたほうが良いと思います。

どちらの方法も、メリット/デメリットがありますので、特徴を理解したうえで使い分けましょう。


2026/06/09

エージェントと実行対象② - UpdateProcessedDoc

前回は、エージェントの実行対象と UnprocessedDocuments プロパティについて整理しましたが、前回触れられなかった「作成または変更されたすべての文書」をいう設定を紹介します。


まずは単純に前回と同じプログラムで、最後に取得した文書の件数を表示するように修正します。

   Dim ns As New NotesSession
   Dim ndb As NotesDatabase
   Dim ndc As NotesDocumentCollection

   Set ndb = ns.CurrentDatabase
   Set ndc = ndb.UnprocessedDocuments   '未処理の文書の一覧

   MsgBox ndc.Count
   '文書件数を表示

エージェントのプロパティでは対象に「作成または変更されたすべての文書」を指定します。

実行するとデータベース内にすべての文書数が返されます。

「なるほど、初回の実行だから『すべての文書』が返ってくるのか...」

と思いながら、もう一度エージェントを実行します。すると、またしても『すべての文書』が対象となってしまいます。

「前回実行時に処理した文書は除外されるのでは?」

そう思うかもしれません。そこで登場するのが UpdateProcessedDoc メソッドです。


UpdateProcessedDoc メソッド

これは NotesSession クラスのメソッドで、デザイナーヘルプによると『エージェントで処理された文書に処理済みのマークを付けます。』とあります。構文は、

  Call notesSession.UpdateProcessedDoc( notesDocument )

となっており、処理済みにしたい文書を引数にセットします。


たとえば、コレクションから最初の文書を取得して処理する場合、次のようなコードとなります。

   Dim ns As New NotesSession
   Dim ndb As NotesDatabase
   Dim ndc As NotesDocumentCollection

   Set ndb = ns.CurrentDatabase
   Set ndc = ndb.UnprocessedDocuments  

   '1件目の処理
   Dim nd As NotesDocument
   Set nd = ndc.GetFirstDocument()   '1件目の文書を取得

   '(処理をここに記述)

   Call ns.UpdateProcessedDoc(nd)   '処理済みフラグをセット


対象の文書をすべて処理する場合には、GetNextDocument を使用して順に処理します。

   'すべての文書を処理
   Dim nd As NotesDocument
   Set nd = ndc.GetFirstDocument()

   While Not(nd Is Nothing)
      '(処理をここに記述)

      Call ns.UpdateProcessedDoc(nd)   '処理済みフラグをセット
      Set nd = ndc.GetNextDocument(nd)   '次の文書を取得
   Wend


まとめ

今回は、「作成または変更されたすべての文書」の設定と UpdateProcessedDoc というメソッドの使い方を紹介しました。この機能を利用すると、新たに作成や更新された文書だけを取得する処理が簡単に記述できるようになります。

対象文書を取得する UnprocessedDocuments プロパティが NotesDatabase クラス、処理済みフラグをセットするメソッドが NotesSession クラスとオブジェクトが違うので、少々わかりにくい点に注意しましょう。

なお、この機能はエージェントの処理済みフラグ、要は Notes/Domino の組み込みの機能を利用します。この処理済み情報は文書そのものに保存されるわけではありません。そのため、エージェントの修正や設計変更時には思わぬ挙動につながることがあります。次回はそのあたりの注意点について紹介したいと思います。


2026/06/03

エージェントと実行対象① - UnprocessedDocuments

NotesDatabase クラスには UnprocessedDocuments というプロパティがあります。これを使えば「現在実行中のエージェントにとって未処理の文書集合」 を取得することができます。

例えば、

   Dim ns As New NotesSession
   Dim ndb As NotesDatabase
   Dim ndc As NotesDocumentCollection

   Set ndb = ns.CurrentDatabase
   Set ndc = ndb.UnprocessedDocuments   '未処理の文書の一覧

と記述すると、ndc に未処理の文書の一覧が取得できます。


どの文書が返る?

では、未処理の文書とは具体的にどのように決定されるのでしょうか?実はエージェントのプロパティにある ”対象” により変化します。

「データベースのすべての文書」を選択するとその名称の通りの動作をします。よって、以下の記述はどちらも同じ結果を返すことになります。

   Set ndc = ndb.UnprocessedDocuments
   Set ndc = ndb.AllDocuments

それ以外の選択肢についての挙動を順にまとめます。


ビューのすべての文書

この設定も名称の通りの動作となります。現在のビューに存在するすべての文書が返されます。

ビューが基準となりますので、文書を開いている状態など、ビュー以外を開いている状態では、エージェントの実行自体がエラーとなります。


ビューのすべての未読文書

この設定もビューを基準とした機能ですので、ビューを開いた状態で実行する必要があります。

ビュー内の未読文書だけを抽出してくれるのですが、ビューの設計で未読文書を表示していなくても構わないようです。こんな設計はわかりにくいだけなのでおすすめはしませんが...

もちろん、データベースのプロパティで「未読マークを使用しない」を設定すると、この機能は動作しなくなります。この場合、UnprocessedDocuments は Count が 0 の空のコレクションを返します。


すべての選択文書

この設定の挙動は3パターンに分かれます。

まず、ビューを表示していて、カーソルで文書を選んだ状態です。この場合は、カーソルのある文書だけが返ります。

同じビューでも、チェックマークを付けて文書を選択している場合は、選択した文書が返ります。この時カーソルのある文書は返されません。例えば以下の例の場合では3文書が返され、「総務部」の文書は含まれません。

ビューではなく文書を開いている場合には、開いている文書1つだけを含むコレクションが返されます。「ビューのすべての文書」や「ビューのすべての未読文書」のようにエラーになりません。

選択肢が「すべての選択文書」となっており”ビューの”といついていない理由なのかもしれませんね。

ただ、ワークスペースでDBアイコンを選択した状態でエージェントを実行すると『選択したエージェントはビューウィンドウから実行してください。』とエラーが出ます。このエラーメッセージにはいただけないですね...。


続きは次回

今回は、エージェントの実行対象と UnprocessedDocuments プロパティの関係についてまとめました。まだ、「作成または変更されたすべての文書」が残っていますが、この設定については次回紹介したいと思います。


2026/05/25

ドメイン検索:索引の再作成の注意点

ドメイン検索について注意点をまとめた記事を以前に書かせていただきました。

その中で、索引の更新には削除文書の反映が含まれないため定期的に索引を再作成することをおススメしました。

今回はその具体的な方法と最新バージョン 14.5.1 で遭遇した新たな現象についてまとめます。


索引を削除するには?

まず、索引の削除のコマンドは load domidx -d です。このコマンドを実行すると索引のフォルダがまるごと消去されます。

次に索引を作成をしようとしたタイミングで、既存の索引が何もないので、対象の全文書を索引するので、再作成となるということですね。


また、索引の削除に関しては下記の技術文書が公開されています。

2020 年の記事なので現在は改善されているかもしれませんが、再作成前に再起動するほうが良いと考えられます。


サーバ環境や対象の DB 数、文書サイズにもよりますが、索引の再作成には通常長い時間が必要となります。索引作成中は正しい検索結果を得られませんので、業務に影響がないようにするためには、土日などの週末を利用するのがおススメです。


索引の再作成の設定

まず、索引の更新は毎日 7:30、12:00、21:00 の 3 回行う前提とします。

この環境において、土曜日を索引再作成の日として、7:30 の索引作成前に索引を削除します。これで土曜の朝は、索引がない状態で索引を更新しようとするので、すべての索引を再作成することになります。

索引削除の設定はプログラム文書で行います。

プログラム文書を新規で作成し、load domidx -d を実行します。索引作成開始時刻の 30 分前の 7:00、土曜日を指定します。

その後、7:30 の索引作成に間に合うように、サーバを OS ごと再起動させます。


14.5.1 では追加の設定が必要

先日ドメイン検索サーバを 14.5.1 にアップグレードした際、気が付いた問題があります。

14.0 までは load domidx -d の実行だけで索引が削除できていたのですが、14.5.1 では削除されなくなっていたんです(14.5 は未検証なので不明)。調査した結果、14.5.1 では稼働中の domidx タスクを停止しておかないと、load domidx -d が正しく動作してくれないようです。

これに対応するため、14.5.1 では load domidx -d の実行前(例えば 10 分前)にタスクを停止させます。具体的には以下のプログラム文書を追加することになります。


まとめ

今回は、ドメイン検索の索引再作成の方法についてまとめました。

14.5.1 では少し挙動が違うので注意してください。というか、仕様変更の理由がファイルの共有違反の回避のようなので、過去のバージョンでタスクを停止することなく、索引を削除できたことの方が問題かもしれませんね。

ただ、索引の更新に文書の削除が反映されないことがそもそもの問題だと思うで、早く改善してほしいと思います...。



2026/05/13

Notes の強制終了

今回は小ネタを短くお伝えします。

あってほしくはない症状なのですが、Notes クライアントを使っているとハングアップすることがあります。

例えば、以下の画面はロケーションを切り替えようとしただけなのですが、パスワード入力後ワークスペースが表示されることなく、止まってしまいました。はじめは(応答なし)と表示されていなかったので、待たされているのか落ちているのか判断できません。動画を撮ろうと準備している 5 分ぐらいの間に(応答なし)と表示されました。

このような現象が出た場合、皆さんはどのような対応をされていますか?


タスクマネージャから終了

ノーツを一般的な Windows アプリと考えるとタスクマネージャで強制終了させることになります。ただ、この方法は OS から見たアプリの強制終了であり、他の関連タスクやメモリ、テンポラリファイルの削除は期待できないでしょう。再度ノーツを起動した場合、安定したクリーンな状態とは言えないと思います。


デバッグツールの使用

私のような一昔前のノーツユーザが慣れ親しんだ方法です。

ノーツクライアントに同梱されているデバッグツール nsd.exe を利用することで強制終了できます。

手順は次の通りです。

  1. コマンドプロンプトを起動する
  2. カレントディレクトリをノーツのデータディレクトリに移動(例:c:\Notes\Data)
  3. プログラムディレクトリの nsd.exe に オプション -kill を付けて実行

しばらくメッセージが表示された後、最後にファイル名が表示されます。このファイルにはさまざまな情報が含まれており、テクニカルサポートに送付すると原因の調査が行えます。

この方法の利点は、タスクマネージャの強制終了とは違い、ノーツ環境をきれいに掃除してくれることにあります。


WIndows メニューから終了

認知度が意外と低いのがこの方法です。

ノーツをインストールしたマシンには[HCL Applications]というメニューが追加されますが、このメニュー内に「HCL Notes の診断データを収集して HCL Notes を終了する」というものがあります。これを利用すると上記デバッグツールと全く同じことを実行できます。

ちなみに「HCL Notes の診断データを収集して HCL Notes を稼働したままにする」は、その名称の通り、情報収集だけしてノーツの実行を継続します。


まとめ

今回はノーツの強制終了の方法を紹介しました。

ハングアップのような不測の事態以外にも、永久ループの LotusScript を実行した場合や時間のかかる処理を実行したが Ctrl + Break で止められない場合などでも利用できます。


ちなみに、このメニューの認知度が低い理由は Windows の仕様が絡んでいると推察します。例えば Windows 10 では次のように最後まで表示されません。これじゃどっちを選べばいいか判別できず使えないですね。

Windows の仕様もいまいちですが、はみ出るような長い名称を付けたノーツにも非があります。


2026/05/11

つないでみよう:#30) Responses API とは?

この連載 つないでみよう は、Web 系アプリ開発ど素人の私が WebAPI 連携にチャレンジする過程をまとめているものです。幅広くさまざまな API に挑戦するつもりだったのですが、最近は OpenAI 社の API ばかりになってきました。WebAPI は種類が豊富で流れが速く OpenAI 分だけですら全然追いつけていないので、その他に手を出す隙はなかなかありません。タイトルを変える必要があるかもしれませんね...

ということで、今回も OpenAI の API がネタとなります。


Responses API とは?

これまでの Chat Completions API の進化版であり、他の API と統合し発展させた新しい標準 API で「今後の新規開発では Responses API を推奨する」と明言しています。

主な特徴としては

  • マルチモーダル対応で、テキスト、画像、ファイルを自然に入力
  • 会話状態を保持し、前回の応答を継続
  • ツール呼び出しを標準化
  • 推論モデルとの相性向上

などがあります。

主な対応モデルとしては、

GPT 系 推論モデル(Reasoning Models)
gpt-5
gpt-5-mini
gpt-5-nano
gpt-5-chat
gpt-4.1
gpt-4.1-mini
gpt-4o
gpt-4o-mini
o3
o3-mini
o4-mini

となっていて、新しいモデルほど Responses API の利用が推奨されているようです。


次回からの予定

下記にこの連載で紹介した OpenAI 社の API の機能を整理します。

最初に紹介した通り Responses API は各 API を統合・発展させた仕様なので、これらの操作はすべて Responses API に置き換えることができるそうです。今後、順に移行作業をしながら、使い方や注意点、新機能などをまとめていきたいと思います。

Responses API は新しい標準とのことなので、どんどん使って早く慣れたいですね。


◇ 会話

#1#2 では、API キーの取得方法など API の基本的な使いから始まり、/chat/completions API を使って AI と会話する方法を紹介しました。送信する JSON に過去の会話を含めることで、会話を継続することができました。

Responses API では、過去の会話を再送することなく、会話を続けることができるようです。


◇ 画像認識

#14 ~ #20  では、/chat/completions の入力に画像を指定することで、画像を説明させたり、OCR のように文字を認識させる方法を紹介しました。

Responses API を使うと、画像認識だけでなく、加工などの編集もできるそうです。


◇ Structured Outputs

AI からの返答を JSON 形式に固定し、指定した構造に合わせて返答させる機能で「構造化出力」と呼ばれています。AI の返答の構造が明確だと再利用が容易なので、プログラムから AI を使用する際には必須の機能と言えます。

#21#24  では、その指定方法と活用事例を紹介しました。Responses API ももちろん対応しています。


◇ 画像生成

画像生成は /chat/completions ではなく /images/generations という別の API を使用していました。#25#28 では DALL-E3 を利用した画像生成、#29 では後継バージョン GPT Image API の使い方をまとめしました。

画像生成も Responses API で対応しており、専用のエンドポイントを使うことなく実現できるようです。


前回 連載:つないでみよう


2026/05/04

つないでみよう:#29)画像生成 ChatGPT Image

先日と言っても 4 月の初め、OpenAI 社から案内メールがありました。前回までこの連載で紹介していた画像生成 AI の DALL-E3 のサービスが、2026 年 5 月 12 日に終了とあります。今後は最新のフラグシップモデルである GPT Image の利用を検討してほしいとのことです。

しばらくこのあたりのウォッチをサボっていたので、改めて調べたことをまとめておきます。


GPT Image の特徴

旧世代の AI である DALL-E は「画像生成専用モデル」だったのですが、GPT Image はマルチモーダル大規模言語モデル(LLM)との連携を前提にしたモデルで、

  • 会話しながら画像を修正
  • 画像内容を理解
  • 画像編集
  • 高度な指示追従

が得意です。単に画像を生成するだけではなく、指示を追加して調整したり、一部分だけ変更したりできるようです。面白そうなのでいずれ挑戦してみたいですね。


GPT Image API の使い方

前回からの流れで、まずは単純な画像生成について確認します。API のヘルプは こちら

エンドポイントは ”/images/generations” となっていて DALL-E3 と変わりませんね。

そしてサンプルの JSON を確認します。model が "gpt-image-1.5" になっていますが、prompt で作成する画像を指示するなど、構造も同じに見えます。

ちなみにヘルプによると、現時点で指定できるモデルは、

  • gpt-image-1
  • gpt-image-1-mini
  • gpt-image-1.5

の 3 種類の記載があります。そして、この連休中に 2.0 の案内が届きました。すごい勢いで進化しているようですね。


DALL-E3 とは少し違う

どうやら使い方は変わっていないようなので、前回までに作成した DALL-E3 用のサンプルをモデル名だけ変更して実行してみました。すると、次の部分でエラーが発生しました。

レスポンスの JSON が空っぽだったようです。デバッガで確認しても NotesJSONNavigator クラスは中身がほぼ表示されないので、調査しようがありません。

そこで Postman で確認してみるとすぐに原因がわかりました。API ツールって便利ですね。

response_format が unknown_parameter だとエラーが出ています。改めてヘルプを確認すると記載がありました。GPT Image モデルではサポートされていないパラメータだそうです。デフォルトが base64 なので、削除するだけでよさそうです。

同様に quality の設定にも変更がありました。DALL-E3 では "hd" と "standard" が指定できましたが、GPT Image では "high"、"medium"、"low" に変更されており、間違えて指定すると同様のエラーとなります。

モデルによって送信する JSON が変わるんですね。インプリ前に仕様の確認とテストは必須のようです。エンドポイントが同じなので油断してしまいました...。


コードの修正

仕様が明確になったので #26 で作成した関数を GPT Image モデル用に修正します。model と quality にセットする値を修正し、response_format を削除します。

Function xGetJSON(vnd As NotesDocument) As String
   Dim jnav As NotesJSONNavigator
   Dim s As String

   'RequestBody(JSON) の準備
   Set jnav = xns.CreateJSONNavigator("")

   '1.model
   Call jnav.AppendElement("gpt-image-1.5","model")
   '1.size
   Call jnav.AppendElement("1024x1024","size")
   '1.quality
   Call jnav.AppendElement("low","quality")
   '1.response_format
   'Call jnav.AppendElement("b64_json","response_format")
   '1.prompt
   Call jnav.AppendElement(vnd.Prompt(0),"prompt")

   xGetJSON = jnav.Stringify
End Function

実行すると無事画像が生成されました。右側は  DALL-E3 で作成した画像なのですが、ずいぶんきれいになっていますね。


まとめ

今回は ChatGPT Image について紹介しました。

旧バージョンの画像生成 API とほぼ同じ仕様となっているのでプログラムはそのまま利用できます。ただ、パラメータの変更など若干の仕様変更がありますので注意しましょう。


前回 連載:つないでみよう 次回


2026/04/30

リスト配列の要素数を取得

以前 リスト配列 について紹介しました。

通常の配列は要素を番号で指定しますが、リスト配列は名前などのキーで管理します。例えば、notes.ini のエントリを管理する場合を考えてみましょう。

[Notes]
KitType=1
Directory=c:\Lotus\Notes\Data
UserInterface=ja
InstallType=2
InstallMode=1
NotesProgram=c:\Lotus\Notes\
FaultRecovery_Build=Release 14.5.1
Timezone=-9
         ・・・

エントリ数は環境によって違いますし、記述された順番もバラバラです。通常の配列ではすべての要素を順にチェックしないと目的にたどり着けませんが、リスト配列だと一発です。

   ' asNotesIni は環境変数名をキーにその値を保持している前提
    MsgBox asNotesIni("Directory")    'データディレクトを表示

細かな使い方は冒頭のリンクの記事を参照いただきたいのですが、うまく使うとプログラムがシンプルにわかりやすく記述できます。

使用頻度が増えてきて初めて気が付いたことがあります。リスト配列内の要素数を取得できないのです。通常の配列で Ubound に相当する機能ですね。

ざっとヘルプを確認する限り見当たらないので自作しました。

Function ListCount(vasList As Variant) As Integer
   Dim i As Integer

   i = 0
   If IsList(vasList) Then
      ' リスト配列の場合件数をカウント
      ForAll v In vasList
         i = i + 1
      End ForAll
   End If

   ListCount = i
End Function

引数で受け取った値がリスト配列か判定し、リストの場合 ForAll でループを回して、件数を数えるだけのシンプルな関数です。

いざ必要となった時に作るのは面倒なので掲載しておきます。


2026/04/27

Domino 体力測定:#5)ビュー操作 ⑤ - ハードウェアパフォーマンスとの関係

しばらく間が空いてしまいましたが、これまで4回にわたり GetAllDocumentsByKey と GetAllEntriesByKey の速度比較を行ってきました。検証のテーマは、

テーマ
#2 一般的な環境(SSD)での基本挙動確認
#3
文書サイズと検索/値取得の特性
#4 ビューの列数と検索/値取得の特性

でした。結果はいずれも GetAllDocumentsByKey が高速であるという結論でした。

連載のきっかけとなった 訂正記事 に挙げたパフォーマンスに影響を与える要因を順番に検証し、GetAllEntriesByKey が早くなるパターンを探してきました。今回は、複数の PC を使用して、CPUやメモリ、ストレージの I/O が与える影響について観察します。

本来であれば別々に観察すべきですが、個人的に用意できるリソースには限りがありますので、おおざっぱなテストになります。予めご了承ください。


検証環境

今回の検証で使用する PC は以下の 3 台で、構成は次の通りです。

  PC1 PC2 PC3
CPU Ryzen 7 7900X Ryzen 5 3600 Celeron G540
メモリ 128 GB 32 GB 2 GB
ストレージ SSD SSD HDD
OS Windows 11 Windows 10 Windows 10 

検証に当たり、各 PC のベンチマークを行いました。主な項目と測定結果の画像は次の通りです。

  PC1 PC2 PC3
All 26,123 11,522 756
CPU 17,564 7,065 3842
ランダムリード 713 590 6
ベンチマーク結果



テスト項目と手順

テスト項目はこれまでと同じです。GetAllEntriesByKey では、ビューから値を取得するだけなら 1 ~ 3、文書フィールドにアクセスするなら 1、2、4、5 の合計時間となり、GetAllDocumentsByKey と比較できます。


検証項目 D)GetAllDocumentsByKey E)GetAllEntriesByKey
1 コレクション作成 GetAllDocumentsByKey で検索し、コレクションを取得GetAllEntriesByKey で検索し、コレクションを取得
2 エントリ取得 GetFirstDocument、GetNextDocument で文書を取得GetFirstEntry、GetNextEntry でエントリを取得
3 値の取得 文書から値を取得ColumnValues で値を取得
4 文書の取得 Document プロパティで文書を取得
5 文書から値の取得 文書から値を取得

測定する DB はこれまでのものを流用します。100 Byte のダミーフィールドを 200 個追加した文書(#3 で紹介)を列数の違うビュー(000 と 200)で検索します。000 はダミーフィールドなしの軽いビュー。200 はすべてのダミーフィールドを列に追加した重いビューをイメージしています(#4 で紹介)。

測定手順も同じです。ビューに対して 20 回の検索を行い、取得したコレクションからエントリと値を順に取得する操作を 50 回行います。測定回数は 10 回としますが、PC3 はあまりに遅いため 3 回としました。


結果

測定した結果をグラフに表すと次の通りです。

(図 #5-1)

縦軸をそろえたグラフは次の通りです。

(図 #5-2)


考察

◇ 基本特性の変化

図 #5-1 を確認します。

上段(軽いビュー)の結果を比較すると PC1、2 に比べ PC3 でその差が幾分詰まっています。貧弱なリソース環境においては GetAllEntriesByKey の方が効率的に動くように設計されているのかもしれません。

下段の重いビューでは、処理の比率に変化は見られません。ビューの列が増えることで、効率の良さを打ち消しているのではないかと想定できます。

どちらの検証結果についても GetAllDocumentsByKey が優位であることには変わりがなく、GetAllEntriesByKey が逆転することはありませんし、その気配すら感じません。


◇ リソースの確保は重要

図 #5-2 では縦軸をそろえているので、絶対的なレスポンスがわかります。

最も時間を要している処理1を使って比較します。PC1 の処理時間を基準にすると次のような差(単位:倍)となりました。

環境 View = 000 View = 200
PC2 2.48 2.30
PC3
11.58 64.77

PC3 ではストレージが HDD であることに加え、CPU やメモリも貧弱です。これが原因で、テスト時にはスラッシングが発生していたと推察できます。これが原因となり PC2 に比べ異常ともいえるパフォーマンスの劣化が発生したと考えられます。リソース確保の重要性がよくわかる結果ですね。


まとめと今後の進め方

PC3 の View = 200 のテストはスラッシングが発生し、スペック以上に能力が低下していたと推察しました。ということは、さらに昔の世代のテストができたのかもしれません。しかし、この状況においても GetAllDocumentsByKey が優位であることには変わりがありませんでした。本当に昔の常識とされた GetAllEntriesByKey 優位というシナリオは存在するのでしょうか?

残された可能性は ODS ぐらいですかね。最近の ODS では GetAllDocumentsByKey に対する最適化が進み、逆転するシナリオが存在しないという可能性です。ODS については腰を据えて、検証してみたいと思っていたので、別途検証したいと思います。よい結果が出れば、塩漬けされた Domino サーバをバージョンアップする理由になるかもしれませんね。


これまでの『Domino 体力測定』は、きっかけとなった 過去の神話 を検証する方向で進めてきました。明確な答えが出ないまま方向転換するのは残念なのですが、過去を振り返るより将来にわたって ”使える” 検証に時間を割きたいと思います。

今後 ODS の評価以外にも、NotesDocumentCollection クラスの GetNextDocument と GetNthDocument の比較など、複数の手法がある開発を題材に比較してみたいと思います。


前回 Domino 体力測定