Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

把 mi-gpt 包装成 Electron 时遇到了问题 #111

Open
lmk123 opened this issue Jun 19, 2024 · 13 comments
Open

把 mi-gpt 包装成 Electron 时遇到了问题 #111

lmk123 opened this issue Jun 19, 2024 · 13 comments

Comments

@lmk123
Copy link
Contributor

lmk123 commented Jun 19, 2024

备注:由于 Electron 跑不通,所以改为开发命令行工具了 😂 https://github.com/lmk123/migpt-cli

我尝试把 mi-gpt 包装成一个 electron 应用,目前在开发阶段(也就是用 electron . 启动)已经成功了,项目地址 https://github.com/lmk123/migpt-gui

但现在我卡在了打包流程上,原因是 Prisma 这个模块在 Electron 里运行时有很多问题。现在能找到的解决方案有以下两个,但是(在我这个从没用过 Prisma 的人眼里 😂)每个都非常复杂且“黑科技”:

我在想有没有可能把数据库操作解耦出来,这样我就可以用别的方式实现数据存储,就像这样:

class MiGPT {
  constructor(config, db) {
    this.db = db || {
      // 增
      create: (data) => {
       require('prisma').create(data)
      },
      // 删
      del: (data) => {
       require('prisma').del(data)
      },
      // 改
      update: ...
      // 查
      query: ...
    }
  }
}
@idootop
Copy link
Owner

idootop commented Jun 19, 2024

我对 prisma 底层的实现也不是很熟,他们是有自己的 shcema 和 query 的语法,在 MiGPT 里的所有 db 操作都是基于 prisma 最上层的语法来使用的,如果能 hook 到他们最底层的 db 操作,应该能达到你说的这个目的。

@idootop
Copy link
Owner

idootop commented Jun 19, 2024

我尝试把 mi-gpt 包装成一个 electron 应用

如果你是直接基于当前的 mi-gpt npm 包,可能会很难做到这一点。

要想有不错的使用体验和完善的功能交互,需要 MIGPT 内部针对 GUI 的场景,对外暴露更多的操作接口。

@lmk123
Copy link
Contributor Author

lmk123 commented Jun 19, 2024

或者还有一个办法,就是反过来把核心代码部分(连接小米云服务、控制音箱发声等)抽出来做成一个单独的包例如 mi-gpt-core,然后现有的 mi-gpt 再基于这个核心包去做数据层相关的功能(长期记忆、短期记忆等)。

然后我就可以用 mi-gpt-core 在上层自己来实现长期记忆、短期记忆等功能

@lmk123
Copy link
Contributor Author

lmk123 commented Jun 19, 2024

如果你是直接基于当前的 mi-gpt npm 包,可能会很难做到这一点。

其实已经算是做到了,除了卡在了最后一步——把 prisma 打包进 electron 这个问题 😂

需要 MIGPT 内部针对 GUI 的场景,对外暴露更多的操作接口。

对的我就是这个意思 😂

@idootop
Copy link
Owner

idootop commented Jun 19, 2024

把核心代码部分抽出来做成一个单独的包例如 mi-gpt-core

这是一个好的提议,实际上从最开始设计 MiGPT 的时候,他的核心控制模块和 AI 对话模块就是分离的,只不过为了方便运行放在了同一个项目内管理。但我现在还没想好应该怎么去处理这部分:把他们重新按照模块分开并定义好互连的接口。

上面你提到的 prisma 问题的核心是:当前 MiGPT 的运行环境一开始就是按照标准 nodejs 环境设计的,所以它里面也掺杂着一些其他的 nodejs 中才有的接口/运行时能力,如果要迁移适配到其他平台(比如 electron、web),我觉得重构更合适一些。

而重构后的框架,应该就是你上面提到的,与平台无关的核心业务逻辑可以抽象到 core 层,其他的诸如网络请求、文件存储、和 db 操作等,由各平台实现。

@idootop
Copy link
Owner

idootop commented Jun 19, 2024

看了一下,目前代价最小的解决方案应该是找到一个跨平台的 db 方案,然后重写之前使用 prisma 进行的所有 db 操作。

或者骚操作一些,看看能不能在 electron 宿主机中开辟出来一个完整的 nodejs 运行时,让 mi-gpt 跑在里面。

@lmk123
Copy link
Contributor Author

lmk123 commented Jun 19, 2024

跨平台的 db 方案

electron 上我没找到啥好用的 db 方案,之前我都是直接往 ~/.my-app 下写 json 文件的方式来做数据存储的

一个完整的 nodejs 运行时,让 mi-gpt 跑在里面。

这个我还真想过。我的想法是把 mi-gpt 带上 node_modules 写入到 ~/.my-app/mi-gpt ,然后在 Electron 里用 child_process.fork('~/.my-app/mi-gpt/index.js') 的方式来跑,理论上应该是可行的,虽然还没试过。

