Article

工具装多了,内存告急——我给 pi 加了个「管家」

adopt

上一篇结尾我留了个钩子:

工具越接越多,新问题来了。

今天就把这个问题摊开讲。


内存是怎么被吃光的

我的 pi 现在挂了四个本地工具:

  • 语音转录(SenseVoice)
  • 配音 + 声音克隆(VoxCPM)
  • 视觉理解(Qwen3-VL)
  • 小红书配图生成

每个工具背后都是一个本地模型。模型要运行,就得占着内存。

具体多少?

VoxCPM 跑在 7860 端口,光它自己就吃掉 4-5GB。Qwen3-VL 跑在 18017 端口,也要 2-3GB。两个一起跑,还没干活,先去了七八个 G。

MacBook 总共 16GB 内存,加上 Chrome、VS Code、微信这些常驻应用,很快就超过了 70%。

机器开始发烫,风扇开始转,键盘区域温热——用过 MacBook 的人都知道这个信号。

以前我的处理方式是:手动关。

用 Activity Monitor 找进程,kill 掉,然后祈祷手速够快别把其他进程带走了。

但问题是,我经常忘了关。

做完一期播客的配音,VoxCPM 还在后台跑着。我去睡了一觉,第二天醒来它还在那里。白白占着几个 G 的内存,什么都没干。

你说它自动会关吗?不会。我什么时候再打开它?不知道。

需要一个自动化的方案。


思路:两个层面的管理

这个问题可以从两个角度解决:

主动解决——我想关的时候,能简单关掉,不用开终端敲命令。

自动解决——系统内存吃紧了,它能自己判断,把不需要的关掉。

两个层面不冲突,而且应该同时存在。一个负责「我主动要做的」,一个负责「我忘记做的」。


第一层:一个管理工具

我先做了 manage_services 这个工具。

它做三件事:

查看状态——说一句「现在有什么在跑」,它列出所有服务,哪个在运行、哪个已停止、空闲了多久。

关掉指定服务——「把视觉模型关掉」,它就找到 Qwen3-VL 的进程,杀掉,释放内存。

批量关停闲置服务——一条指令,自动关掉所有超过 30 分钟没用的后台服务。

代码逻辑很简单:读一个 registry.json,里面登记了每个服务的名称、端口号、停止命令、最后使用时间。操作的时候查端口,发信号,等进程结束。

有了这个,我不用再开 Activity Monitor 了。直接在 pi 里说一句「关掉配音模型」就行。


但这不是最关键的。最关键的在于——我会不会想不起来关

会。而且经常。

所以第二层才是真正的解法。


第二层:自动的内存管家

pi 有一个机制叫 UserPromptSubmit 钩子——每次用户输入 prompt 之前,可以运行一个脚本。

我在钩子里放了一个脚本 memory-guard.mjs,它每次做的事情:

  1. 读取系统内存使用率(用 vm_stat + pagesize 算出来)
  2. 如果低于 70%,什么都不做,退出
  3. 如果超过 70%,遍历 registry,找出所有运行中且超过 30 分钟未使用的服务
  4. 关掉它们,更新 registry
  5. 在 prompt 上下文里插入一行提示:「内存占用 73%,已自动停止闲置服务:Qwen3-VL 视觉模型」

整个流程不到一秒钟。

我完全感觉不到它的存在——直到我需要内存的时候,发现它已经被释放了。

你们有没有这种感觉?电脑卡了,打开 Activity Monitor,发现一堆不知道什么时候打开的进程在吃内存。

这个脚本就是替我做这件事的——在我发现之前,它已经处理好了。


两个方案怎么配合的

现在的工作流是这样的:

日常使用,自动管家的阈值是 70%。内存够用,它不出声。内存紧张了,它悄悄关掉超过半小时没用的服务,在上下文里报备一句。

如果我正在进行密集任务(比如连续做多个配音),不想它自动关停服务——手动用 manage_services status 看状态,自己决定要不要停。

还有一个细节:每个工具在完成任务时,会调用 touchService() 更新 last_used 时间戳。这样管家的判断依据是准确的——不会把一个正在使用的服务当成闲置的关掉。

上一层兜底,下一层精细控制。


registry.json 长什么样

就是一个 JSON 文件,记录着每个服务的元数据:

voxcpm → 端口 7860,最后使用 30 分钟前
qwen3vl → 端口 18017,最后使用 5 分钟前

每一行很简单,但足够让管家判断哪些能关、哪些不能关。

如果有人问:「为什么不直接查进程名,还要单独维护一个 registry?」

因为进程名不一定看得出来是哪个服务,而且不同模型的停止方式不一样。VoxCPM 用 kill 信号关,Qwen3-VL 用专门的 stop.sh 脚本关。一个统一登记的注册表,减少了很多猜测。


这套方案给我的启发

其实这件事本质是:

有多少个后台服务,就要有对应的生命周期管理。

以前只有一个 SenseVoice 在做转录,内存压力不大,不需要在意。

但现在工具多了,转录、配音、视觉、生图——每个模型都是一个常驻进程。不加管理,它们就一直在那待着,不管你需要不需要。

这其实和编程里「资源管理」的概念很像:打开的资源一定要记得关闭。只不过这次,被管理的资源变成了本地模型,而「自动关停」是我的垃圾回收机制。


文章开头提到上一篇的坑,说工具越多问题越多。今天填上了。

但也有新的问题:

四个工具都跑在本机,每个都需要独立的模型文件和 Python 环境。单是依赖环境就占了十几 G 的磁盘空间。每次升级模型还要手动更新。

不过——这是下一个话题了。

下一期预告:统一管理模型依赖,不再各自为政。

感兴趣的点个关注,我写好就发。


本系列持续更新,记录把各种工具接进 AI 助手 pi 的完整过程。