1.はじめに
会社のプロジェクトで生成AIをクローズド環境で構築する際にMckay Wrigley氏によって作成されたChatbot UIのlegacy版を使用してチャットボット環境を社内に展開しています。このプロジェクトの延長でRAG(Retrieval-Augmented Generation)を用いた社内知識源を利用した社内問い合わせシステムを検討しているのですが、Chatbot UIのプラグイン開発の情報が非常に少なく、少しハマってしまったため、このページで整理します。
結論から言うと「Issues #599の対策のためのPull requests #536の対応」が必須です。
2.Chatbot UI legacyとは
Chatbot UI legacyは、Mckay Wrigley氏によって開発されたChatGPTのクローンツールの旧バージョンです。Chatbot UIの最新版はVersion 2.0.0です。
レガシー版は新しいアップデートはありませんが、バグが比較的収束しており、安定性が高いため、Chatbot UI Ver2.0.0の導入計画を策定するまでの中継ぎとして利用しています。Chatbot UIが他のChatGPTクローンに比べて優れている点はPlugin開発が可能な点です。
2024年5月2日時点では、Mckay Wrigley氏のGitHUBページのChatbot UI legacyには2023年4月4日以降~2023年4月20日のコミットが含まれていないため正しく動作しませんでしたので、このページに掲載のプラグインを動作させたい方は「Chatbot UI legacyの問題 #chatbot - Qiita」ページに掲載のKentaroAOKI氏がPull Requestのためのforkから取得されるか、私のQiita記事を参考に私がKentaroAOKI氏のPull RequestからForkしてさらに対策を講じたFork「https://github.com/potofo/chatbot-ui/」から取得してください。
本家のChatbot UI legacyのブランチ
Chatbot UI legacyの問題を整理したQiita記事
対策を講じたFork
3.Pluginについて
Chatbot UI legacyには標準でGoogle検索プラグインが実装されています。
Google検索プラグインではGoogle Custome Search API(1日100リクエストまでは無料)を利用してGoogleを検索し、以下のOne-Shotプロンプトに検索結果を挟み込んでLLMに回答をさせています。
このPlugin機能がRAG(Retrieval-Augmented Generation)に応用できると考えています。
const answerPrompt = endent`
Provide me with the information I requested.
Use the sources to provide an accurate response.
Respond in markdown format.
Cite the sources you used as a markdown link as you use them at the end of each sentence by number of the source (ex: [[1]](link.com)).
Provide an accurate response and then stop.
Today's date is ${new Date().toLocaleDateString()}.
Example Input:
What's the weather in San Francisco today?
Example Sources:
[Weather in San Francisco](https://www.google.com/search?q=weather+san+francisco)
Example Response:
It's 70 degrees and sunny in San Francisco today. [[1]](https://www.google.com/search?q=weather+san+francisco)
Input:
${userMessage.content.trim()}
Sources:
${filteredSources.map((source) => {
return endent`
${source.title} (${source.link}):
${source.text}
`;
})}
Response:
`;
4.仕様
今回の構成ではPCのNode.js上で動作させるChatbot UI legacyにオウム返しをする単純なプラグインを組み込みます。
No | 区分 | 名称 | 備考 |
---|---|---|---|
1 | LLM | Azure OpenAI ServiceのGPT-35-Tuebo-16K | |
2 | チャットボット | Chatbot UI legacy | https://github.com/potofo/chatbot-ui/ |
3 | Simple plugin | このページで作成するユーザプロンプトをオウム返しするプラグイン | 「hello」と入力すると「The prompt you entered is ‘hello’. Do you need anything else?」と応答します。 |
5.作業の流れ
作業は以下の5つのファイルの修正で完了します。
No | 修正ファイル | 修正内容 | 備考 |
---|---|---|---|
1 | components/Chat/Chat.tsx | updatedConversationのタイポを修正します。 誤:updateConversation 正:updatedConversation | https://github.com/mckaywrigley/chatbot-ui/issues/599の対策のためのhttps://github.com/mckaywrigley/chatbot-ui/pull/536の対応 OpenAIのLLMはステートレス(データを保持しない)のため、通常のチャットボットではLLMとのやりとりをmessagesと呼ばれる短期記憶に記録します。updatedConversationはPluginを使用した際のやりとりを、messagesに追加するための関数なのですが、このスペルが間違っているため、messagesオブジェクトが破壊されメンバのlengthにアクセス不可能となっています。 |
2 | types/plugin.ts | Chatbot UIのプロンプト入力欄の左側にあるPluginに列挙するPlugin名(今回はSimple Plugin)を追加定義しています。 | |
3 | utils/app/api.ts | Chatbot UIのプロンプト入力欄の左側にあるPluginでSimple PluginのIDが設定された際に実際に呼び出すPluginプログラム(今回はapi/simple.ts))を追加定義しています。PluginはPagesからの相対パスのようです。 | |
4 | pages/api/simple.ts | 作成するプラグイン本体 |
6.ハマりポイント
TF氏の「オレオレChatGPTのかんたん実装(Google検索+ベクトル検索)」サイトを参考にPluginの実装をしようとすると「TypeError: Cannot read properties of undefined (reading 'length')」とmessagesにlengthメンバが定義されていないというエラーが発生してPluginが正しく動作しませんでした。
いろいろ調べた結果、Google Search Pluginの問題として掲載されたいたIssuesの「Google Search Plugin returns 500 #528」が原因とわかりました。
IssuesはGoogle Search Pluginとして掲載されていますが、Pluginを開発する場合は、この対策を行わないと同じ問題でハマります。
私もIssuesの題名でスルーしていた問題だったので、解決に時間がかかりました。
Unhandled Runtime Error
TypeError: Cannot read properties of undefined (reading 'length')
components\Chat\Chat.tsx (400:44) @ length
398 | onScroll={handleScroll}
399 | >
> 400 | {selectedConversation?.messages.length === 0 ? (
| ^
401 | <>
402 | <div className="mx-auto flex flex-col space-y-5 md:space-y-10 px-3 pt-5 md:pt-12 sm:max-w-[600px]">
403 | <div className="text-center text-3xl font-semibold text-gray-800 dark:text-gray-100">
7.修正内容
7.1 Chat.tsx
components/Chat/Chat.tsx
7.2 plugin.ts
types/plugin.ts
7.3 api.ts
utils/app/api.ts
7.4 simple.ts
このシンプルなPluginはmessagesを受け取り最後に入力されたユーザプロンプトをオウム返しします。
messagesはjson形式で以下のようなデータとして短期記憶として蓄積され、通常はLLMに渡されます。
messages: [
{ role: 'user', content: 'hello' },
{
role: 'assistant',
content: 'The prompt you entered is ‘hello’. Do you need anything else?'
},
{ role: 'user', content: 'who are you?' },
{
role: 'assistant',
content: 'The prompt you entered is ‘who are you?’. Do you need anything else?'
},
{ role: 'user', content: 'good evening.' }
]
pages/api/simple.ts
// In Chatbot UI Ver 2023/04/20, to make this simple plugin work, bug fixes in Chat.tsx are required.
// Specifically, you need to modify line 227 of components/Chat/Chat.tsx from "value: updateConversation," to "value: updatedConversation," in order to address this issue.
// If this fix is not implemented, a TypeError will occur in the {selectedConversation?.messages.length === 0 ? section of components/Chat/Chat.tsx.
// Please refer to the following issues for more information:
// https://github.com/mckaywrigley/chatbot-ui/issues/599
// https://github.com/mckaywrigley/chatbot-ui/pull/536
import { NextApiRequest, NextApiResponse } from 'next';
import { ChatBody, Message } from '@/types/chat';
export default async function simpleHandler(
req: NextApiRequest,
res: NextApiResponse<any>,
) {
try {
const { messages } = req.body as ChatBody;
const userPrompt = messages[messages.length - 1]; // prompt from user
// Always give the same answer.
const answer = 'The prompt you entered is ‘' + userPrompt.content + '’. Do you need anything else?';
// handle response
res.status(200).json({ answer });
} catch (error) {
console.error('Error:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
}
8.動作確認
Chatbot UIの起動方法は「mckaywrigley/chatbot-ui at legacy (github.com)」ページの「Running Locally」を参考にしてください。
実行は「npm run dev」です。
Webサーバを起動後、Edgeブラウザでhttp://127.0.0.1:3000にアクセスしてChatbot UIの画面を表示します。
そしてプロンプト入力欄の左にある⚡マークをマウスで左クリックして「ChatGPT」のプルダウンから「Simple Plugin」を選択して
プロンプトを入力します。
すると期待通りの「The prompt you entered is ‘hello’. Do you need anything else?」が応答されます。