硬核经验:AI 平台化的一些探索
失踪人口回归...,最近忙着搭建 AI 平台,已经一周多没有更新过文章了。现在开发取得了阶段性小成,那就简单写一点思考和开发心得吧。
平台化
AI 百花齐放(Bard[1]、ChatGPT[2]、Claude[3]、Poe[4] 等等),大家的选择也越来越多了(浏览器标签开得多了,傻傻分不清),那有没有一个专注于 AI 的平台,在聚合它们的同时再扩展一些额外的功能呢?据我所知,没有。
这就是我想做的事情,聚合只是第一步,好用,庞大的插件生态则是更大的目标(幻想中...)。设想一下,在一个应用里可以完成各种操作,比如和 AI 对话,浏览日常网站,记笔记等岂不是很嗨皮的一件事情吗?
技术选型
在这之前,我曾是 Tauri[5] 的狂热信徒,但因为 ChatGPT 桌面应用[6](开源揭秘:35k+ Stars ChatGPT 桌面应用)的前车之鉴(大约有一半以上的 issues 都来自于系统 WebView 的兼容性),让我开始犹豫了。虽然我挺喜欢 Tauri (Tauri 系列)和 Rust[7](Rust 在前端系列),但想做更多的事情,稳定可靠是前提。所以这次选型,我决定尝试 Electron[8]。在我看来技术没有好坏,只有在适合的位置才能发挥它最大的作用。
📌 系统 WebView现在我到处吐槽系统 WebView 是大坑。我其实想表达的不是类似 Tauri 这种打包方案很差,只是 WebView 和系统强绑定,极不稳定。用户量大了(操作系统五花八门,系统版本数不胜数),容易出事…
系统 WebView,是由操作系统提供的用于在应用程序中展示网页内容的组件。对于桌面操作系统来说,比如 Windows、macOS 或者 Linux,它们的系统 WebView 可能是基于各自默认浏览器引擎(如 WebView2[9]、Safari[10] 或 WebKitGTK[11])或者是其它通用的 Web 引擎(如 Blink[12] 或 WebKit[13])。
优点:
性能:系统 WebView 一般直接使用操作系统底层 API 进行渲染,性能较高。
兼容性:作为系统的一部分,它对设备硬件和操作系统的特性有很好的支持和兼容性。
系统集成:它们通常与操作系统的其他部分紧密集成,可以更好地支持一些系统级的功能。
维护:系统 WebView 由操作系统维护,这意味着它们可以从操作系统的更新中获益,比如安全补丁或性能改进。
缺点:
版本更新:系统 WebView 的更新通常与操作系统的更新同步,如果操作系统没有进行更新,那么 WebView 中可能存在的问题就不能得到解决。
API 一致性:不同操作系统的系统 WebView 的 API 可能不同,这可能会导致开发者在开发跨平台应用时面临一些挑战。
特性支持:不同的系统 WebView 对 Web 技术的支持程度也可能不同,有的系统 WebView 可能没有支持最新的 Web 标准。
调试:相比独立的浏览器,系统 WebView 可能在调试工具和设施上提供的支持较少。
对于开发者来说,选择使用系统 WebView 还是其他方式(如 Electron 或者自定义 WebView)主要取决于项目的具体需求。
📌 平台 vs 工具我对平台和工具的理解:
🚀 平台:模块多,生态,Electron 适合
🛠️ 工具:模块少,专一,Tauri 适合
举个例子:VS Code[14] 就是平台,因为它有完善的插件体系来拓展平台功能。要支撑起生态,平台基座的稳定性就显得很重要了,应用安装包体积在生态面前我认为可以忽略,随着模块增多,这个缺点会缩小。
🤔 思考关于 Tauri 的一点想法:tauri 上游依赖 wry[15] 目前在疯狂适配各种系统 webview,如果 webview 存在问题它并不能改善或解决。要是 tauri 支持切换 webview 到 chromium[16] 就有趣了。因为相比于安装包体积的减小,稳定性和后端使用 rust 才是最大的吸引力(相比于 nodejs[17])。
稳定性
作为一个有追求的开发者,什么框架,库之类的都想用更快,更好的。追求新技术是家常便饭,但是一味追新有可能会让自己陷入深渊。新技术衍生出的生态会涌入一大批尝鲜的开发者(有能力的贡献生态,没能力的坐等被实现)。开荒期,能用就是第一步(能用不代表好用,有的用就不错了),这时为了满足个人的定制化需求就需要你投入大量的精力在一些基础建设之上(搞了两个月,还在实现基建,本末倒置,忘记了开发应用的初衷)。
😔 踩坑一2023.07.24
解决问题更容易让人兴奋:弃坑 Tauri 后,这几天在研究 Electron,准备用它做一个 AI 平台。为了解决 Electron 源码泄漏问题,我正在考虑将 js 源码转为 v8 字节码(因为不可逆,可以简单理解为对源码加密)。目前已经跑通测试,接下来开源一个 vite 插件(逐渐跑偏了,折腾了几天,发现是个大工程)。
😔 踩坑二2023.07.25
源码泄漏的问题我排查了两天,终于搜到了相关 issues。刚开始一直以为是 electron forge[18](Electron 官方团队维护的一键打包方案) 配置问题,结果是 vite 插件的 bug。
它会导致重大事故:如果正在使用
electron-forge/plugin-vite
构建应用,它会直接泄漏源代码(未 build 的代码)。踩了几天坑后得出结论,electron-forge vite 方案并不成熟。如果要使用,需要重写一大批插件来处理各种问题(有点本末倒置了)。暂时先放弃折腾这些,还是先把重心放在开发应用的任务上。后面有时间再慢慢优化,开发底层插件。
相关 issues:The packaged file volume is too large and contains nodes_ modules electron-forge package (--template=vite)[19]
平台功能
最近除了疯狂思考项目架构(代码如何组织,技术如何选型,打包后的 ASAR 文件源码如何加密等)之外,也在思考应用的交互细节(比如主窗口支持分屏操作,可以一边和 AI 对话,一边记笔记或浏览网页。如何开启/关闭分屏,如何设置 URL 等)。为了实现平台化的目标,这些只是最基础的一小部分功能,要将使用体验和网页完全区别开,做差异化,才是一个有趣的东西。
前期它可能是一个 AI 聚合平台,但后期它可能是写作平台,工具平台,以及其他无限的可能。
源码安全
使用 Electron 打包桌面应用时,安装包中打包一份 asar
文件,对 asar
文件进行解压,可以看到应用源代码。这是所有常规 Electron 应用都会遇到的问题,据我所知,目前社区有两种方案,一种是直接将 js 文件编译成 v8 字节码(如 Bytenode[20] 则将 js 源码编译为 v8 字节码,因为 js 是动态语言,所以一般认为此过程不可逆),另一种是对 js 文件进行压缩混淆加密(如 JavaScript obfuscator[21] 它会将变量方法名替换为随机字符串,增加阅读难度。此过程可逆,并不安全)。
📌 Electron ASARElectron ASAR[22] 是 Electron 使用的一种简单的归档格式,类似于
.tar
。ASAR,即 Atom Shell Archive Format,是一种将多个文件合并到一个文件中的格式,而不会影响在 Electron 应用程序中的执行。Electron 通过 ASAR 实现了以下特性:
更快的应用程序启动时间,因为只需要读取一个文件
更容易的应用程序分发,因为只需要分发一个文件
可以隔离应用程序的源代码,使其不易被用户直接访问或修改
然而,ASAR 也有其限制。例如,一些 Node.js 的 API,如
fs.readFile
,无法直接在 ASAR 归档文件中读取单个文件。为了解决这个问题,Electron 提供了一种方式来从 ASAR 归档中读取文件,通过将文件路径中的.asar
替换为.asar.unpacked
。你可以使用 Electron 自带的
asar
命令行工具创建和解压 ASAR 归档文件,或者在 Node.js 中直接使用asar
包操作 ASAR 归档。
📌 Google V8V8[23] 是 Google 开发的开源 JavaScript 引擎,用于 Google Chrome 和 Chromium 网络浏览器。V8 引擎将 JavaScript 代码编译成更底层的机器代码,然后在计算机上执行。这样做的主要目的是为了提高代码的执行效率。
V8 字节码(V8 Bytecode)是 V8 引擎中的一个概念。在最初的实现中,V8 使用即时(JIT)编译将 JavaScript 代码直接转换为机器代码。然而,这种方法在处理复杂的 JavaScript 代码或在内存受限的设备上可能会出现效率问题。
为了解决这个问题,V8 引擎的新版本引入了字节码解释器 Ignition[24]。在这个模型中,JavaScript 代码首先被编译成一种中间的字节码,然后由 Ignition 解释器执行这些字节码。这种方式可以更有效地利用内存,并允许更高级的代码优化。
字节码通常不会被直接用于执行,但在某些情况下,例如当代码只执行一次或者为了提高启动速度时,它可以被直接执行。字节码还可以被进一步编译为机器代码,以提高频繁执行的代码的性能,这是由 V8 的优化编译器 TurboFan[25] 完成的。
所以说,V8 字节码是 V8 JavaScript 引擎中用于提高性能和内存效率的一个重要组成部分。
V8 字节码主要是为了提高 JavaScript 的执行效率,而不是为了保护源代码。事实上,一旦 JavaScript 代码被浏览器加载,它就可以通过各种开发者工具进行查看和调试,无论它是否被编译成了 V8 字节码。
尽管如此,你可以通过一些方式来增加对源代码的保护,例如:
混淆:这是一个过程,它会修改你的源代码以使其更难阅读和理解。混淆可以改变变量名、函数名、和/或其他代码元素,以使它们失去原本的意义。
压缩:这个过程移除源代码中的所有不必要的字符(如空格、换行符和注释),从而减小文件的大小。这不仅可以提高代码的载入速度,还可以使代码更难阅读。
代码加密:有些工具可以将 JavaScript 代码转换为一种加密形式,该形式需要一个特殊的解密密钥才能执行。这可以在一定程度上阻止未经授权的人员阅读源代码,但也会增加代码执行的复杂性。
但是需要注意的是,以上提到的所有方法都无法提供完全的源代码保护。JavaScript 是一种客户端脚本语言,意味着它必须在用户的浏览器上执行。无论代码被混淆、压缩还是加密,最终都需要转换为可执行的 JavaScript 代码。因此,有足够技术水平的人仍然可以通过一定的工具和技术来“反混淆”、“反压缩”或者“反加密”代码。
V8 字节码在理论上是可以被逆向的,这意味着可以将其转换回源代码形式。然而,逆向工程通常是一个复杂的过程,需要深入的专业知识,并且即使是在理想的情况下,也很难(如果不是不可能的话)完全复原源代码。这是因为编译过程通常会丢失某些源代码的信息,比如注释和某些编程构造可能会被优化掉。
对于 JavaScript 和 V8 引擎来说,有几个因素可以使得逆向工程更加困难:
JavaScript 是一种动态类型语言,这意味着在编译时并不知道变量的确切类型。这在逆向工程过程中可能会导致问题,因为字节码不包含这种类型信息。
V8 引擎使用了许多优化策略,比如内联函数和消除死代码。这些优化可能会改变字节码的结构,使得它不再直接对应源代码。
V8 的字节码设计为解释执行,而不是为了容易地反编译。这意味着字节码的结构可能不直观,且难以手动解析。
所以,虽然理论上可能,但在实践中使用 V8 字节码进行逆向工程是一个极其复杂的任务,往往需要深入的专业知识和大量的时间。
总结
有时候好的交互并不需要自己去苦思冥想,借鉴优秀设计,还可以轻松兼容用户已有使用习惯。
这几天研究了不少东西,边学 Electron,边开发,人都麻了,算是阶段性小成(所有核心代码构建后全部转为 v8 字节码,借鉴 Arc 浏览器用 webview tag[26] 实现分屏,边框准备借鉴 VS Code 状态栏)。接下来又要踩本地脚本代码如何注入了,感觉每天都在踩坑(最近睡觉都在思考如何写代码,人都魔怔了)。
有朋友问过一个问题,为啥不使用 Arc 浏览器[27]呢,它还支持各种浏览器插件?我想说浏览器是很棒,但它缺乏和系统交互的能力,我想做的是一个可以和系统进行交互的 AI 平台化应用。
关注公众号,发送 "electron" 进开发交流群
References
Bard: https://bard.google.com
[2]ChatGPT: https://chat.openai.com
[3]Claude: https://claude.ai
[4]Poe: https://poe.com
[5]Tauri: https://tauri.app
[6]ChatGPT 桌面应用: https://github.com/lencx/ChatGPT
[7]Rust: https://www.rust-lang.org
[8]Electron: https://www.electronjs.org
[9]WebView2: https://developer.microsoft.com/en-us/microsoft-edge/webview2
[10]Safari: https://www.apple.com/safari
[11]WebKitGTK: https://webkitgtk.org
[12]Blink: https://www.chromium.org/blink
[13]WebKit: https://webkit.org
[14]VS Code: https://code.visualstudio.com
[15]wry: https://github.com/tauri-apps/wry
[16]chromium: https://www.chromium.org
[17]nodejs: https://nodejs.org
[18]electron forge: https://www.electronforge.io
[19]The packaged file volume is too large and contains nodes_ modules electron-forge package (--template=vite): https://github.com/electron/forge/issues/3224
[20]Bytenode: https://github.com/bytenode/bytenode
[21]JavaScript obfuscator: https://github.com/javascript-obfuscator/javascript-obfuscator
[22]Electron ASAR: https://github.com/electron/asar
[23]V8: https://v8.dev
[24]Ignition: https://v8.dev/docs/ignition
[25]TurboFan: https://v8.dev/docs/turbofan
[26]webview tag: https://www.electronjs.org/docs/latest/api/webview-tag
[27]Arc 浏览器: https://arc.net