微信扫码
添加专属顾问
我要投稿
从 CLI 到桌面 App,再到技能市场,Hermes Agent 的完整生态让 AI 技能触手可及。 核心内容: 1. 用户需求驱动:解决 CLI 使用门槛和技能共享问题 2. 技术选型:Tauri 2 实现轻量级桌面应用,复用 Rust 技术栈 3. 创新架构:Command API 桥接前后端,React+TS 实现友好界面
上两篇我们聊了 Rust 重写 Hermes Agent和学习闭环。这次,我们把它装进了一个桌面 App,并接上了一个技能市场。注意这个技能市场中,是可以承载我们在使用 Hermes Agent 的过程中,这匹马自己主动生成的技能。
上篇文章发出来之后,有朋友私信我:
"技能系统很酷,但我每次要用,得打开终端、敲命令、还要记参数……能不能有个界面?"
另一个问题同样被问到了:
"我的 Hermes自己创建的技能只有我自己用,有没有办法分享给别人?或者直接用别人沉淀好的技能?"
这几天的工作,其实就是针对这两个问题,就是最新两个迭代要回答的。
答案是:Hermes Desktop + SkillHub 技能市场。
我们没有做一个 Electron 应用。原因很简单——Electron 的本质是用 Chromium + Node.js 跑一个 Web 应用,打包出来随随便便 200MB 起步。
Tauri 的思路完全不同:
更重要的是:我们的整个 Agent 栈(hermes-agent、hermes-tools、hermes-llm……)本来就是 Rust 写的。Tauri 的后端天然复用全部 13 个 crate,没有任何额外的桥接层。
apps/hermes-desktop/
src-tauri/ ← Rust 后端(直接 use hermes_agent::AIAgent)
src/lib.rs ← Tauri Commands 定义
src/ ← React + TypeScript 前端
App.tsx ← 全部 UI 逻辑前端:React + TypeScript + Tailwind CSS + Vite。标准现代 Web 技术栈,没有任何奇特选择。
Tauri 的核心机制是 Commands——前端通过 invoke() 调用 Rust 函数,拿到类型安全的返回值。
我们定义了一套完整的 Command API:
// 聊天核心
#[tauri::command]
async fn chat(state: State<'_, AppState>, message: String) -> Result<ChatResponse, String>
// 会话管理
#[tauri::command]
async fn list_sessions(state: State<'_, AppState>) -> Result<Vec<SessionItem>, String>
#[tauri::command]
async fn load_session(state: State<'_, AppState>, session_id: String) -> Result<Vec<DisplayLine>, String>
// 配置读写
#[tauri::command]
async fn get_settings(state: State<'_, AppState>) -> Result<SettingsView, String>
#[tauri::command]
async fn save_settings(state: State<'_, AppState>, payload: SettingsPayload) -> Result<String, String>
// 技能管理
#[tauri::command]
async fn list_skills(state: State<'_, AppState>) -> Result<Vec<SkillListItem>, String>
#[tauri::command]
async fn install_market_skill(state: State<'_, AppState>, slug: String) -> Result<SkillInstallResult, String>这个设计有个关键点:AppState 里的 AIAgent 是真实的 Rust Agent 实例,不是什么 HTTP 调用或者进程间通信——前端和 Agent 运行在同一个进程里,调用开销接近于零。
struct Session {
agent: hermes_agent::AIAgent, // 真实的 Agent 实例
history: Vec<Message>,
model_label: String,
}整个 UI 分三个主视图,通过左侧边栏切换:
① 聊天界面(Chat)
左侧是历史会话列表,右侧是对话区域。几个设计细节值得说:
<details> 块,默认收起。不想看细节的用户不会被干扰,想 debug 的用户点开就有。// 工具日志渲染逻辑
if (item.kind === "tools") {
return (
<details className="group rounded-md border border-dashed border-primary/30">
<summary>⚡ 工具执行日志({item.logs.length} 条)</summary>
{/* 展开后显示每条工具调用 */}
</details>
);
}react-markdown + remark-gfm 渲染,代码块有语法高亮样式,表格、列表都正常展示。② 设置界面(Settings)
模型名称、Base URL、API Key、流式输出开关、记忆系统开关、会话持久化……所有配置都在这里,保存后直接写入 config.yaml,无需重启。
③ 技能中心(Skills)
这个面板是这次迭代的重头戏,下面单独展开。
之前的技能系统是完全本地的:Agent 自己创建技能,存在 ~/.hermes/skills/ 目录,只有你自己能用。
这带来了两个问题:
SkillHub 要解决的就是这个问题:让技能成为可流通的资产。
SkillHubClient我们在 hermes-skills crate 里加入了 SkillHubClient,封装了和技能市场 API 的全部交互:
pub struct SkillHubClient {
base_url: String,
client: reqwest::Client,
}
impl SkillHubClient {
// 拉取分类列表
pub async fn categories(&self) -> Result<Vec<String>, String>
// 获取推荐技能
pub async fn recommended(&self, limit: u32) -> Result<Vec<RemoteSkillSummary>, String>
// 分页浏览(支持按分类筛选)
pub async fn list_skills(&self, category: Option<&str>, page: u32, page_size: u32)
-> Result<RemotePage<RemoteSkillSummary>, String>
// 全文搜索
pub async fn search_skills(&self, query: &str, category: Option<&str>, ...)
-> Result<RemotePage<RemoteSkillSummary>, String>
// 获取技能详情(含完整 SKILL.md 内容)
pub async fn skill_detail(&self, id_or_slug: &str) -> Result<RemoteSkillDetail, String>
}网络不稳定怎么办?SkillHub 服务器挂了怎么办?
我们设计了一套 优雅降级 机制,通过 CatalogDataSource 枚举追踪数据来源:
pub enum CatalogDataSource {
Remote, // 来自 SkillHub 服务器
Seeded, // 来自内置精选(离线可用)
Mixed, // 服务器数据不足,混合了内置精选补充
}每个请求都有 _with_fallback 版本:
pub async fn recommended_with_fallback(
&self,
limit: u32,
) -> (Vec<RemoteSkillSummary>, CatalogDataSource) {
match self.recommended(limit).await {
Ok(remote) if !remote.is_empty() => {
// 远程数据够用,直接返回
// 如果远程数量少于 seeded,混合补充
if remote.len() < seeded.len() {
(merge(remote, seeded), CatalogDataSource::Mixed)
} else {
(remote, CatalogDataSource::Remote)
}
}
// 网络失败?用内置精选,用户完全无感
_ => (seeded_recommended(limit), CatalogDataSource::Seeded),
}
}离线状态下,技能市场仍然可以浏览和使用内置精选。等网络恢复,自动切回远程数据。
技能中心界面分两个 Tab,界面前面已经给过了:
「已安装」Tab:展示本地技能列表,可以查看详情、删除、直接编辑 SKILL.md 内容。
「市场」Tab:
// 安装技能只需要一行
const result = await invoke<SkillInstallResult>("install_market_skill", { slug });安装本质上是把技能的 SKILL.md 内容写入本地 ~/.hermes/skills/<name>/SKILL.md,Agent 下次启动时自动发现并加载。
除了新功能,这次迭代也做了大量质量提升工作。
user_profile 工具现在支持按置信度过滤:低置信度(observed,0.7)的事实在空间不足时会被自动淘汰,只有用户亲口确认(stated/corrected,1.0)的高置信度事实才会稳定保留。
这意味着 Agent 对你的画像会越来越准——不确定的猜测自然消退,确定的认知持续强化。
AgentCallbacks 现在支持更细粒度的事件:工具调用开始、工具调用结束、流式 token 回调,方便桌面端做实时 UI 更新(比如工具执行时显示 loading 状态)。
经过这几轮迭代,整个系统的形态已经非常清晰:
MCP │ hermes-skills│
└──────────────────────────────┬──────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────┐
│数据与知识层 │
│SQLite (会话历史 + FTS5)│SKILL.md (本地技能库) │
│MEMORY.md / USER.md│SkillHub (远程技能市场)│
└─────────────────────────────────────────────────────────┘" data-show-line-number="false" style="font-size: 90%;border-radius: 4px;display: -webkit-box;padding: 0.5em 1em 1em;overflow-x: auto;text-indent: 0;color: inherit;background: none;white-space: nowrap;margin: 0;font-family: 'Fira Code', Menlo, Operator Mono, Consolas, Monaco, monospace;">┌─────────────────────────────────────────────────────────┐
│ 用户接入层 │
│ hermes CLI │ Hermes Desktop │ Gateway (Telegram…) │
└──────────────────────────────┬──────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────┐
│ Agent 核心层 │
│ hermes-agent (AIAgent 主循环) │
│ hermes-llm │ hermes-tools │ hermes-mcp │ hermes-skills│
└──────────────────────────────┬──────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────┐
│ 数据与知识层 │
│ SQLite (会话历史 + FTS5) │ SKILL.md (本地技能库) │
│ MEMORY.md / USER.md │ SkillHub (远程技能市场) │
└─────────────────────────────────────────────────────────┘三层架构,职责清晰:
无论你从哪个入口进来,你的记忆、你的技能、你的画像都是共享的。在桌面 App 里让 Agent 学会了一个 Docker 排错技巧,下次打开终端用 CLI 时,这个技能照样可用。
让我描述一个完整的使用场景:
第一次打开 Hermes Desktop,你在设置页填入 API Key,选好模型,保存。
进入聊天,你说:"帮我分析一下这个 Python 项目的性能瓶颈"。Agent 开始工作:调用 terminal 执行 python -m cProfile,调用 read_file 读源码,调用 web_search 查相关文档……工具日志自动折叠,你只看到最终的分析报告。
完成后,Agent 主动提议把这套分析流程保存为技能。你同意,技能创建完毕。
打开技能中心,在市场 Tab 搜索 "code review",发现有个评分很高的代码审查技能,一键安装。下次让 Agent 做代码审查,它会先调出这个技能里的检查清单。
三个月后,你换了台电脑。cargo build --release 编译一个二进制,把 ~/.hermes/ 目录拷贝过去,所有的记忆、技能、画像全部迁移完毕。无需重新配置任何服务。
这几个迭代做完,我意识到我们在做的事情其实已经超出了"写一个 AI Agent"的范畴。
我们在构建的是 AI Agent 的基础设施:
这套基础设施有一个核心信念:AI Agent 产生的知识,应该成为可复用的资产,绝对不仅仅是随会话消失的文字。
技能是资产。记忆是资产。画像是资产。这些资产在你的设备上,不依赖任何云服务,可以迁移,可以分享,可以越用越多。
下一步?我们在考虑 SkillHub 的社区共建机制——让用户把自己沉淀的好技能提交到市场,让这个知识网络真正活起来。
三篇文章聊完了这个项目从 0 到 1 的历程。如果你对 Rust + AI Agent 这个方向感兴趣,欢迎来 hermes-rs 看看. 注意,这个项目目前仅仅对我的小群(公众号菜单-联系我-加群)内开源。
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业
2026-04-15
真正的 Skill 商店,为什么变成了微信公众号和小红书?
2026-04-15
装了N个skills之后,鹅厂员工觉得“最香”的是哪一个?
2026-04-15
Karpathy用Claude Code的最佳实践,开源了!飙升到 3.4 万 Star。
2026-04-15
产品经理的数据可视化神器:AntV 官方 Chart Skill 开箱即用
2026-04-15
我写了个 Skill,让 AI 自动运维你的 Java 应用
2026-04-15
PumpkinClaw 1.0 正式发布:让聊天变成工作,让笔记变成资产
2026-04-14
这个开源项目把前任做成 Skill,网友:这是什么赛博受虐狂?
2026-04-14
企业Skill的准确率,为什么总是上线即翻车?
2026-03-03
2026-04-05
2026-03-03
2026-03-04
2026-03-17
2026-03-10
2026-03-17
2026-03-05
2026-03-26
2026-03-05