前言
之前一直想把視頻AI總結的功能集成到Video Roll中,但是由於調用各廠商的AI接口基本上無法實現完全免費,即使免費額度也是有限制的,所以想免費提供給用户使用就捉襟見肘。另一種方式就是讓用户自己填各個廠商的key來做一箇中間件而已。於是覺得沒太大必要,一直擱置了這個功能。但是最近Chrome宣佈138版本將正式內置AI接口,比如Summarizer, Translater等。於是我趕緊下載Chrome 138 beta版本來進行測試,經測試效果還是非常不錯,它的模型是基於Gemini Nano,於是我將其立馬集成到了新版的Video Roll中,待Chrome 138正式發佈,即可上線。
注意:Chrome 138 beta第一次需要在chrome://flags來啓用Summarizer API
檢測API和下載
按照官方文檔的方式進行調用即可,這個過程中會有兩個比較重要的步驟,第一是檢測Summarizer API是否可用,如果可用,第一次可能需要下載模型,下載完成後才能調用後續的API。
關於API是否可用,官方給了基本配置,但目前經我的測試,實際使用很多不確定因素。比如頭天開啓後可以正常使用,第二天重啓電腦後就提示無法使用了,也不知道是電量不足還是其他原因。
- Operating system: Windows 10 or 11; macOS 13+ (Ventura and onwards); or Linux. Chrome for Android, iOS, and ChromeOS are not yet supported by our APIs backed by Gemini Nano.
- Storage: At least 22 GB on the volume that contains your Chrome profile.
- GPU: Strictly more than 4 GB of VRAM.
- Network: Unlimited data or an unmetered connection.
public async downloadModel(): Promise<void> {
return new Promise(async (resolve, reject) => {
if (this.isInitialized) {
resolve();
}
try {
if (!("Summarizer" in self)) {
console.log("Summarizer API 不可用");
reject(new Error("Summarizer API not loaded"));
}
const availability = await window.Summarizer.availability();
console.log("--availability", availability);
if (availability === "unavailable") {
console.log("Summarizer API 不可用");
reject(new Error("Summarizer API unavailable"));
return;
}
const options: SummaryOptions = {
sharedContext: "This is a YouTube video transcript summary.",
type: "key-points",
format: "markdown",
length: "long",
};
if (availability === "available") {
// The Summarizer API can be used immediately .
this.summarizer = await window.Summarizer.create(options);
} else {
// The Summarizer API can be used after the model is downloaded.
this.summarizer = await window.Summarizer.create(options);
this.summarizer.addEventListener("downloadprogress", (e) => {
console.log(`Downloaded ${e.loaded * 100}%`);
const progress = e.total
? (e.loaded / e.total) * 100
: e.loaded * 100;
console.log(`Summarizer 模型下載進度: ${progress.toFixed(2)}%`);
});
await this.summarizer.ready;
}
this.isInitialized = true;
console.log("Summarizer 服務初始化成功");
resolve();
} catch (error) {
console.log("Summarizer 服務初始化失敗:", error);
this.isInitialized = false;
reject(
error instanceof Error
? error
: new Error("Failed to initialize summarizer")
);
}
});
}
調用總結
總結可以使用流式和批量兩種方式,為了更好的用户體驗,一般使用流式輸出
/**
* Generate summary from text
* @param {string} text - The text to summarize
* @param {SummaryOptions} [options] - Summary options
* @returns {Promise<string | null>} The generated summary or null if generation fails
*/
public async generateSummary(
text: string,
tabId: number,
options: Partial<SummaryOptions> = {}
): Promise<string | null> {
if (!text?.trim()) {
logDebug("文本為空,無法生成摘要");
return null;
}
try {
if (!this.isInitialized || !this.summarizer) {
await this.initialize();
}
if (!this.summarizer) {
throw new Error("Summarizer not initialized");
}
const summaryOptions: SummaryOptions = {
context:
options.context || "This is a YouTube video transcript section.",
length: options.length || "long",
format: "markdown",
type: options.type || "key-points",
};
const result = await this.summarizer.summarizeStreaming(text, {
context:
"這是一個帶時間線的Youtube視頻字幕摘要, 請用中文生成摘要,並在摘要標註時間段",
});
// 創建解碼器
const decoder = new TextDecoder();
let summary = "";
// 處理流式響應
const reader = result.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
summary += value;
// 觸發進度更新事件
sendRuntimeMessage(tabId, {
type: ActionType.SUMMARIZING,
tabId,
payload: {
text: summary,
complete: false,
},
});
}
// 觸發進度更新事件
sendRuntimeMessage(tabId, {
type: ActionType.SUMMARIZE_DONE,
tabId,
payload: {
text: summary,
complete: true,
},
});
return summary;
} catch (error) {
logDebug("生成摘要失敗:", error);
return null;
}
}
限制
目前測試了輸入大概超過20000個字符串就會提示too large,模型無法輸出的情況。這也可能是由於客户端性能和模型的限制。
總結
總的來説客户端內置AI是一個巨大的進步,這讓前端工程師可以直接在客户端使用AI,也能通過完成一些簡單的任務。