シュッと Cloudflare の Worker AI を触ってみる

ちゃっす(/・ω・)/



前から気になってはいた Cloudflare さんの Worker AI を触った見たぞ☆

 


Run machine learning models, powered by serverless GPUs, on Cloudflare’s global network.

Workers AI allows you to run machine learning models, on the Cloudflare network, from your own code – whether that be from Workers, Pages, or anywhere via REST API.

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

Cloudflareのグローバルネットワーク上でサーバーレスGPUによる機械学習モデルを実行します。
Workers AIは、Cloudflareのネットワーク上で機械学習モデルを実行することができます。

DeePL


Cloudflare Workers っちゅーのはもともとあったでございますが、こやつはシュッと AI の機能を使える感じでございますな(/・ω・)/


今使えるモデルはこんな感じ

Models

使えるモデル

んで、まぁ使い方は公式チュートリアルを見てもらうのがよいでござる(/・ω・)/

ざっくり書いておくと

  1. アカウントがない人はとりあえず Cloudflare に登録する

  2. wrangler ってのを手元の環境で使えるようにする

  3. npm create cloudflare@latest みたいな感じでプロジェクトを作る

  4. コード書きかき

  5. ローカルデバッグ

  6. でぷろーい


ってな感じ(/・ω・)/


チュートリアルでは Worker を経由して AI の機能を使う方法と AI の機能を Cloudflare の API として呼び出す方法が書かれているけれど、今回は Worker から AI 機能を呼び出すぞ☆


先にコード書いておく(書き捨てなので適当である)

import { Ai } from '@cloudflare/ai'

export interface Env {
	// If you set another name in wrangler.toml as the value for 'binding',
	// replace "AI" with the variable name you defined.
	AI: any;
}

const handler: ExportedHandler = {
	async fetch(request: Request, env: Env) {

		if (request.method === "GET") {
			return new Response("The request was a GET");
		}

		const BASIC_USER = "admin";
		const BASIC_PASS = "admin";

		/**
		 * Throws exception on verification failure.
		 * @param {string} user
		 * @param {string} pass
		 * @throws {UnauthorizedException}
		 */
		async function verifyCredentials(user, pass) {
			if (BASIC_USER !== user) {
				throw new UnauthorizedException("Invalid credentials.");
			}

			if (BASIC_PASS !== pass) {
				throw new UnauthorizedException("Invalid credentials.");
			}
		}

		/**
		 * Parse HTTP Basic Authorization value.
		 * @param {Request} request
		 * @throws {BadRequestException}
		 * @returns {{ user: string, pass: string }}
		 */
		async function basicAuthentication(request) {
			const Authorization = request.headers.get("Authorization");

			const [scheme, encoded] = Authorization.split(" ");

			// The Authorization header must start with Basic, followed by a space.
			if (!encoded || scheme !== "Basic") {
				throw new BadRequestException("Malformed authorization header.");
			}

			// Decodes the base64 value and performs unicode normalization.
			// @see https://datatracker.ietf.org/doc/html/rfc7613#section-3.3.2 (and #section-4.2.2)
			// @see https://dev.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/normalize
			const buffer = Uint8Array.from(atob(encoded), (character) =>
				character.charCodeAt(0)
			);
			const decoded = new TextDecoder().decode(buffer).normalize();

			// The username & password are split by the first colon.
			//=> example: "username:password"
			const index = decoded.indexOf(":");

			// The user & password are split by the first colon and MUST NOT contain control characters.
			// @see https://tools.ietf.org/html/rfc5234#appendix-B.1 (=> "CTL = %x00-1F / %x7F")
			if (index === -1 || /[\0-\x1F\x7F]/.test(decoded)) {
				throw new BadRequestException("Invalid authorization value.");
			}

			return {
				user: decoded.substring(0, index),
				pass: decoded.substring(index + 1),
			};
		}

		async function UnauthorizedException(reason) {
			this.status = 401;
			this.statusText = "Unauthorized";
			this.reason = reason;
		}

		async function BadRequestException(reason) {
			this.status = 400;
			this.statusText = "Bad Request";
			this.reason = reason;
		}

		/**
		 * readRequestBody reads in the incoming request body
		 * Use await readRequestBody(..) in an async function to get the string
		 * @param {Request} request the incoming request to read from
		 */
		async function readRequestBody(request: Request) {
			const contentType = request.headers.get("content-type");
			if (contentType.includes("application/json")) {
				const jsonData = await request.json();
				return jsonData.query;
			} else {
				// Perhaps some other type of data was submitted in the form
				// like an image, or some other binary data.
				return "a file";
			}
		}

		const { protocol, pathname } = new URL(request.url);

		// In the case of a Basic authentication, the exchange MUST happen over an HTTPS (TLS) connection to be secure.
		if (
			"https:" !== protocol ||
			"https" !== request.headers.get("x-forwarded-proto")
		) {
			throw new BadRequestException("Please use a HTTPS connection.");
		}

		if (!request.headers.has("Authorization")) {
			// Not authenticated.
			return new Response("You need to set credentials.", {
				status: 401,
				headers: {
					// Prompts the user for credentials.
					"WWW-Authenticate": 'Basic realm="my scope", charset="UTF-8"',
				},
			});
		}

		// Throws exception when authorization fails.
		const { user, pass } = basicAuthentication(request);
		verifyCredentials(user, pass);

		switch (pathname) {
			case "/":
				const query = await readRequestBody(request);

				const ai = new Ai(env.AI);

				const preprocess_response = await ai.run('@cf/meta/m2m100-1.2b', {
					text: query,
					source_lang: "japanese",
					target_lang: "english"
				}
				);

				const llm_response = await ai.run('@cf/meta/llama-2-7b-chat-int8', {
					messages: [
						{ "role": "system", "content": "You are a friendly assistant" },
						{ "role": "user", "content": preprocess_response.translated_text }
					]
					// prompt: preprocess_response.translated_text
				}
				);

				const response = await ai.run('@cf/meta/m2m100-1.2b', {
					text: llm_response.response,
					source_lang: "english",
					target_lang: "japanese"
				}
				);

				// Only returns this response when no exception is thrown.
				return new Response(response.translated_text, {
					status: 200,
					headers: {
						"Cache-Control": "no-store",
					},
				});

			case "/favicon.ico":
			case "/robots.txt":
				return new Response(null, { status: 204 });
		}

		return new Response("Not Found.", { status: 404 });
	}
};


