用 Rust + Tauri 打造 GUI:简单方法管理多个 AI Agent 的 skills
发布时间:2026/9/29 3:48:50来源:尧图网络
1. 多 AI Agent 场景下 skills 分散的真实痛点如果你电脑里同时装了 Claude Code、Codex、Qwen Code、iFlow CLI、OpenCode 这一票命令行 AI Agent大概率会遇到同一个问题每个 Agent 都有自己的 skills 目录和配置格式装一个技能要在每个 Agent 里重复一遍。我本地npm list -g一跑光全局 CLI 就有八九个anthropic-ai/claude-code、openai/codex、qwen-code/qwen-code、opencode-ai全在列每个都往自己的~/.xxx/skills或skills.json里塞东西时间一长根本记不清哪个 Agent 装了哪些技能。更麻烦的是切换和排查。你想把 Claude Code 里调好的一个 skill 同步到 Codex得手动找到源目录、复制文件、再改目标 Agent 的配置文件中间任何一步路径写错Agent 启动时就静默忽略你甚至不知道它没加载。skills 数量一多这种手工维护基本不可持续。这篇要解决的就是这件事用 Rust Tauri 做一个轻量 GUI 管理台把多个 AI Agent 的 skills 目录统一扫描出来在一个界面里查看、启用、切换、同步。Tauri 的好处是前端用 Web 技术写界面、后端用 Rust 做文件系统操作打包出来体积小、跨平台Windows/macOS/Linux 都能跑非常适合这种「本地文件管理 图形界面」的工具型需求。下面从项目骨架开始一步步把可复制的代码和验证步骤给出来。2. TaoToken 前置给 Agent 准备统一的模型接入层在动手写 GUI 之前先解决一个容易被忽略的前置问题这些 Agent 各自要连模型如果每个都单独配 key、单独改 base_url管理成本会翻倍。我的做法是先用 TaoToken 把模型接入统一掉Agent 侧只认一个 API 地址和一把 keyskills 管理台就只管 skills不用再操心模型配置。TaoToken 在这里扮演的是「统一模型网关」的角色兼容 Anthropic 和 OpenAI 两种协议风格Claude Code、Codex、Qwen Code 这类 CLI 都能直接对接。你只需要在控制台生成一把 API Key然后把各 Agent 的 base_url 指向https://taotoken.net/api模型名按需选择即可。这样后面 skills 管理台切换 Agent 时模型层是稳定的不会因为换 Agent 就要重新配一遍鉴权。具体操作路径先到控制台创建 API Key再对照接入文档把每个 Agent 的环境变量或配置文件改好。如果你主要跑 Claude Code 这类 Anthropic 协议的 Agent可以直接参考 ClaudeCodeAnthropic 的接入说明如果还想在网页里先验证模型通不通用模型对话页面发一条测试消息最快。长期要跑编码和 Agent 任务的话Coding Plan 的额度模型更适合高频调用不用每次担心额度。注意API Key 属于敏感凭证不要写进前端代码或提交到 Git 仓库建议放在本地环境变量或系统钥匙串里由 Rust 后端读取。3. 可复制配置Tauri 项目骨架与 skills 扫描实现3.1 初始化 Tauri 项目先确保本机有 Rust 工具链和 Node.js然后创建项目cargo install create-tauri-app --locked cargo create-tauri-app skills-manager cd skills-manager npm install选择前端框架时选 vanilla 或 React 都行这里用 vanilla 减少依赖。项目结构大致是src-tauri/放 Rust 后端src/放前端页面。核心逻辑全部写在src-tauri/src/main.rs和单独的模块里。3.2 定义 Agent 与 skills 的数据结构在src-tauri/src/agents.rs里定义 Agent 描述和 skills 条目。不同 Agent 的 skills 目录约定不一样用一个配置表把它们映射起来use serde::{Deserialize, Serialize}; use std::path::PathBuf; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AgentConfig { pub id: String, pub name: String, pub skills_dir: PathBuf, pub config_file: OptionPathBuf, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SkillEntry { pub name: String, pub path: PathBuf, pub enabled: bool, pub agents: VecString, } pub fn default_agents() - VecAgentConfig { let home dirs::home_dir().unwrap_or_default(); vec![ AgentConfig { id: claude.into(), name: Claude Code.into(), skills_dir: home.join(.claude).join(skills), config_file: Some(home.join(.claude).join(settings.json)), }, AgentConfig { id: codex.into(), name: Codex.into(), skills_dir: home.join(.codex).join(skills), config_file: Some(home.join(.codex).join(config.toml)), }, AgentConfig { id: qwen.into(), name: Qwen Code.into(), skills_dir: home.join(.qwen).join(skills), config_file: None, }, ] }dirscrate 负责跨平台拿到用户主目录避免硬编码C:\Users\xxx。记得在Cargo.toml里加上dirs 5和serde、serde_json。3.3 扫描 skills 目录扫描逻辑放在src-tauri/src/scanner.rs遍历每个 Agent 的 skills 目录把子目录名当作 skill 名同时记录它出现在哪些 Agent 下use crate::agents::{AgentConfig, SkillEntry}; use std::collections::HashMap; use std::fs; pub fn scan_all(agents: [AgentConfig]) - VecSkillEntry { let mut map: HashMapString, SkillEntry HashMap::new(); for agent in agents { let entries match fs::read_dir(agent.skills_dir) { Ok(e) e, Err(_) continue, }; for entry in entries.flatten() { if !entry.path().is_dir() { continue; } let name entry.file_name().to_string_lossy().to_string(); map.entry(name.clone()) .and_modify(|s| s.agents.push(agent.id.clone())) .or_insert(SkillEntry { name, path: entry.path(), enabled: true, agents: vec![agent.id.clone()], }); } } let mut list: VecSkillEntry map.into_values().collect(); list.sort_by(|a, b| a.name.cmp(b.name)); list }这段代码的关键点是HashMap去重同一个 skill 名在多个 Agent 下出现时合并成一条记录agents字段列出所有装了它的 Agent。这样前端一眼就能看出「这个 skill 装了 3 个 Agent那个只装了 1 个」。3.4 暴露 Tauri 命令给前端在main.rs里注册命令让前端能调用扫描和切换mod agents; mod scanner; use agents::default_agents; #[tauri::command] fn list_skills() - Vecagents::SkillEntry { let agents default_agents(); scanner::scan_all(agents) } #[tauri::command] fn toggle_skill(agent_id: String, skill_name: String, enable: bool) - Result(), String { let agents default_agents(); let agent agents.iter().find(|a| a.id agent_id) .ok_or(agent not found)?; let target agent.skills_dir.join(skill_name); if enable { if !target.exists() { return Err(format!(skill {} not found for {}, skill_name, agent_id)); } } // 实际启用/禁用可改为重命名目录或写配置这里演示写标记文件 let flag agent.skills_dir.join(format!(.{}.disabled, skill_name)); if enable { let _ std::fs::remove_file(flag); } else { std::fs::write(flag, bdisabled).map_err(|e| e.to_string())?; } Ok(()) } fn main() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![list_skills, toggle_skill]) .run(tauri::generate_context!()) .expect(error while running tauri application); }前端用invoke(list_skills)拿到列表渲染成表格用invoke(toggle_skill, { agentId, skillName, enable })做切换。禁用采用写.xxx.disabled标记文件的方式不直接删目录避免误操作丢数据。4. 验证请求与成功结果4.1 启动开发模式npm run tauri dev首次编译 Rust 依赖会慢一些等窗口弹出后界面上应该能看到扫描出来的 skills 列表。如果本地确实装了多个 Agent列表里会出现同名 skill 合并后的记录agents列显示多个 Agent 名。4.2 验证扫描结果在终端里对照一下比如 Claude Code 的 skills 目录ls ~/.claude/skills把输出和 GUI 列表比对数量应该一致。如果 GUI 里少了某个 Agent 的 skill先确认default_agents()里的路径和实际安装路径是否匹配不同版本 CLI 的目录约定可能不同。4.3 验证切换与模型连通点一下某个 skill 的禁用开关然后ls -a ~/.claude/skills | grep disabled能看到.xxx.disabled标记文件就说明写入成功。接着验证 Agent 侧模型接入是否正常以 Claude Code 为例配置好环境变量后发一条测试请求export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_API_KEY你的key claude -p 回复 ok返回ok说明模型层通了此时 skills 管理台和模型接入是两条独立但互补的链路互不干扰。5. 本篇常见错排查扫描结果为空九成是路径不对。先在终端echo $HOME确认主目录再逐个ls检查default_agents()里的路径是否存在。Windows 下注意反斜杠和dirscrate 返回的路径格式不要自己拼字符串。同名 skill 被覆盖如果两个 Agent 下同名 skill 实际内容不同合并成一条会丢信息。可以在SkillEntry里加paths: VecPathBuf保留每个 Agent 下的真实路径前端展开显示。toggle 后 Agent 仍加载 skill标记文件方案只是演示真实 Agent 不一定认这个标记。正确做法是改各 Agent 的配置文件如settings.json里的 skills 白名单或者把目录移出 skills 根目录。改配置前先备份原文件。Tauri 编译报 serde 版本冲突Cargo.toml里统一 serde 版本serde { version 1, features [derive] }不要混用 0.9 和 1.x。前端 invoke 报 command not found检查generate_handler!里是否注册了对应命令名前端传参的 key 要用 camelCaseTauri 会自动转 snake_case。模型请求 401确认 API Key 没写错、没多余空格base_url 用https://taotoken.net/api不带路径后缀。Anthropic 协议和 OpenAI 协议的 Agent 要分别对照接入文档确认字段名。6. 把管理台和接入层串起来到这里一个能扫描、查看、切换多 Agent skills 的 Tauri GUI 骨架就跑通了。Rust 后端负责文件系统这块脏活前端只管展示和交互职责清晰后续加「skill 市场安装」「跨 Agent 同步」也就是多写几个 command 的事。实际用下来我建议把模型接入和 skills 管理分开维护模型层用 TaoToken 统一Agent 侧只改一次 base_url 和 keyskills 层用这个管理台集中管。需要生成 key 就去 API Keys 页面接入细节查接入文档想先在网页里试模型就开模型对话长期跑编码任务再上 Coding Plan。这样一套组合下来多 Agent 环境的维护成本能压到很低剩下的精力留给真正写代码的部分。
网站建设高端定制企业官网