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

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

Notesクライアント 生成AI

話題のClaude3をNotesクライアントから使ってみる(中編)

Notesフォールドに入力した質問をClaude3に投げてみる

 前回、固定の質問を投げることに成功したので、今回はNotesフィールドに入力された質問をPOSTし、回答を取得します。
 フォームには質問入力フィールド、回答入力フィールドを作成し、送信ボタンを配置します。(質問履歴フィールド、回答履歴フィールドなどは後の方で使います。)
※サンプルコードは、わかりやすくするためにOn Errorなどは実装していませんが、本番実装する際は必ず入れて下さい。

【Notesフォーム】

【問合せJSONフォーマット】

{
    model : "claude-3-opus-20240229",
    max_tokens : 1024,
    messages: [
        {
            "role": "user",
            "content": "おはようございます"
        }
    ]
}

 まず、質問内容に改行コード、特殊文字が入っているとエラーになりますので、特殊文字を置き換える関数を作ります。

【LotusScriptコード-特殊文字変換関数】

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

 次に、ChatGPTのAPI呼出時と同じように「送信」ボタンクリックイベントに質問をPOSTし、レスポンスを回答に書き込むコードを実装します。

【LotusScriptコード-送信ボタン】

Sub Click(Source As Button)
	'---------- ---------- ---------- ---------- ---------- 
	'Claude3 APIから取得したレスポンスを書込み-文字列
	'
	'---------- ---------- ---------- ---------- ---------- 	
	Const APIURL = "https://api.anthropic.com/v1/messages"
	Const APIKEY = "(取得したAPIKEY)"
	Const MODEL = "claude-3-opus-20240229"
	Const MAXTOKENS = 1024
	
	'クラス・変数宣言	
	Dim ws				As New NotesUIWorkspace
	Dim uidoc			As NotesUIDocument
	Dim doc			As NotesDocument
	Dim session		As New NotesSession
	Dim jsonNav		As NotesJSONNavigator
	
	Dim vXml			As Variant					'XMLオブジェクト	
	Dim sRequest		As String					'入力した質問
	Dim sBodyJson		As String					'送信するJSON文字列
	Dim sContent		As String					'回答本文
	
	'クラス・変数セット
	Set uidoc		= ws.CurrentDocument
	Set doc		= uidoc.Document
	
	'入力チェック
	sRequest		= doc.Request(0)
	If sRequest = "" Then
		Msgbox "質問を入力して下さい。"
		Exit Sub
	End If
	
	'Getリクエストを実行し、結果を取得
	Set vXml = CreateObject("MSXML2.XMLHTTP")	
	vXml.Open "POST", APIURL, False
	vXml.setRequestHeader "Content-Type", "application/json"
	vXml.setRequestHeader "x-api-key", APIKEY
	vXml.setRequestHeader "anthropic-version", "2023-06-01"
	
	'改行コード、特殊文字対応
	sRequest = ReplaceSpecialCharacters(sRequest)
	
	'POST用JSON作成
	sBodyJson		= |{"model":"| & MODEL & |","max_tokens": | & MAXTOKENS & |, "messages":[|_
	&| {"role":"user", "content":"| & sRequest & |"} |_
	&|] }|	
	vXml.send sBodyJson
	
	'エラーレスポンスチェック
	If vXml.Status <> 200 Then
		Msgbox  vXml.responseText , 16 , "Error : " & Cstr(vXml.Status)
		Exit Sub
	End If
	
	'レスポンスから回答を抽出
	Set jsonNav	= session.CreateJSONNavigator(vXml.responseText)
	sContent		= jsonNav.GetElementByPointer("/content/0/text").Value
	
	'回答をフォームに書込み
	doc.HttpStatus	= vXml.Status
	doc.Response	= sContent	
	
End Sub

前回の質問に関連する質問を投げるには?

 前回の質問に追加の質問を行いたい場合はどのようなフォーマットでPOSTすれば良いのでしょうか?APIヘルプを見ると下記のようになっています。https://docs.anthropic.com/claude/reference/messages_post
 前回の質問は「user」ロール、前回の回答は「assistant」ロールでJSON配列に入れて、最後に現在の質問を入れるようです。

【問合せJSONフォーマット】