export default handler;


内容をざくっというと


リクエストとして受け取った query をプロンプトとして LLM を実行して結果を返しているぞ(/・ω・)/


ただ LLM のモデルが日本語得意なものじゃないので、LLM の呼び出し前後で japanese -> english, english -> japanese の翻訳モデルを利用しているぞ(/・ω・)/



でまぁ、デプロイするとグローバルに公開されるので Example の内容を基にベーシック認証をいれておりますのよ(/・ω・)/



んでまぁこんな感じでよびだしますん

import requests
import json
from requests.auth import HTTPBasicAuth

# 認証情報
username = 'admin'
password = 'admin'

# URL とデータ
url = 'http://localhost:8787/'
data = {
    'query': 'AI ってなんですか?',
}
headers = {
    'Content-Type': 'application/json'
}

response = requests.post(url, data=json.dumps(data), headers=headers, auth=HTTPBasicAuth(username, password))

# レスポンスを表示
print(response.status_code)
print(response.text)


お返事

AIは人工知能の発展を意味し、通常、人間の知能を必要とするタスクを実行することができる
コンピュータシステムを指します。
AIシステムはアルゴリズムや機械学習技術を使用してデータを分析し、
パターンを認識し、そのデータに基づいて予測や決定を下します。
AIには、以下のような多くの異なるタイプがあります: 

1.狭いまたは弱いAI:このタイプのAIは、顔認識、言語翻訳、
またはチェスやGoのようなゲームをプレイするように設計されています。

2.一般的または強力なAI:このタイプのAIは、人間のような理由、問題解決、
学習などの知的タスクを実行するように設計されています。

3.スーパーインテリジェンス:このタイプのAIは、人間の頭脳よりもはるかに知的であり、
人間の能力を超えた複雑な問題


んで、使ってみて気づいたのでございますが結構制限がきびすぃ( ・ω・)
一度に処理できるトークン数が日本語だとむむむという感じ。
一問一答の短文回答とかならいいかなぁ。

Limits

Workers AI is currently in Open Beta and is not recommended for production data and traffic, and limits + access are subject to change

During the open beta, the following limits are in place:

Inference requests per minute (per model)@cf/meta/llama-2-7b-chat-int8 - 50 reqs/min
@cf/openai/whisper - 4000 reqs/min
@cf/meta/m2m100-1.2b - 4000 reqs/min
@cf/huggingface/distilbert-sst-2-int8 - 6000 reqs/min
@cf/microsoft/resnet-50 - 6000 reqs/min
@cf/baai/bge-base-en-v1.5 - 6000 reqs/min


Note that these limits are estimates, subject to change, and will vary by location while in Open Beta.

Other Limits@cf/meta/llama-2-7b-chat-int8 (max tokens) - 768 input / 256 output
@cf/meta/m2m100-1.2b (max tokens) - 256 output

https://developers.cloudflare.com/workers-ai/platform/limits/



まぁしょうがないよねぇということでベクトル検索ができるようになるチュートリアルページがあったのでこっちもやろうと思ったのだけれど

Vectorize の機能は有料プランじゃないとまだ使えないっぽ(/・ω・)/



ので、今回は断念でござる( ・ω・)



ちなみに、Cloudflare で提供される embeddings モデルは日本語弱そうであるが、別の API を使うこともできるようである(未検証)


BYO embeddings from your favorite AI API

Vectorize isn’t just limited to Workers AI, though: it’s a fully-fledged, standalone vector database.

If you’re already using OpenAI’s Embedding API, Cohere’s multilingual model, or any other embedding API, then you can easily bring-your-own (BYO) vectors to Vectorize.

It works just the same: generate your embeddings, insert them into Vectorize, and pass your queries through the model before you query your index. Vectorize includes a few shortcuts for some of the most popular embedding models.

https://blog.cloudflare.com/vectorize-vector-database-open-beta/



っちゅーことで非常に簡単ではあるけれど触ってみた(/・ω・)/



Platform として色々と Cloudflare が整備されてきているので今後に期待大だけれど丸っと日本語対応した LLM アプリを Cloudflare だけで完結できるかというと今のところ微妙かも(/・ω・)/



というわけでおしまい。

この記事が気に入ったらサポートをしてみませんか?