堅牢なAIアシスタントのバックエンドを構築
AI技術が進歩するにつれて、AIアシスタントはビジネス・カスタマー・サポートの管理においてますます重要な役割を果たすようになると予想されている。ほとんどの企業は、すでにワークフローに何らかのAIを組み込んでいる。
この記事では、AIアシスタントについて、その驚くべき利点と潜在的な欠点を紹介する。
さらに、OpenAIと Back4appを使ってAIベースのアシスタントを作成するための包括的なステップバイステップガイドを提供します。
AIアシスタントとは何か?
AIアシスタントとは、テキストや音声によるプロンプトを受け取り、応答を生成するソフトウェアの一部である。
とりわけ、この技術は自然言語処理(NLP)と機械学習を利用している。
AIアシスタントの目的は、人間のような会話を模倣することだ。
この記事では、AIアシスタントとAIチャットボットという言葉を同じ意味で使うことにする。しかし、アシスタントは、タスクを実行することもできるチャットボットのより複雑なバージョンであると主張する人もいるかもしれない。
人気のあるAIアシスタントの例としては、ChatGPT、Claude、Geminiなどがある。
AIアシスタントのアプリケーションには以下のようなものがある:
- カスタマーサポート(AIアシスタントが顧客からの問い合わせに答えるように訓練できる)
- 開発者支援(AIアシスタントがコードを生成し、開発者の効率的な作業を支援する)
- 教育(アシスタントは教育目的やユーザーオンボーディングに使用できる)
- マーケティング&アナリティクス(アシスタントがマーケティング資料を作成し、データを分析できる)
- エンターテイメント(AIチャットボットは、エンターテイメントや創造的な応答を作成することができます)
AIアシスタントのメリット
AIアシスタントには多くの利点がある。それらを見てみよう!
生産性を高める
AIアシスタントは、ビジネスの生産性を大幅に向上させることができる。
タスク整理、ビジネスオペレーション、カスタマーサポート、マーケティングなどに活用できる。
AIを活用することで、24時間365日対応可能な拡張性の高い自律的なカスタマー・サポートを構築することができる。
さらに、開発者はAIアシスタントを活用してコードを書いたり、レビューしたり、文書化したりできる。GitHub Copilot、OpenAI Codex、CodePalなど、この目的に特化したツールはすでに数多くある。
使いやすさ
AIアシスタントは人間の文章を「理解」するため、最も身近なソフトウェアのひとつだ。どんなに技術に精通していても、ほとんど誰でも使うことができる。
コスト削減
AIアシスタントは、業務プロセスを最適化し、カスタマーサポートを自動化することで、大幅なコスト削減に貢献する。AIが企業のコスト削減に最大20%貢献するという調査結果もある。
情報検索
AIアシスタントの最大の利点のひとつは、情報を取得する能力だ。AIアシスタントは複数の文書に素早く問い合わせを行い、ユーザーが読みやすいシンプルな回答を返すことができる。
さらに、解答に出典を添えることもできる。
OpenAIのAPIが最近進化したおかげで、ビジネスデータを使って簡単にボットを訓練できるようになりました。
カスタマイズ
AIアシスタントは高度にカスタマイズ可能だ。アシスタントの再利用は、指示やいわゆる最初のプロンプトを変更するのと同じくらい簡単だ。
さらに、AIアシスタントはコミュニケーション相手に応じてパーソナライズされた返答を返すことができる。
例えば、ユーザーの問い合わせに母国語で答えたり、過去の会話の文脈を考慮したりすることができる。
AIアシスタントの欠点
欠点がないものはない。
幻覚
AIのチャットボットは時々、誤った情報や無意味な情報を生成することがある。そのような場合、私たちはチャットボットが幻覚を見たと言います。
その理由は、ボットが実際に何を言っているのか理解していないからだ。
ChatGPTのようなAIチャットボットがどのように機能するのか興味がある方は、こちらの記事をご覧ください。
複雑な操作ができない
AIアシスタントは、単純で反復的な作業には向いているが、複雑な作業を求められると惨敗する。
彼らは一般的な常識や人間の知性に欠けている。答えがチャットボットの学習教材の中にない場合、チャットボットはおそらく間違った答えを返すでしょう。
しかも、最適な結果が得られる保証はない。
メンテナンス
ビジネス文書で訓練されたチャットボットが正確で最新の情報を提供するには、最新の文書で定期的にチャットボットを更新する必要があります。この継続的なメンテナンスがなければ、チャットボットは古い応答を提供する可能性があります。
プライバシーとセキュリティ
アシスタントは時として、機密のビジネス情報や個人的なユーザーデータを扱わなければならない。これは倫理的な問題を引き起こし、アシスタントが機密情報を漏らす可能性を生む。
AIアシスタントを作るには?
このチュートリアルでは、GPTを搭載したバーチャル・アシスタントを作成します。
私たちのバーチャル・アシスタントは、数学の質問に答えることに特化します。スレッドを活用し、フォローアップの質問に答えることができます。
さらに、コードは再利用しやすく、他のアプリケーションにも適応できるように設計されている。
バックエンドにはOpenAIと Back4appを使用し、フロントエンドはReact(TailwindCSSを使用)を使って構築します。
前提条件
- JavaScript ES6の基本的な理解
- ReactおよびReact Hooksの使用経験
- OpenAIのアカウントとクレジット
- 無料のBack4appアカウント
目的
# | バックエンド | フロントエンド |
1 | Back4appアプリケーションの作成 | 新しいプロジェクトをブートストラップする |
2 | アプリのNode.jsバージョンをアップグレードする | TailwindCSSのインストール |
3 | OpenAIの秘密鍵を作成する | ユーザーインターフェースのコード化 |
4 | 秘密鍵をenv変数として追加する | Parse SDKのインストールと設定 |
5 | Cloud Code関数の作成 | バックエンドに接続する |
コーディングを始めよう!
バックエンド
チュートリアルのこの段階では、バックエンドを担当します。Back4appアプリを作成し、OpenAIのシークレットキーを作成し、環境変数として追加し、必要なCloud Code関数を書きます。
Back4appアプリの作成
まず、Back4appアカウントにログインするか、アカウントをお持ちでない場合は作成してください。
ログインすると、アプリ一覧にリダイレクトされます。アプリを作成するには、”Build new app “をクリックしてください。
Back4appプラットフォームでは、BaaS(Backend as a Service)とCaaS(Containers as a Service)の2種類のアプリをデプロイできる。私たちはバックエンドを構築しているので、BaaSを使いましょう。
次に、アプリに有益な名前を付け、データベースはNoSQLのままにして、”Create “をクリックする。
プラットフォームがアプリを作成するまでおよそ3分待つ。Back4appは、アプリレイヤーの作成からデータベースのセットアップ、セキュリティ、スケーリングなど全てを行います。
完了すると、アプリのデータベース・インターフェースにリダイレクトされます。
ノードのバージョンを変更する
デフォルトでは、Back4appアプリはParse v4.10.4を使用しています。このバージョンのParse ServerはNode v14を使用しており、Node v18+を必要とするOpenAIのライブラリとは互換性がありません。
Parse Serverのバージョンをアップグレードしましょう。
まず、サイドバーの「サーバー設定」を選択し、下図のように「Parseサーバーの管理」設定に移動します。
Parse Serverのバージョンを5.2.3
以降に変更する。
保存」をクリックし、Back4appがサーバーのバージョンをアップグレードするまで数分待ちます。
Node.jsのバージョンを確認するには、以下のCloud Code関数を定義します:
Parse.Cloud.define("getNodeVersion", async(request) => {
return process.version;
});
次に、組み込みの “API > Console > REST “を使用して、functions/getNodeVersionに
POST
リクエストを送信してトリガーする。
OpenAI APIキー
続いて、バックエンドからOpenAIに接続するためのOpenAI APIキーを作成しましょう。
OpenAI のダッシュボードに移動し、”Default project” をクリックし、”Create project” をクリックして新しいプロジェクトを作成します。
プロジェクトにわかりやすい名前をつける — ここでは “back4app “とする。その後、”Create “をクリックする。
OpenAI は自動的に新しく作成されたプロジェクトに切り替わるはずです。
次に、ナバーで「ダッシュボード」を選択し、サイドバーで「APIキー」を選択して「APIキー」セクションに移動します。Create new secret key “をクリックし、APIキーの作成プロセスを開始する。
すべての設定をデフォルトのままにして、「秘密鍵を作成」をクリックして鍵の作成を確認します。
シークレットキーは一度しか見ることができないので、注意してください。
秘密鍵は、パスワードと同じように安全に扱ってください。もし誰かがそれを手に入れたら、多額のOpenAI料金を請求されるかもしれません。さらに、使用制限を設定することをお勧めします。
環境変数の設定
ソースコードに秘密鍵が公開されるのを避けるため、環境変数として追加する。
サーバー設定 > 環境変数 > 設定」に移動する。
次に、.envファイルで設定するようにOPENAI_API_KEY
環境変数を設定する:
OPENAI_API_KEY=<your_openai_api_key>
<your_openai_api_key> を前の手順の OpenAI 秘密キーに置き換えてください。
最後に “Save “をクリックして環境変数を保存する。
これでCloud Codeの機能から秘密鍵にアクセスできるようになった:
const secretKey = process.env.OPENAI_API_KEY;
console.log(secretKey);
// sk-proj-...
Cloud Code
バックエンドロジックを実装するために、Cloud Code 関数を利用します。Cloud Code関数は Parse Server の堅牢な機能で、開発者はカスタム サーバーサイド JavaScript コードを実行できます。
これらのトリガーは、REST経由、Parse SDKを使用、または時間単位でスケジュールすることができます。
Functions as a Serviceの詳細については、サーバーレス・ファンクションとは?
まず、サイドバーの「Cloud Code > Functions & Web Hosting」を選択し、Cloud Codeにアクセスします。
画面が2つのセクションに分かれているのがわかるだろう。左側にディレクトリ構造、右側にJavaScriptコードエディターがあります。デフォルトでは、2つのディレクトリがあります:
- Cloud Code関数やその他のカスタムコードのデプロイに使用されるクラウドフォルダ
- 静的コンテンツ(画像、動画、アイコンなど)の配置に使用されるpublicフォルダ
OpenAIライブラリをインストールする
OpenAI APIと対話するために、OpenAI JavaScriptライブラリをインストールします。
Cloud Codeを使ってNPMパッケージをインストールするのは簡単です。そのためには、インストールしたいパッケージをリストしたpackage.jsonファイルをクラウドフォルダに作成します。
// cloud/package.json
{
"dependencies": {
"openai": "^4.51.0"
}
}
最新の
openai
パッケージ・バージョンを入手するには、NPMパッケージ・ページをチェックしてください。
そして画面右上の「Deploy」をクリックする。
インストールに成功すると、クラウドフォルダに新しく生成されたpackage-lock.jsonが表示されるはずです。ロックファイルにはopenai
パッケージが含まれているはずです。
Cloud Code機能
次に、Cloud Codeの機能を処理しよう。
以下の4つの関数を作成する:
setup()
は仮想アシスタントを作成し、その設定をデータベースに保存します。createThread()
は新しい仮想アシスタントのスレッドを作成します。deleteThread(threadId)
は、既存の仮想アシスタントのスレッドを削除します。addMessage(threadId, message)
は、スレッドにメッセージを追加し、レスポンスを生成します。
次のコードをcloud/main.jsに貼り付ける:
// cloud/main.js
const OpenAI = require("openai");
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
const ASSISTANT_INITIAL_MESSAGE = "Hi, my name is Math Bot. How can I help you?";
const ASSISTANT_SETTINGS = {
name: "Math Bot",
instructions: "Very smart math bot that answers math questions.",
model: "gpt-3.5-turbo-0125",
tools: [],
};
Parse.Cloud.define("setup", async (request) => {
const Assistant = Parse.Object.extend("Assistant");
const query = await new Parse.Query(Assistant);
const count = await query.count();
// Check if virtual assistant already exists
if (count !== 0) {
throw new Parse.Error(
Parse.Error.VALIDATION_ERROR,
"A virtual assistant already exists!",
);
}
// Use OpenAI's API to create an assistant
const openAssistant = await openai.beta.assistants.create(
ASSISTANT_SETTINGS,
);
// Store the assistant in Back4app database
const assistant = new Assistant();
for (const key in ASSISTANT_SETTINGS) {
assistant.set(key, ASSISTANT_SETTINGS[key]);
}
assistant.set("initialMessage", ASSISTANT_INITIAL_MESSAGE);
assistant.set("assistantId", openAssistant.id);
await assistant.save();
return assistant.get("assistantId");
});
Parse.Cloud.define("createThread", async (request) => {
const thread = await openai.beta.threads.create();
return thread.id;
});
Parse.Cloud.define("deleteThread", async (request) => {
const _threadId = request.params.threadId;
return await openai.beta.threads.del(_threadId);
});
Parse.Cloud.define("addMessage", async (request) => {
const _threadId = request.params.threadId;
const _message = request.params.message;
// Verify all the parameters are provided
if (!_threadId || !_message) {
throw new Parse.Error(
Parse.Error.VALIDATION_ERROR,
"You need to provide: threadId & message.",
);
}
const Assistant = Parse.Object.extend("Assistant");
const query = await new Parse.Query(Assistant);
const count = await query.count();
// Check if a virtual assistant exists
if (count === 0) {
throw new Parse.Error(
Parse.Error.VALIDATION_ERROR,
"A virtual assistant does not exist!",
);
}
const assistant = await new Parse.Query(Assistant).first();
const assistantId = assistant.get("assistantId");
// Get the thread, add the message, and generate a response
let buffer = "";
const message = await openai.beta.threads.messages.create(
_threadId, {role: "user", content: _message},
);
let run = await openai.beta.threads.runs.createAndPoll(
_threadId, {assistant_id: assistantId},
);
// Add the last message to the buffer
if (run.status === "completed") {
const messages = await openai.beta.threads.messages.list(run.thread_id);
buffer += messages.data[0].content[0].text.value;
} else {
console.error("Failed to run the assistant.");
}
return buffer;
});
コード概要
- コードはOpenAIライブラリをインポートして初期化します。
- 次に、アシスタントの設定(
名前
、指示
、initialMessageなど
)を定義します。 - Codeは4つのCloud Code関数を提供する。
setup()
では、アシスタントとスレッドを取得し、スレッドにメッセージを追加します。次に、アシスタントを使用してレスポンスを生成するためにrunを使用します。
テスト・Cloud Code・ファンクション
フロントエンドに移る前に、バックエンドが期待通りに動くことを確認しなければならない。
テスト目的のため、Back4appの組み込みコンソールを使用する。サイドバーの “API > Console > REST “に移動する。
まず、setup()
関数を起動する:
- リクエストの種類
POST
- どのエンドポイント:
functions/setup
- マスターキーの使用:
false
- ランアス:空白のまま
- クエリーパラメーター:空白のまま
これで新しいアシスタントが作成され、データベースに保存されます。データベースビューに移動すると、アシスタント
クラスに1つの行があることがわかります。
続いて、会話のエンドポイントをテストしてみよう。
functions/createThreadに
POSTして
スレッドを作成する。スレッドIDに注意してください。functions/addMessageに
以下のパラメータで質問をPOST
する{"threadId":"", "message":"2+2は何ですか?"}。
生成されたレスポンスが正しいか確認してください。functions/deleteThreadに
以下のパラメータ{"threadId":""}を
指定してスレッドを削除する。
バックエンドはうまく機能しているようだ!
フロントエンド
このチュートリアルでは、新しいReactプロジェクトをブートストラップし、TailwindCSSをインストールし、UIを実装し、Parse SDKをセットアップし、必要なロジックを実装します。
Viteアプリの作成
Reactテンプレートを使って新しいViteプロジェクトをブートストラップすることから始めましょう:
$ npm create vite@latest frontend -- --template react
新しく作成したフォルダにディレクトリを変更し、依存関係をインストールする:
$ cd frontend
$ npm install
開発サーバーを実行する:
$ npm run dev
お気に入りのウェブブラウザを開き、http://localhost:5174/。デフォルトのVite + Reactランディングページが表示されるはずです。
TailwindCSS
TailwindCSSは、ユーティリティ・ファーストのCSSフレームワークで、HTMLのままでカスタム・デザインを素早く構築することができます。
このフレームワークは、スタイリングに対して高度にカスタマイズ可能でレスポンシブなアプローチを提供する。
TailwindCSSをインストールするには、公式ガイドに従ってください。
ビュー
続いて、UIを実装してみよう。
コードをより整理するために、まずいくつかのコンポーネントを作成します。まず、srcフォルダーの中にcomponentsフォルダーを作り、その中に以下の3つのファイルを置く:
その上で、このavatar.pngを src/assetsフォルダに追加する。
この時点で、あなたのディレクトリ構造は次のようになっているはずだ:
frontend/
└── src/
├── components/
│ ├── Spinner.jsx
│ ├── AssistantMessage.jsx
│ └── UserMessage.jsx
├── assets/
│ └── avatar.png
└── ...
次に、以下のコードをsrc/App.jsxに記述する:
// src/App.jsx
import {useEffect, useState} from "react";
import AssistantMessage from "./components/AssistantMessage.jsx";
import UserMessage from "./components/UserMessage.jsx";
import Spinner from "./components/Spinner.jsx";
function App() {
const [initialMessage, setInitialMessage] = useState(undefined);
const [loading, setLoading] = useState(true);
const [threadId, setThreadId] = useState(undefined);
const [message, setMessage] = useState("");
const [messages, setMessages] = useState([
{role: "assistant", content: "Welcome! How can I help you today?"},
{role: "user", content: "What is 2+2?"},
{role: "assistant", content: "2+2 is 4."},
]);
async function getInitialMessage() {
// TODO: get the initial message
}
async function reset(message) {
// TODO: create a new thread
}
useEffect(() => {
setLoading(false);
// TODO: get the initial message
}, []);
function onSubmit(event) {
// TODO: add the message to the thread and generate response
}
function onNewThread() {
// TODO: create a new thread
}
return (
<main className="container mx-auto py-8 px-8 md:px-32 lg:px-64 h-[100vh]">
<div className="pb-12 space-y-2">
<h1 className="text-3xl font-bold">
back4app-openai-virtual-assistant
</h1>
<p>
An AI-powered virtual assistant built using OpenAI + Back4app.
</p>
</div>
<div className="space-y-2">
{messages.map((message, index) => {
switch (message.role) {
case "assistant":
return <AssistantMessage key={index} content={message.content}/>;
case "user":
return <UserMessage key={index} content={message.content}/>;
default:
return <></>;
}
})}
{loading && <Spinner/>}
</div>
<form className="inline-block flex flex-row pt-12" onSubmit={onSubmit}>
<input
type="text"
className="w-full p-2 border border-gray-300 rounded-md outline-none"
placeholder="Type a message..."
value={message}
onChange={(event) => setMessage(event.target.value)}
/>
<button
type="submit"
className="bg-blue-500 hover:bg-blue-600 text-white p-2 px-3 rounded-md ml-2"
>
Send
</button>
<button
type="button"
className="bg-green-500 text-white p-2 px-3 rounded-md ml-2"
onClick={onNewThread}
>
New
</button>
</form>
</main>
);
}
export default App;
このコードはシンプルなUIを作成し、メッセージ
ステートからメッセージをレンダリングします。その上、バーチャルアシスタントにメッセージを送信するためのテキストボックスとボタンを提供します。
開発サーバーをもう一度起動し、http://localhost:5174/。ChatGPTのようなユーザーインターフェースが表示されるはずです。
Parse SDKのインストール
Back4appベースのバックエンドに接続するには、Parse SDKを使います。
Parse SDKは、開発者がParseベースのバックエンドとシームレスにやり取りできるようにし、効率的なデータクエリ、ユーザー認証、通知、リアルタイムデータ処理などを可能にします。
まず、npm経由でParseをインストールする:
$ npm install parse
次に、Back4appアプリケーションに移動します。サイドバーの “App Settings > Security & Keys “を選択します。そして、”アプリケーションID “と “JavaScriptキー “をメモしてください。
秘密鍵をソース・コードで公開する代わりに、プロジェクト・ルートに.envファイルを作成する:
VITE_BACK4APP_APPLICATION_ID=<your_back4app_application_id>
VITE_BACK4APP_JAVASCRIPT_KEY=<your_back4app_javascript_key>
変数を実際のキーに置き換えてください。
次に、src/main.jsxに移動し、環境変数を使ってParseを初期化する:
// src/main.jsx
// ...
import Parse from "parse/dist/parse.min.js";
// Initialize Parse SDK using the Back4app API keys
Parse.initialize(
import.meta.env.VITE_BACK4APP_APPLICATION_ID,
import.meta.env.VITE_BACK4APP_JAVASCRIPT_KEY,
);
Parse.serverURL = "https://parseapi.back4app.com/";
ReactDOM.createRoot(document.getElementById("root")).render(
// ...
);
インポートすることで、すべてのビューでParseインスタンスを使用できるようになります:
import Parse from "parse/dist/parse.min.js";
ロジック
最後に、src/App.jsxのReactフックを以下のように置き換える:
// src/App.jsx
// ...
import Parse from "parse/dist/parse.min.js";
function App() {
// ...
async function getInitialMessage() {
const Assistant = Parse.Object.extend("Assistant");
const assistant = await new Parse.Query(Assistant).first();
return assistant.get("initialMessage");
}
async function reset(message) {
setMessages([
{role: "assistant", content: message},
]);
setMessage("");
const threadId = await Parse.Cloud.run("createThread");
setThreadId(threadId);
}
useEffect(() => {
(async () => {
const assistantInitialMessage = await getInitialMessage();
setInitialMessage(assistantInitialMessage);
await reset(assistantInitialMessage);
setLoading(false);
})();
}, []);
function onSubmit(event) {
event.preventDefault();
if (loading || !threadId || !message) return;
setMessages([
...messages,
{role: "user", content: message},
]);
setMessage("");
setLoading(true);
(async () => {
const response = await Parse.Cloud.run("addMessage", {threadId, message});
setMessages(messages => [
...messages,
{role: "assistant", content: response},
]);
setLoading(false);
})();
}
function onNewThread() {
if (loading || !threadId) return;
setLoading(true);
(async () => {
await reset(initialMessage);
setLoading(false);
})();
}
return (
// ...
);
}
export default App;
このコードはメッセージを処理し、バックエンドに送信します。また、ユーザーがボタンをクリックして新しいスレッドを作成することもできます。
ほら!これでフロントエンドは完成だ。
開発サーバーを再起動し、アプリをテストして、すべてが動作することを確認する。
アシスタントのフロントエンドをデプロイしたい場合は、Reactアプリをホストするには?
結論
この記事は、パーソナライズされたAIベースのアシスタントを構築するために必要なすべてを教えてくれました。
これで、OpenAIのアシスタントAPIと Back4appを使って、AIアシスタントをバック&デプロイできるようになります、
最終的なソースコードは、back4app-openai-virtual-assistantのGitHubリポジトリにあります。