長い Web ページを読むとき、全文を読み始める前に「何を主張しているのか」「どこまで読む必要があるのか」を把握できると、読む時間の使い方を変えられます。とくに生成 AI によって長文を作りやすくなった現在は、有益な知見が含まれていても文章量そのものが読解の負荷になる場面があります。todesking は、この状況を「有益な知見がスロップに包まれて出てくることが一般的になった」と表現しています[1]。
そこで、現在開いている Web ページを 1 回の操作で要約し、原文を残したまま Chrome の Side Panel に結果を表示する拡張機能 web-digest を作りました。実装は GitHub で公開しています[2]。
要約には さくらの AI Engine を使います。Chrome 側ではページ本文の取得、文書構造の保持、長文の分割、処理状態の管理を行い、意味を保った圧縮をさくらの AI Engine に担当させます。長いページは先頭から機械的に切り捨てず、見出しや段落の境界で分割して部分要約を作り、最後にページ全体の要約へ統合します。
左 は人間が読むにはバチクソに長い大論考、右は中心命題・根拠・条件を残したまま圧縮した要約です。原文を短い文章へ置き換えるのではなく、原文を保持したまま読む側で圧縮する、という設計です[3]。
さくらの AI Engine で Web ページを要約する構成
web-digest の処理経路は次のように単純化できます。
Web ページを開く
↓
Chrome のツールバーボタンを押す
↓
activeTab で現在のタブだけに一時アクセスする
↓
chrome.scripting で本文抽出処理を実行する
↓
見出し・段落・リスト・引用・表・コードをブロックとして保持する
↓
長文なら文書構造に沿って分割する
↓
さくらの AI Engine へ要約を依頼する
↓
必要なら部分要約を再統合する
↓
Side Panel に最終要約を表示する
ページを読む処理と AI API への通信を同じ場所へ混ぜていません。本文抽出処理は DOM を読み取るだけで、API トークンを持たず、ネットワーク通信も行いません。外部通信は service worker からさくらの AI Engine へ送る経路に集約しています。
この分離によって、Chrome 拡張が必要以上の権限やデータを各処理へ配る必要がなくなります。
動作環境とインストール
実際に試すには、次のものが必要です。
- Google Chrome 116 以上
- さくらの AI Engine を利用できる環境
- 自身で発行したさくらの AI Engine のアカウントトークン
-
https://api.ai.sakura.ad.jpへの HTTPS 通信
リポジトリの manifest.json では minimum_chrome_version が 114 になっていますが、現在の実装は chrome.sidePanel.open() を使っています。このメソッドは Chrome 116 以降で利用できるため、実際の再現条件は Chrome 116 以上です。Side Panel API 自体は Chrome 114 から利用できますが、open() の利用可能バージョンはそれより後です。
まずリポジトリを取得します。
git clone https://github.com/id774/web-digest.git
ビルド処理はありません。取得したリポジトリのルートが、そのまま Chrome へ読み込ませる拡張機能です。
Chrome で次の順に操作します。
-
chrome://extensionsを開きます。 - Developer mode を有効にします。
- Load unpacked を選びます。
-
manifest.jsonを含むweb-digestディレクトリを選びます。 - Extensions メニューから
web-digestをツールバーへ固定します。
この時点ではページ本文は読みません。要約処理が始まるのは、対象ページを開いて web-digest のボタンを押したときだけです。
Manifest V3 では現在のページだけを読む
web-digest は Manifest V3 の拡張機能です。現在の manifest.json で要求している権限は次の 4 つです。
{
"manifest_version": 3,
"permissions": [
"activeTab",
"scripting",
"storage",
"sidePanel"
],
"host_permissions": [
"https://api.ai.sakura.ad.jp/*"
]
}
Web サイト全体に対する "<all_urls>" は指定していません。
activeTab は、利用者が拡張機能を明示的に呼び出したときだけ、現在のタブへ一時的なアクセスを与える権限です。Chrome 公式も、多くの用途で "<all_urls>" の代わりに activeTab を利用できると説明しています[4]。
この設計では、ページを開いただけでは何も起きません。ツールバーボタンを押した操作そのものを、ページ本文へアクセスする開始点にしています。
activeTab と chrome.scripting を組み合わせる
実際の本文抽出は chrome.scripting.executeScript() で行います。chrome.scripting は、実行時に対象を決めて JavaScript を注入できる API です。activeTab と組み合わせれば、恒久的な host permission を持たずに現在のタブへ処理を実行できます[5]。
web-digest では service worker から次のように抽出処理を実行しています。
const results = await chrome.scripting.executeScript({
target: { tabId },
files: ["src/extract/extract.js"],
});
const extracted = results && results[0] ? results[0].result : null;
extract.js は常駐する content script ではありません。ボタンを押した時点で対象タブへ 1 回だけ注入され、ページを読み取って結果を返します。
ページ遷移を監視して自動要約する処理もありません。chrome.action.onClicked が要約開始の唯一の入口です。
chrome.action.onClicked.addListener((tab) => {
if (!tab || typeof tab.id !== "number") return;
const tabId = tab.id;
chrome.sidePanel.setOptions({
tabId,
path: "src/panel/panel.html",
enabled: true,
});
chrome.sidePanel.open({ tabId });
runSummary(tabId, tab.title || "");
});
タブを閉じた場合や別ページへ移動した場合は状態を破棄しますが、それを契機に新しい要約処理は開始しません。
Web ページから本文構造を取り出す
要約の前に、ページ全体の DOM をそのまま AI Engine へ送ることはしません。ナビゲーション、フッター、フォーム、非表示要素などを除き、文章として意味を持つ部分をブロックへ変換します。
対象にしている主な要素は次のとおりです。
| HTML 要素 |
web-digest での扱い |
|---|---|
h1 から h6
|
heading |
p |
paragraph |
li |
list-item |
blockquote |
quote |
pre |
code |
th, td
|
table-cell |
本文候補のルートは、まず main、[role="main"]、article の順に探します。それだけで十分な本文を取得できない場合は、article、section、div の文章量とリンク密度から候補を選び、それでも見つからなければ body まで広げます。
一方、次のような領域は本文候補から除外します。
nav
header
footer
aside
form
dialog
script
style
noscript
template
iframe
svg
canvas
button
select
textarea
input
label
リンク文字列が要素内テキストの 70% 以上を占めるブロックも除外します。ナビゲーション、関連記事一覧、広告周辺のリンク集合を本文として扱いにくくするためです。
抽出処理はページを書き換えません。DOM へボタンを追加せず、属性や CSS も変更せず、URL も抽出結果へ含めません。返すのはページタイトルと順序付きの本文ブロックです。
長いページは見出しと段落の境界で分割する
短いページなら、整形した本文を 1 回の API 要求で要約します。
現在の実装では、1 回の要求へ渡す本文材料の上限を 40,000 文字にしています。
export const MIN_MATERIAL_CHARS = 200;
export const MAX_REQUEST_MATERIAL_CHARS = 40000;
この 40,000 文字は、さくらの AI Engine の API 仕様上の最大値ではありません。プロンプトと応答のための余裕を残しつつ、特定モデルの token-to-character 比率を仮定しないために web-digest 側で置いている保守的な処理予算です。
200 文字未満しか本文材料を取得できなかった場合は、要約するだけの内容がないものとして処理を中止します。
40,000 文字を超えた場合は、先頭から 40,000 文字ずつ切るのではなく、次の優先順位で分割します。
- 大きな見出し
- 下位見出し
- 段落、リスト、引用、コード、表
- 単一ブロックだけで上限を超える場合に限り、ブロック内部
見出しは著者自身が文章へ与えた論理的な境界です。方法を説明する章と制約を説明する章が分かれている場合、固定文字数だけで切るよりも、見出しを優先した方が論点のまとまりを残しやすくなります。
長文を分割して段階的に要約する考え方自体は、長文要約の研究でも採られています。Zhang らの SummN は、長い入力を分割して複数段階で粗い要約を作り、そこから最終的な要約を生成する枠組みを提案しています[6]。
また、モデルのコンテキスト長に全文が収まることと、その中の情報を均等に利用できることは同じではありません。Liu らは、長い入力の中で重要情報が置かれる位置によって性能が変わり、とくに中間部の情報利用が弱くなる場合があることを報告しています[7]。
このため web-digest では、「入力可能なら無条件に全文を 1 回で送る」という設計にはしていません。
部分要約をもう一度統合する
分割した各チャンクを要約した後、それらをそのまま連結して終わりにはしません。
原文
├─ チャンク 1 → 部分要約 1
├─ チャンク 2 → 部分要約 2
└─ チャンク 3 → 部分要約 3
↓
ページ全体として再統合
↓
最終要約
部分要約を集めた結果が再び 40,000 文字を超える場合は、中間結果をもう一度分割して圧縮します。実装上は再帰的に統合し、異常な非圧縮応答が続いた場合に無限化しないよう深さ 8 で打ち切ります。
チャンクにはページタイトルだけでなく、その位置までの見出し文脈も付けます。
SECTION: 大見出し > 小見出し
本文...
同じ文章でも「方法」「結果」「制約」のどこに置かれているかで役割が変わるため、分割後にも最低限の文書内位置を残します。
要約はさくらの AI Engine に任せる
web-digest で意味的な要約を実行するのは さくらの AI Engine です。
さくらの AI Engine は OpenAI 互換の Chat Completions API を提供しており、アカウントトークンを Bearer 認証に使えます[8]。
web-digest が利用するエンドポイントは次のとおりです。
https://api.ai.sakura.ad.jp/v1/chat/completions
Inference API の仕様でも、model と messages を必須とする Chat Completions API として公開されています[9]。
API 固有の処理は src/engine/engine.js へ集約しています。
export const ENGINE_BASE_URL = "https://api.ai.sakura.ad.jp/v1";
export function buildRequest({ model, messages, token }) {
return {
url: `${ENGINE_BASE_URL}/chat/completions`,
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({ model, messages }),
};
}
送っている生成パラメータは model と messages だけです。現在の実装では max_tokens、temperature、stream を指定していません。
要約の内容を決める指示は prompts/summarize.md に分離しています。API endpoint、認証、応答形式を扱うコードと、「何を残して何を減らすか」という要約方針を同じファイルに埋め込まない構成です。
自分の API トークンを使う
認証は BYOK 方式です。利用者自身のさくらの AI Engine のアカウントトークンを Options 画面で設定します。
トークンは chrome.storage.local に保存されます。
export const STORAGE_KEY_TOKEN = "apiToken";
export const STORAGE_KEY_MODEL = "model";
await chrome.storage.local.set({
[STORAGE_KEY_TOKEN]: token,
[STORAGE_KEY_MODEL]: model,
});
作者共通の API トークンはありません。独自の中継サーバーも置いていません。
そのため、ページ本文の通信経路は次の形になります。
Chrome
↓
さくらの AI Engine
Chrome
↓
web-digest 独自バックエンド
↓
さくらの AI Engine
という経路ではありません。
ただし、独自バックエンドがないことと、外部へ本文を送らないことは別です。要約対象の本文は、要約処理のためにさくらの AI Engine へ送信されます。機密情報を含むページで利用する場合は、この送信を許容できるかを判断する必要があります。
モデルは設定から変更できる
現在の web-digest のデフォルトモデルは gpt-oss-120b です。
export const DEFAULT_MODEL = "gpt-oss-120b";
さくらの AI Engine の公式利用手順でも、Chat Completions API の例として gpt-oss-120b が使われています[8]。
モデル名を Options 画面で指定した場合はその値を使い、空欄ならデフォルトへ戻します。利用可能なモデルはサービス側で変わり得るため、実際に使えるモデルはさくらの AI Engine のコントロールパネルで確認するのが確実です。
API 通信は一つのモジュールへ閉じ込める
API 要求のタイムアウトは現在 120 秒です。
export const REQUEST_TIMEOUT_MS = 120000;
応答は非 streaming で受け取り、choices[0].message.content を最終的な生成テキストとして読みます。
HTTP エラーも API 通信モジュールで分類します。
| 状態 |
web-digest での扱い |
|---|---|
401 |
API トークン拒否 |
400, 413, 422 かつ長さ関連エラー |
入力量超過 |
429 |
Rate limit |
5xx |
AI Engine 利用不可 |
| タイムアウト | AI Engine timeout |
| 通信例外 | AI Engine unreachable |
失敗した要求を自動再試行しません。1 回のボタンクリックを 1 回の実行単位として扱い、長文の場合だけ、その 1 実行の内部で必要な複数回の API 要求を行います。
要約は Side Panel に表示して原文を残す
完成した要約は別タブではなく Chrome の Side Panel に表示します。
Side Panel API は Web ページの横に拡張機能の UI を表示する API で、Chrome 114 以降の Manifest V3 で利用できます[10]。ただし、前述のとおり web-digest が使う chrome.sidePanel.open() は Chrome 116 以降です。
この配置には実務上の利点があります。
要約だけで概要を把握できるなら、その場で読み終えられます。条件や根拠が重要なら、左側に残っている原文へすぐ戻れます。要約を原文の代替物として保存するのではなく、全文をどこまで読むか判断する入口として使えます。
ページ本体へ要約 DOM を挿入しないため、対象サイトの CSS や JavaScript に干渉する範囲も増やしません。
実際に長いページを要約する
初回だけ、さくらの AI Engine のトークンを設定します。
-
chrome://extensionsを開きます。 -
web-digestの Details を開きます。 - Extension options を開きます。
- Sakura AI Engine API token に自身のトークンを入力します。
- 必要なら Model を変更します。
- Save を押します。
-
Saved.とA token is configured.が表示されることを確認します。
トークン保存後、入力欄は空になります。保存済みトークンを画面へ再表示しないためです。
要約するときの操作は 1 回だけです。
- 要約したい Web ページを開きます。
- Chrome のツールバーにある
web-digestを押します。 - Side Panel が開きます。
- 処理中は
Summarizing… this can take a while.と表示されます。 - 成功するとページタイトルと最終要約が表示されます。
通常の長さなら、さくらの AI Engine への要約要求は 1 回です。
40,000 文字を超えるページでは内部で複数回の API 要求が発生しますが、チャンクごとの操作は不要です。部分要約から統合までを 1 回の実行として扱います。
同じページをもう一度要約したい場合は、ツールバーボタンをもう一度押します。
権限とデータの流れを確認する
拡張機能では、権限の数だけでなく「どの情報をどの処理まで届かせるか」も分けています。
| 処理 | 扱う情報 | 扱わない情報 |
|---|---|---|
| 本文抽出 | 現在のページの本文構造 | API トークン、AI Engine 通信 |
| service worker | 抽出済み本文、設定、実行状態 | ページ DOM の直接変更 |
| AI Engine 通信 |
model, messages, API トークン |
Chrome の閲覧履歴 |
| Side Panel | 実行状態、ページタイトル、要約 | API 認証処理、本文抽出 |
| Options | API トークン、モデル名 | 閲覧ページ本文、要約本文 |
Chrome 公式のプライバシー指針でも、将来必要になるかもしれない権限を先に広く要求するのではなく、必要な場面だけ activeTab などで権限を得る考え方が推奨されています[11]。
web-digest の manifest には、閲覧サイト全体への host permission、tabs、history はありません。外部通信先として宣言している host permission は次の 1 つです。
{
"host_permissions": [
"https://api.ai.sakura.ad.jp/*"
]
}
本文抽出処理から URL も返しません。
一方、API トークンはローカルの Chrome profile 内に保存されるため、端末や profile を操作できる利用者から暗号学的に秘匿できるものではありません。BYOK は「トークンをソースコードへ埋め込まない」「作者のサーバーへ送らない」という境界を作るものであり、ローカル端末を信頼しなくてよい仕組みではありません。
制約と注意点
すべての Web ページから本文を取れるわけではない
本文抽出はサイト名ごとの専用処理ではなく、HTML の構造と文章密度を使う汎用方式です。
そのため、次のようなページでは十分な本文を取れない場合があります。
- 特殊な DOM 構造
- Shadow DOM に主要本文を持つページ
- 読み込み後に特殊な方法で内容を生成するページ
- 認証状態へ強く依存するページ
- 一般的な本文構造を持たないページ
本文が 200 文字未満なら、無理に要約結果を作らず処理を止めます。
Chrome 自体がスクリプト注入を許可しないページがある
chrome:// など、拡張機能が通常の Web ページと同じ方法では読めない対象があります。Chrome Web Store や組み込み PDF viewer なども通常ページと同じ前提では扱えません。
この場合は、さくらの AI Engine へ送る以前に本文取得で失敗します。
AI 要約は原文の意味を保証しない
web-digest は、中心命題、主要な根拠、因果関係、条件、留保を残し、重複を減らすことを要約プロンプトで求めています。
それでも、生成モデルによる要約が原文と完全に同じ意味を保つ保証はありません。
重要な数値、仕様、制度、研究結果、契約条件などを判断に使う場合は、Side Panel の要約だけで確定せず、隣に残っている原文へ戻る必要があります。
まとめ
web-digest は、Chrome だけで要約を完結させる拡張機能ではありません。
Chrome 側では、利用者がボタンを押したページだけを activeTab と chrome.scripting で読み、見出しや段落の構造を残して整形します。長文なら 40,000 文字の処理予算を基準に文書構造へ沿って分割します。
意味的な圧縮は さくらの AI Engine が担当します。利用者自身の API トークンを使って OpenAI 互換 Chat Completions API へ直接送り、部分要約が必要なら複数段階で処理し、最終結果を Chrome の Side Panel へ戻します。
この構成にすると、Web サイト全体への恒久的な host permission や独自バックエンドを持たずに、長い Web ページを実用的な粒度へ圧縮できます。原文はそのまま残るため、要約で全体像をつかみ、必要な箇所だけ原文へ戻る読み方も維持できます。
参考文献
- todesking, 有益な知見がスロップに包まれて出てくることが一般的になった、本当に過酷な時代。(2026-08-14). https://x.com/todesking/status/2088587244840137003
- id774, web-digest, GitHub repository(参照 2026-08-16). https://github.com/id774/web-digest
- id774, 長い Web ページを構造に沿って要約する Chrome 拡張 web-digest を作った(2026-08-17). https://blog.id774.net/entry/2026/08/17/5517
- Chrome for Developers, The "activeTab" permission(参照 2026-08-16). https://developer.chrome.com/docs/extensions/develop/concepts/activeTab
- Chrome for Developers, chrome.scripting(参照 2026-08-16). https://developer.chrome.com/docs/extensions/reference/api/scripting
- Yusen Zhang et al., SummN: A Multi-Stage Summarization Framework for Long Input Dialogues and Documents(2022). https://aclanthology.org/2022.acl-long.112/
- Nelson F. Liu et al., Lost in the Middle: How Language Models Use Long Contexts(2024). https://aclanthology.org/2024.tacl-1.9/
- さくらインターネット, さくらの AI Engine 利用手順(参照 2026-08-16). https://manual.sakura.ad.jp/cloud/ai-engine/02-howto.html
- さくらインターネット, さくらの AI Engine Inference API(参照 2026-08-16). https://manual.sakura.ad.jp/api/cloud/ai-engine/inference.html
- Chrome for Developers, chrome.sidePanel(参照 2026-08-16). https://developer.chrome.com/docs/extensions/reference/api/sidePanel
- Chrome for Developers, Protect user privacy(参照 2026-08-16). https://developer.chrome.com/docs/extensions/develop/security-privacy/user-privacy
