使用 Summarizer API

Summarizer API 提供了一种异步(基于 Promise)的机制,允许网站将一段文本输入到浏览器自身的内部 AI 模型中,并根据指定的选项请求该模型返回文本摘要。本文档将介绍如何使用 Summarizer API 的基础知识。

创建 summarizer

Summarizer API 的所有功能都通过一个单一的接口访问——Summarizer

让浏览器 AI 模型输出摘要的第一步是创建一个 Summarizer 对象实例。这通过调用 Summarizer.create() 静态方法完成,该方法接受一个选项对象作为参数,用于指定你想要的摘要类型等选项。

js
const summarizer = await Summarizer.create({
  sharedContext:
    "A general summary to help a user decide if the text is worth reading",
  type: "tldr",
  length: "short",
  format: "markdown",
  expectedInputLanguages: ["en-US"],
  outputLanguage: "en-US",
});

sharedContext 选项提供了一个字符串,可以帮助 AI 模型为文本的使用场景生成更合适的摘要,而 type 则指定你想要的摘要类型,例如要点列表或“tl;dr”摘要。

我们还指定了所需的 length(长度)、输出 format(格式)、expectedInputLanguages(预期输入语言)和所需的 outputLanguage(输出语言)。如果未指定输入和输出语言,则会自动检测输入文本的语言,输出语言将与输入语言匹配。

如果浏览器的 AI 模型不支持指定的输入或输出语言,则会抛出错误。

注意: 有关可用选项的完整列表,请参阅 create() 参考页面。

检查配置支持情况

在创建 Summarizer 之前,可以使用 Summarizer.availability() 静态方法检查当前浏览器是否支持你所需的配置。例如:

js
const availability = await Summarizer.availability({
  type: "tldr",
  length: "short",
  format: "markdown",
  expectedInputLanguages: ["en-US"],
  outputLanguage: "en-US",
});

此方法返回一个枚举值,指示是否支持指定的一组选项,或者将支持。

  • downloadable 表示浏览器支持请求的选项,但需要先下载 AI 模型或该模型的某些微调数据。
  • downloading 表示浏览器支持请求的选项,但需要完成正在进行的下载才能继续。
  • available 表示浏览器支持给定的配置,无需任何新下载。
  • unavailable 表示浏览器不支持给定的配置。

如果需要下载,一旦使用 create() 方法创建了 Summarizer 实例,浏览器就会自动开始下载。你可以通过“监控下载进度”部分自动跟踪下载进度。

生成摘要

确定你所需的配置有效并创建了 Summarizer 实例后,可以通过在该实例上调用 Summarizer.summarize() 实例方法来生成摘要,并将要摘要的文本作为参数传递。

js
const summary = await summarizer.summarize(myTextString);
console.log(summary);

它还可以选择性地接受一个选项对象作为第二个参数,该对象可以接受特定于此摘要的 context(上下文)字符串,以及一个 abort signal(中止信号),允许摘要请求被中止(请参阅下一节)。

summarize() 方法有一个流式版本——Summarizer.summarizeStreaming()——它允许你将摘要作为 ReadableStream 返回。

js
const stream = summarizer.summarizeStreaming(myTextString);
let summary = "";

for await (const chunk of stream) {
  summary += chunk;
}

console.log("Stream complete");
summaryOutput.textContent = summary;

创建 Summarizer 实例后,可以使用 Summarizer.destroy() 实例方法将其移除。如果不再使用 Summarizer 对象,销毁它们是有意义的,因为它们在其处理过程中会占用大量资源。

取消摘要操作

你可以使用 AbortController 取消挂起的 create()summarize()summarizeStreaming() 操作。

js
const controller = new AbortController();
const summary = await summarizer.summarize(myTextString, {
  signal: controller.signal,
});

// ...

controller.abort();

监控下载进度

如果某个 summarizer 的 AI 模型正在下载(availability() 返回 downloadabledownloading),提供反馈给用户,告知他们需要等待多久才能完成操作会很有帮助。

Summarizer.create() 方法可以接受一个 monitor 属性,其值是一个回调函数,该函数接受一个 CreateMonitor 实例作为参数。CreateMonitor 有一个可用的 downloadprogress 事件,当 AI 模型下载进度取得进展时会触发。你可以使用此事件来显示加载进度数据。

js
const summarizer = await Summarizer.create({
  sharedContext:
    "A general summary to help a user decide if the text is worth reading",
  type: "tldr",
  length: "short",
  monitor(monitor) {
    monitor.addEventListener("downloadprogress", (e) => {
      console.log(`Downloaded ${Math.floor(e.loaded * 100)}%`);
    });
  },
});

使用配额

一些实现有一个输入配额,用于规定网站在给定时间段内可以请求的操作数量。总配额可以通过 Summarizer.inputQuota 属性访问,而特定摘要操作的配额使用情况可以通过 Summarizer.measureInputUsage() 方法返回。

例如,在下面的代码片段中,我们使用 create() 创建了一个新的 Summarizer 实例,然后通过 inputQuota 返回总输入配额,并通过 measureInputUsage() 返回摘要特定文本字符串的输入配额使用情况。

然后,我们测试该字符串的单个输入使用量是否大于总可用配额。如果是,则抛出适当的错误;否则,我们使用 summarize() 开始摘要该字符串。

js
const summarizer = await Summarizer.create({
  sharedContext:
    "A general summary to help a user decide if the text is worth reading",
  type: "tldr",
  length: "short",
});

const totalInputQuota = summarizer.inputQuota;
const inputUsage = await summarizer.measureInputUsage(myTextString);

if (inputUsage > totalInputQuota) {
  throw new Error("Boo, insufficient quota to generate a summary.");
} else {
  console.log("Yay, quota available to generate a summary.");
  const summary = await summarizer.summarize(myTextString);
  // ...
}

