はじめに
最近、毎日生成AIの話題ばっかりですね。今一番ホットなのは、2024/5/13に公開されたGPT-4oです。かなりまともな回答ができるようになってきて、さらにGPT-4 turboの半額というスペシャルプライスです。
今回はNotesクライアントのLotusScriptを使って下記の実装を行ってみます。
・テキスト問合せ
・テキストファイル+テキスト問合せ
・画像ファイル+テキスト問合せ
※NomadWebでも動くのかと思いましたが、私のテストした環境では動きませんでした。今後検証が進めばソースコードを差し替えます。Nomad MobileのAndroid版では動作を確認しています。
APIキーの取得方法などは、こちらの記事を参照して下さい。
NotesクライアントからChatGPTに質問してみよう(前編) – Notes開発者のためのXPagesデザインレシピ (enjoyxpages.sakura.ne.jp)
GPT-4oを使ったテキスト問合せ
質問を入力するためのフィールド、回答を書き込むフィールド、問合せを送信するためのボタンを配置します。
【フォーム】
問合せに必要なのは下記のようなJSONです。
【JSON】
{
"model": "gpt-4o-2024-05-13",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "Hello!"
}
]
}
]
}
LotusScriptを使って、先ほど記載したJSONフォーマットを作り、送信ボタンからPOSTするとGPT-4oのレスポンスを書き込むことができます。LotusScriptは|(縦棒)でも文字列を定義できるので、”ダブルコーテーションだらけにならずに良いですね。
【LotusScript】
Sub Click(Source As Button)
On Error Goto ErrRtn
'---------- ---------- ---------- ---------- ----------
'OpenAI APIから取得したレスポンスを書込み
'
'---------- ---------- ---------- ---------- ----------
Const APIURL = "https://api.openai.com/v1/chat/completions"
Const APIKEY = "(取得したAPIKEY)"
Const MODEL = "gpt-4o-2024-05-13"
'クラス・変数宣言
Dim ws As New NotesUIWorkspace
Dim uidoc As NotesUIDocument
Dim doc As NotesDocument
Dim session As New NotesSession
Dim http As NotesHTTPRequest
Dim jsonNav As NotesJSONNavigator
Dim sRequest As String '入力した質問
Dim sBodyJson As String '送信するJSON文字列
Dim sContent As String '回答本文
Dim vResHeader As Variant 'httpレスポンスヘッダー全体
Dim vResCode As Variant 'httpレスポンスヘッダー0行目リスト
Dim iResCode As Integer 'httpレスポンスコード
'クラス・変数セット
Set http = session.CreateHTTPRequest()
Set uidoc = ws.CurrentDocument
Set doc = uidoc.Document
'入力チェック
sRequest = doc.Request(0)
If sRequest = "" Then
Msgbox "質問を入力して下さい。"
Exit Sub
End If
'Getリクエストを実行し、結果を取得
Call http.SetHeaderField("Content-Type", "application/json")
Call http.SetHeaderField("Authorization", "Bearer " & APIKEY)
http.PreferJSONNavigator = True
'特殊文字対策
sRequest = ReplaceSpecialCharacters(sRequest)
'リクエスト送信
sBodyJson = |{"model":"| & MODEL & |", "messages":[| _
& |{"role":"user", "content":"| & sRequest & |"}| _
& |] }|
Set jsonNav = http.post(APIURL, sBodyJson)
'エラーレスポンスチェック
vResHeader = http.GetResponseHeaders()
vResCode = Split(vResHeader(0)," ")
iResCode = Cint(vResCode(1))
If iResCode <> 200 Then
Msgbox sContent , 16 , "Error : " & Cstr(iResCode)
Exit Sub
End If
'レスポンスから回答を抽出
sContent = jsonNav.GetElementByPointer("/choices/0/message/content").Value
'回答をフォームに書込み
doc.HttpStatus = iResCode
doc.Response = sContent
Exit Sub
ErrRtn: 'エラールーチン
Msgbox Error() & Chr(13) & "Line: " & Erl(), 16, "Error"
Print Error(), "ErrorCode: " & Err(), "Line: " & Erl()
Print "モジュール: 送信でエラーが発生しました。"
End
End Sub
Function ReplaceSpecialCharacters(sTargetStr As String) As String
'---------- ---------- ---------- ---------- ----------
' 生成AIでエラーになる特殊文字を置換した文字列を返す(改行コード、タブ、
' 引数 :sTarget-置換対象文字列
' 戻り値 :置換済み文字列
'---------- ---------- ---------- ---------- ----------
'改行コード、特殊文字対応
sTargetStr = Replace(sTargetStr, Chr(13) & Chr(10), "\n") '改行を句点に変換
sTargetStr = Replace(sTargetStr, """", "\""") 'ダブルコートをエスケープ
sTargetStr = Replace(sTargetStr, Chr(9), " ") 'TABを半角スペースに変換
ReplaceSpecialCharacters = sTargetStr
End Function
テキストファイルと問合せをPOSTする
次にテキストファイルと問合せを一緒に送ってみましょう。どんな活用シーンがあるかというと
・人事規定をアップし、受けられるサポートを質問する
・契約書をアップし、注意すべきポイントを確認する
・誤字脱字、語尾の修正など、ドキュメントの統一
などテキストを扱っているものであればいろいろな活用シーンが考えられます。
先ほどのフォームにテキストファイルを選択するフィールドと、ファイル選択ボタンを追加します。
【フォーム】
JSONは入力した質問に加えて、テキストファイルから読み込んだ内容を加えて送信します。
【JSON】
{
"model": "gpt-4o-2024-05-13",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "うつ病になりました。下記の人事規定から受けられるサポートを教えて下さい。"
},
{
"type": "text",
"text": text_content
}
]
}
]
}
テキスト問合せと同様にLotusScriptでJSONを作り、GPT-4oにPOSTします。
【LotusScript】
Sub Click(Source As Button)
On Error Goto ErrRtn
'---------- ---------- ---------- ---------- ----------
'OpenAI APIから取得したレスポンスを書込み
'
'---------- ---------- ---------- ---------- ----------
Const APIURL = "https://api.openai.com/v1/chat/completions"
Const APIKEY = "(取得したAPIKEY)"
Const MODEL = "gpt-4o-2024-05-13"
'クラス・変数宣言
Dim ws As New NotesUIWorkspace
Dim uidoc As NotesUIDocument
Dim doc As NotesDocument
Dim session As New NotesSession
Dim http As NotesHTTPRequest
Dim jsonNav As NotesJSONNavigator
Dim stream As NotesStream 'テキストファイル読み込み
Dim sRequest As String '入力した質問
Dim sBodyJson As String '送信するJSON文字列
Dim sContent As String '回答本文
Dim vResHeader As Variant 'httpレスポンスヘッダー全体
Dim vResCode As Variant 'httpレスポンスヘッダー0行目リスト
Dim iResCode As Integer 'httpレスポンスコード
Dim sTextPath As String 'テキストファイルパス
Dim sTextContent As String 'テキストファイル本文
'クラス・変数セット
Set http = session.CreateHTTPRequest()
Set uidoc = ws.CurrentDocument
Set doc = uidoc.Document
'入力チェック
sRequest = doc.Request(0)
If sRequest = "" Then
Msgbox "質問を入力して下さい。",16,"エラー"
Exit Sub
End If
sTextPath = doc.TextPath(0)
If sTextPath = "" Then
Msgbox "分析対象のテキストファイルを指定してください。",16,"エラー"
Exit Sub
End If
'テキストファイルの読み込み
Set stream = session.CreateStream
Call stream.Open(sTextPath,"UTF-8")
sTextContent = stream.ReadText
sTextContent = ReplaceSpecialCharacters(sTextContent)
'Getリクエストを実行し、結果を取得
Call http.SetHeaderField("Content-Type", "application/json")
Call http.SetHeaderField("Authorization", "Bearer " & APIKEY)
http.PreferJSONNavigator = True
'特殊文字対策
sRequest = ReplaceSpecialCharacters(sRequest)
'リクエスト送信
sBodyJson = |{"model":"| & MODEL & |", "messages":[| _
& |{"role":"user", "content":[| _
& |{"type":"text","text":"| & sRequest & |"},| _
& |{"type":"text","text":"| & sTextContent & |"}| _
& |] }| _
& |] }|
Set jsonNav = http.post(APIURL, sBodyJson)
'エラーレスポンスチェック
vResHeader = http.GetResponseHeaders()
vResCode = Split(vResHeader(0)," ")
iResCode = Cint(vResCode(1))
If iResCode <> 200 Then
Msgbox sContent , 16 , "Error : " & Cstr(iResCode)
Exit Sub
End If
'レスポンスから回答を抽出
sContent = jsonNav.GetElementByPointer("/choices/0/message/content").Value
'回答をフォームに書込み
doc.HttpStatus = iResCode
doc.Response = sContent
Exit Sub
ErrRtn: 'エラールーチン
Msgbox Error() & Chr(13) & "Line: " & Erl(), 16, "Error"
Print Error(), "ErrorCode: " & Err(), "Line: " & Erl()
Print "モジュール: 送信でエラーが発生しました。"
End
End Sub
'<<<<<<<<<< ReplaceSpecialCharacters関数は省略 >>>>>>>>>>
実行結果イメージは下記のようになります。人間を否定しないところが生成AIの良いところですね。めんどくさがったりもしません。
【実行結果イメージ】
次回予告
このまま画像のPOSTまで書こうかと思いましたが、長くなるので前後編に分けます。次回はお待ちかねの画像POSTです。