CloudflareAI+hermes-2-pro-mistral-7b で Function Calling
注意
書いたのはいいが、 最新の Cloudflare の agents-starter
を見てるとあんまり Cloudflare 自体が Cloudflare AI のやる気がないように見える。モデル更新頻度も微妙。
たぶん Cloudflare AI ではなく Cloudflare AI Gateway 側に軸足が移ってそうな気配がある。そりゃ現状のローカルLLM相当のものしか使えず、性能微妙だもんね...
とはいえ、手元で Worker に自前 Stream を作ったりして SSE 対応するとこまでやったので、お焚き上げとして置いておく。
なんか Cloudflare AI でいいモデル追加されてないかなと眺めてたら、 hermes-2-pro-mistral-7b
というモデルを見つけた。 Function Calling 対応とある。
Playground で試したところ、日本語もいける。
これ以外に Function Calling 対応モデルは現状ない。そもそも Cloudflare AI に Function Calling どうするんだと調べたところ、この npm パッケージが見つかった。
コミット見ると 10ヶ月には存在していたので、この界隈では情報が古い可能性があるが、今後他のFunction Calling対応ツールが追加される可能性もあるし、一応試しておく。
簡単なAI実行ワーカー
wrangler.jsonc に AI Binding を書いておく。
"ai": {
"binding": "AI"
},
import { runWithTools } from "@cloudflare/ai-utils";
export interface Env {
AI: Ai;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const response = await runWithTools(
env.AI as any,
"@hf/nousresearch/hermes-2-pro-mistral-7b",
{
messages: [{ role: "user", content: "今日の天気は" }],
tools: [
{
name: "get-weather",
description: "Gets weather information of a particular city",
parameters: {
type: "object",
properties: {
city: {
type: "string",
description: "The city name",
},
},
required: ["city"],
},
function: async ({ city }) => {
return "晴れ";
},
},
],
}
);
return new Response(JSON.stringify(response, null, 2));
},
};
curl http://localhost:8787
たたくと、レスポンスが取れる。
Form+SSE でストリーム対応する
AI はやっぱりストリームで動かしたいので、Stream APIで試した。
import { runWithTools } from "@cloudflare/ai-utils";
export interface Env {
AI: Ai;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const url = new URL(request.url);
// stream api
if (url.pathname === "/api/stream") {
return handleApiStream(request, env, ctx);
}
// html
return new Response(getHtmlContent(), {
headers: {
"Content-Type": "text/html;charset=UTF-8",
},
});
},
};
// APIストリームを処理する関数
async function handleApiStream(
request: Request,
env: Env,
_ctx: ExecutionContext
) {
// リクエストからユーザーの入力を取得
if (request.method !== "GET") {
return new Response("Method Not Allowed", {
status: 405,
headers: {
Allow: "GET",
},
});
}
const url = new URL(request.url);
const userInput = url.searchParams.get("userInput") ?? "";
const response = await runWithTools(
env.AI as any,
"@hf/nousresearch/hermes-2-pro-mistral-7b",
{
messages: [{ role: "user", content: userInput }],
tools: [
{
name: "get-weather",
description: "Gets weather information of a particular city",
parameters: {
type: "object",
properties: {
city: {
type: "string",
description: "The city name",
},
},
required: ["city"],
},
function: async ({ city }) => {
if (Math.random() < 0.5) {
return "晴れ";
} else {
return "曇り";
}
},
},
],
},
{
streamFinalResponse: true,
}
);
return new Response(response as ReadableStream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
},
});
}
// HTMLコンテンツを生成する関数
function getHtmlContent() {
const inlineScriptContent = inlineScript
.toString()
// strip the function name
.replace(/function\sinlineScript\(\)\s\{/, "")
// remove last }
.slice(0, -1)
.trim();
return `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Stream Demo</title>
<style>
body { font-family: sans-serif; margin: 20px; }
#output { white-space: pre-wrap; margin-top: 10px; }
</style>
</head>
<body>
<form id="form">
<input type="text" id="input" value="今日の天気は">
<button type="submit">送信</button>
</form>
<div id="output"></div>
<script>${inlineScriptContent}</script>
</body>
</html>`;
function inlineScript() {
/** INLINE CODE */
document.getElementById("form")!.addEventListener("submit", (e) => {
e.preventDefault();
const input = document.getElementById("input") as HTMLInputElement;
const output = document.getElementById("output") as HTMLDivElement;
output.textContent = "読み込み中...";
input.disabled = true;
const source = new EventSource(
"/api/stream?userInput=" + encodeURIComponent(input.value)
);
source.onopen = () => {
output.textContent = "";
};
source.onmessage = (event) => {
if (event.data && event.data === "[DONE]") {
source.close();
input.disabled = false;
return;
}
const data = JSON.parse(event.data);
if (data.response) {
output.textContent += data.response;
}
};
source.onerror = () => {
source.close();
input.disabled = false;
};
});
}
}
何に使うの
Cloudflare に閉じて簡単なツール実行を振り分けるのに使えそう。
とはいえ、最初に書いたように、 真面目にやるなら Cloudflare AI Gateway を使ったほうがいい。
Discussion