前回は、GPT4o に画像認識させ、その結果を JSON で受け取ることができました。今回は、その JSON を分析して必要な値を取得できるようにします。
JSON を解析する準備
今回の作業ではレスポンスの JSON を操作することになります。それには NotesJSONNavigator のオブジェクトが必要となります。そこで、これまで使用していた xAskGPT 関数の戻り値を文字列から NotesJSONNavigator のオブジェクトに変更します。
Function xAskGPT(ByVal vsJSON_Post As String) As NotesJSONNavigator ・・・ sURL = "https://api.openai.com/v1/chat/completions" 'エンドポイント Set jnav = http.Post(sURL, vsJSON_Post) 'Responceをセット Set xAskGPT = jnav End Function |
この変更に伴い、メインルーチンも調整します。
Sub Initialize ・・・ sJSON_Post = xGetJSON(nd) 'Call xSetRT(nd, "JSON_Send", sJSON_Post) '必要な時だけ有効化 'GPT4o 問い合わせ Dim jnavResponce As NotesJSONNavigator Set jnavResponce = xAskGPT(sJSON_Post) 'レスポンスの JSON を取得 sJSON_Responce = jnavResponce.Stringify Call xSetRT(nd, "JSON_Responce", sJSON_Responce) Call nd.Save(True, False) End Sub |
ここまでで機能的な変化はありませんが、この状態を今回のスタート地点とします。
汎用関数の準備
階層化された JSON から必要な値を取得するにはプログラムが長く複雑になりがちです。その対策として、子エレメントを操作する汎用的な関数を 2 つ作成します。
◇ xGetChildElementByName 関数
名前を指定して、子エレメントを取得する関数です。
Function xGetChildElementByName(voParent As Variant, ByVal vsName As String) As NotesJSONElement Dim jobj As NotesJSONObject Dim sType As String On Error Resume Next sType = TypeName(voParent) If sType = "NOTESJSONELEMENT" Then Set jobj = voParent.Value Set xGetChildElementByName = jobj.GetElementByName(vsName) ElseIf sType = "NOTESJSONNAVIGATOR" Then Set xGetChildElementByName = voParent.GetElementByName(vsName) ElseIf sType = "NOTESJSONOBJECT" Then Set xGetChildElementByName = voParent.GetElementByName(vsName) End If End Function |
◇ xGetChildElementByName 関数
名前を指定して、子エレメントの値を取得する関数です。
Function xGetChildValueByName(voParent As Variant, ByVal vsName As String) As String Dim je As NotesJSONElement Dim sType As String On Error Resume Next sType = TypeName(voParent) If sType = "NOTESJSONELEMENT" Then Set je = xGetChildElementByName(voParent, vsName) ElseIf sType = "NOTESJSONNAVIGATOR" Then Set je = voParent.GetElementByName(vsName) ElseIf sType = "NOTESJSONOBJECT" Then Set je = voParent.GetElementByName(vsName) End If xGetChildValueByName = je.Value End Function |
レスポンスの確認と作成する機能
前置きが長くなりましたが、ここからが本題です。
まずは、レスポンスの JSON を再確認します。整形した全体は次の通りとなります。
OCR としての JSON は choices - message - content の順でエレメントを取得すればよいということですね。また、content の値が JSON となっているカスケードされた構造となります。
今回作成する機能は、この content の値を NotesJSONNavigator のオブジェクトとして取得するところまでとします。オブジェクト化しておけば、レシートの項目に柔軟にアクセスできますね。
OCR としての JSON の取得
content の値をオブジェクトで取得する関数は以下の通りです。上記の JSON 構造に従い順に取得しています。
Function xGetJSONObj_OCR(vjnav As NotesJSONNavigator) As NotesJSONNavigator Dim je As NotesJSONElement Dim ja As NotesJSONArray Dim sOCR As String 'choices 取得 Set je = xGetChildElementByName(vjnav, "choices") If Not (je Is Nothing) Then 'choices は配列なので NotesJSONArray で受ける Set ja = je.Value If ja.Size > 0 Then '配列の 1 件目を取得 Set je = ja.GetFirstElement() 'message 取得 Set je = xGetChildElementByName(je, "message") If Not (je Is Nothing) Then 'content 取得 sOCR = xGetChildValueByName(je, "content") 'オブジェクト化して戻り値にセット Set xGetJSONObj_OCR = xns.CreateJSONNavigator(sOCR) End If End If End If End Function |
準備段階で説明した関数を利用しているので、この関数は JSON の操作に特化できています。このように、JSON の煩雑な操作を分離しておくとプログラムの可読性が上がりますね。
メインルーチンの修正とテスト
最後にメインルーチンであるエージェントを修正して、作成した xGetJSONObj_OCR 関数をコールします。また、OCR としての JSON は NotesJSONNavigator オブジェクトで返されます。これを使用して、JSON をテキストとしてフィールドに保存、汎用関数を経由して、合計金額を抽出して表示しています。
Sub Initialize ・・・ Dim jnavOCR As NotesJSONNavigator ・・・ 'GPT4o 問い合わせ Set jnavResponce = xAskGPT(sJSON_Post) sJSON_Responce = jnavResponce.Stringify Call xSetRT(nd, "JSON_Responce", sJSON_Responce) 'OCR としての返答取得 Set jnavOCR = xGetJSONObj_OCR(jnavResponce) Call xSetRT(nd, "JSON_OCR", jnavOCR.Stringify()) MsgBox "合計金額 = " & xGetChildValueByName(jnavOCR, "合計金額") Call nd.Save(True, False) End Sub |
なお、フォームには取得した JSON を確認するために JSON_OCR フィールドを追加してください。
実行するとこのフィールドに JSON が表示され、合計金額がめっそじボックスで表示されます。
まとめ
GPT4o の画像認識機能を使用して、OCR として利用することに挑戦しました。今回のプログラムで返される JSON は次の通りでした(整形済み)。
{ "店舗名":"FamilyMart 南堀江店", "購入日":"2024/07/08", "合計金額":"278" } |
今回はサンプルですのでシンプルな構造としました。
指示の仕方によっては、購入品名や単価など明細を取得できます。明細は複数になるので、カンマなど区切り文字で返させたり、階層化した JSON で返させることも可能なようです。すべては、AI の役割の設定とリクエストの仕方次第ということですね。
ご興味のある方はいろいろと試してみてください。
前回 | 連載:つないでみよう | 次回 |
0 件のコメント:
コメントを投稿