前回までで、一番単純な会話ができるようになったので、次は画像生成をさせてみましょう。
Responses API は、画像生成にも対応しているので、/images/generations など別の API をコールする必要はありません。例えば、次のような JSON を送信することで、Responses API に画像生成させることができます。
|
{ "model": "gpt-5-nano", "input": "ビーチリゾートに行きたくなるような、白い砂浜とヤシの木の画像", "tools": [ { "type": "image_generation", "model": "gpt-image-2", "size": "1024x1024", "output_format": "png", "quality": "low" } ] } |
重要なのは tools 配列を指定し、その中で type に image_generation を設定している点です。設定値からもわかるように、Responses API では画像生成も「ツール」の一つとして扱われます。次の model が画像生成を担当するモデルです。現在の推奨モデルは gpt-image-2 で、size、output_format、quality は画像生成時のオプションです。指定できる項目や設定値はモデルによって異なるため、必要に応じて公式ドキュメントを確認してください。
この例では、プロンプトの解釈を gpt-5-nano が担当し、実際の画像生成を gpt-image-2 が担当します。このように Responses API では、それぞれの役割に応じてモデルを組み合わせて利用できるのが特徴です。
画像生成機能の作成方針
前回までに紹介した会話のサンプルでは、新規と継続で別々のフォーム、エージェントで作成しました。今回の画像生成では、1つにまとめたいと思います。
新規か継続の判断は、文書に実行結果が入っているかで判断するものとします。具体的には、Result_ID に値が入っていれば、継続として処理します。
フォームの作成
画像生成用のフォームをサンプルアプリに追加します。
前回 #34 で作成した fGPT_Res02 フォーム(継続元ID フィールドがある)をコピペして、画像生成用フォーム fGPT_Res03 を作成します。
フィールドは基本的にはそのままなのですが、今回の返答が画像となります。そこで、実行結果のフィールドをリッチテキスト Result_Image に変更します。
エージェントの作成
フォームと同様にエージェントも前回作成のエージェントをコピペして作業を開始します。
◇ Initialize
基本的な処理の流れに変更はありません。ただ、新規と継続をまとめたことで中盤の太字の部分が大きく違います。また、この変更に関連して変数名や処理順を一部変更していますので、Initialize 全体を掲載しています。
| Sub Initialize Dim nuiw As New NotesUIWorkspace Dim nuid As NotesUIDocument Dim ndb As NotesDatabase Dim ndCur As NotesDocument 'UI で開いている文書 Dim ndReq As NotesDocument '問い合わせに利用する文書 Dim jnavRequest As NotesJSONNavigator Dim jnavResponse As NotesJSONNavigator Dim s As String '初期化 Set xns = New NotesSession Set ndb = xns.CurrentDatabase '現在の文書(= 継続元の文書) Set nuid = nuiw.Currentdocument Set ndCur = nuid.Document ' UI の文書は読み込みモードの前提 ' 新規 or 継続の判定 If ndCur.Result_ID(0) = "" Then '新規リクエスト(現在の文書で問い合わせ) Set ndReq = ndCur Else '継続したリクエスト(現在の文書を参照させる設定で新規作成) s = nuiw.Prompt(PROMPT_OKCANCELEDIT, "継続", "次のプロンプトを入力してください。") If s = "" Then Exit Sub '新規文書作成 Set ndReq = ndb.CreateDocument() ndReq.Form = "fGPT_Res03" ndReq.Model = ndCur.Model ndReq.PrevID = ndCur.Result_ID '現在の文書を参照 ndReq.Prompt = s End If 'Request Body(JSON) の作成 Set jnavRequest = xGetRequest_JSON(ndReq) Call xSetJSON(ndReq, "Request", jnavRequest) 'Request Body の保存 '問い合わせ実行 Set jnavResponse = xAskResponses(jnavRequest) Call xSetJSON(ndReq, "Response", jnavResponse) 'Response の保存 '返答を取得 If xGetResponce(ndReq, jnavResponse) Then Call ndReq.Save(True, False) '文書を開きなおす Call nuid.Close() Call nuiw.EditDocument(False, ndReq) End If End Sub |
◇ xGetRequest_JSON
Responses API に送信する関数で、画像生成を指定する tools 配列を追加しています。今回はサンプルですので、model や size などには固定値を追加しています。
| Sub
Function xGetRequest_JSON(vnd As NotesDocument) As NotesJSONNavigator Dim jnav As NotesJSONNavigator Dim jaTol As NotesJSONArray Dim joTol As NotesJSONObject 'RequestBody(JSON) の準備 Set jnav = xns.CreateJSONNavigator("") '1.model Call jnav.AppendElement(vnd.Model(0),"model") '1.previous_response_id 継続の場合のみ設定 If vnd.PrevID(0) <> "" Then Call jnav.AppendElement(vnd.PrevID(0),"previous_response_id") End If '1.input Call jnav.AppendElement(vnd.Prompt(0),"input") '1.tools 配列 Set jaTol = jnav.AppendArray("tools") '2.image_generation Set joTol = jaTol.AppendObject() Call joTol.AppendElement("image_generation", "type") Call joTol.AppendElement("gpt-image-2", "model") Call joTol.AppendElement("1024x1024", "size") Call joTol.AppendElement("png", "output_format") Call joTol.AppendElement("low", "quality") Set xGetRequest_JSON = jnav End Function |
◇ xAskResponses
画像生成は会話より応答時間がかかります。そこで、タイムアウトを 450 秒に設定します(デフォルトは 30 秒)。ちなみに私が試した限り上記生成条件で、300 秒以内で返答が返ってきました。
| Sub
Function xAskResponses(vjnavRequest As NotesJSONNavigator) As NotesJSONNavigator ・・・(省略)・・・ 'HTTP ヘッダーの設定 Call http.SetHeaderField("Content-Type", "application/json") Call http.SetHeaderField("Authorization", "Bearer " & xcsBearer) 'API 実行 http.TimeOutSec = 450 '画像生成は時間がかかるので http.PreferJSONNavigator = True ・・・(省略)・・・ End Function |
◇ xGetResponce
API のレスポンスから返答を取得する処理も変化します。生成された画像は output 配列内の type が image_generation_call にあります。これを取り出し、result の値が画像データとなります。
|
Function xGetResponce(vnd As NotesDocument, vjnav As NotesJSONNavigator) As Boolean Dim je As NotesJSONElement Dim sBase64 As String Set je = vjnav.Getfirstelement() 'id 取得 vnd.Result_ID = GetPropertyValue(vjnav, "id") 'output 取得してセット Set je = GetProperty(vjnav, "output") '"type": "image_generation_call" の要素を取得 Set je = FindInArray(je, "type", "image_generation_call") '生成画像(result)を取得してセット sBase64 = GetPropertyValue(je, "result") Call xSetImageFile(vnd, "Result_Image", sBase64) xGetResponce = True End Function |
画像データは Base64 でエンコードされているので、以下の3つの関数を通して、デコードし、画像ファイルに変換します。
◇ xSetImageFile
Base64 の画像データをデコード、画像ファイル化し、文書に添付する関数です。
|
Private Function xSetImageFile(vnd As NotesDocument, ByVal vsFld As String, vsBase64 As String) Dim nsImg As NotesStream Dim nrti As NotesRichTextItem Dim sFP As String sFP = xns.GetEnvironmentString("Directory", True) sFP = sFP & "\" & Format(Now, "yyyymmddhhnnss") & ".png" On Error Resume Next Kill sFP '存在してたら削除 On Error GoTo Err_General Set nsImg = xns.CreateStream() Call Base64ToBinary(vsBase64, nsImg) 'デコード Call StreamToImageFile(nsImg, sFP) '画像ファイルとして保存 '画像ファイルを文書に添付 Call vnd.RemoveItem(vsFld) Set nrti = vnd.CreateRichTextItem(vsFld) Call nrti.EmbedObject(EMBED_ATTACHMENT, "", sFP) Exit_Proc: Exit Function Err_General: MsgBox Error$ Resume Exit_Proc End Function |
◇ Base64ToBinary / StreamToImageFile
これらの関数は OpenNTF の LotusScript Gold Collection プロジェクトから拝借した関数です。詳しくは #28 で紹介しているので必要に応じて参照ください。関数自体は下記に再掲載します。
|
%REM Sub Base64ToBinary Description: Given a string of base64-encoded data, write into a binary stream we are passed. This is done rather than creating the stream here and returning it, so that you can stream directly into a file if you choose. %END REM Sub Base64ToBinary(strBase64$, streamOut As NotesStream) ' Given a string of base64 encoded data, this routine decodes and writes the original binary data into a NotesStream Dim doc As NotesDocument Dim mime As NotesMIMEEntity Dim streamIn As NotesStream Dim db As NotesDatabase Dim session As New NotesSession Set db = session.CurrentDatabase Set doc = db.CreateDocument Set mime = doc.CreateMIMEEntity("Body") ' the mime classes already know how to do this conversion, Set streamIn = session.CreateStream Call streamIn.WriteText(strBase64) streamIn.Position = 0 Call mime.SetContentFromText(streamIn, "binary", ENC_BASE64) Call mime.GetContentAsBytes(streamOut, True) ' decode as you stream out the data. End Sub |
| Sub StreamToImageFile(vnstImage As NotesStream, ByVal vsFP As String) Dim nstOut As NotesStream On Error Resume Next Kill vsFP '存在してたら削除 Set nstOut = xns.CreateStream() Call nstOut.Open(vsFP) vnstImage.Position = 0 Do Until vnstImage.Position >= vnstImage.Bytes Call nstOut.Write(vnstImage.Read(16000)) Loop End Sub |
想定外の改造
画像生成のサンプルとしては、上記までで完了のつもりでした。ただ、実行するとエラーが発生しました。
原因は、API のレスポンスが大きく、リッチテキストに書き込む処理で リッチテキストの制限 に引っ掛かっていたためだと思われます(どちらも 64 KB まで)。
- リッチテキストフィールドの単一段落の最大サイズ
- 文書当たりのテキスト (要約) データの最大量
そこで、JSON のサイズが大きい場合、添付ファイルとして保存するように変更します。
◇ xSetJSON
JSON を文書に保存する関数で、サイズが大きい場合(10000 バイト)、後述する JSONToFile を通して、ファイルに保存します。
|
Private Function xSetJSON(vnd As NotesDocument, ByVal vsFld As String, vjnav As NotesJSONNavigator) ・・・(省略)・・・ '日時の記録 Call vnd.ReplaceItemValue(vsFld & "_DT", Now) 'JSON の記録 'いったん削除してからセット Dim sFP As String Call vnd.RemoveItem(vsFld & "_JSON") Set nrti = vnd.CreateRichTextItem(vsFld & "_JSON") If Len(sText) > 10000 Then '大きいのでファイルで保存 sFP = JSONToFile(vjnav, "") 'ファイル名は指定なし Call nrti.EmbedObject(EMBED_ATTACHMENT, "", sFP) Kill sFP '添付したのでファイルは削除 Else 'そのままテキストで保存 Call nrti.AppendText(sText) End If End Function |
◇ JSONToFile
JSON をファイルに保存する関数です。この関数はほかでも使いそうなので、lsJSONNavi ライブラリに作成します。
|
Public Function JSONToFile(vjnav As NotesJSONNavigator, ByVal vsFileName As String) As String Dim ns As New NotesSession Dim nstOut As NotesStream Dim sFP As String sFP = ns.Getenvironmentstring("Directory", True) If vsFileName = "" Then sFP = sFP & "\" & Format(Now, "yyyymmddhhnnss") & ".json" Else sFP = sFP & "\" & vsFileName End If On Error Resume Next Kill sFP '存在してたら削除 On Error GoTo Err_General Set nstOut = ns.CreateStream() Call nstOut.Open(sFP) Call nstOut.WriteText(vjnav.Stringify()) Call nstOut.Close() JSONToFile = sFP Exit_Proc: Exit Function Err_General: MsgBox Error$ Resume Exit_Proc End Function |
動作検証
出来上がったら動作検証します。
最初のリクエストを書いた文書を開き、エージェントを実行します。しばらくすると文書が開きなおし、画像が添付された状態となります。API からの返答は大きくなるので、JSON は返答だけが添付ファイルになっているはずです。この状態から再度エージェントを実行すると作成された画像に対するリクエストが送信できます。
今日も朝から暑い中 Notes 三昧なので、『ビーチリゾートに行きたくなるような、白い砂浜とヤシの木の画像』ネタだけは休日仕様にしてみました。
結果は次のような感じでした。
青い海に真っ白な砂浜、デッキチェアにパラソル、イメージ通りの画像ですね。次のリクエストでは『この画像を夕景に変更してください。』と送信しました。構図は少々変わってしまってますが、見事な夕焼けですね。
あぁ、リゾートしたい...
| 前回 | 連載:つないでみよう |






