比较麻烦的点在于要处理不同平台 win、darwin x64、darwin arm64、linux 的 prisma 二进制文件,也就是实现类似 prisma 的 npm run postintall 的功能。

不过这样一来似乎跟前面在 Electron 里打包 prisma 的做法差不多

@lmk123
Copy link
Contributor Author

lmk123 commented Jun 19, 2024

另外,按照目前我对这个项目的理解,我想象中的 mi-gpt-core 可能是这样,也许能给你一点灵感 😂

import { MiCtrl, AICtrl } from 'mi-gpt-core'

const aiCtrl = new AICtrl({
    systemTemplate: 'xxx',
    bot: {...},
    master: {...},
    room: {...},
    onAsk: (prompt, onStream) => {
        // 由开发者自行接入各种 AI 服务来获取回答
        return new Promise((resolve, reject) => {
            if (onStream) {
                // 通过 stream 传递回答
                onStream('我是傻妞')
                onStream('我是傻妞,请问有什么')
                onStream('我是傻妞,请问有什么可以帮你?', true) // true 表示结束了
                resolve('我是傻妞,请问有什么可以帮你?')
            } else {
                // 一次性传递整个回答
                resolve('我是傻妞,请问有什么可以帮你?')
            }
        })
    }
})

const miCtrl = new MiCtrl({
    userId: '111111',
    password: '22222',
    did: '小爱音箱Pro',
    ttsCommand: [5, 1],
    wakeUpCommand: [5, 3],
    playingCommand: [3, 1, 1],
    exitKeepAliveAfter: 60,
    keywords: {
        call: ["请", "傻妞"],
        wakeUp: ["召唤傻妞", "打开傻妞"],
        exit: ["退出傻妞", "关闭傻妞"],
    }
})

// 监听到用户说了特定关键词时触发的事件
miCtrl.on('keyword', (payload) => {
    ctrl.mute() // 让小爱的回答静音

    switch (payload.keyword) {
        // 单次调用 AI 响应用户消息
        case 'call':
            Promise.all([
                miCtrl.tts('让我先想想'), // speaker.onAIAsking
                aiCtrl.ask(payload.message, {timeout: 10}),
            ]).then(async ([_, res]) => {
                await miCtrl.tts(res) // 播放 AI 的回答
                await miCtrl.tts('我说完了') // speaker.onAIReplied
            }).catch((err) => {
                miCtrl.tts('出错了,请稍后再试吧!') // speaker.onAIError
            })
            break
        case 'wakeUp':
            miCtrl.wakeUp()
            break
        case 'exit':
            miCtrl.exit()
            break
    }
})

// 进入唤醒模式时触发的事件
miCtrl.on('wakeUp', (wakeUpHandler) => {
    miCtrl.tts('你好,我是傻妞,很高兴认识你') // speaker.onEnterAI
    // 用户在唤醒模式下每说一句话都会触发此事件
    wakeUpHandler.on('message', (message) => {
        Promise.all([
            miCtrl.tts('让我先想想'), // speaker.onAIAsking
            aiCtrl.ask(message, {timeout: 10}),
        ]).then(async ([_, res]) => {
            await miCtrl.tts(res) // 播放 AI 的回答
            await miCtrl.tts('我说完了') // speaker.onAIReplied
        }).catch((err) => {
            miCtrl.tts('出错了,请稍后再试吧!') // speaker.onAIError
        })
    })
})

miCtrl.on('exit', () => {
    miCtrl.tts('傻妞已退出') // speaker.onExitAI
})

@idootop
Copy link
Owner

idootop commented Jun 20, 2024

首先,感谢你给出的提案,我看到了以下几个方面:

  • 提供对各种生命周期事件的监听与 hook 能力:这里可能需要提供更细致,更易于使用和扩展的事件监听与自定义原始业务逻辑的接口与能力。从你的提案中我看到了 wechaty 的影子,都提供了很好的参考方向。但根据我之前的实际开发经验,MiGPT 内部存在一些不同事件业务逻辑上的耦合,要想抽离出干净的可自定义的接口,需要先系统梳理当前哪些事件和行为,适合并且支持被导出 hook。其中对于业务逻辑耦合的部分(实际上是更偏控制底层控制相关的逻辑),我在想最终暴露出来的业务接口,外部开发者需不需要考虑底层或中间层的一些处理过程,还是说对外提供的是非常上层的业务接口,开发者直接上手写自己的业务代码即可。比如注册自己的语音指令,hook 原来的 AI 回复的输入输出,可能做到这几点对于大部分的第三方自定义场景就已经足够了。
  • 提供外部操控小爱音箱的各种能力接口:这一点是在上面提供了事件 hook 能力之后,外部开发者可以有能力去调用小爱音箱的一些基础操控能力,比如语音播报一条消息,播放暂停音乐等。这一部分对外提供的接口和实现相对比较简单和稳定,但对于外部开发者的使用而言,会变得非常有用。

