Notes開発者のためのXPagesデザインレシピ

簡単でCoolなXPagesアプリケーションを作るための情報を発信していきます

Notesクライアント 生成AI

NotesクライアントからChatGPTに質問してみよう(番外編1)

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)では、質問の仕方によってより良い回答が得られるか?の実験をしてみたいと思います。