Electron LanguageModelUtility 完全解析:在 Utility 进程中构建本地 AI 语言模型能力
【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron
Electron 通过实验性的 Prompt API 让渲染进程可以调用本地大语言模型(LLM),而LanguageModelUtility是这条链路上运行在 Utility 进程中的核心类。本文基于当前仓库中 docs/api/language-model-utility.md 的完整 API 定义展开,逐一讲清构造器、静态方法、实例属性与实例方法的语义,结合 结构类型文档 与 lib/utility/api/language-model-utility.ts 的实现源码,说明它如何与 localAIHandler 协作,帮助你在 Electron 应用中落地本地 AI 推理能力。
一、类定位:Utility 进程中的本地 AI 语言模型实现
LanguageModelUtility的官方定位是 “Implement local AI language models”(实现本地 AI 语言模型),其所属进程为Utility 进程(见 glossary 中对 Utility 进程的定义)。
这个设计符合 Electron 的多进程模型:重计算、可能崩溃的模型推理被隔离在主进程与渲染进程之外的独立 Utility 进程中运行,主进程通过session注册本地 AI 处理器,渲染进程通过 Prompt API 发起请求,三者通过 IPC/Mojo 边界解耦。从源码结构看,该类的 JavaScript 侧实现由以下文件组织:
- lib/utility/api/language-model-utility.ts:
LanguageModelUtility类的 TS 实现; - lib/utility/api/module-list.ts:将
'LanguageModelUtility'与'localAIHandler'两个模块注册进 Utility 进程的模块清单; - lib/utility/api/local-ai-handler.ts:
localAIHandler模块,直接包装 C++ 侧绑定electron_utility_local_ai_handler并挂上EventEmitter原型; - lib/utility/init.ts:Utility 进程初始化时通过
v8Util.setHiddenValue注册isLanguageModel/isLanguageModelClass判断函数,供框架识别传递到各进程中的LanguageModelUtility实例。
二、构造器:new LanguageModelUtility(initialState)
构造器接收一个initialState对象,包含两个数字字段:
| 字段 | 类型 | 说明 |
|---|---|---|
contextUsage | number | 当前上下文窗口中已占用的 token 数 |
contextWindow | number | 上下文窗口总容量(token 数) |
对应源码(language-model-utility.ts#L10-L13)中,构造器只是把这两个值直接赋给实例属性:
interface LanguageModelConstructorValues { contextUsage: number; contextWindow: number; } export default class LanguageModelUtility implements Electron.LanguageModelUtility { contextUsage: number; contextWindow: number; constructor(values: LanguageModelConstructorValues) { this.contextUsage = values.contextUsage; this.contextWindow = values.contextWindow; } // ... }文档中有一条明确的注意事项(NOTE):不要在类之外直接调用该构造器,因为这样创建的实例不会与localAIHandler正确建立连接。创建实例的正确方式是下一节的静态方法LanguageModelUtility.create()。
三、静态方法
3.1LanguageModelUtility.create(options)(实验性)
options:LanguageModelCreateOptions- 返回值:
Promise<LanguageModelUtility>
使用提供的options创建一个新的LanguageModelUtility实例。
LanguageModelCreateOptions结构继承自LanguageModelCreateCoreOptions,完整字段如下(结合 language-model-create-options.md 与 language-model-create-core-options.md):
| 字段 | 类型 | 是否可选 | 说明 |
|---|---|---|---|
signal | AbortSignal | 否 | 取消信号,用于中止创建过程 |
initialPrompts | LanguageModelMessage[] | 是 | 创建时注入的初始提示消息列表 |
expectedInputs | LanguageModelExpected[] | 是 | 声明模型预期接收的输入模态 |
expectedOutputs | LanguageModelExpected[] | 是 | 声明模型预期产生的输出模态 |
其中LanguageModelExpected的字段为:
type(string):text/image/audio三者之一;languages(string[],可选):语言列表。
从当前仓库的 JS 侧实现看,create()目前是一个占位实现,返回contextUsage: 0, contextWindow: 0的空上下文实例(language-model-utility.ts#L15-L20):
static async create(): Promise<LanguageModelUtility> { return new LanguageModelUtility({ contextUsage: 0, contextWindow: 0 }); }这说明 JS 层只是整个调用链的端点之一,真正的模型装载发生在 C++ 侧的 Prompt API 基础设施中,JavaScript 实现负责承载上下文状态并暴露 API 形状。
3.2LanguageModelUtility.availability([options])(实验性)
options(可选):LanguageModelCreateCoreOptions- 返回值:
Promise<string>
探测语言模型的可用性,返回以下四个字符串之一:
| 返回值 | 含义 |
|---|---|
available | 模型已就绪,可立即使用 |
downloadable | 模型尚未下载,可以下载 |
downloading | 模型正在下载中 |
unavailable | 当前环境下模型不可用 |
该状态机对应用端做 UI 引导非常关键:在展示“开始对话”之前,先调用availability()判断是否需要先触发模型下载流程。当前 JS 侧实现同样为占位逻辑,固定返回'available'(language-model-utility.ts#L22-L24),真实判定逻辑在底层实现中完成。
四、实例属性
languageModelUtility.contextUsage(实验性)
number,表示当前上下文窗口中已使用的 token 数量。
languageModelUtility.contextWindow(实验性)
number,表示上下文窗口总大小(token 数)。
这两个属性与构造器参数一一对应,是衡量“还能再塞多少上下文”的直接依据:可用余量约为contextWindow - contextUsage。
五、实例方法
5.1languageModelUtility.prompt(input, options)(实验性)
input:LanguageModelMessage[]options:LanguageModelPromptOptions- 返回值:
Promise<string> | Promise<import('stream/web').ReadableStream<string>>
向模型发起提示并获取响应。返回值是Promise<string>或Promise<ReadableStream<string>>的联合类型,即调用方既可以拿到完整的字符串响应,也可以消费一个文本流(适合逐字渲染的对话界面)。
LanguageModelPromptOptions的字段(见 language-model-prompt-options.md):
| 字段 | 类型 | 是否可选 | 说明 |
|---|---|---|---|
responseConstraint | Object | RegExp | 是 | JSON Schema 对象或正则表达式,用于约束响应必须匹配指定结构(结构化输出) |
signal | AbortSignal | 否 | 取消信号 |
LanguageModelMessage的结构(见 language-model-message.md):
| 字段 | 类型 | 是否可选 | 说明 |
|---|---|---|---|
role | string | 否 | 取值system/user/assistant |
content | LanguageModelMessageContent[] | 否 | 消息内容数组 |
prefix | boolean | 是 | 标记是否为前缀消息 |
LanguageModelMessageContent则支持多模态(见 language-model-message-content.md):
| 字段 | 类型 | 说明 |
|---|---|---|
type | string | 取值text/image/audio |
value | ArrayBuffer | string | 文本内容用 string,二进制内容用 ArrayBuffer |
一个符合上述结构的调用示例(参数形状以文档为准):
const response = await lm.prompt( [ { role: 'system', content: [{ type: 'text', value: 'You are a helpful assistant.' }] }, { role: 'user', content: [{ type: 'text', value: '总结这段话的核心观点。' }] } ], { signal: new AbortController().signal, responseConstraint: { type: 'object', properties: { summary: { type: 'string' } } } } );5.2languageModelUtility.append(input, options)(实验性)
input:LanguageModelMessage[]options:LanguageModelAppendOptions(仅含必选字段signal: AbortSignal)- 返回值:
Promise<undefined>
向模型追加消息但不触发响应生成,用于多轮对话中维护上下文(例如把上一轮的 assistant 回复回填进会话历史)。对应 JS 实现为空的 async 方法(language-model-utility.ts#L30)。
5.3languageModelUtility.measureContextUsage(input, options)(实验性)
input:LanguageModelMessage[]options:LanguageModelPromptOptions- 返回值:
Promise<number>
测量给定输入会占用多少 token,但不实际发起推理。这是实现“输入框剩余 token 提示”“上下文超限预警”的配套 API:先measureContextUsage,再决定是否prompt。JS 侧占位实现返回0(language-model-utility.ts#L32-L34)。
5.4languageModelUtility.clone(options)(实验性)
options:LanguageModelCloneOptions(仅含必选字段signal: AbortSignal)- 返回值:
Promise<LanguageModelUtility>
克隆一个LanguageModelUtility,克隆出的实例保留原有的上下文与初始提示。从源码实现看,克隆即把当前的contextUsage与contextWindow复制到新实例(language-model-utility.ts#L36-L41):
async clone() { return new LanguageModelUtility({ contextUsage: this.contextUsage, contextWindow: this.contextWindow }); }典型场景是从一个已预热上下文的会话派生出独立分支(如并行探索多个问题),分支之间互不污染。
5.5languageModelUtility.destroy()(实验性)
销毁模型,同时中止所有正在进行中的执行。JS 侧实现为空操作(language-model-utility.ts#L43),资源释放由底层完成。调用方应在会话结束、窗口关闭时显式调用,避免遗留模型状态占用 Utility 进程资源。
六、与 localAIHandler 的协作关系
LanguageModelUtility不是孤立使用的。文档对构造器的 NOTE 提示它必须与localAIHandler正确连接,而 docs/api/local-ai-handler.md 说明了这条链路的另一半:
- 主进程侧:通过
ses.registerLocalAIHandler(handler)把一个脚本注册到指定 session,该脚本即运行在 Utility 进程中; - Utility 进程侧:脚本调用
localAIHandler.setPromptAPIHandler(promptAPIHandler)注册 Prompt API 绑定处理器,处理器签名为Function<typeof LanguageModelUtility | null>,接收details对象,包含:webContentsId:发起 Prompt API 调用的 WebContents 唯一 id;securityOrigin:调用页面的 origin;frameToken:发起调用 frame 的 frame token;renderProcessId:承载该 frame 的渲染进程 id。
- 请求路由:每对
webContentsId与securityOrigin触发一次绑定请求。处理器返回null即拒绝该渲染进程创建新的 Prompt API 会话;若要使既有 Prompt API 会话失效,则用ses.registerLocalAIHandler(null)清除 handler。 - 排队语义:若渲染进程在
setPromptAPIHandler()调用之前就调用了 Prompt API,请求会被排队,handler 设置后统一冲刷;排队过多时会丢弃最旧的待处理请求并拒绝渲染进程中的 pending promise。文档因此建议尽早调用setPromptAPIHandler()。
从源码结构看,Utility 进程中的localAIHandler模块直接是 C++ 绑定electron_utility_local_ai_handler的 JS 包装(lib/utility/api/local-ai-handler.ts),而 lib/utility/init.ts 中注册的isLanguageModel/isLanguageModelClass隐藏值,则是框架在跨进程传递LanguageModelUtility实例时进行类型识别的机制。仓库中的功能测试 spec/api-local-ai-handler-spec.ts 也表明,localAIHandler模块的行为受 Prompt API 特性开关控制(features.isPromptAPIEnabled()为真时才执行)。
七、API 使用小结
将文档定义的 API 汇总为速查表:
| 成员 | 签名 | 返回值 | 用途 |
|---|---|---|---|
new LanguageModelUtility(initialState) | initialState: { contextUsage, contextWindow } | 实例 | 仅限框架内部使用,勿直接构造 |
LanguageModelUtility.create(options) | options: LanguageModelCreateOptions | Promise<LanguageModelUtility> | 创建实例(推荐入口) |
LanguageModelUtility.availability([options]) | options?: LanguageModelCreateCoreOptions | Promise<string> | 返回available/downloadable/downloading/unavailable |
instance.contextUsage | — | number | 当前已用 token 数 |
instance.contextWindow | — | number | 上下文窗口容量(token) |
instance.prompt(input, options) | 消息数组 + Prompt 选项 | Promise<string> \| Promise<ReadableStream<string>> | 发起推理,支持结构化响应约束 |
instance.append(input, options) | 消息数组 + signal | Promise<undefined> | 仅追加上下文,不触发响应 |
instance.measureContextUsage(input, options) | 消息数组 + Prompt 选项 | Promise<number> | 预估算力/上下文占用 |
instance.clone(options) | LanguageModelCloneOptions | Promise<LanguageModelUtility> | 保留上下文与初始提示地克隆 |
instance.destroy() | — | 无 | 销毁模型并中止进行中的执行 |
需要说明的是:该 API 族整体标注为Experimental,当前仓库的 JavaScript 实现(lib/utility/api/language-model-utility.ts)对create、availability、prompt、measureContextUsage等均为形状正确的占位实现,真实的模型装载与推理由 C++ 侧 Prompt API 基础设施承担,且相关功能受 Prompt API 特性开关控制。在应用层使用这些 API 时,应以availability()的结果驱动 UI 流程,用contextUsage/contextWindow监控上下文水位,并在会话结束时调用destroy()释放资源。
【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考