AIチャットボットをCloudflare Workersで作りました!!

AIチャットボットをCloudflare Workersで作りました!!

logo 雑ポエム

投稿日: 2023年10月10日

お知らせ
cloudflare

筆者はひっっっっっっじょうにケチな人間です
これまで幾度となく個人開発への挑戦をしてきましたが、必ずFree tierの範囲内でおさまる技術選定しかしてきませんでした

例: Firebase, Fly.io, その他色々

そんな筆者でも課金したくなるくらいCloudflareでの開発体験が良すぎてしまい、ついにCloudflare workersの有料プランに入ってしまいました!

ちなみにPricingはこんな感じ

https://developers.cloudflare.com/workers/platform/pricing/

まぁぶっちゃけ最初の5$以上に使うことなんてほとんどないだろうと思っています。それを見越して

「今までお世話になったし・・・5$くらい!!いいかぁ!!!」

<  チャリーン

と、課金しちゃいました。なのでこのサイトにDDOSアタックされたら破産しちゃうのでやめてくださいね!

さて、前置きはそれくらいにして

なんで有料課金をしようとしたのか、何を作ったのか、それについて今回はお話しようと思います!

(´ε` )


よし、チャットボット作ろう!!!!


今現在筆者は個人開発のアイデアに詰まっている状態です。いろんなサイトで類似事例を探したり、マーケティングやデザインの勉強などをしたりして何かアイデアはないかと必死になっています。

色んなサイトを覗いてみるとどのサイトでもチャットボットがおいてあるんですよね。なぜでしょうか?

< 労働人口ガ---

< カスタマーサポートの人件費削減ガー

・・・

まぁ

とりあえずチャットボット作ってみるのありだな!よし!作ろう!!

というのが経緯です。理由なんて何もないです。ただなんとなくそこまで複雑でなく、連休中に作れそうな規模感だったので作ってみました。

Cloudflare WorkersでAIが使えるじゃん



ただ単にチャットボットを作るだけでも面白いのですが、どうせならChatGPTみたいなAIを使ってみたいなと思いました。ただあいつのAPI機能を使うには課金しないといけなく、そいつを使おう物なら破産なんか目に見えています。
llamaみたいな言語モデルを自分で使うのもありですが、サーバー環境建てるのも面倒だなぁと思っていたところ、Cloudflare WorkersでAIが使えるじゃん!!ということを思い出しました。

https://blog.cloudflare.com/workers-ai/



@cloudflare/ai というライブラリ経由でかんたんにllamaを読み出せるっぽいですね!

  import { getIp } from './util.mjs';
import { Ai } from '@cloudflare/ai';

const limitMessage = 10;
const kvKey = 'chat_limit';

/**
 *
 * @param {{
 * type: 'message' | 'limit' | 'error'
 * message: string
 * }} param0
 */
const makeMessage = ({ type, message }) => {
	return JSON.stringify({
		type,
		message,
	});
};
export class Chatbot {
	constructor(env) {
		this.env = env;
		this.messages = [];

		/**
		 * @type {KVNamespace}
		 */
		this.kv = env[kvKey];
		/**
		 * @type {Ai}
		 */
		this.ai = new Ai(env.AI);
	}

	/**
	 *
	 * @param {Request} request
	 * @returns
	 */
	async chat(request) {
		const body = await request.json();
		const message = body.message;
		/**
		 * @type {string[]}
		 */
		const histories = body.histories;
		const connectionId = getIp(request, this.env);
		const historyKey = `history:${connectionId}`;
		const kvHistories = await this.kv.get(historyKey, 'text');
		console.log(kvHistories);
		if (kvHistories !== null && Number(kvHistories) >= limitMessage) {
			return new Response(
				makeMessage({
					type: 'limit',
					message: `${limitMessage}件以上のメッセージは送信できません。1時間後にまたお試しください`,
				}),
				{
					status: 429,
				}
			);
		}

		const messages = [
			{
				role: 'system',
				content: `あなたはhttps://u-yas.dev という個人サイトの管理を務めるAIです。ユーザーと愉快なコミュニケーションを取る性格です。`,
			},
			...(body.histories.length > 0
				? [
						{
							role: 'system',
							content: histories.join('\n'),
						},
				  ]
				: []),
			{
				role: 'user',
				content: message,
			},
		];
		const aiResponse = await this.ai
			.run('@cf/meta/llama-2-7b-chat-int8', {
				messages,
			})
			.then((r) => r.response);
    // llama-2は英語でしか返信してこないので日本語に翻訳する
		const translated = await this.ai
			.run('@cf/meta/m2m100-1.2b', {
				text: aiResponse,
				target_lang: 'japanese',
				source_lang: 'english',
			})
			.then((r) => {
				return r.translated_text;
			});
    // counterを増加させる。
    // 本当はdurable objectでもいいのかも
		const count = kvHistories === null ? 1 : Number(kvHistories) + 1;
		this.kv.put(historyKey, count.toString());

		return new Response(makeMessage({ type: 'message', message: translated }));
	}
}

  

本当はDurable Object(以下 DO)にして、カウンター機能部分をDOで実装するのが理想なのかな?と思ったのですが、それだけのためにDurable Object使うほどでもないなと思い、KVで実装しました。

今回有料プランに入ったのはこのときにDOの動作検証をするためだったのが理由です!!結局使わなかったけど!!!DOは有料プランのみ利用可能なのでご注意ください!!

できたもの

こんな感じです

フロントエンド部分はDaisyUIのおかげでなんとかそれっぽく見えるようなUIにできました・・・



みなさんもCloudflare WorkersでAIしましょう!!

では!!

© 2023 u-yas All rights reserved.