Jina AIとCloudflare Workers AIを使って、Webページの要約を生成する
生成AIを活用し、リリースノートなどの要約を自動生成する仕組みを紹介。Jina AIでMarkdown変換、Cloudflare Workers AIで要約と日本語化を行う手順を解説。個人や社内向けには有用だが、エンドユーザー向けには課金モデルの検討が必要。
目次
APIやOSSのアップデートログなど、開発者としてキャッチアップしておきたいが、熟読する時間はあまり確保できない・・・というシーンはさまざまな場所で発生します。こういうときに欲しくなるのが、「いい感じに要約やまとめて教えてくれる存在」です。これまではコミュニティとそのつながりに活路を見出すことが多かったのですが、SaaSやOSSの種類が多様化する中、あまりメジャーではないもののキャッチアップは以前として大変です。
ということで、生成AIを使ってリリースノートなどから要約をいい感じに作れそうな仕組みを現在検討しています。今回はその中でも、Jina AIとCloudflare Workers AIを使ったパターンを紹介します。
Jina AIでMarkdown化し、Workers AIで要約を作る
仕組み・設計としては見出しの通りです。公開されているリリースノートやChangelogをMarkdownへ変換し、それを要約します。Workers AIを選んだ理由はDeveloper Spotlight programで獲得したバウチャーがあるので、実質無料でAIリソースを使えるためです。そしてWorkers AIを利用する関係から、JSDOM系などのライブラリがWorkersで動くかどうかの検証とバンドルサイズへの注意を払う必要が生まれました。これらの考慮を無視するため、URLベースでMarkdown変換ができるJina AIを使ってMarkdown変換を行います。
変換 -> 要約 -> 日本語化
実装フローとしては「変換 -> 要約 -> 日本語化」の3ステップです。要約後に日本語訳させている理由は、要約に利用しているモデルが、出力言語の指定やプロンプトのカスタマイズに対応していないためです。ClaudeやOpenAIなどを使われる場合は、プロンプト側の調整で「変換 -> 要約」の2ステップにまとめることができます。
要約はJina AI APIを利用する
Markdownへの変換はとてもシンプルです。https://r.jina.ai/{変換したいページのURL}
に対してGETリクエストを送りましょう。Markdownに変換した結果が返ってきますので、それを次のステップで利用します。
const url = `https://r.jina.ai/https://docs.stripe.com/changelog/basil/2025-03-31/restrict-coupon-duration`
const res = await fetch(url)
const data = await res.text()
Workers AIのSummarization Modelで要約を生成する
次のステップでは要約を作ります。プロンプトによる指示をLlamaやDeepSeekに出しても良いのですが、「Summarization Model」というカテゴリのモデル(bart-large-cnn)がベータ提供されているのを見つけたので、こっちを試します。
const summary = await AI.run("@cf/facebook/bart-large-cnn", {
input_text: data,
max_length: 1024 // default
});
このモデルを使うメリットは、とにかくコードがシンプルになることです。通常要約に関する指示をプロンプトで定義する必要がありますが、要約特化のモデルゆえにそのような指示は不要です。要約の長さも指定できますので、Xなどの文字数制限がある媒体に投稿したい場合にも使えそうです。
実行結果もシンプルなJSONで取得できます。1点要望があるとすれば、min_length
も指定できるといいかなぁという所でしょうか。今回渡したURLの記事はある程度の長さがありました。ただ、要約に回すと本当にシンプルな要約として返ってきます。
{
summary: 'Coupons can no longer have a specified end time. Instead, coupons can be applied to invoices, quotes, or checkout sessions.'
}
Workers AIで日本語訳する
最後に要約した内容を日本語化しましょう。翻訳に関しても、m2m100-1.2b
という専用のモデルが用意されています。翻訳対象の文字列と言語の設定を行なうだけでOKでした。
const response = await AI.run(
"@cf/meta/m2m100-1.2b",
{
text: summary.summary,
source_lang: "english",
target_lang: "japanese",
}
);
こちらの処理結果は次のとおりです。Summarizationもそうですが、レスポンスの構造が独自ですので、LangChain.jsやMastraに組み込むときはひと工夫が必要かもしれません。
{
"translated_text": "指定された終了時間を有していない割引クーポンのサポートを削除します. Removes coupon and promo code parameters with stackable discounts."
}
おまけ: DeepSeekでも翻訳できる
翻訳結果が気に入らない場合は、DeepSeekなどのモデルで翻訳させることもできます。今回のケースですと、このような実装になるかと思います。
const { response } = await AI.run("@cf/deepseek-ai/deepseek-r1-distill-qwen-32b", {
prompt: `Please translate the following text to Japanese] \n${summary.summary}`,
});
ちなみに、こちらの方法ですとusage
が取得できるため、例えばStripe Billingを使った使用量ベースの課金などの計算にも応用できます。
{
"response": " Removes support for the \"Free shipping\" coupon type. Removes the \"Continue as Guest\" option.\n</think>\n\n指定された終了時間が無い割引クーポンのサポートを廃止します。スタッカブル割引に対応したクーポンとプロモーションコードのパラメータを廃止します。\"Free shipping\"というクーポンタイプのサポートを廃止します。\"Continue as Guest\"というオプションを廃止します。",
"usage": {
"prompt_tokens": 36,
"completion_tokens": 105,
"total_tokens": 141
}
}
微妙にthink
タグが混ざっているのは気になりますが、翻訳モデルよりも情報量が多めですね。ただ、要約した結果には書いていない情報が混ざっている気はします。。
個人または社内向けにはGood / エンドユーザー向けには・・・?
使った感想としては、個人や部署でちょっとした情報収集ツール的に使うには簡単に作れて良さそうだなと思います。プロンプトの調整ができないので、「そういうもの」として使うようなイメージですね。ただ、エンドユーザー向けの機能として提供する場合には、usage
のデータが取れない部分をどうするかが要注意ポイントに感じました。少なくとも使った分だけ課金するモデルで提供するシステムでは、DeepSeek / LLamaなどのモデルを利用する手法を選ばざるを得ないと思います。一方でバックグラウンドで動かすジョブの一部などであれば、原価計算は必要なものの、ユーザーごとやリクエストごとの厳密なトラッキングまでは不要になる可能性もあります。
ざっくり実装できる点と、Cloudflare Workflowsなどでワークフロー化しやすい部分は魅力的ですので、サクッと情報収集する仕組みを作りたい方などはぜひお試しください。