{
    model : "claude-3-opus-20240229",
    max_tokens : 1024,
    messages: [
        {
            "role": "user",
            "content": "Hello there."
        },
        {
            "role": "assistant",
            "content": "Hi, I'm Claude. How can I help you?"
        },
        {
            "role": "user",
            "content": "Can you explain LLMs in plain English?"
        },
    ]
}

 これを実現するには、過去の質問と回答をNotesフィールドに保存する必要がありますので、質問履歴フィールド(複数値可)、と回答履歴フィールド(複数値可)を作成し、「質問・回答を残す」ボタンから@式で現在の質問と回答を追記する実装を行います。

【@式-質問・回答を残すボタン】

REM {質問・回答入力確認};
@If(@IsNull(Request);
	@Do(
		@Prompt([Ok];"エラー";"質問を入力してください。");
		@Return("")
	);
	@IsNull(Response);
	@Do(
		@Prompt([Ok];"エラー";"回答を入力してください。");
		@Return("")
	);
	@Success);

REM {質問、回答を履歴フィールドに追加};
FIELD RequestList := @Trim(RequestList : Request);
FIELD ResponseList := @Trim(ResponseList : Response);
FIELD Request := "";
FIELD Response := "";
@True

 後はPOST前に質問履歴フィールドと回答履歴フィールドを「user」ロールと「assistant」ロールに分けてJSONを作り、最後に今回の質問を連結してPOSTします。

【LotusScriptコード-送信ボタン】

Sub Click(Source As Button)
	'---------- ---------- ---------- ---------- ---------- 
	'Claude3 APIから取得したレスポンスを書込み-文字列
	'
	'---------- ---------- ---------- ---------- ---------- 	
	Const APIURL = "https://api.anthropic.com/v1/messages"
	Const APIKEY = "(取得したAPIKEY)"
	Const MODEL = "claude-3-opus-20240229"
	Const MAXTOKENS = 1024
	
	'クラス・変数宣言	
	Dim ws				As New NotesUIWorkspace
	Dim uidoc			As NotesUIDocument
	Dim doc			As NotesDocument
	Dim session		As New NotesSession
	Dim jsonNav		As NotesJSONNavigator
	
	Dim vXml			As Variant					'XMLオブジェクト	
	Dim sRequest		As String					'入力した質問
	Dim sBodyJson		As String					'送信するJSON文字列
	Dim sContent		As String					'回答本文
	Dim vRequestList	As Variant					'過去の質問リスト
	Dim vResponseList As Variant					'過去の回答リスト
	Dim sHistoryJson	As String					'過去の質問・回答JSON
	Dim iHistoryCnt	As Integer					'過去の質問カウンタ
	
	'クラス・変数セット
	Set uidoc		= ws.CurrentDocument
	Set doc		= uidoc.Document
	iHistoryCnt		= 0
	
	'入力チェック
	sRequest		= doc.Request(0)
	If sRequest = "" Then
		Msgbox "質問を入力して下さい。",16,"エラー"
		Exit Sub
	End If
	
	'過去の質問と回答をJSON文字列に変換
	vRequestList	= doc.RequestList
	vResponseList	= doc.ResponseList
	Forall x In vRequestList
		If x = "" Then
			Exit Forall
		End If
		sHistoryJson = |{"role":"user","content":"| & ReplaceSpecialCharacters(Cstr(x)) & |"},| _
		& |{"role":"assistant","content":"| & ReplaceSpecialCharacters(Cstr(vResponseList(iHistoryCnt))) & |"},| 
		iHistoryCnt	= iHistoryCnt + 1
	End Forall
	
	'Getリクエストを実行し、結果を取得
	Set vXml = CreateObject("MSXML2.XMLHTTP")	
	vXml.Open "POST", APIURL, False
	vXml.setRequestHeader "Content-Type", "application/json"
	vXml.setRequestHeader "x-api-key", APIKEY
	vXml.setRequestHeader "anthropic-version", "2023-06-01"
	
	'改行コード、特殊文字対応
	sRequest = ReplaceSpecialCharacters(sRequest)
	
	'POST用JSON作成
	sBodyJson		= |{"model":"| & MODEL & |","max_tokens": | & MAXTOKENS & |, "messages":[|_
	& sHistoryJson _
	&| {"role":"user", "content":"| & sRequest & |"} |_
	&|] }|	
	vXml.send sBodyJson
	
	'エラーレスポンスチェック
	If vXml.Status <> 200 Then
		Msgbox  vXml.responseText , 16 , "Error : " & Cstr(vXml.Status)
		Exit Sub
	End If
	
	'レスポンスから回答を抽出
	Set jsonNav	= session.CreateJSONNavigator(vXml.responseText)
	sContent		= jsonNav.GetElementByPointer("/content/0/text").Value
	
	'回答をフォームに書込み
	doc.HttpStatus	= vXml.Status
	doc.Response	= sContent	
	
