1. 项目概述与核心价值
如果你和我一样,是 Alfred 的重度用户,同时又经常需要在多个大语言模型(LLM)之间切换——比如用 OpenAI 的 GPT-4 处理复杂逻辑,用 Claude 写长文档,用 Gemini 查最新信息——那你一定体会过那种“甜蜜的烦恼”。每个模型都有自己的官方工具或第三方 Workflow,来回切换不仅麻烦,还打断了连续的工作流。更别提当某个服务的 API 突然抽风时,那种束手无策的感觉了。我之前就遇到过,正赶着 deadline,常用的 OpenAI API Key 突然被风控了,而 Alfred 官方后来推出的 ChatGPT Workflow 只支持 OpenAI,那一刻真是叫天天不应。
这就是我动手开发alfred-chathub的最初动机。它不是一个简单的聚合器,而是一个深度集成在 Alfred 内部的、统一的多模型对话终端。它的核心价值在于,用一个统一的、高效的交互界面,将 OpenAI (ChatGPT)、Anthropic (Claude)、Google (Gemini)、阿里 (通义千问)、DeepSeek 等主流 LLM 的服务无缝接入你的 Alfred 工作流。你不再需要记住不同工具的触发关键词,也不需要为每个服务单独配置复杂的代理或参数。通过一个简单的chat命令,配合快速切换命令chp,你就能在几秒钟内调用任意一个模型,并且所有的对话历史都被统一管理。对于开发者、写作者、研究者以及任何依赖 AI 辅助进行高效工作的朋友来说,这相当于给你的 Alfred 装上了一颗可以随时切换“思维模式”的超级大脑。
2. 核心设计思路与技术选型
2.1 为什么选择 Python 重构而非 JXA
Alfred 官方在 5.5 版本推出的 ChatGPT Workflow 是一个很好的起点,它证明了在 Alfred 内集成 AI 对话的可行性。然而,它的实现基于 JXA (JavaScript for Automation),这对于快速实现一个单一功能(比如只调用 OpenAI)是足够的,但在扩展性和可维护性上存在明显短板。
JXA 是苹果系统级的脚本语言,虽然能与 macOS 的自动化框架深度集成,但其生态远不如 Python 或 Node.js 繁荣。这意味着,当你需要引入一个新的第三方 API 客户端库(比如 Anthropic 或 Google 的官方 SDK)时,在 JXA 环境下会异常困难,甚至需要自己从零实现 HTTP 请求和 JSON 解析。此外,错误处理、异步请求、配置管理这些在现代脚本中很常见的需求,在 JXA 里写起来也相当繁琐。
因此,我决定用 Python 进行彻底的重构。Python 的选择基于几个关键考量:
- 生态丰富:所有主流 LLM 服务商都提供了成熟、稳定的 Python SDK 或至少是完善的 HTTP 客户端示例。引入新服务就像
pip install anthropic一样简单。 - 开发效率:Python 语法简洁,处理 JSON、网络请求、文件 I/O 等任务非常高效,能快速实现核心的聊天逻辑和配置管理。
- 跨平台一致性:虽然 Alfred 是 macOS 专属,但 Python 确保了 Workflow 核心逻辑在大多数开发者的环境中行为一致。通过
/usr/bin/env python3调用,也尊重了用户通过 pyenv、conda 等工具管理的 Python 环境。 - 可维护性:Python 代码结构清晰,易于模块化。将不同厂商的 API 调用封装成独立的类,使得新增一个模型支持变得非常标准化,通常只需要实现一个继承自基础类的子类,填写对应的 API 端点、密钥头和参数映射即可。
这个选择从根本上解决了扩展性问题,让alfred-chathub从一个“OpenAI 专用工具”进化成了一个“LLM 服务集成平台”。
2.2 统一配置与状态管理架构
多模型支持带来的一个核心挑战是配置和状态管理。每个服务都需要 API Key、模型名称,有的还需要自定义端点(比如使用第三方代理或本地部署的模型)。如果让用户每次聊天前都手动输入这些,体验将是灾难性的。
我的设计是采用 Alfred 内置的Workflow Environment Variables和User Configuration Panel来实现持久化配置。具体架构如下:
- 全局配置变量:在 Workflow 的配置面板中,为每个支持的提供商(如
OPENAI_API_KEY,ANTHROPIC_API_KEY)预留了对应的变量字段。这些变量是静态的,一次性填写,长期保存。 - 动态活跃提供商:引入一个特殊的变量,例如
ACTIVE_PROVIDER,用来存储当前选中的提供商名称(如openai,anthropic)。 - 运行时逻辑:当用户触发
chat命令时,Workflow 脚本会首先读取ACTIVE_PROVIDER的值,然后根据这个值去读取对应提供商的 API Key 和模型等配置,最后构造请求。chp(change provider) 命令的功能就是优雅地更新这个ACTIVE_PROVIDER变量。 - 配置面板联动:
chp命令不仅更新内部变量,还会通过 Alfred 的 AppleScript API 直接更新配置面板中对应下拉菜单的选中项。这样,用户在配置面板里看到的状态总是与运行时状态同步,提供了清晰的可视化反馈。
这种设计分离了“存储所有密钥”和“决定使用哪个密钥”这两个关注点,既保证了灵活性(可随时切换),又保证了安全性(密钥持久化存储,无需每次输入)。
注意:Alfred 的环境变量虽然方便,但并非绝对安全的存储方式。对于极高敏感性的场景,建议使用 macOS 钥匙串(Keychain)来存储 API Key。当前版本为追求易用性和大多数用户的使用习惯,采用了环境变量方案。你可以在配置面板中看到,密钥输入框的类型是“密码”,这至少保证了在界面上不会明文显示。
2.3 对话历史与上下文管理
一个实用的聊天工具必须能记住对话历史。alfred-chathub将每次对话的上下文以 JSON 格式保存在本地文件中(通常位于~/Library/Application Support/Alfred/Workflow Data/[workflow-id]/目录下)。这个设计考虑了以下几点:
- 独立性:每个提供商、每个模型的对话历史是独立的。你用 GPT-4 聊编程和用 Claude 聊写作的历史不会混在一起,避免了上下文污染。
- 结构化管理:历史文件不仅存储消息列表(
[{"role": "user", "content": "..."}, ...]),还关联了模型名称、时间戳等元数据,便于未来可能的搜索或管理功能扩展。 - 性能与容量:本地文件操作速度极快,避免了网络延迟。同时,我实现了简单的轮转或清理逻辑(例如,只保留最近 N 轮对话或设置单次对话最大 token 限制),防止历史文件无限膨胀影响 Alfred 脚本的读取性能。
当用户通过热键(如建议的Ctrl+Shift+Z)打开历史记录时,脚本会读取对应的历史文件,并将最近的对话以列表形式展示在 Alfred 结果框中,选择后即可重新载入上下文继续对话。
3. 详细配置与实操指南
3.1 环境准备与依赖检查
在安装 Workflow 之前,需要确保你的系统满足两个基本条件:
Alfred 5 与 Powerpack:这是硬性要求。本 Workflow 大量使用了 Alfred 5 引入的 User Configuration 面板和更强大的自动化接口,这些功能在免费版或旧版中不可用。请确保你的 Alfred 已升级到最新版本并拥有有效的 Powerpack 许可证。
Python 3 环境:Workflow 脚本通过
/usr/bin/env python3来调用 Python。这是为了兼容用户自定义的 Python 环境(如通过 Homebrew 安装的 Python、pyenv 虚拟环境等)。你可以在终端中运行以下命令来验证:/usr/bin/env python3 --version如果正确显示 Python 3.x 的版本号(如
Python 3.11.6),则说明环境就绪。如果报错“command not found”,你需要先安装 Python 3。推荐使用 Homebrew:brew install python@3.11。此外,还需要确保 Python 环境能访问互联网(如需配置代理,后面会讲),并且有权限读写 Alfred 的工作流数据目录。
3.2 获取并配置各平台 API Key
这是最关键的一步。你需要为你计划使用的每个 LLM 服务去对应的平台申请 API Key。以下是各平台的直达链接和关键提示:
- OpenAI:访问 OpenAI API Keys 。点击“Create new secret key”生成。务必妥善保管,它只显示一次。注意 OpenAI 的 API 有调用费用,且部分模型(如 GPT-4)需要单独申请权限。
- Anthropic (Claude):访问 Anthropic Console Keys 。同样点击“Create Key”生成。Anthropic 的 API 也是付费的,但有免费的试用额度。
- Google Gemini:访问 Gemini API Key 。在 Google AI Studio 中点击“Get API key”创建。目前(截至知识截止日期)Gemini API 有免费的调用额度,是入门体验的不错选择。
- 通义千问 (Qwen):访问 DashScope API Key 。这是阿里云灵积平台的密钥。你需要有一个阿里云账号,并在灵积平台开通服务。它同样提供免费的调用额度。
- DeepSeek:访问 DeepSeek API Keys 。注册登录后即可创建。DeepSeek 目前提供了非常慷慨的免费额度,对于日常使用来说性价比极高。
实操心得:建议在浏览器中创建一个书签文件夹,专门存放这些 API 管理后台的链接。因为你会经常需要来这里查看用量、余额或重新生成密钥。另外,对于付费服务,强烈建议在账户设置中设置用量提醒和每月限额,以防意外的高额消费。
3.3 Workflow 安装与基础配置
安装:从项目的 GitHub Release 页面下载最新的
.alfredworkflow文件。双击该文件,Alfred 会弹出导入确认窗口,点击“导入”即可。导入后,Workflow 会自动出现在 Alfred 的 Workflows 列表中。打开配置面板:在 Alfred 偏好设置中,进入
Workflows选项卡,找到Alfred Chathub,选中它,右侧会显示其配置面板。或者,你可以在 Alfred 启动器中输入chathub然后选择Configure Workflow快速打开。填写 API Key:在配置面板的
Variables部分,你会看到以各提供商命名的变量,如OPENAI_API_KEY、ANTHROPIC_API_KEY等。将你在上一步获取的密钥,分别粘贴到对应的变量值中。请确保不要填错位置。选择默认提供商和模型:
ACTIVE_PROVIDER:这个变量决定了你使用chat命令时默认调用的服务。从下拉菜单中选择一个,比如openai。OPENAI_MODEL、ANTHROPIC_MODEL等:每个提供商都有对应的模型选择变量。例如,对于 OpenAI,你可以填入gpt-4-turbo-preview、gpt-3.5-turbo;对于 Anthropic,可以填claude-3-opus-20240229、claude-3-sonnet-20240229等。请查阅各平台的文档,确保填写的模型名称准确且你的账户有权访问。
(可选)配置网络代理:如果你的网络环境需要代理才能访问这些 API,可以在
HTTP_PROXY或HTTPS_PROXY变量中填写你的代理地址,格式如http://127.0.0.1:7890。Workflow 底层的 Pythonrequests库会识别这些环境变量。
3.4 核心功能使用详解
配置完成后,你就可以开始使用了。所有功能都通过 Alfred 的输入框触发。
开始新对话:按下
Alfred热键(默认是Option + Space),然后输入chat,后面跟上你的问题,按回车即可。例如:chat 用简单的语言解释量子纠缠Alfred 会显示一个“Thinking...”的提示,然后从当前活跃的提供商(由
ACTIVE_PROVIDER决定)获取答案并显示。快速切换对话模型(
chp命令):这是本 Workflow 的精华功能。在 Alfred 中输入chp,你会看到一个列表,展示了所有已配置密钥的提供商。当前活跃的提供商会有一个✓标记。使用方向键上下选择另一个提供商,按回车确认。切换是即时生效的,并且配置面板中的ACTIVE_PROVIDER值也会同步更新。下次你使用chat命令时,就会自动调用新选择的模型。管理对话历史:
- 查看历史:我强烈建议你为“Chat History”这个功能设置一个热键。在 Workflow 配置面板的顶部,找到“Triggers”下的“Chat History”,双击其热键栏,按下你喜欢的组合键,例如
Ctrl+Shift+Z。之后,按下这个热键,就会直接弹出最近与当前活跃提供商的对话历史列表,选择一条即可重新加载整个对话上下文。 - 新建对话:同样,为“New Chat”设置一个热键,如
Ctrl+Shift+X。按下它会清空当前活跃提供商的对话历史,开启一个全新的会话。这在话题切换时非常有用。
- 查看历史:我强烈建议你为“Chat History”这个功能设置一个热键。在 Workflow 配置面板的顶部,找到“Triggers”下的“Chat History”,双击其热键栏,按下你喜欢的组合键,例如
理解结果展示:Alfred 的“Large Text”组件用于显示较长的回复。如果回答很长,它会自动打开一个可滚动的大文本窗口。对于短回答,则直接显示在 Alfred 的结果框内。你可以使用
Cmd+C快速复制整个回答。
4. 高级技巧与深度定制
4.1 为不同任务预设模型配置
你可能会发现,某些任务固定用某个模型效果更好。比如,创意写作常用 Claude,代码生成常用 GPT-4,快速查询用 Gemini。虽然可以用chp切换,但每次还要手动选择模型型号。我们可以利用 Alfred 的环境变量和“片段文本”功能实现更快的切换。
方法:创建快速切换脚本
- 在 Workflow 中,你可以添加一个“Run Script”组件,语言选择
/bin/bash。 - 在脚本中,使用
plutil命令或defaults write命令直接修改 Alfred 的 Workflow 环境变量文件。例如,一个切换到 Claude 并指定 Opus 模型的脚本可能如下:# 切换到 Anthropic 并设置模型为 claude-3-opus osascript -e "tell application \"Alfred 5\" to set configuration \"ACTIVE_PROVIDER\" to value \"anthropic\" in workflow \"com.yourname.alfred-chathub\" with exportable false" osascript -e "tell application \"Alfred 5\" to set configuration \"ANTHROPIC_MODEL\" to value \"claude-3-opus-20240229\" in workflow \"com.yourname.alfred-chathub\" with exportable false" echo "已切换到 Claude 3 Opus" - 为这个脚本分配一个热键,比如
Ctrl+Opt+Cmd+C。这样,按下这个组合键,就能一键切换到预设的“创作模式”。
注意:直接修改环境变量文件有一定风险,且需要准确的 Workflow Bundle ID。更稳健的方法是在本 Workflow 的基础上,复制一份,修改其中的默认配置变量,然后保存为另一个独立的 Workflow,如 “Alfred Chathub (写作专用)”。这样你可以为不同的专用 Workflow 设置不同的热键,实现物理隔离的“模式切换”。
4.2 实现流式输出与实时响应
默认情况下,Workflow 会等待 LLM 的完整响应返回后再在 Alfred 中显示。这对于短回答没问题,但对于长文生成,等待时间会让人焦虑。许多 LLM API 支持 Server-Sent Events (SSE) 的流式响应。
实现思路:
- 修改调用 API 的 Python 代码,将请求参数中的
stream设置为True。 - 在脚本中,不是一次性收集所有响应,而是逐块(chunk)读取 SSE 数据。
- 每读取到一块包含有效文本的数据,就通过 Alfred 反馈脚本 (
alfred-feedback) 实时更新 Alfred 结果框中的显示内容。这需要用到 Alfred 的 JSON 输出格式,并持续向 Alfred 推送新的反馈。
技术难点:在 Alfred 的 Workflow 脚本中实现“实时更新”界面比较棘手,因为标准的 Script Filter 是一次性输出所有结果。一种变通方案是,对于需要流式输出的场景,改为触发一个外部脚本,该脚本打开一个小的原生 macOS 文本窗口(例如通过osascript调用display dialog的增强形式,或使用 Python 的tkinter创建简易 GUI)来显示实时流出的文字。这超出了当前 Workflow 的简洁设计范畴,但为深度定制提供了方向。
4.3 集成自定义模型与本地模型
alfred-chathub的架构设计使得添加新的 LLM 提供商相对简单。如果你使用 OpenAI 兼容的 API(例如,本地部署的text-generation-webui(oobabooga)、vLLM服务器,或云服务如Together AI、Replicate等),可以将其作为“类 OpenAI”提供商集成。
操作步骤:
- 在配置面板的 Variables 中,添加新的变量,如
LOCAL_API_KEY(如果不需要可填 dummy)和LOCAL_MODEL。 - 在代码中,复制一份
OpenAIProvider类,重命名为LocalAIProvider。 - 修改这个新类的
API_BASE属性,将其指向你的本地或兼容服务的端点,例如http://localhost:5000/v1。 - 更新提供商列表,将这个新类加入注册表。
- 最后,在配置面板的
ACTIVE_PROVIDER下拉菜单选项中,手动添加local这个选项(可能需要直接编辑 Workflow 的info.plist文件,在config字段中添加)。
这样,你就可以在chp列表中看到local选项,并使用chat命令与你的自定义模型对话了。这为高级用户打开了无限可能。
5. 常见问题排查与优化建议
在实际使用中,你可能会遇到一些问题。下面是一个快速排查指南:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
输入chat后无反应,或 Alfred 显示“运行错误” | 1. Python 路径错误。 2. 缺少必要的 Python 包。 | 1. 在终端运行/usr/bin/env python3 --version确认 Python 3 可用。2. Workflow 首次运行时会尝试安装依赖 ( requests,anthropic等)。检查 Alfred 的调试面板(Workflow 右上角的小虫子图标)看是否有安装错误。可尝试手动在终端运行python3 -m pip install requests anthropic google-generativeai等。 |
| 调用 API 时返回“Invalid API Key”或“Authentication Error” | 1. API Key 填写错误或含有空格。 2. 该 Key 没有对应服务的权限。 3. 密钥已过期或被撤销。 | 1. 仔细检查配置面板中的密钥,确保是从官网复制粘贴的完整密钥,首尾无空格。 2. 登录对应平台,确认该 API Key 是否已创建且处于启用状态。 3. 某些平台(如 OpenAI)的 Key 有过期时间,去后台重新生成一个并更新配置。 |
| 请求超时或网络连接错误 | 1. 网络不稳定。 2. 需要配置代理才能访问 API。 3. 目标 API 服务暂时不可用。 | 1. 检查常规网络连接。 2. 在 Workflow 的 HTTP_PROXY/HTTPS_PROXY变量中正确配置代理地址。3. 访问对应服务的状态页面(如 OpenAI Status )查看是否发生服务中断。 |
chp命令切换后,chat命令仍使用之前的模型 | ACTIVE_PROVIDER环境变量未成功更新。 | 确保你使用的是最新版本的 Workflow。旧版本可能存在状态同步的 Bug。尝试完全重启 Alfred(退出并重新启动)。 |
| 对话历史丢失或混乱 | 1. 历史文件被误删。 2. 同时运行了多个 Alfred 实例导致文件读写冲突。 3. Workflow 数据目录权限问题。 | 1. 历史文件是便利功能,重要内容建议自行保存。 2. 确保只运行一个 Alfred 实例。 3. 检查 ~/Library/Application Support/Alfred/Workflow Data/目录的读写权限。 |
| 回复内容被截断 | Alfred 的“Large Text”组件有显示限制,或 API 返回的 token 已达上限。 | 对于超长文,API 本身可能就会分块返回。Workflow 会尝试拼接。如果是在 Alfred 结果框内显示被截断,可以按Cmd+L在 Large Text 窗口中查看完整内容。 |
性能优化建议:
- 减少冷启动延迟:首次触发
chat命令时,Python 脚本需要加载模块,可能会有少许延迟。保持 Alfred 常开,这个延迟通常只在第一次比较明显。 - 管理历史文件大小:定期检查 Workflow 数据目录下的历史 JSON 文件。如果文件过大,可以手动删除或编写一个简单的清理脚本定期执行。
- 使用更快的模型:对于实时性要求高的简单问答,可以切换到更轻量的模型,如
gpt-3.5-turbo、claude-3-haiku或gemini-pro,它们的响应速度通常更快。
这个 Workflow 的本质是将强大的 LLM 能力“编织”进你已有的、以 Alfred 为核心的高效桌面工作流中。它消除了在不同浏览器标签、应用之间切换的摩擦,让 AI 对话变得像查询词典一样自然和快速。从最初的只为解决 OpenAI API 失效的应急之需,到如今成为一个支持多模型、可扩展的效率工具,它的演进也反映了我个人对“工具当服务于人,而非束缚于人”这一理念的实践。如果你在使用的过程中有任何改进想法,或者遇到了新的问题,非常欢迎在项目的 GitHub 仓库提出。好的工具总是在社区的共同打磨下才变得愈发趁手。