如果你尝试运行一个超出可用配额的摘要操作,将抛出一个 QuotaExceededError DOMException

完整示例

让我们看一个完整的示例,演示 Summarizer API 的实际应用。

HTML

在我们的标记语言中,我们首先定义了一个输入 <form>,允许用户设置要摘要的文本和配置选项。这包括一个用于输入要摘要文本的 <textarea>,一个用于显示用户设置文本字符数的 <output> 元素,以及两个 <select> 元素,用于选择 summarizer 的 type(类型)和 length(长度)。

html
<h2>Input</h2>

<form>
  <div>
    <label for="summary-text">Enter text to summarize:</label>
    <textarea id="summary-text" name="summaryText" rows="6"></textarea>
    <output class="input-count">Input character count: </output>
  </div>
  <div>
    <label for="summary-type">Summary type:</label>
    <select id="summary-type" name="summaryType">
      <option value="headline">Headline</option>
      <option value="key-points">Key points</option>
      <option value="teaser">Teaser</option>
      <option value="tldr" selected>tldr</option>
    </select>
  </div>
  <div>
    <label for="summary-length">Summary length:</label>
    <select id="summary-length" name="summaryLength">
      <option value="short" selected>Short</option>
      <option value="medium">Medium</option>
      <option value="long">Long</option>
    </select>
  </div>
  <button type="submit">Submit</button>
</form>

我们标记语言的后半部分包括一个 <p> 元素来显示生成的摘要,以及第二个 <output> 元素来显示摘要的字符数。

html
<h2>Summary output</h2>

<p class="summary-output"></p>
<output class="output-count">Output summary character count: 0</output>

请注意,我们不会展示此示例的 CSS,因为其中没有内容与理解 Summarizer API 相关。

JavaScript

在我们的脚本中,我们首先获取对 <form><textarea>、提交 <button>、摘要输出 <p> 以及两个 <output> 元素的引用。

js
const form = document.querySelector("form");
const textarea = document.querySelector("textarea");
const submitBtn = document.querySelector("button");

const summaryOutput = document.querySelector(".summary-output");
const inputCount = document.querySelector(".input-count");
const outputCount = document.querySelector(".output-count");

接下来,我们使用 EventTarget.addEventListener() 方法监听两组事件:

  • <form> 元素上的 submit 事件;当单击提交按钮时,会调用 handleSubmission() 函数。
  • <textarea> 元素上的 input 事件;当更改当前的 <textarea> 值时,会调用 updateInputCount() 函数。
js
form.addEventListener("submit", handleSubmission);
textarea.addEventListener("input", updateInputCount);

接下来定义的 updateInputCount() 函数将第一个 <output> 元素的 textContent 设置为一个包含 <textarea> 值长度的字符串。我们还定义了一个相应的 displayOutputCount() 函数,它对第二个 <output> 元素执行相同的操作。在 handleSubmission() 函数的接近尾声时,摘要返回后,才会调用此函数。

js
function updateInputCount() {
  inputCount.textContent = `Input character count: ${textarea.value.length}`;
}

function displayOutputCount() {
  outputCount.textContent = `Output summary character count: ${summaryOutput.textContent.length}`;
}

现在我们定义 handleSubmission() 函数本身。在阻止默认表单提交后,我们创建了一个新的 FormData 对象实例,其中包含我们所有的 <form> 数据名值对。然后,我们运行一些数据验证测试,检查 <textarea> 的内容(summaryText)是否为空或太短而浪费资源,如果文本不满足要求,则在摘要输出 <p> 中打印错误消息。

如果文本通过了测试,我们就使用 create() 方法创建一个新的 Summarizer 对象,传递一个 sharedContext 字符串以及在表单中选择的 typesummaryType)和 lengthsummaryLength)值。然后,我们将输出摘要的 <p><output> 设置为“pending”(待处理)消息,并在运行 summarize() 操作时禁用 <submit> 按钮。

成功返回 summary 值后,我们将其设置为输出摘要 <p> 元素的 textContent,调用 displayOutputCount() 在第二个 <output> 元素中显示输出字符数,并重新启用提交 <button>

js
async function handleSubmission(e) {
  e.preventDefault();
  const formData = new FormData(form);

  if (formData.get("summaryText") === "") {
    summaryOutput.innerHTML = `<span class="error">No text entered to summarize!</span>`;
    return;
  } else if (formData.get("summaryText").length < 100) {
    summaryOutput.innerHTML = `<span class="error">I'm not trying to summarize something that short!</span>`;
    return;
  }
  summaryOutput.innerHTML = "";

  try {
    const summarizer = await Summarizer.create({
      sharedContext:
        "A general summary to help a user decide if the text is worth reading",
      type: formData.get("summaryType"),
      length: formData.get("summaryLength"),
    });

    summaryOutput.textContent = "...generating summary...";
    outputCount.textContent = "Output summary character count: -";
    submitBtn.disabled = true;

    const summary = await summarizer.summarize(formData.get("summaryText"));

    summaryOutput.textContent = summary;
    displayOutputCount();
    submitBtn.disabled = false;
  } catch (e) {
    summaryOutput.innerHTML = `<span class="error">${e}</span>`;
  }
}

最后一步是在脚本的顶层调用 updateInputCount() 函数,以确保包含输入计数的第一个 <output> 元素在页面加载时始终显示正确的值。

js
updateInputCount();

结果

渲染后的示例如下所示:

尝试在“Input”(输入)<textarea> 中输入一段文本,然后按提交按钮生成 AI 生成的摘要。你最喜欢的维基百科页面的文本是理想的选择。尝试使用不同的选项组合生成多个摘要,看看它们如何影响输出。

另见