End Sub

【実行結果】

system属性の実装

 Claude3もChatGPTのAPIと同じようにsystem属性を持っていますので、同じように実装することができます。ChatGPTの場合は、message属性の内側にsystem属性がありましたが、Claude3ではmessage属性の外側に出ていて、message属性全体に反映する内容であることが明確になっています。このあたりは後発な分整理されている感じです。

【問合せJSONフォーマット】

{
    model : "claude-3-opus-20240229",
    max_tokens : 1024,
    system : "Respond only in Yoda-speak.",
    messages: [
        {
            "role": "user",
            "content": "おはようございます"
        }
    ]
}

 ChatGPTの時と同じように定数でsystem属性を定義してLotusScriptに反映してみます。本番実装の場合は、設定文書などを作りフィールドから設定できるようにすると便利に使えます。

【LotusScriptコード-送信ボタン】

Sub Click(Source As Button)
	'---------- ---------- ---------- ---------- ---------- 
	'Claude3 APIから取得したレスポンスを書込み-文字列、system属性あり
	'
	'---------- ---------- ---------- ---------- ---------- 	
	Const APIURL = "https://api.anthropic.com/v1/messages"
	Const APIKEY = "(取得したAPIKEY)"
	Const MODEL = "claude-3-sonnet-20240229"
	Const MAXTOKENS = 1024
	Const SYSTEM = "語尾に「ぺこ」をつけて会話します。"
	
	'クラス・変数宣言	
	Dim ws				As New NotesUIWorkspace
	Dim uidoc			As NotesUIDocument
	Dim doc			As NotesDocument
	Dim session		As New NotesSession
	Dim jsonNav		As NotesJSONNavigator
	
	Dim vXml			As Variant					'XMLオブジェクト	
	Dim sRequest		As String					'入力した質問
	Dim sBodyJson		As String					'送信するJSON文字列
	Dim sContent		As String					'回答本文
	
	'クラス・変数セット
	Set uidoc		= ws.CurrentDocument
	Set doc		= uidoc.Document
	
	'入力チェック
	sRequest		= doc.Request(0)
	If sRequest = "" Then
		Msgbox "質問を入力して下さい。"
		Exit Sub
	End If
	
	'Getリクエストを実行し、結果を取得
	Set vXml = CreateObject("MSXML2.XMLHTTP")	
	vXml.Open "POST", APIURL, False
	vXml.setRequestHeader "Content-Type", "application/json"
	vXml.setRequestHeader "x-api-key", APIKEY
	vXml.setRequestHeader "anthropic-version", "2023-06-01"
	
	'改行コード、特殊文字対応
	sRequest = ReplaceSpecialCharacters(sRequest)
	
	'POST用JSON作成
	sBodyJson		= |{"model":"| & MODEL & |","max_tokens": | & MAXTOKENS & |,"system":"| & SYSTEM & |", "messages":[|_
	&| {"role":"user", "content":"| & sRequest & |"} |_
	&|] }|	
	vXml.send sBodyJson
	
	'エラーレスポンスチェック
	If vXml.Status <> 200 Then
		Msgbox  vXml.responseText , 16 , "Error : " & Cstr(vXml.Status)
		Exit Sub
	End If
	
	'レスポンスから回答を抽出
	Set jsonNav	= session.CreateJSONNavigator(vXml.responseText)
	sContent		= jsonNav.GetElementByPointer("/content/0/text").Value
	
	'回答をフォームに書込み
	doc.HttpStatus	= vXml.Status
	doc.Response	= sContent	
	
End Sub

【実行結果】

次回予告

次回は画像をPOSTして内容を分析して貰います。