ChatGPTへの質問をNomadMobileから行いたい
前回の前編、後編で紹介したChatGPTへの問合せはMicrosoftの「MSXML2.ServerXMLHTTP.6.0」を使用していました。この方法は安定して使える反面、Windowsクライアントしか使えないという制約があります。そこで番外編では、NotesV10から追加されたNotesHTTPRequestクラスを使用し、NomadMobileで動作するコードに変更してみたいと思います。
【完成イメージ】
DesignerヘルプによるとNotesHTTPRequest.Postメソッドの戻り値はVariant型となっているので、Valiant型の変数で受け取った戻り値をMsgboxで表示してみます。
なぜかエラーが発生してしまいました。デバッグモードで確認すると戻り値にはバイト型の数字配列が入っているようです。
【LotusScriptソース】
Sub Click(Source As Button)
'---------- ---------- ---------- ---------- ----------
'OpenAI APIから取得したレスポンスを書込み
'
'---------- ---------- ---------- ---------- ----------
Const APIURL = "https://api.openai.com/v1/chat/completions"
Const APIKEY = "(取得したAPIKEY)"
Const MODEL = "gpt-3.5-turbo"
'クラス・変数宣言
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レスポンスコード
Dim vResponse As Variant '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
'質問の改行、特殊文字対策
sRequest = Replace(sRequest, Chr(13) & Chr(10), "\n") '改行を句点に変換
sRequest = Replace(sRequest, """", "\""") 'ダブルコートをエスケープ
sRequest = Replace(sRequest, Chr(9), " ") 'TABを半角スペースに変換
'Getリクエストを実行し、結果を取得
Call http.SetHeaderField("Content-Type", "application/json")
Call http.SetHeaderField("Authorization", "Bearer " & APIKEY)
sBodyJson = |{"model":"| & MODEL & |", "messages":[ {"role":"user", "content":"| & sRequest & |"} ] }|
vResponse = http.post(APIURL, sBodyJson)
'内容を確認
Msgbox vResponse
End Sub
【画面イメージ-エラーメッセージ】
【デバッグモード】
謎の数字リストを文字列に変換する
デバッグモードで確認したバイト配列を解析してみます。これはASCIIコードの配列じゃね?ということで、バイト配列を文字列に変換する関数を書いてみます。文字列に変換すると想定したレスポンスを得られました。
【LotusScriptソース-ASCIIコード変換関数】
Function AsciiToString(vAsciiList) As String
'---------- ---------- ---------- ---------- ----------
' アスキーコード配列を文字列に変換
' 引数 :vAsciiList - ASCIIコード配列
' 戻り値 :変換連結後文字列
'---------- ---------- ---------- ---------- ----------
Dim sAsciiString As String
Forall x In vAsciiList
sAsciiString = sAsciiString & Chr(x)
End Forall
AsciiToString = sAsciiString
End Function
【LotusScriptソース-変換、表示部分のみ】
'内容を確認
Msgbox AsciiToString(vResponse)
【画面イメージ】
2バイト文字への対応に対応するには?
先ほどの出力結果を見ると回答の部分が文字化けしています。HCLのナレッジベースを検索してみると対応方法が記載されていました。
NotesHTTPRequest と NotesJSONNavigator の制限事項と今後の考慮事項 (hcltechsw.com)
この記事によるとNotes10.0.1FP2でNotesHTTPRequest.PreferJSONNavigatorクラスが追加されており、Trueを設定するとNotesHTTPRequestの戻り値をNotesJSONNavigator受け取れるようになるようです。このプロパティを使って書き換えると下記のようなソースになります。
【LotusScriptソース】
Sub Click(Source As Button)
'---------- ---------- ---------- ---------- ----------
'OpenAI APIから取得したレスポンスを書込み
'
'---------- ---------- ---------- ---------- ----------
Const APIURL = "https://api.openai.com/v1/chat/completions"
Const APIKEY = "(取得したAPIKEY)"
Const MODEL = "gpt-3.5-turbo"
'クラス・変数宣言
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
'質問の改行、特殊文字対策
sRequest = Replace(sRequest, Chr(13) & Chr(10), "\n") '改行を句点に変換
sRequest = Replace(sRequest, """", "\""") 'ダブルコートをエスケープ
sRequest = Replace(sRequest, Chr(9), " ") 'TABを半角スペースに変換
'Getリクエストを実行し、結果を取得
Call http.SetHeaderField("Content-Type", "application/json")
Call http.SetHeaderField("Authorization", "Bearer " & APIKEY)
http.PreferJSONNavigator = True
sBodyJson = |{"model":"| & MODEL & |", "messages":[ {"role":"user", "content":"| & sRequest & |"} ] }|
Set jsonNav = http.post(APIURL, sBodyJson)
sContent = jsonNav.GetElementByPointer("/choices/0/message/content").Value
'エラーレスポンスチェック
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
'回答をフォームに書込み
doc.HttpStatus = iResCode
doc.Response = sContent
End Sub
次回予告
次回(番外編2)では、質問の仕方によってより良い回答が得られるか?の実験をしてみたいと思います。