我觉得 MiGPT 的下一步有必要从最初的设计目标:通过 docker 一键部署运行 进一步扩大到 提供更友好、易上手的使用界面更灵活、更开放、更便捷的开发生态

这本来是 MiGPT 后期插件系统的设计规划,但是现在你提到的跨平台的问题,本质也可以算作底层框架模块化可定制的一部分。最重要的,这也是为将来 MiGPT 面向普通用户的使用界面和扩展能力做准备,对外提供更灵活更方便的使用和开发(插件、Workflow)体验。

所以 MiGPT 底层框架的重构升级,势在必行!

我觉得新的架构可以从以下几点出发:

  • 项目结构上使用 Monorepo 的方式去管理:核心分成 3 部分:1. 音箱的底层能力(当前的 mi-service-lite),2. 音箱相关交互事件的处理与分发(mi-gpt-core),3. 上层第三方服务模块(比如:LLM 模块,TTS 模块,和 AI 问答模块等)。其中音箱控制底层,我们可以考虑抽象出对其他音箱品牌的支持(假设对应音箱品牌有相关开放能力); LLM 模块 处理对不同大模型接口的适配(比如 OpenAI、豆包、腾讯混元等模型);TTS 模块支持更多家 TTS 服务提供商。
  • 拆分 AI 问答模块为独立项目:我想强调的是,AI 问答并非只针对小爱音箱语音助手此类场景,它拥有更广泛和更独立的使用场景。这个模块反而是最开始我最看重和最感兴趣的存在,终极目标是打造一个全能的、高智商和情商的人工智能助理的存在,比如钢铁侠中的贾维斯。他应该支持最基础的 Agent tool/function(联网搜索等),RAG(提供领域知识库或个人信息),对话上下文(长短期记忆)等模块。在小爱音箱这个场景中,可以通过 agent tool/function 的方式,将米家设备的能力列表嵌入到 LLM 指令中,让其自我决策自动调用。但按照当前的 LLM 的能力上限来看,效果未必太好,好考虑 Prompt 的调优,token 成本的消耗以及安全性/用户操作授权等问题。但我相信随着 LLM 理解能力的提升,当 AGI 到来的那一刻,这些都不再是问题。
  • 更完备的 GUI 使用/控制/管理终端: todo 稍后补充(比如之前群里提到过的聊天对话界面,智能家居 Agent Workflow 的编辑/运行和分享等功能)

先写到这,后面有时间我们继续探讨。

最后,再次感谢你对 MiGPT 的关注、贡献和提出的各种宝贵的意见。

另外我想询问一下你的意见,是否感兴趣加入 MiGPT 的核心开发工作,一起构建全新的未来?

@lmk123
Copy link
Contributor Author

lmk123 commented Jun 20, 2024

很感谢你详细介绍了此项目未来的规划!我对于你提出的两个目标非常认同。也很感谢你邀请我加入核心开发工作,我对此仔细思考了一番。

从前面的规划来看,重构的工作量不小,但我不能保证我有多少时间参与其中 😂 最近正是 Chrome 即将下架 Manifest V2 的关键时间节点,我正在全力将我个人的项目迁移到 Manifest V3,这于我而言是个很不稳定的因素。

在你提到的两个目标当中,我对 提供更友好、易上手的使用界面 更感兴趣一些,所以,我想我可以继续利用碎片时间做一些界面上的探索,如果遇到问题了,我会像今天这样反馈给你,这样也算是提前把 GUI 上的坑踩了 😂

@idootop
Copy link
Owner

idootop commented Jun 20, 2024

😂 哈哈,没关系。上面只是探讨下 MiGPT 未来一些可能的优化方向和展望。

我个人近期的空闲时间也不是很多,短期内没有继续向下扩展新功能或者重构的规划。

后面我会把精力放到创作其他更有趣和有价值的开源项目上,帮助更多的人,MiGPT 的开发应该到此告一段落了。

PS:我今天刚刚把火狐浏览器上的翻译插件换到了划词翻译(之前的沙拉查词好像下架搜不到了),开箱即用还是挺方便的 👍

@idootop
Copy link
Owner

idootop commented Jun 20, 2024

@lmk123 最后祝你的浏览器插件能够顺利完成迁移 🎉

@zqzhan2024
Copy link

我已经将Mi-GPT的改成支持RAG,支持智谱AI,并成功运行。

问题就是慢,偶尔人说话听不见。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants