ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

开源AI工作台:自建workbuddy替代品的设计与实现

开源AI工作台:自建workbuddy替代品的设计与实现 很多人问我为什么放着现成的工具不用非要自己写一个 workbuddy 替代品。其实原因很简单我在深度使用了半年之后发现它的思路确实好但闭源、订阅制、skill 生态封闭这三个问题卡得我很难受。正好去年开始我把大量工作流都搬到了本地越用越觉得需要一个能完全掌控的 AI 工作台于是就有了这个开源项目——魔力工作台。它不是 workbuddy 的简单复刻而是一个把“AI 对话、代码编辑、技能扩展、缓存管理、模型路由”全部本地化重写的开源实现。写这篇文章是想把项目的完整设计思路、核心模块实现和落地过程整理出来给同样想自建 AI 工作台的人一个可以直接参考的样例。全文不会只讲概念重点放在实操skill 怎么写、模型怎么路由、缓存目录怎么改、Tauri 在老旧 Windows 上踩的坑怎么填。无论你是被商业工具收费劝退的开发者还是想在嵌入式设备、教学场景里集成 AI 助手的爱好者这篇文章应该都能帮你省掉不少弯路。2. 为什么我要做这个替代品workbuddy 的定位与痛点2.1 workbuddy 到底在解决什么问题想理解这个项目的价值先得搞清楚 workbuddy 这类工具的本质。它和传统 IDE 最大的区别是传统 IDE 把“写代码”当成核心AI 只是辅助而 workbuddy 这类工具把“指挥 AI 干活”当成核心代码编辑只是其中一个动作。简单说它更像一个 AI 调度台你通过对话、指令、skill 来驱动它完成各种任务而不是自己动手敲每一行代码。它的“skill”机制尤其值得说。你可以把 skill 理解成给 AI 预装好的技能包里面有提示词模板、可执行的脚本、参数定义和元信息。比如一个“代码审查 skill”它会自动指导 AI 按特定步骤分析代码、定位问题、生成报告。这种机制把原本需要反复手工输入的上下文变成了可复用的资产这才是工作台类工具真正的灵魂。但商业实现往往有自己的局限。workbuddy 的 skill 是闭源托管的扩展能力被限定在官方框架内云端服务和订阅价格也是硬成本。更麻烦的是它的缓存目录、模型配置都是黑盒我试过想把它默认生成的几百 MB 缓存迁移到别的盘折腾半天还是靠搜索别人的教程才搞定。这种时候我就想既然它的核心思路我理解了为什么不做一个自己能完全改得动的版本2.2 我决定自建工作台的三个核心诉求动手之前我先把自己的需求列成了三条硬指标所有功能设计都围绕这三条展开。数据必须完全本地化。对话记录、缓存、skill 配置、模型密钥都要存在本地文件里不上云、不锁定在某个厂商的服务器上。这样我才能自由迁移、自由备份、自由审查。skill 必须标准化且可自由扩展。不仅要能加载官方风格的 skill还要允许用户写自己的脚本、自己的提示词、自己的依赖甚至可以直接把整套 skill 目录丢给其他人复用。模型层必须可路由。同一个对话流程里能根据任务类型自动切换到不同的大模型。比如写代码用本地开源模型深度分析用云端更强的模型而不是把所有请求都绑死在一家服务上。这三点看起来不复杂但真正落地时涉及的模块设计远超预期。消息总线要多路由、上下文要统一、缓存要有独立管理、skill 要有加载引擎和冲突处理机制。下面我逐个说。3. 整体架构魔力工作台的模块拆解与技术选型3.1 核心模块划分与主流程魔力工作台的整体结构可以用一句话概括一个消息中枢四个可插拔模块。消息中枢负责所有数据流转四个模块分别是对话界面层、skill 引擎、模型路由、本地存储与配置中心。主流程是这样的用户在对话界面输入消息消息中枢先做预处理上下文组装、工具调用解析然后交给 skill 引擎去判断当前是否匹配了某个已启用的 skill。如果匹配就把 skill 里的提示词模板和脚本参数合并进请求接着进入模型路由由路由规则决定这次请求要交给哪个大模型最后把模型结果写回界面同时把关键中间过程存到本地缓存目录。这里最值得讲的是为什么用“消息总线 插件注册”而不是直来直去的调用链。刚开始我写第一版时就是简单的函数嵌套输入 - 调模型 - 输出看起来简单但加了一个 skill 之后就开始乱套。因为 skill 可能要改提示词、要注入脚本结果、还要决定走哪个模型如果全靠函数参数传递每个新功能都要改主流程。后来我借鉴了硬件上“主板 插槽”的思路把所有能力都注册成插槽消息中枢只负责调度模块之间通过标准接口通信。改一个模块不影响其他模块这才是工作台类项目该有的架构。3.2 技术选型Tauri React Go 而不是全家桶技术选型上我踩了一点弯路。最开始我用纯 Web 实现界面是快但缓存管理、本地文件读写、进程调度都受浏览器沙箱限制于是决定改成桌面应用。在 Electron 和 Tauri 之间我几乎没有犹豫——Electron 打包完动辄 200 MB内存占用也高Tauri 用系统 WebView安装包能做到 10 MB 左右内存占用小一个量级。前端我选了 React。说实话选型时没那么多花里胡哨的理由就是生态最成熟、示例最多市面上能找到的 AI 对话界面组件基本都是 React 写的改起来省时间。后端我选了个比较冷门的组合Rust 写 Tauri 的命令层Go 写本地网关。Rust 负责和界面通信、文件读写Go 负责模型请求转发、skill 脚本调度。为什么要拆成两个语言因为 Tauri 的插件系统虽然方便但跑长时间运行的并发任务时Rust 的编译速度和开发效率都不如 Go 顺手。模型网关是一个典型的长驻进程服务要处理并发请求、超时重试、负载均衡这些正是 Go 的强项。本地网关直接监听 127.0.0.1 的随机端口Tauri 启动时自动拉起两个进程通过 JSON-RPC 通信。3.3 与 workbuddy 的能力对照能力项workbuddy魔力工作台闭源/自托管云端服务 客户端完全本地支持离线Skill 定义官方托管格式算半开放标准目录 YAML 任意脚本模型绑定固定几个平台任意 OpenAI 兼容 API / 本地模型缓存目录默认系统用户目录修改麻烦配置文件指定环境变量可覆盖数据所有权云端存储全部本地文件随时可备份成本订阅制只花模型调用费可白嫖本地模型插件扩展受限任意脚本可注册为 skill对比看得很清楚魔力工作台不是要超越 workbuddy而是在“可掌控、可扩展、可离线“这三个方向上做了更彻底的设计。至于 UI 精美程度、多设备同步这类体验问题说实话目前确实还比不过商业产品这也是开源项目需要靠生态来补的短板。4. 核心模块设计与实现细节4.1 Skill 引擎让 AI 学会“用工具”skill 引擎是整套系统的功能核心也是我和 workbuddy 设计差异最大的地方。workbuddy 的 skill 更像一个配置包里面主要是提示词和参数表我在魔力工作台里把 skill 定义成了“ 提示词 脚本 ”的复合体这意味着 skill 不仅能指导 AI 怎么回答还能让 AI 在回答问题之前先执行一段真实代码、读取本地文件、调用系统命令甚至是调用另一个模型。先看标准的 skill 目录结构skills/ └── code-reviewer/ ├── skill.yaml ├── main.py ├── requirements.txt └── assets/ └── review_template.mdskill.yaml 是元信息文件内容包括标识符、名称、描述、触发条件和参数定义。下面是一个实际的例子name: code-reviewer description: 对指定目录下的代码进行多维度审查 version: 1.0.0 trigger: type: keyword keywords: [审查代码, code review] params: - name: target_dir type: string required: true description: 要审查的代码目录 - name: strict type: boolean default: false加载流程是这样的工作台启动时扫描 skills 目录读取每个子目录下的 skill.yaml注册到引擎的索引表里。当用户输入的消息命中某个 skill 的 trigger 关键词时引擎会读取这个 skill 的提示词模板把用户消息和参数合并成新的请求上下文同时把 main.py 的调用结果也注入进去。main.py 的输出会被当作“ 工具观察结果 ”作为额外上下文传给模型这相当于给 AI 戴上了一副能看文件、能跑命令的“ 眼镜”。这里我想特别强调一个设计细节每个 skill 的脚本必须运行在独立的子进程里而不能直接在主进程内执行。原因很直接——脚本可能是用户自己写的一旦崩溃不能把整个工作台带挂。我统一用 Go 的 exec 包拉起子进程设置 30 秒超时超时kill 掉并把 stderr 重定向到日志文件。实测下来一个写得不规范的 skill 脚本最多只会浪费 30 秒时间完全不会影响其他功能。skill 之间的冲突处理也是个容易忽略的坑。刚开始我把所有 skill 的关键词放在一个大表里简单匹配结果经常出现两个 skill 同时命中同一个输入。后来我加了优先级字段和“ 互斥组 ”概念。priority 默认为 100数值越大优先级越高互斥组里的 skill 一旦命中同组其他 skill 自动失效。这样设计的好处是用户可以组合不同维度的 skill但同类场景不会互相打架。4.2 模型路由与模型网关模型路由是本项目相对 workbuddy 最大的差异化能力。workbuddy 的做法是你手动在设置里选当前用哪个模型所有请求都走同一个。魔力工作台则是把模型路由做成了动态规则引擎可以按任务类型、按 skill、按用户输入的语义自动选择合适的模型。路由规则写在配置文件的 router 段里router: rules: - name: coding-task when: skill: code-reviewer or_keywords: [写代码, debug, 重构] model: local-qwen2.5-coder - name: deep-analysis when: keywords: [深度分析, 研究, 总结报告] model: cloud-gpt4o - name: default model: local-qwen2.5规则按顺序匹配从上到下第一个命中的生效。每条规则的核心是模型名模型本身定义在 models 段models: - name: local-qwen2.5 type: openai-compatible base_url: http://127.0.0.1:11434/v1 api_key: local - name: cloud-gpt4o type: openai-compatible base_url: https://api.example.com/v1 api_key: ${OPENAI_API_KEY} timeout: 30模型网关的工作是接收消息中枢发来的请求解析模型名检查缓存策略转发到对应的 base_url并把响应流式返回给客户端。这套路由机制的灵感其实来自我日常工作的习惯。我在嵌入式项目里写代码时用本地小模型就够了响应快、不需要联网但写方案文档、做技术评审时本地模型的深度不够得切到更强的云端模型。以前每个任务都要手动切换构建路由引擎之后几乎是隐形的这个体验真的回不去了。4.3 缓存目录与配置中心把“改不动的地方”变成“想怎么改就怎么改”workbuddy 用户最常问的搜索关键词之一就是“怎么修改系统缓存目录”我自己也踩过这个坑。缓存目录这件事看着小其实很影响实际使用默认放在 C 盘用户目录下几百 GB 的 code index 和会话记录用一段时间 C 盘就飘红。workbuddy 的缓存目录藏在深层配置里改了还要重建索引。魔力工作台从设计上就把配置中心独立成一个模块。默认缓存路径是安装目录下的 data/ 文件夹但你可以通过两种方式覆盖一是修改工作台根目录的 config.yaml 文件里的 cache_dir 字段二是设置环境变量 MAGICBENCH_CACHE_DIR。环境变量的优先级更高这样在脚本、容器环境里部署时不用读配置文件也能做到路径隔离。配置中心支持四个层级从低到高分别是默认值、config.yaml、用户环境变量、命令行参数。这种层级设计的好处是默认值保证开箱即用config.yaml 提供常用配置入口环境变量适配自动化部署命令行参数适配临时调试。每个配置项都记录当前的生效来源我用一个 debug 子命令就能查看所有配置项当前用的是哪个层级排查问题非常方便。一个典型的缓存目录迁移场景是这样的在 Windows 上把项目装在 D 盘想缓存也放 D 盘只需要在 config.yaml 里加一行 cache_dir: D:\magicbench-cache重启工作台即可。工作台会自动把旧的缓存移动过去而不是重新下载一遍这个 “ 自动迁移 ” 功能是我做完之后觉得最值回票价的细节。4.4 场景扩展从 AI 编程到小程序教学等更多可能开源方案的价值在于它可以被塞进不同场景里。除了替代 workbuddy 做 AI 编程助手魔力工作台的 skill 机制让它可以很轻松地变身成其他工具。我最近做的一个实验是把它当小程序教学工具给 skill 引擎写了一个“ 小程序代码生成器 ”的 skill学生在小程序端提交需求描述后端通过消息总线唤醒这个 skill自动生成小程序页面代码并返回预览链接。这个场景里 workbuddy 是做不到的因为它的 skill 生态是封闭的你不能把 skill 挂到一个网页后端上。而魔力工作台因为消息总线是标准 JSON-RPC 接口任何语言都能调用skill 脚本本身就是普通 Python 代码所以能做的事情远超“ 对话 代码 ”。另一个常见场景是把它接进局域网内的嵌入式设备上当做设备 Agent 的对话入口模型可以跑在设备模拟器上也可以转发到服务端靠的就是模型路由的灵活性。5. 从零开始构建开源工作台的实操记录5.1 环境准备与项目启动先说明一下我做了两个发行渠道一个是在 Gitee 上发源码包一个是在 GitHub Releases 提供 Windows / macOS 的预编译安装包。源码方式适合想自己改二开的预编译版适合开箱即用。这里以源码方式做演示。环境依赖其实不多Node.js 18、Rust 1.75、Go 1.22外加一个任意兼容 OpenAI 接口的模型服务或者云端 API 密钥。# 克隆代码 git clone https://github.com/yourname/magicbench.git cd magicbench # 安装前端依赖 npm install # 构建 Tauri 前端 npm run build # 运行 Go 网关 cd gateway go build -o magicbench-gateway . cd .. # 启动开发模式 npm run tauri dev第一次启动需要几十秒因为 Tauri 要编译 Rust 后端。看到终端出现 gateway started 字样就说明本地网关已经跑起来了。这时候打开界面默认在设置页可以把模型配好名字随意base_url 填你的 OpenAI 兼容接口地址api_key 填你的密钥。本地 Ollama 用户填 http://127.0.0.1:11434/v1 即可。5.2 配置模型路由实现“本地优先”我自己的典型配置是本地部署一个 7B 参数的代码模型专门处理写函数、改 bug、补注释这类高频低难度任务云端配一个更强的通用模型做深度分析、长文档总结、复杂重构等高难度任务。模型路由规则我做了个优先级排列能本地处理的不上云必须上云的按任务类型兜底。实际操作中本地优先策略的好处非常直观。一是响应快局域网调用本地模型一般 1-3 秒出首 token云端动不动 5 秒以上二是省钱多数日常请求的 token 量不大全走云端一个月下来也是笔开销三是隐私代码片段默认不出本机只有明确触发高难度任务才走外部 API。当然本地优先也不是无脑选择。本地 7B 模型在上下文超过 8K 时效果明显下降所以我额外加了一条规则如果请求的上下文 token 数超过 6000自动切换云端模型不管触发什么关键词。这个兜底规则防止了“ 本地模型硬扛长上下文 ”导致的答非所问。5.3 实战编写一个可以复用的自定义 Skill新手最容易卡住的环节就是写 skill。我带你看一个完整案例是我自己常用的一款“ 会议纪要转任务清单 ”skill。先在 skills 目录下建一个 meeting-to-todo 文件夹skills/meeting-to-todo/ ├── skill.yaml ├── main.pyskill.yaml 里写好元信息和触发规则name: meeting-to-todo description: 把会议纪要文本解析为可执行的任务清单 version: 1.0.0 trigger: type: keyword keywords: [会议纪要, 会议记录, 整理任务] params: - name: raw_text type: string required: truemain.py 负责解析文本把看起来像任务的句子抽出来#!/usr/bin/env python3 import sys, re, json def parse_tasks(text: str): tasks [] for line in text.strip().splitlines(): line line.strip() if re.match(r^[-*]\s, line): tasks.append({ content: re.sub(r^[-*]\s, , line), priority: medium, source: meeting }) return tasks if __name__ __main__: raw sys.stdin.read() result parse_tasks(raw) print(json.dumps(result, ensure_asciiFalse))工作台的 skill 引擎会自动把 raw_text 参数从用户输入中提取出来通过 stdin 传进 main.py再把脚本输出的 JSON 当作“ 工具观察结果 ”附加到模型上下文里。模型会参考这个结构化结果生成最终的任务清单。这里想提醒几个容易踩的坑。第一skill.py 的 print 输出会在传参时被捕获所以千万别在里面打印额外的调试日志否则模型会把日志当成结果要打日志写到 stderr。第二一定要处理 stdin 为空的情况否则 skill 启动一次就报一次错。第三params 里的 required 字段务必设置准确缺少必填参数时引擎会直接拒绝触发而不是报个晦涩异常。5.4 与 Cursor、小程序等外部工具的对接尝试魔力工作台本身不依赖 Cursor但很多人习惯用 Cursor 写代码用工作台管理 skill 和模型。我在项目里加了一个“ 外部工具桥接 ”模块通过 HTTP 回调的方式把它嵌入已有的工具链。最常用的姿势是启动一个工作台实例后把网关地址和端口暴露给本地其他工具其他工具通过 POST JSON 到 /v1/chat 接口传用户消息和可选的 skill_name 字段工作台会按完整流程处理并返回模型结果。小程序对接也不难。我在一个测试用例里用小程序端做了一个简易的查询接口直接把用户输入透传给工作台的网关再把返回的任务清单列表渲染到页面上。全程用了不到 50 行胶水代码核心全靠标准 JSON-RPC 替换。这个小实验让我确信只要核心模型足够开放封装成任意端上的“ AI 助手 ”只是时间问题。6. 常见问题与排查实录6.1 高频问题速查现象常见原因排查方法模型一直不回复base_url 填错或 api_key 无效先 curl 一下 base_url 看是否通skill 不触发trigger 关键词没写对在 debug 模式看匹配日志skill 脚本执行报错main.py 依赖缺失在终端手动运行看报错信息缓存目录迁移失败路径里有空格或权限不足检查路径引号用管理员权限重试Windows 下启动闪退WebView2 运行时缺失安装 WebView2 Runtime对话日志乱码系统编码非 UTF-8设置环境变量 PYTHONIOENCODINGutf-8排查的准则是“ 分层定位 ”。先看界面有没有报错再看网关日志有没有收请求再看模型网关有没有收到请求最后看模型服务有没有返回。只要这四层里某一层断了问题就锁定在哪一层不要上来就怀疑是代码 bug。6.2 三个真实踩坑记录第一个坑是 Windows 7 兼容性。后台不少用户反馈老电脑装不了因为 Tauri 2.x 的 WebView2 在 Windows 7 上需要手动装运行时装完还有兼容模式的问题。我后来专门保留了一个基于系统 IE 内核的降级渲染桥接层虽然界面效果差一些但总算能让老机器跑起来。workbuddy 官方并不支持 Win7这也算开源方案对老旧设备的一份坚持。第二个坑是缓存目录迁移后的权限问题。把缓存从 C 盘迁到 D 盘之后Go 网关在进行文件写入时出现了 access denied因为默认情况下 Go 进程以普通用户权限运行而 D 盘目录的 ACL 不继承当前用户。解决方法是安装时在快捷方式属性里勾选“ 以管理员身份运行 ”或者在 config.yaml 里把 cache_dir 指向一个用户有完全控制的目录。第三个坑是开源模型对 function calling 的支持差异。并不是所有 OpenAI 兼容接口都完整支持 function calling。某些本地模型虽然能用 /v1/chat/completions 接口但传入 tools 参数时直接报错导致 skill 调用全部失效。我的解决办法是在模型定义里加了一个 capability 字段models: - name: local-mini type: openai-compatible base_url: http://127.0.0.1:1234/v1 api_key: local capabilities: function_calling: false路由引擎看到这个模型不支持 function calling就把 skill 脚本的输出以纯文本方式拼进 system 消息而不是走 tools 机制。这个字段救了很多本地模型用户。6.3 一些能提升幸福感的细节建议日志文件默认按天轮转保留最近 7 天排查问题时去 logs 目录翻当天的 gateway.log 即可。对于经常调试 skill 的人我强烈建议开 debug 模式npm run tauri dev -- --debug它会输出完整的请求上下文和 skill 触发链路的日志省掉大量猜测时间。另外项目自带的几个测试用例脚本也很值得读一下它们本身就是如何调用 openai 兼容接口、如何解析 skill 返回结构的最佳范例。7. 一点个人体会这个项目从零开始到最后开源前后花了大概四个月重写一次架构推倒重来的代码差不多占了第一版的三分之二。但做完之后最大的体会不是技术上的而是理念上的一个好的 AI 工具不是功能越多越好而是让使用者能自由替换每一层。workbuddy 的工作台概念我很认可但把 skill 封闭起来、把模型绑定到固定平台本质上还是把一个开放时代的问题用封闭的方式解决。开源版本的价值恰恰在于把决定权还给了用户——你可以换界面、换路由规则、换模型、甚至换掉整个网关实现只要接口规范还在整个系统就不会散架。最后再分享一个经验写 skill 时不要图省事把一次性脚本塞进去尽量把公共逻辑抽成独立模块因为你会发现同一个 skill 换个场景就想再用一次这时候一个好的结构能让你少写无数重复代码。
返回列表