用AI解决Mac环境变量的管理问题
以后做了什么软件得能让AI会用

上周遇到了一些环境变量的问题,于是发现自己对 Mac 的环境变量有很多误解。要是放以前,我应该会写一篇文章来详细介绍 Mac 下环境变量的各种知识及误区,但是 AI 时代了,写这种东西没太多意义了。因为想知道的问 AI 就好,不想知道的让 AI 去做就好,所以我还是聚焦于解决自己的问题吧。
上周所说的核心问题是,用 brew services 启动 ollama 服务后,它没能读取到我在 ~/.zprofile中设置的环境变量OLLAMA_MODELS。原因是通过 brew services 启动的服务,父进程是 launchd,与 zsh 无关,而~/.zprofile中配置的环境变量只有 zsh 及其子进程能获取到。
因为我开发了几个项目,会用到本地模型,需要用到像OLLAMA_MODELS、MODELSCOPE_CACHE这类把模型存储到外接硬盘的环境变量。由于我是用 Claude Code 开发,所以开发过程会在命令行中启动服务,但实际运行时会作为服务在后台运行。
我的需求就是只在一个地方配置一次环境变量,然后多个项目,不管是在命令行运行,还是在后台运行都能用到我配置了一次的这些环境变量。
可能我的场景比较小众吧,网上没能找到很好的解决方案,上周我写到的五种解决方案都不完美。不过换一个角度看,整个系统共用一套环境变量,这个事情本身也算不得优雅。只是对于我的开发场景,这样做确实更简单。
还是那句话,既然已经是 AI 时代了,或许我的思路应该转变一下,有些问题应该尝试用 AI 的方式来解决。例如上周所说的方案中,有一个是“自定义 plist 管理 ollama”,也就是自己写个 plist 来管理你要启动的服务,放弃使用 brew services。
这个方案的问题就是,你很难保证自己以后绝不再执行 brew services restart 这类命令,因为过段时间你可能就把自定义的那套机制忘记了。但如果把这事交给 AI,它是不会轻易忘记的。
所以,你只要在系统提示词里添加一些说明,例如,告诉 AI 所有涉及到用 brew services 来操作 ollama 服务的地方,都改为我们自定义的管理方式,不要直接使用 brew services,而是使用 launchctl 操作。并且以后把这些事情都交给 AI 就行了。
当然你得写清楚具体的操作方法,可以让 AI 替你写。不过一旦具体了,就不是三言两语能描述的了,尤其我还要求只在一个地方配置一次,那肯定要涉及一个共用的配置文件,让 shell 和 launchd 都得同时加载。
你可以想象到,这让 AI 写出来得多少字啊,放全局的系统提示词里,每次对话都加载,肯定不合适。如果只放特定项目呢,也没必要,因为大部分对话都不会涉及到 brew services。如果你用智能体已经有些经验了,这时候肯定就会想起 skills 了。
是的,可以把这个能力做成一个 skill,这样只有真正用到 brew services 的时候,才会把这一堆方案加载进上下文。方法也简单,你开启一个新的会话,直接让 Claude Code 给你解决“自定义 plist 管理 ollama”的问题,甚至给你解决统一配置的问题。然后你调用 /skill-creator,把整个过程封装成一个 skill。
我说得比较粗糙,如果你有类似的需求场景,这样去做了,肯定还需要解决很多细节问题,但这个思路肯定是对的。
我之所以说得粗糙,是因为我没那么做。Skill 是可以解决这个问题,但对我来说还是有两个痛点。一是稳定性,AI 的行为毕竟是概率性,你用多了难免会遇到一些意外,我还是对确定性有所执着;二是简单性,如果处理环境变量还得跟 AI 对话,这也太麻烦了,我一个写代码的怎能如此?
所以我决定抛弃 AI,写一个命令行工具来解决这个问题,然后我又决定拥抱 AI,用 AI 来写这个命令行工具。
其实刚有 AI 那会我就已经开始写各种脚本来处理一些本地的问题了,当时是搭配 Alfred 来使用的,已经能够提升不少工作效率了。但那时还没有 vibe coding,只能写一些简单的脚本。如今有了 Claude Code 这样的工具,有时真的会有无所不能的感觉。
我给这个工具起名为 envonce,它会提供若干组全局的环境变量,可以组合搭配使用,还能完全接管 brew services 所管理的服务,自动合并原有 plist 中的环境变量。反正编程成本变低了,也难免有些过度设计。
代码我传到了 github,有感兴趣的可以看看。 https://github.com/laidbackgeek/envonce
我也大致展示下使用效果:
# 安装
brew tap laidbackgeek/homebrew-tap
brew trust laidbackgeek/tap
brew install envonce
# 初始化
envonce init
# Homebrew 管理的 ollama 有两个 ollama 的环境变量
ps eww -p $(pgrep -f 'ollama serve') | tr ' ' '\n' | grep OLLAMA
OLLAMA_KV_CACHE_TYPE=q8_0
OLLAMA_FLASH_ATTENTION=1
# 设置环境变量
envonce env set OLLAMA_MODELS=/Volumes/SSD/ollama/models
# 接管 ollama
envonce service take ollama
envonce service restart ollama
# 此时 ollama 服务融合了之前的两个环境变量和我新添加的环境变量
ps eww -p $(pgrep -f 'ollama serve') | tr ' ' '\n' | grep '^OLLAMA'
OLLAMA_FLASH_ATTENTION=1
OLLAMA_KV_CACHE_TYPE=q8_0
OLLAMA_MODELS=/Volumes/SSD/ollama/models
# 新开命令行窗口,也能读到新增的环境变量
echo $OLLAMA_MODELS
/Volumes/SSD/ollama/models
# 升级 ollama
brew upgrade ollama
......
==> Upgraded 1 outdated package
ollama 0.31.1 -> 0.32.0
# 再次查看环境变量,未发生变化
ps eww -p $(pgrep -f 'ollama serve') | tr ' ' '\n' | grep '^OLLAMA'
OLLAMA_FLASH_ATTENTION=1
OLLAMA_KV_CACHE_TYPE=q8_0
OLLAMA_MODELS=/Volumes/SSD/ollama/models
虽然有了这个命令行工具,但 skill 还是不能省,因为还是要避免 Claude Code 去执行 brew services。只不过我的 skill 是直接使用 envonce 的,便简洁多了,也算是 envonce 的一个配套模块吧。
如果是四年前遇到这个问题,我只会默默地去改 plist,那时我还不太擅长写这类脚本。如果是三年前,我可能会借助 ChatGPT 写个脚本做些简单处理,估计也得花个几小时。而到了今天,同样是花几小时(主要是GLM5.2太慢了),已经足够我写个过度设计的命令行工具了,还能教 AI 来使用它。
我以前也提到过技术人的一些局限,总是盯着技术而不是问题。如今有了 AI 的支持再不转变就太晚了,未来的生活应该都是如此吧,不是我们会什么就做什么,而是需要做什么就做什么。