
浏览器新提案 navigator.models:让网页在本地请求并运行指定开源模型
来源标题:A browser API for choosing and running local AI models
浏览器新提案 navigator.models 允许网页在用户授权后请求并运行指定的开源权重模型,且不做静默降级。该 API 让开发者按模型 ID 指定同一版本,跨浏览器运行,并可在多站点间共享模型权重以减少重复下载。模型在本地推理,浏览器负责用户授权与资源限制,数据可不出设备。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
你的应用所针对的模型。 经用户许可。在浏览器中。 navigator.models 是一项拟议的浏览器 API,面向需要在设备端运行特定开放权重模型的 Web 应用。 围绕某个模型构建的工作流未必能适用于另一个模型。应用应当能够请求其所构建和测试时所针对的模型,无论用户使用哪种浏览器。 Web Models 让这一请求变得明确。页面指定一个模型 ID。浏览器运行该模型,或者告知页面无法运行。不存在悄悄的降级回退。 扫描收据、转录音频和搜索文档所需的最佳模型可能各不相同。模型 ID 让开发者能够在任何浏览器中请求同一个模型及其发布版本。 有些模型下载需要数 GB 空间,运行也需要大量内存。浏览器可以跨站点共享它们的权重,这样每个站点就不必再下载和存储另一份副本。 应用请求某个模型,浏览器则询问用户。用户可以查看并撤销每个站点和每个模型的访问权限。浏览器还会监控模型的设置、执行和资源限制。 当有人搜索自己的笔记或文档时,不应被迫把它们发送给模型提供商。本地推理可以将这些数据保留在设备上,并且在模型和内容已经就位时离线工作。 navigator.models 上的每一次顶层推理调用都指定一个模型 ID。 你的应用选择模型并调用 API。用户授予访问权限。浏览器负责运行。 01// 请求你的应用所构建和测试时所针对的模型。 02const model = '<model-id>'; 03 04button.addEventListener('click', async () => { 05 if (!('models' in navigator)) return showFallback(); 06 07 try { 08 const { status } = await navigator.models.requestAccess(model); 09 if (status !== 'granted') return showFallback(); 10 11 const result = await navigator.models.generate(model, { 12 messages: [{ role: 'user', content: 'Explain the web in one sentence.' }], 13 }); 14 15 output.textContent = result.data ?? ''; 16 } catch { 17 showFallback(); 18 } 19}); [ ]你的页面请求它。用户批准它。浏览器运行它。 这只是一段示意性的 JavaScript。<model-id> 是占位符。你的应用负责提供 button、output 和 showFallback()。首次访问请求必须由用户手势触发。本页面不会运行任何模型。 发送提示词,获得完整响应。 将文本转换为向量,用于搜索和相似度计算。 对于一次性任务,直接调用即可。如果你会再次使用该模型,就把它保留在会话中。对话历史仍然由你的应用管理。 一旦授予访问权限,浏览器会告诉你该模型支持哪些能力。视觉、音频、工具、结构化输出、推理和 embeddings 都取决于你所选择的模型。 每个 ID 都绑定固定的权重、tokenizer 以及默认的输入和生成设置。发布版本发生变化就会获得新的 ID,因此开发者可以自行决定何时迁移到新版本。共享目录、模型退役和跨浏览器兼容性测试仍然是悬而未决的问题。 打开示例查看代码。模型 ID 均为占位符;请将它们替换为你的应用实际使用的具体模型 ID。 01// 占位符:替换为你所选 LLM 的 ID。 02const model = '<llm-model-id>'; 03 04button.addEventListener('click', async () => { 05 if (!('models' in navigator)) return showFallback(); 06 07 try { 08 const { status } = await navigator.models.requestAccess(model); 09 if (status !== 'granted') return showFallback(); 10 11 const result = await navigator.models.generate(model, { 12 messages: [ 13 { role: 'system', content: 'Summarize the key points in three bullets.' }, 14 { role: 'user', content: documentInput.value }, 15 ], 16 }); 17 18 if (result.finishReason !== 'stop') return showFallback(); 19 output.textContent = result.data ?? ''; 20 } catch { 21 showFallback(); 22 } 23}); [ ]请将输入控制在模型的上下文窗口之内。较长的文档应拆分为多个部分。 这里,documentInput 是一个包含文章或文档的文本区域。button、output 和 showFallback() 由你的应用提供。这些示例仅作示意,不会在此页面上执行。 01// 占位符:一个支持视觉并具备结构化输出能力的模型。 02const model = '<vision-model-id>'; 03 04button.addEventListener('click', async () => { 05 if (!('models' in navigator)) return showFallback(); 06 const image = imageInput.files?.[0]; 07 if (!image) return showFallback(); 08 09 try { 10 const { status } = await navigator.models.requestAccess(model); 11 if (status !== 'granted') return showFallback(); 12 13 const result = await navigator.models.generate(model, { 14 messages: [{ 15 role: 'user', 16 content: [ 17 { type: 'text', text: 'Extract the merchant, date, and total.' }, 18 { type: 'image', image }, 19 ], 20 }], 21 output: { 22 schema: { 23 type: 'object', 24 properties: { 25 merchant: { type: 'string' }, 26 date: { type: 'string' }, 27 total: { type: 'number' }, 28 }, 29 required: ['merchant', 'date', 'total'], 30 }, 31 }, 32 }); 33 34 if (result.finishReason !== 'stop') return showFallback(); 35 output.textContent = JSON.stringify(result.data, null, 2); 36 } catch { 37 showFallback(); 38 } 39}); [ ]已完成的响应会符合 schema。你的应用仍应让人能够复核提取出的值。 这里,imageInput 是一个文件输入框,用于选择受支持的收据图片(不超过 10 MiB)。button、output 和 showFallback() 由你的应用提供。这些示例仅作示意,不会在此页面上执行。 01// 占位符:替换为你所选 embedding 模型的 ID。 02const model = '<embedding-model-id>'; 03 04button.addEventListener('click', async () => { 05 if (!('models' in navigator)) return showFallback(); 06 07 try {
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。