仕組みや原理の理解は大事にして欲しいのですが、
手段でしかないJavaScriptコードの書き方等で苦労してもらうのは本望ではないので、各課題のヒントを記載していきます。
課題1 - 作業1
問題ないと思うので記載省略。
課題1 - 作業2
正規表現を利用してカンマを改行に変換するのが早い。
今回は、カンマの後に半角スペースがあるので、「, 」を改行コードに置換すればOK
Visual Studio Code での例
改行コードは「\n」で指定。「\r\n」だと「\r」が改行コードではなく、文字として認識される模様。
サクラエディタでの例
改行コードは「\r\n」で指定。
課題1 - 作業3
Excel作業で、問題ないと思うので記載省略。
課題1 - 作業4 / 作業5
こちらもExcel作業で、問題ないと思うので記載省略。
課題2 - 作業1
問題ないと思うので記載省略。
課題2 - 作業2
問題ないと思うので記載省略。
課題2 - 作業3
Script部分のコード例を記載します。
1番目のHTMLファイルをベースに、2番目のHTMLファイルから「uploadData」関数を流用すれば作成可能です。
<script>
const openAiApiKey = '個々人で異なる';
const urlEmbedding = 'https://api.openai.com/v1/embeddings';
const pineconApiKey = '個々人で異なる';
const urlVectorDbIndex = '個々人で異なる';
const urlVectorDbUl = urlVectorDbIndex + '/vectors/upsert'
async function execute() {
const text = document.getElementById('originalText').value;
const vector = await getEmbedding(text);
document.getElementById('vectorData').textContent = vector;
const userInput = document.getElementById('vectorData').value;
const vectorArray = userInput.split(',').map(Number);
const originalText = document.getElementById('originalText').value;
const result = await uploadData(vectorArray, originalText);
document.getElementById('response').innerText = result;
}
async function getEmbedding(text) {
const data = {
input: text,
model: 'text-embedding-ada-002'
//model: 'text-embedding-3-large'
};
const response = await fetch(urlEmbedding, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${openAiApiKey}`
},
body: JSON.stringify(data)
});
const result = await response.json();
const embedding = result.data[0].embedding; // JSONから必要データを抽出
return embedding.map(item => JSON.stringify(item)).join(', '); // 後工程用にカンマ区切りでベクトル値を表現
}
async function uploadData(vector, txt) {
const Payload = {
vectors: [{
id: Date.now().toString(), // レコードID値は1970/1/1からの経過ミリ秒数
values: vector,
metadata: { text: txt }
}]
};
const response = await fetch(urlVectorDbUl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Api-Key': pineconApiKey
},
body: JSON.stringify(Payload)
});
const data = await response.json();
return '結果: ' + JSON.stringify(data);
}
</script>
課題3
特に問題ないと思うので省略。
課題4
Script部分のコード例を記載します。
凝った作りにすることもできる箇所ですが、サンプルとして分かり易そうなコードにしました。
ハマる人が多そうなのは、Pineconeから取得したデータから、テキスト部分(例.:「私の性格は***です」「Xさんの性格***です」)を抽出する処理かと思います。
let privateData = await getPrivateJson(vector); privateData = privateData.matches.map(match => match.metadata.text); // text項目のみ抽出
<script>
// APIキー
const openAiApiKey = '個々人で異なる';
const pineconApiKey = '個々人で異なる';
// APIのURL:openAI
const urlEmbedding = 'https://api.openai.com/v1/embeddings';
const urlAiChat = 'https://api.openai.com/v1/chat/completions';
// APIのURL:Pinecone
const urlVectorDbIndex = '個々人で異なる';
const urlVectorDbQuery = urlVectorDbIndex + '/query'
const chatBox = document.getElementById('chat-box');
const userInput = document.getElementById('user-input');
// 重要処理1:システムプロンプト設定
let messages = [{
role: "system",
content: "あなたはRAGシステムのAIアシスタントです。ユーザーが入力した文章は「#ユーザー入力文:」の後に設定し、ベクトルデータベースから取得する「入力文とベクトル値が近似するプライベート情報」は「#プライベート情報:」の後に設定しますので、2つの情報を加味した回答を作成してください。"
}];
async function sendMessage() {
document.getElementById('user-input').blur(); // スマホのキーボード表示を解除
const message = userInput.value.trim();
if (message === "") return;
appendMessage(message, 'user-message');
userInput.value = "";
try {
// 重要処理2:入力文のベクトル値を取得
const vector = await getEmbedding(message);
console.log('埋め込みベクトル:', vector);
// 重要処理3:ベクトルが近似するプライベート情報を取得
let privateData = await getPrivateJson(vector);
privateData = privateData.matches.map(match => match.metadata.text); // text項目のみ抽出
console.log('プライベートデータ:', privateData);
// 重要処理4:プライベート情報を含めたユーザープロンプトを作成
messages.push({
role: "user", content: "#ユーザー入力文:" + message + "\n #プライベート情報:" + privateData
});
// 重要処理5:ChatGPTへの通信&回答表示
const bodyRaw = JSON.stringify({
model: "gpt-4o",
messages: messages,
});
const response = await fetch(urlAiChat, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${openAiApiKey}`
},
body: bodyRaw
});
const data = await response.json();
const botMessages = data.choices.map(choice => choice.message.content);
botMessages.forEach(botMessage => {
appendMessage(botMessage, 'bot-message');
messages.push({
role: "assistant",
content: [
{
type: "text",
text: botMessage
}
]
});
console.log(messages);
});
}
catch (error) {
appendMessage('エラーが発生しました。', 'bot-message');
}
}
function appendMessage(text, className) {
const messageElement = document.createElement('div');
messageElement.className = `message ${className}`;
messageElement.textContent = text;
chatBox.appendChild(messageElement);
chatBox.scrollTop = chatBox.scrollHeight;
}
// 関数記述部~重要処理2:入力文のベクトル値を取得~
async function getEmbedding(text) {
const data = {
input: text,
model: 'text-embedding-ada-002'
};
const response = await fetch(urlEmbedding, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${openAiApiKey}`
},
body: JSON.stringify(data)
});
const result = await response.json();
const embedding = result.data[0].embedding;
return embedding.map(item => JSON.stringify(item)).join(', '); //カンマ区切りに整形
}
// 関数記述部~重要処理3:ベクトルが近似する情報を取得~
async function getPrivateJson(input) {
const vector = input.split(',').map(Number); //配列として格納
const response = await fetch(urlVectorDbQuery, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Api-Key': pineconApiKey
},
body: JSON.stringify({
"vector": vector,
"topK": 5, // 取得レコード数の指定だが、類似度が高いレコードが少ない場合、指定数を下回るレコード数しか取得できない
"includeMetadata": true // ベクトル化前の文章はメタデータ(付属情報)として保存されている
})
});
return await response.json();
}
function resetChat() {
document.getElementById('chat-box').innerHTML = "";
messages.splice(1); //システムプロンプト設定のみ残す
}
</script>
課題5
特に問題ないと思うので省略。
課題6
特に問題ないと思うので省略。