跳转至

本地 AI 处理器

此 API 是实验性的。 它可能会在后续 Electron 版本中更改或被移除。

Electron 通过允许你将调用路由到运行在 utility 进程 中的本地 LLM,支持 Chromium 的实验性 Prompt API (LanguageModel)Web API。Web 内容会像在任意浏览器中一样调用 LanguageModel.create() 和 LanguageModel.prompt(),而你的 Electron 应用决定由哪个模型处理请求。

工作原理

本地 AI 处理器架构涉及三个进程:

  1. 主进程 — 创建 UtilityProcess,然后通过 ses.registerLocalAIHandler() 将其注册为处理指定会话的 Prompt API 调用。
  2. utility 进程 — 运行一个脚本,该脚本调用 localAIHandler.setPromptAPIHandler() 以提供一个 LanguageModelUtility 子类。
  3. 渲染进程 — Web 内容使用来自 Chromium 实验性 Prompt API 的 LanguageModel API (例如 LanguageModel.create()、model.prompt())。

当渲染进程调用 Prompt API 时,Electron 会通过主进程将请求代理到已注册的 utility 进程,该进程会调用你的 LanguageModel 实现,并将结果直接发送回渲染进程。

前提条件

任何将使用 Prompt API 的 BrowserWindow 都必须通过 AIPromptAPI 功能启用 Prompt API Blink 功能。若要启用多模态输入,还需添加 AIPromptAPIMultimodalInput。

const win = new BrowserWindow({
  webPreferences: {
    enableBlinkFeatures: 'AIPromptAPI'
  }
})

快速开始

1. 创建 utility 进程脚本

utility 进程脚本会注册你的 LanguageModelUtility 子类。处理函数会接收一个包含调用者信息的 details 对象,并且必须返回一个继承自 LanguageModelUtility 的类。

ai-handler.js (Utility Process)
const { localAIHandler, LanguageModelUtility } = require('electron/utility')

localAIHandler.setPromptAPIHandler((details) => {
  // details.webContentsId — ID of the calling WebContents
  // details.securityOrigin — origin of the calling page

  return class MyLanguageModel extends LanguageModelUtility {
    static async create (options) {
      // options.signal - AbortSignal to cancel the creation of the model
      // options.initialPrompts - initial prompts to pass to the language model

      return new MyLanguageModel({
        contextUsage: 0,
        contextWindow: 4096
      })
    }

    static async availability () {
      // Return 'available', 'downloadable', 'downloading', or 'unavailable'
      return 'available'
    }

    async prompt (input) {
      // input is a LanguageModelMessage[]
      // Return a string response from your model, or a ReadableStream
      // to return a streaming response.
      return 'This is a response from your local LLM!'
    }

    async clone () {
      return new MyLanguageModel({
        contextUsage: this.contextUsage,
        contextWindow: this.contextWindow
      })
    }

    destroy () {
      // Clean up model resources
    }
  }
})

2. 在主进程中注册处理器

分叉 utility 进程,并将其注册为某个会话的 AI 处理器:

main.js (Main Process)
const { app, BrowserWindow, utilityProcess } = require('electron')

const path = require('node:path')

app.whenReady().then(() => {
  // Fork the utility process running your AI handler script
  const aiHandler = utilityProcess.fork(path.join(__dirname, 'ai-handler.js'))

  // Create a window with the Prompt API enabled
  const win = new BrowserWindow({
    webPreferences: {
      enableBlinkFeatures: 'AIPromptAPI'
    }
  })

  // Connect the AI handler to this session
  win.webContents.session.registerLocalAIHandler(aiHandler)

  win.loadFile('index.html')
})

3. 在渲染进程中使用 Prompt API

你的 Web 内容现在可以使用标准的 LanguageModel API,它是渲染进程中可用的全局对象:

index.html (Renderer Process)
<script>
async function askAI () {
  const model = await LanguageModel.create()
  const response = await model.prompt('What is Electron?')
  document.getElementById('response').textContent = response
}
</script>

<button onclick="askAI()">Ask AI</button>
<p id="response"></p>

实现真实模型

快速开始示例返回一个硬编码字符串。真实实现会与本地模型集成。有关使用 node-llama-cpp 连接 GGUF(GPT-Generated Unified Format)模型的示例,请参阅 electron/llm。

清除处理器

要将 AI 处理器与会话断开连接,请传入 null:

js @ts-type={win:Electron.BrowserWindow} win.webContents.session.registerLocalAIHandler(null)

清除后,使用该会话的渲染进程发出的任何 LanguageModel.create() 调用都将失败。

延迟注册处理器

如果渲染进程在 ses.registerLocalAIHandler() 已被调用之后、但在 utility 进程中 localAIHandler.setPromptAPIHandler() 被调用之前使用 Prompt API,请求不会立即被拒绝。相反,Electron 会将待处理请求排队。一旦调用 setPromptAPIHandler(),所有排队的请求都会被刷新并按正常方式处理。

如果在处理器设置之前到达的请求过多,最旧的待处理请求会被丢弃,并且渲染进程中的待处理 Promise 会被拒绝。请在 utility 进程脚本中尽早调用 setPromptAPIHandler(),以避免出现这种情况。

安全注意事项

传递给处理器的 details 对象包含 webContentsId 和 securityOrigin。请使用这些信息来决定是否处理某个请求,以及何时复用模型实例,何时提供新实例,以便在源之间提供适当的隔离。

延伸阅读

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 el/electron