MoreRSS

site iconAmeow | 阿猫修改

后端工程师,写Go 和 Python,运营「猫鱼周刊」。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Ameow | 阿猫的 RSS 预览

使用 Hermes 在飞书打造 AI Agent 团队

2026-05-14 18:40:19

前言

关于如何安装和部署 Hermes 以及接入飞书,飞书官方的这两篇博客(Hermes Agent 安装与部署指南:一步步教你如何使用“爱马仕 Agent”(附飞书接入教程)Hermes Agent 全解析:与 OpenClaw 对比及飞书接入指南)已经讲得非常详细,但是这样只能拉起来一个 Agent,还不能称为「团队」,真正的团队应该有多个 AI Agent,各自负责不同的岗位。当然你可以通过在多台机器或者虚拟机上部署多个 Hermes 实例来实现这样的效果,不过 Hermes 自带了一个 profile 的功能,可以在一个 Hermes 实例上就能开出多个角色,下面我们来看看如何操作吧。

前提

你可以参考上面提到的飞书的官方教程,先安装好 Hermes 以及走通飞书的接入,走通跟 Agent 的 DM(私聊)。简要的步骤为:

curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash

source ~/.bashrc

安装完成后,按照提示选择 Quick setup,配置好模型和飞书的连接,私聊机器人,然后进行配对。具体过程参考官方教程即可,不再赘述。

创建角色

第一个 Agent 跑通后,可以开始创建新的角色。每个角色具有独立的配置、SOUL.md (人设)、记忆、会话、skill 等,可以实现职责的分离。接下来我们以产品经理和市场营销这两个角色为例,创建两个新的角色。

hermes profile create pm --clone
hermes profile create marketing --clone

这里 --clone 选项是为了直接复制模型的配置,所以后续我们还需要修改他们的 SOUL.md 来实现不同的人设区分。

接下来,我们要创建新的飞书机器人,方便我们后续在飞书中找到对应的角色。先配置第一个:

pm setup

还是选择 Quick setup,过程跟前面第一次配置的时候基本一样,选择模型这一步一路回车即可,复用之前的配置。

一直到配置消息这一步,在选择平台的时候还是回车。

这里输入 y,重新配置,然后打开链接新建一个对应的机器人,继续一路回车完成后面的流程。

完成后,点击网页上的「打开应用」,然后跟 Agent 发一个 hi,然后拿到 hermes paring approve feishu XXX 这样的配对码。这里需要注意的是,创建新的角色后,我们的命令就变成了对应的名称,所以这里要粘贴执行的实际是 pm paring approve feishu XXX

接下来我们修改它对应的人设,有两种方式,你可以直接在对应的聊天里跟它说:

修改一下你的 SOUL.md,你是某某公司的产品经理,...

也可以编辑对应目录下的 SOUL.md,例如 ~/.hermes/profiles/pm/SOUL.md

重复上面的步骤,完成 marketing 的配置。

需要注意 profile 并没有隔离 AI Agent 的工作区,也就是说,你虽然开了三个 AI Agent,它仍然可以修改别人目录下的东西,我在使用过程中就遇到修改错了别人的 SOUL.md 的情况。你可以在 SOUL.md 中指明它可以修改的目录,又或者简单让它「记住你只能修改 xx 目录」,但不能完全防止它修改到其他的文件。如果你明确需要各个 Agent 之间完全独立,还是需要部署多个 Hermes 实例。

群聊

你可以把真人和 AI Agent 都拉到一个群里,实现 AI Agent 和 AI Agent,AI Agent 和人之间的协作。先创建一个群聊,正常拉入真人,然后在设置-群机器人-添加机器人中,添加刚才创建的几个 AI Agent。

接下来有几个配置需要修改。要令机器人之间可以相互交流,需要修改 .env 文件(分别是 ~/.hermes/.env~/.hermes/profiles/pm/.env~/.hermes/profiles/marketing/.env),在底部添加一行。

FEISHU_ALLOW_BOTS=mentions

要让不同的人在群聊中跟 AI Agent 可以共享 session(可以干预别人在群里跟 AI Agent 的聊天),还需要修改一个配置。

hermes config set group_sessions_per_user false
pm config set group_sessions_per_user false
marketing config set group_sessions_per_user false

如果你觉得每次调用工具会产生大量的消息,也可以在这一步关掉:

hermes config set display.tool_progress off
(其他两个同理)

最后,重启一下对应的 gateway。

hermes gateway restart
pm gateway restart
marketing gateway restart

这里为了行文逻辑,放到了后面来修改,事实上在新增时,如果使用 --clone,修改过的这些配置会直接克隆过去,不用再手动修改了。

飞书 CLI

要让 AI Agent 访问到知识库或者在线文档等,还需要安装飞书 CLI。任选一个 AI Agent,告诉他:请帮我安装飞书CLI:https://github.com/larksuite/cli,然后根据它的提示完成授权。后续就可以操作文档了。

后续操作过程中,有可能它还需要申请新的权限,按照提示打开链接授权完成后,再@一下它即可。

Agent 协作

要想实现 AI Agent 之间的协作,可以把多个 AI Agent 拉到一个群聊里面,例如:

@产品经理 帮我把这个产品想法整理成一个 PRD。

等产品经理输出完后,再调用营销专家:

@市场营销 基于上面 PM 的 PRD,帮我提炼定位、落地页首屏文案和首发推广计划。

也可以连起来:

@产品经理 帮我把这个产品想法整理成一个 PRD,然后交给 @市场营销 提炼定位、落地页首屏文案和首发推广计划。

不过这种方式好像暂时不能由第一个机器人直接触发第二个机器人,可能是使用的 @ 的方式不对,还需要手动再触发一下。

多人使用

前面说到,你可以把多个真人也拉到群聊里面使用,当然其他人也可以跟这个 AI Agent 私聊。

其他人要使用 AI Agent 时,首先需要提交一个「应用使用申请」,管理员通过后才可以对话。也可以在飞书管理后台-工作台-应用管理里找到对应的 AI Agent,修改应用可用范围,设置为对应的成员或者全部成员。

然后需要先跟 AI Agent 私聊一个 hi,像前面一样获得一个 hermes paring approve feishu XXX 这样的配对码,然后对应在终端里执行,才可以开始私聊和群聊。如果你觉得这个过程比较繁琐,也可以对应配置 .envFEISHU_ALLOW_ALL_USERS=true

结语

到这里,一个由多个 Hermes profile 组成的飞书 AI Agent 小团队就基本搭起来了:从最初的单个 Agent,到产品经理、市场营销等不同角色,再到把它们拉进同一个群聊里协作,整个过程并不复杂,核心思路就是用 profile 来拆分角色,用飞书机器人来区分入口,再通过群聊把它们组织成一个「团队」。

当然,这套方案目前还不是一个完全成熟的多 Agent 编排系统。比如 profile 之间的文件系统并没有强隔离,机器人之间的自动接力也还不够顺滑,一些权限和配对流程对多人使用来说也略显繁琐。但好处是它足够轻量,不需要部署多套服务,也不需要一开始就引入复杂的 Agent 框架,就能快速验证「多个 AI 角色一起工作」这件事。

如果你只是想在团队内部快速搭建一个产品经理、研发助手、运营专家、客服助手之类的 AI 协作空间,Hermes + 飞书 + profile 已经是一个很值得尝试的方案。后续随着 Hermes 本身的 profile、权限、群聊协作能力继续完善,这种「把 AI Agent 当成团队成员拉进群里」的工作方式,应该还会有更多有意思的玩法。

猫鱼周刊 vol. 097 五一快乐

2026-05-03 23:28:10

关于本刊

这是猫鱼周刊的第 98 期,本系列每周日更新,主要内容为每周收集内容的分享,同时发布在

博客:阿猫的博客-猫鱼周刊

RSS:猫鱼周刊

邮件订阅:猫鱼周刊

微信公众号:猫兄的和谐号列车

私信:[email protected]

INIT

摄于香港九龙城区保良局颜宝铃书院外,是一个外墙上的壁画,看了下落款应该是在 2005 年左右画的。过去 20 年了,居然还保存得非常好,而且附近的外墙上也有很多这种壁画。

五一快乐!这周的内容不算很多,浅浅分享一下最近的所见所闻,以及摸索的一些东西吧。

STDIN

失去懒惰的代价

原文链接

作者先引用了 Larry Wall 在《Programming Perl》里提到的程序员三大美德:懒惰、不耐烦、傲慢自大。这里的「懒惰」指的是程序员会为了避免未来重复劳动,主动去寻找更好的抽象、把系统做得更简单、更容易组合。作者认为,这种「懒惰」其实非常深刻,因为它背后是愿意现在多花脑力,换未来更少的复杂度和更高的效率。作者认为现在出现了很多 brogrammer,尤其是 LLM 的出现之后,很多人炫耀写了多少代码、产出了多少功能,而不是关注抽象是否更清晰、系统是否更简洁,LLM 更是把「堆代码」的速度放大到极致」。LLM 没有「懒惰」这种美德,因为对它来说,劳动几乎没有成本。 它不会像人类工程师那样在意未来的维护成本、认知负担和系统复杂度,它会很自然地继续往屎山上继续拉屎。

我在去年中的时候就提到过,vibe coding 基本上等于十倍速生成屎山,当然那时候 vibe coding 这件事情还没有那么普及。最近公司开始给大家配 Codex 了,才算是见识到了 brogrammer 的厉害。我有一个同事,以烧光配额里的 token 为荣,正好当时 GPT 5.4 过度设计的情况非常严重,以至于我 review 他的代码的时候非常痛苦。我把这个问题提出来质问他的时候,他还一脸轻松地说「AI 的发展会解决这些问题」。我挺无语的,软件工程中一直存在着一种破窗效应,一旦开始有人往代码里面拉屎,那后续的代码质量都难以保证。

一个来自 1930 年的古早语言模型

原文链接

一般来说,我们认为模型的训练语料肯定是越新越好、越多越好,而 talkie 这个模型只用 1931 年之前的英文历史文本训练,一方面是让人能「和过去的人对话」,另一方面是把它当成研究工具,去观察语言模型在没有现代知识污染时到底会表现出什么能力。

如果你感兴趣,可以在线体验一下。我尝试问它中国有什么适合旅游的地方,它回答了上海、南京和北京,而且地名还是用的威妥玛拼音

STDOUT

语音输入

最近经常刷到一些做 vibe coding 的硬件,一个麦克风、几个按键这样,说是能提升 vibe coding 的效率;也看到不少 TypeLess 之类的软件的分享,以及各家 ASR 模型的发布等等,感觉可以重拾一下语音输入。

硬件方面,我买了大疆最近新出的 Mic Mini 2, ¥329 的手机接收器一收一发套装。因为我正好有 Action 4,可以直连,而索尼的相机也可以通过一百多的热靴转 USB-C 来实现完美的外接。发射器小小一个,夹在衣服上没什么感觉。比较惊艳的是它的降噪效果真的不错,人声收录得非常清楚。

软件方面,搜罗了一圈,主要有以下这几个:

  • TypeLess:可能是当今业界的标杆,功能最齐全效果最好;付费软件,免费有用量限制
  • 微信语音输入法:也有不少提起,但是我不喜欢使用第三方输入法(隐私泄露问题),pass
  • 豆包语音输入法:说是很强,但是 macOS 版本还在内测,没有找到安装包
  • 其他类似 TypeLess 的开源/闭源软件:在 V2EX 上一搜一大堆,试了几个,留下了 OpenLess

体验了几天,我觉得语音输入有一定的可取之处,但是我还没有完全习惯。我打字的速度应该算比较快的那一档,基本上能够跟得上说话的速度,而且一句话下来,手敲和语音识别+LLM 改写+人工修正的时间差不多,所以在输入效率上其实是没有什么提高的。跟传统的语音输入相比,基于大模型 ASR 的语音输入倒是有不少体验的提升,例如嗯啊这些语气词可以被 LLM 改写去掉,中英文混输的识别也比较精准。比较难受的是,Mic Mini 2 上不能直接触发录音,还是需要手动按键盘上的某个键才可以开始录音。当然,我看到有人做了用 Switch 手柄 + Mic Mini 2 这样的方案,可以用 Switch 上的按键绑定语音输入键、回车键、切换权限等。如果大疆后续做个功能,可以把 Mic Mini 2 上的某个键绑定成开始语音输入,那将会是绝杀。

回到语音输入这件事上,我觉得它非常适合「嘴比脑快」的情况,要想达到最佳的效果,你可以不用刻意组织语言,有什么说什么,让 LLM 帮你理清楚,还是需要不少适应的。反正暂时来说,我还是更加喜欢键盘这种输入方式。

漫步香港

五一当天我去香港逛了半天,一半时间在土瓜湾、九龙城这几个地方闲逛拍照,另一半时间在黄埔天地参加了星球大战的活动。

先说扫街这部分,因为香港已经去了很多次,这次特意选了比较小众的地方,也是市井气息比较浓的地方,所以虽然正好是五一,也没遇到什么游客,可以很舒服慢慢逛慢慢拍。香港的街道总是给人一种很破旧但是又很多彩、很干净的感觉,旧楼的外墙使用的颜色都很大胆,街道斑斑驳驳但是很干净。

正好聊到干净,我觉得除了人口素质高,另一方面是相关法例非常完善,而且执行力度很大。例如乱丢垃圾是定额罚款三千港币,在禁烟区抽烟/管有电子烟也是定额罚款三千港币,行人闯红灯是两千港币,在港铁已付车费区域饮食也是两千港币。像我经常吐槽国内的游烟问题在香港可能一次性就被罚六千港币——吸烟三千,乱丢烟头再罚三千,这种执行力度可以说能极大遏制这种行为。

日落之后,我顺路逛到黄埔天地,正好朋友出 cos,我也参加了也夜跑的活动。所谓夜跑,其实就是沿着海边步道,来回一公里左右的距离。现场估计有几千人聚集在一起,氛围非常好。

晚上回程坐的东铁线,之前我都是坐高铁或者大巴回,这次尝试了个新路线。不过东铁线的末班车在九点四十几的样子,所以如果再晚又买不到高铁可能就要坐大巴去罗湖了,有机会再试试吧。

MISC

编码套餐看板

网站链接

现在各家 Coding Plan 都在限购,就算买到限流的情况也很严重,国外的套餐也不好买。这个网站提供了各家 Coding Plan 的方案比较,以及性能指标,可以帮你快速对比选择。

不过截至现在(2026.05.03),里面有些信息可能不准确了(例如阿里的这个 Coding Plan 已经下架了),最终还是以供应商的页面为准。

Pokopia 数据库

网站链接

Pokopia 的在线图鉴。比较可惜的是 UI 没有做到像游戏里面那样的。

hyperframes

项目链接

通过编写 HTML 来渲染视频,而且支持通过 AI Agent 来生成。项目提供了对应的 Skills,你给出一个创意或者方向,就可以让 AI Agent 来生成对应的动画脚本,TTS 配音,然后合成视频。

我写了一个 skill,可以用来生成一段短视频,解释某一个概念,例如公私钥加密的概念这样。

OpenLess

项目链接

TypeLess 的开源替代。主要支持语音识别,以及后续的润色功能,自定义词典的功能也有。实测下来搭配豆包 ASR 体验不错。

EOF

本周刊已在 GitHub 开源,欢迎 star。同时,如果你有好的内容,也欢迎投稿。如果你觉得周刊的内容不错,可以分享给你的朋友,让更多人了解到好的内容,对我也是一种认可和鼓励。(或许你也可以请我喝杯咖啡

另外,我建了一个交流群,欢迎入群讨论或反馈,可以通过文章头部的联系邮箱私信我获得入群方式。

猫鱼周刊 vol. 096 AI 无法取代胶片

2026-04-26 22:06:05

关于本刊

这是猫鱼周刊的第 97 期,本系列每周日更新,主要内容为每周收集内容的分享,同时发布在

博客:阿猫的博客-猫鱼周刊

RSS:猫鱼周刊

邮件订阅:猫鱼周刊

微信公众号:猫兄的和谐号列车

私信:[email protected]

INIT

好久不见。

这张图摄于香港赤柱浅水湾海滩,去年七月底去的,终于把胶片洗出来了。这卷是 Harman Phoenix 2,一个蓝紫色的片基,说是照片蓝色表现会比较好,但是实际冲扫之后效果比较难看,倒是自己调了一下色之后好看很多。胶片比数码的宽容度高很多,而且更有 vibe,看着这张照片我感觉自己又回到那个海边,灿烂的阳光照在蓝色的海面上,沙滩上的救生艇闪闪发光。

话说回来,又快一个月没有更新了,这个周刊已经快要变成月刊了。最近工作上比较忙,压力也比较大,加上在练车,所以周末剩余的时间精力就比较少了。我不确定周刊这件事后续会变成什么样子,不过周刊肯定不会不辞而别,如果有一天我觉得实在写不下去了,一定会让它体体面面结束。

STDIN

「离开的理由」 vs 「留下的意义」

原文链接

作者和同事讨论到「一个人到底为什么会选择长期留在一家公司」,同事觉得「觉得既然进来了,只要公司没做出什么触及底线、让他过不下去的事」就可以一直留在这家公司,而作者会一直评估当前的平台能否满足他未来一段时间的发展、产出的价值和他的成长是否对等、这些东西他是否看中等等,寻求的是「留下的意义」。

我觉得我是那种「既来之则安之」的人。我在现在这家公司已经工作了四年多,从毕业前实习开始,一直就在这里,算是我人生中第一份也是唯一一份工作,久得以至于我觉得相当于在这里又读了一遍大学。虽然从实习的时候就动过心思找所谓「大厂」或者更高薪的职位,一直到现在也时不时觉得不如辞职,但是我一直都没有付诸实际行动,一是因为我很喜欢安稳的环境,太多变数和未知会使我不安,反而压力更大;二是我一直没想好如果换一份工作,那我要做什么,跟现在的工作会有不一样(开发的工作其实千篇一律都是跟产品运营扯皮、给前任开发拉的屎擦屁股);三是校招的时候经历的几次所谓「压力面」让我对面试这件事始终有点 PTSD。在现司有一个比较好的地方是时不时有一些探索性的任务,可以给到我机会实践一些新颖的东西(我称之为「造火箭」),而不是重重复复做一些费力不讨好的事情(我称之为「打螺丝」)。如果换公司,「造火箭/打螺丝」工作的比例是否还能保持现在这个水平就不好说了。一来一去,居然让我找到了一点平衡,虽然在现司的工资不算很高,但是压力尚可,做的事也算有点价值,也懒得折腾跳槽,居然就留下了。

我身边有的同龄朋友在几年间已经跳槽了几次换了两个城市工作,等于是我的反面。其实这也是我觉得反复跳槽其实意义不大的原因,不管你跳几次槽,加多少薪,这个公司入职的时候觉得如何好,过一段时间(最多两三年)就会厌倦,产生很多负面的情绪。不过,写到这里我都没怎么提钱的事,跳槽的功效之一就是涨薪,如果你很在乎工资的差距,那前面这些想必你也不会太在意了。

Things you didn't know about indexes

原文链接

一篇关于 PostgreSQL 索引的科普。其实重点不是在索引本身这些知识点,而是我觉得国外的学习路线似乎跟国内有比较大的差异。回忆了一下索引的学习过程,大概是这样:

  • 数据库原理这门课学习了数据库的基础,知道索引可以用来加速检索,但是没深入讲常用的索引类型,以及怎么建索引
  • 当然在学这门课之前,在做一些项目的时候其实已经用过 MySQL,但对索引还是没什么概念
  • 临毕业找工作,八股里突然就有很多相关的概念,什么最左前缀原则,一头雾水硬背
  • 工作后,由于性能是很现实的问题,建表的时候才开始认真考虑索引这件事

我想突显的问题是,国内的教学方法很多知识很难串联起来,尤其是「八股」严重破坏了渐进式学习的过程,更蛋疼的是毕业的时候外面的公司就默认你可以一步登天,时至今日我不少同事八股倒背如流但在排查问题的时候就是不能把现象和原理串联起来解释清楚。

在看了非常多国外的文档之后,我发现他们的学习方式其实是渐进式的,国内的学习方式则是拼图式的。例如说组成一个系统需要一百块积木,国内大学的课程会单独讲解这一百块积木每一块是怎么样的,但不会讲解积木之间的关系,或者怎么把积木拼起来用;而八股则是考你这些积木中很不常见的那一块,它有什么奇怪的窍门;而你毕业找工作的时候,公司就会要求你把这一堆积木拼起来,还要利用起某些积木的怪癖。相比之下,国外的学习方式则是先给你一个简化版的积木,例如说十块,让你先把整个系统搭起来跑通,再去细化里面一块积木其实可以深挖出很多知识点。如果有一些积木平时我们不用去深究(例如这里的索引),可能到职业中后期再去慢慢学习(也可以说是「解决问题导向的」)。

也可以换个说法,是广度优先 vs 深度优先,是了解更多的概念和他们之间的关系,还是深入学习每一个概念。在当今这个年代,我会觉得广度优先学习的人更有优势,因为你不是闭卷考试,深度相关的东西可以随时查阅文档,甚至在这个年代可以交给 AI,而广度直接决定你是否能识别到这是什么问题,应该去查什么地方,或者以什么方式向 AI 发问/下指令。

STDOUT

AI 无法取代胶片

因为我一次囤了四卷胶卷一起去冲洗,所以这次一次性洗出来非常多照片,之前一卷一卷记录的方式太多了,索性放在周刊里分享吧。

某个周末的傍晚,从海雅缤纷城吃完寿司郎出来,刚好看到傍晚太阳温暖柔和的光线,打在广场的鸽群上,旁边正好有个妈妈扶着孩子蹒跚学步,很温暖的一幕。

一个晴天的下午,广州六运小区,非常鲜艳的颜色,红蓝色的球场,明黄色的外墙,同样红蓝色的店招。最近因为火灾隐患,这条街可能光辉不再了,这可能也是绝版光景。

某个下午,深圳大芬油画村,一束光正好打在油画女主人公的眼睛上。

香港,天星码头旁的钟楼,蓝色的天,明亮的阳光打在钟楼上。

之所以说「AI 无法取代胶片」,一方面是胶片理论上保存了按下快门那一刻物理存在的东西,而不是数码照片里的一堆 0 和 1,在冲洗之后你可以获得看得见摸得着的底片;另一方面,用 AI 生成的照片没有「灵魂」,没有记忆,没有像我展示的这几张照片一样,即使过去很长时间,也能回想起在哪里拍的,回想起当时的环境甚至体验。

近期玩的一些游戏

最近这周开坑了两个新游戏,都非常值得推荐。

首先是《PRAGMATA》,刚好在 B 站上刷到发布的预告(是的,我是前几天才刷到的),又顺着去看了之前跳票的道歉视频(《Pragmata》发售延后通知《Pragmata》延期通知 - 与大家见面仍需时日),戴安娜实在是太可爱了,于是激情购入。

犹豫了一下买 PS5 版还是 Steam 版,看到网上分享说在 PC 上可以支持 PS5 手柄的震动和自适应扳机之后,我选择了买 Steam 版。进游戏之后,画质调高,记得关掉 DLSS 和插帧,因为卡普空的头发丝做得太细致了,打开 DLSS 后会有黑影一直闪烁非常影响体验;另外一点是游戏可以在设置里面切换 PS5 手柄的图标,如果你使用 PS5 的手柄来游玩记得切换一下。

游戏玩法主要就是右手走格子破解之后 FPS 射击,可以搭配不同的武器(也可以说道具)和戴安娜的技能,实现更高输出。主角本身有位移,可以打出极限闪避然后有子弹时间,操作上限其实比较高。游戏整体是箱庭式关卡,跟剑星有点类似,有点像剑星的 FPS 版,不过对操作的要求相对低很多,正常难度打下来也不怎么卡关。每一章的剧情大约一两个小时,我还没打完,网上说法是十个小时左右的量。有人吐槽太长,作为打工牛马精力不错,这不怎么长的剧情反而更有机会被我打完,反而是优点了(x。另外还有一个很有意思的就是在避难所里面可以收集打印一些场景和道具给戴安娜玩,也可以跟戴安娜聊天,还会解锁戴安娜画的画,真的非常有意思,这个交互非常独特。

另一个是 Pokemon Pokopia。我之前没玩过宝可梦系列的游戏,动森我浅玩过但也不是受众,但是 Pokopia 玩了一下午觉得挺上头。这个游戏非常休闲,没有什么挑战,失败没有惩罚,任务没有先后,你可以慢慢探索,非常治愈。而且在 Switch 上玩,你可以随时暂停,随时又捡起来玩,挺适合在 vibe coding 的间隙用来填补时间。

MISC

docmd

项目链接

一个静态文档网站生成器,类似 Docusaurusstarlight等,有比较完善的 AI 支持(可以生成 llms.txt)。我体验了一下,主要是上线文档类静态网站的体验是最好的,非常开箱即用,Marked 的文档就是用它搭建的,半个小时就搞定了这个文档站的建设。

Iconwiz

网站链接

一个用 AI 制作 App icon 的工具。提供了 GPT Image、SeedDream、Nanobanana 等系列模型的生成,支持 iOS、Android 等规格直接打包导出。

每次生图会消耗 1 点数,注册会赠送 5 点数,基本够你生成一个项目的 icon 了。点数可以购买,也可以订阅,单独购买的话最低是 $5 50 点数,我不是稳定需求,临时用用,就充了 $5。

EOF

本周刊已在 GitHub 开源,欢迎 star。同时,如果你有好的内容,也欢迎投稿。如果你觉得周刊的内容不错,可以分享给你的朋友,让更多人了解到好的内容,对我也是一种认可和鼓励。(或许你也可以请我喝杯咖啡

另外,我建了一个交流群,欢迎入群讨论或反馈,可以通过文章头部的联系邮箱私信我获得入群方式。

猫鱼周刊 vol. 095 谨防赛博夺舍

2026-03-29 22:16:45

关于本刊

这是猫鱼周刊的第 96 期,本系列每周日更新,主要内容为每周收集内容的分享,同时发布在

博客:阿猫的博客-猫鱼周刊

RSS:猫鱼周刊

邮件订阅:猫鱼周刊

微信公众号:猫兄的和谐号列车

私信:[email protected]

INIT

好久不见。这张照片是某天下午下楼摸鱼的时候拍的,公司之前的楼下转角处有个茶餐厅,在角落这里搞了个小狗幼儿园,图片里的两位是店里的,时常能看到不少小狗在这里聚会玩耍。我不是很喜欢狗的人,但是在工作累了的时候下楼看到小狗在阳光下奔跑玩耍,还是很治愈的。以至于公司要搬走了,我的第一想法居然是,啊那我再也不能摸鱼下楼看小狗了。

Anyway,最近这段时间也挺忙的,开始学车,也尝试写了一下 iOS App。后面慢慢细聊。

STDIN

Apifox 供应链投毒攻击 — 完整技术分析

原文链接

Apifox 因未严格启用 sandbox 参数,并暴露了 Node.js 的 API 接口,导致攻击者可通过 JS 控制 Apifox 的终端,启动过程中加载了一个被投毒的脚本,采集了用户的 .ssh 目录以及命令行历史等。

本来我很久都不使用 Apifox 了,最近偶然做需求打开了几次,没想到就中招了。我写了一个脚本,根据文章的描述,识别是否有文章中提到的 IoC 和本地痕迹,以及本地有什么高风险的凭据材料需要轮换。运行之后发现我是妥妥中招了,所以这周一个非常蛋疼的事情就是找到一个方式可以避免这种攻击,然后轮换全部的密钥。

最近类似的事情还有 LiteLLM 也遭到了类似的供应链投毒攻击,这种针对开发者的攻击防不胜防,不管你使用商业软件还是开源软件都避免不了,防范的手段还是控制本地不要留存这种敏感凭据。另外有个大家可能都很容易忽视的点,那就是避免在命令行中明文输入密码,就是例如通过 -p xxx 这样去传递密码参数,这样你的密码就会留存在历史中,可以用环境变量或者 interactive 的方式去输入。

当 AI 陷入沉思:长时程思考下的有效交互设计研究

原文链接

当 LLM 出现思维链之后,大家发现如果让 AI 不断「自言自语」,可能会得到更加完善的结果,所以「深度思考」应运而生。对应的界面交互上,从一开始全部流式展示,到后面逐渐迭代出一些动画、结构化过程等等,这篇文章做了很深入的思考和研究。

不得不说,文章中多次提到 Claude,在实际使用中,我确实也觉得是 Claude 的使用体验最佳,不过这种交互体验实际上算是「锦上添花」,不算得上非常重要的选购因素。

另外我觉得有一个交互的点可能暂时没有人考虑到,以及由于现在龙虾类产品的出现,可能令这个问题加剧—— AI 什么时候完成了工作。作为「略懂」的人,我会知道结束的边界在于模型输出 EOS token,一般的实现就是到这里就不再刷新,然后气泡底部出现交互。这看起来没什么问题,但是复杂情况就在于,如果这时候用户让 AI 做一些很复杂的事情,但是 AI 并没有这个能力,却又像模像样地骗用户自己正在做 ppt,并且通过邮件发给他,不少用户信以为真,而且在发现 AI 做不到这个之后产生巨大的挫败感。对于传统的在线聊天式 AI,只要生成结束了,气泡不再刷新了,那就是结束了,再发消息就是一轮新的交互。而龙虾又复杂化了这个事情,因为它有后台进程,甚至不管你有没有发消息,它其实都在工作,所以这时候你发消息问进度是有意义的。

我们如何在 Cursor 中比较模型质量

原文链接

Cursor 团队搞了个 CursorBench 来评测不同的模型,任务来自真实的 Cursor 用量,而不是公开代码仓库,CursorBench 不仅更能区分不同模型,也比公开基准更贴近开发者的真实结果。他们发现现在的公开基准存在不少局限,在前沿水平上,这些基准已经无法区分那些对开发者而言实用价值截然不同的模型。他们构建的 CursorBench 会使模型的区分度更大,更加能体现 coding 场景的能力。

评测的结果倒是没什么特别意外的,GPT 5.4 最强,其次是 Opus 4.6,倒是声称对标 Opus 4.5 的 GLM-5 实际的差距还蛮大。比较意外之前传出来他们的 Composer 2 使用的微调 Kimi K2.5 居然没有出现在榜单里。

他们的实践说明,在实际使用的时候,还需要根据具体的场景来构建 benchmark,光看各种模型吹得天花乱坠意义不大,大家在公开基准上都很卷,你的实际场景用起来很可能又不是那样。

STDOUT

把 SSH 私钥放进 YubiKey

文章链接

在 Apifox 供应链投毒事件之后,我目前风险最大的就是本地使用的 SSH 密钥。这个东西自从配开发环境的时候就生成了,然后一直放在那里,很少再去管它。在跟 ChatGPT 探讨之后,它给我几个方案,要么使用例如 1Password SSH Agent 之类的方案去管理 SSH 密钥,要么使用 FIDO2 SSH key,或者用 Yubikey 的 PIV/OpenPGP。第一种方案需要另外投入成本,而 Yubikey 我手头就有好几个,用 GPG 签名也很久了,所以最终使用了 gpg-agent 的这种方案。另外值得一提的是,FIDO2 这种方式其实非常好,主流服务也支持,但 *-sk的公钥还是在很多地方有兼容性问题,所以用不了。

说实话,这个折腾起来还挺麻烦的,如果你之前没有 Yubikey,硬件投入也不少,然后把整套流程跑通也花了我大半天。之后每次使用的时候,还要把 Yubikey 插入电脑并输入 pin。好处是以后我大概再也不用担心 SSH 私钥被盗的事情了。

回应标题,如果你的 SSH 私钥就这么生成之后被遗忘在 .ssh,你很容易就被「赛博夺舍」,我这个方案不妨考虑一下。

Marked

尝试用 Swift 写了一个原生 iOS App,至此,前端后端安卓 iOS 我都有成型的作品了,虽然在这个过程中主要的功臣是 AI。总之,这是一个记录类型的 App,除了简单记录某件事件以外,还可以填入一些自定义字段,例如健身时练的部位、有氧的事件等等,后续可以根据自定义字段来做分析;还有一个有意思的功能是,可以记录「已经多久没做 x 事」这种戒断类型的事件,除了培养好习惯,戒掉坏习惯同样重要。如果你对 Marked 感兴趣,它现在还没有上架,你可以在 Testflight 体验。

顺便也分享一下我的开发经验。

一开始,我打算用 Figma AI 或者谷歌的 Stitch 之类先出一版设计稿,然后改下交互之类的,我最终选择了 Stitch 来尝试,主要是因为它的免费额度比较多,而且支持 MCP。我先尝试了直接在网站上输入我的简单需求,让他设计好界面以及对应的交互,但是发现它一开始只会生成首页的交互,里面具体的交互需要你一步一步跟他聊天才会有,而且具体的字段每个页面可能都不一样。倒是有一个很突出的点,它会先生成一个 DESIGN.md,所以所有界面都可以获得比较一致的设计风格。我的改进方式是先把我的初始需求写成一个 init.md,然后跟模型讨论,让他生成出一个 PRD,并且带有每一个页面的交互和字段的描述,再根据这个文档,让他调用 MCP 来生成页面,最终能生成出全部页面,字段设计等看起来也没什么大问题。

但是最后我实现的时候,发现这个设计稿完全没有作用,我的工作流程里不涉及 UI 设计,我的交互其实文字已经描述清楚了。更糟糕的是,这个设计稿里的每一个 screen 其实只是一个 html,Figma 的也是类似的设计,而我要写的是 Swift 的原生 UI,设计稿没法反映苹果原生的 UI,而且生成出来的 screen 经常出现一些无意义的边框或者评论等,修改起来也很费劲。

另外踩的一个坑是,有一个 Emoji 选择器,一开始用的是一个开源的包,但是在新版本 iOS 上有个 bug,会导致下面的类别选择器有异常。AI 卡了很久都没有解决,直到我在 GitHub issues 里找到一个对应的问题,并且顺藤摸瓜找到了一个修复的 fork 才解决。现在 AI 很大的问题就是经常遇到问题无法解决,需要人类介入。我觉得 Skill 也许是这个问题的解决方案,因为「开源的包遇到问题-去翻翻 issues」这个流程其实是我的经验,本质上这些操作它也可以完成的。另一方面大家也许可以不那么焦虑被 AI 取代,因为它们遇到小小的问题可能就会卡住。

MISC

Humanizer-zh

项目链接

一个用于去除文本中 AI 生成痕迹的 Skill。之前做了很多关于 AI 生成检测的研究,看到这个感觉还是挺有意思的,让 AI 生成更加拟人。

project-nomad

项目链接

一个离线的知识库,打包了 AI 助手、离线地图等工具,不过安装时仍然需要联网。整个项目的背景有点「末日风」,而且部署的成本还不低(根据项目的推荐硬件,甜点配置大概需要 500-800 刀,需要本地能跑 LLM),实用性不算很强。不过如果你已经花了很多钱搞末日地堡,这个价格估计你会承受得起(x。

CanIRun.ai

网站链接

一个自动检测你的硬件配置,并计算你的电脑能运行什么 AI 的工具网站。比较有意思的是,它除了会给你预估的 VRAM 需求,还会根据你的设备的具体硬件配置(VRAM、RAM、内存带宽等)估算不同量化下的输出速度,以及不同量化的效果来供你评估。

EOF

本周刊已在 GitHub 开源,欢迎 star。同时,如果你有好的内容,也欢迎投稿。如果你觉得周刊的内容不错,可以分享给你的朋友,让更多人了解到好的内容,对我也是一种认可和鼓励。(或许你也可以请我喝杯咖啡

另外,我建了一个交流群,欢迎入群讨论或反馈,可以通过文章头部的联系邮箱私信我获得入群方式。

把 SSH 私钥放进 YubiKey:一套更安全、可迁移的密钥管理方案

2026-03-27 02:48:38

前言

近期供应链投毒事件频发,针对开发者工作流的攻击也越来越常见。无论是 Apifox、LiteLLM 这类事件,还是其他面向本地开发环境、依赖链和工具链的攻击,防范起来都不容易。一个更稳妥的思路,是尽量不要让私钥以普通文件的形式长期留在本地设备中;如果把私钥放进独立的硬件里,即使本机环境被入侵,也能在一定程度上降低密钥失窃的风险。

YubiKey 是 Yubico 推出的一种硬件安全密钥,通常是一个小型的 USB 设备,也有支持 USB-A、USB-C、NFC 等不同接口和连接方式的型号。它本质上是一块专门用于保存和使用敏感凭据的安全硬件,可以用于网站登录的双重验证,也可以承载 OpenPGP、PIV、FIDO 等类型的密钥材料。

在 SSH 的场景下,使用 YubiKey 的核心价值在于:让私钥尽量不离开硬件设备本身。相比将私钥长期保存在电脑磁盘里,这种方式可以显著降低因主机被入侵、文件被窃取、备份泄露或误拷贝而造成的风险。日常使用时,你仍然可以像平时一样完成 GitHub 认证和服务器登录,但真正的签名操作是在 YubiKey 内部完成的,外部能够获取的只有公钥,而无法直接导出私钥。

本文介绍一套基于 OpenPGP 子钥的实践方案:通过离线保存主密钥、将子钥写入 YubiKey、准备多把备用设备,并结合定期续期与轮换机制,在提升 SSH 密钥安全性的同时,也兼顾多设备使用、硬件损坏后的恢复能力,以及日常认证的可维护性。限于篇幅,本文不会深入介绍过多背景知识,目的是让读者迅速走通流程,把自己的密钥保护起来。

前期准备

首先你需要准备一个 Yubikey 或者类似的硬件安全密钥。建议购买 Yubikey,型号为 Yubikey 5 NFC 和 Yubikey 5c NFC,区别是前者是 USB-A 接口而后者是 USB-C 接口。另一个值得关注的点是固件版本,Yubikey 的固件出厂之后就不可升级,不同固件对加密算法的支持不同,功能特性也有差别,详见Firmware Overview。常见可以购买到的版本一般为 5.4.3(前两年活动有优惠,我也囤了几个) 和 5.7.x(最新版本),在日常使用和本文所使用的功能来说区别不大。

另外,也有一些国产的平替可供选择(例如 CanoKey),但是这类产品的兼容性以及对应的文档、社区支持相对都比较差,不建议选择。

接下来的操作,有观点建议使用虚拟机或者干净安装的系统甚至无网的终端来完成,如果有条件,可以自行折腾。便捷来说,断网操作也可以了。

安装依赖

brew install gnupg yubikey-personalization ykman pinentry-mac wget

准备 Yubikey

$ gpg --edit-card

Reader ...........: Yubico YubiKey OTP FIDO CCID
Application ID ...: [MASKED]
Application type .: OpenPGP
Version ..........: 3.4
Manufacturer .....: Yubico
Serial number ....: [MASKED]
Name of cardholder: [not set]
Language prefs ...: [not set]
Salutation .......:
URL of public key : [not set]
Login data .......: [not set]
Signature PIN ....: not forced
Key attributes ...: rsa2048 rsa2048 rsa2048
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
KDF setting ......: off
UIF setting ......: Sign=off Decrypt=off Auth=off
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]

接下来先设置两个 pin,一个是平常解锁用的 pin,另一个是管理 pin(用于 --edit-card 的)。

gpg/card> admin
Admin commands are allowed

gpg/card> passwd
gpg: OpenPGP card no. [MASKED] detected

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection?

分别输入 1 和 3,根据提示来修改 pin,默认的 pin 为 123456,管理 pin 为 12345678,4 的恢复代码按需求设置(不是必须的)。然后也可以设置一下 name lang url 之类,如果你设置了会在 gpg --card-status 展示对应的信息,这个不是必须的。如果你有多个 Yubikey,重复以上操作即可,当然也可以不在一开始先设置好全部 Yubikey 的信息,在后面换卡的时候再设置也行。

生成密钥

$ gpg --expert --full-generate-key
gpg (GnuPG) 2.4.9; Copyright (C) 2025 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
   (9) ECC (sign and encrypt) *default*
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (13) Existing key
  (14) Existing key from card
Your selection? 9
Please select which elliptic curve you want:
   (1) Curve 25519 *default*
   (2) Curve 448
   (3) NIST P-256
   (4) NIST P-384
   (5) NIST P-521
   (6) Brainpool P-256
   (7) Brainpool P-384
   (8) Brainpool P-512
   (9) secp256k1
Your selection? 9
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: user
Email address: [email protected]
Comment:
You selected this USER-ID:
    "user <[email protected]>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O

输入 O 回车之后会弹出一个 passphrase 的框,建议设置一个密码。

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: /tmp/gpg-5gJAI/trustdb.gpg: trustdb created
gpg: directory '/tmp/gpg-5gJAI/openpgp-revocs.d' created
gpg: revocation certificate stored as '/tmp/gpg-5gJAI/openpgp-revocs.d/EF5D701CAEB2283BB8D10305F43A691EE48B10EC.rev'
public and secret key created and signed.

pub   secp256k1 2026-03-26 [SC]
      EF5D701CAEB2283BB8D10305F43A691EE48B10EC
uid                      user <[email protected]>
sub   secp256k1 2026-03-26 [E]

这时候主密钥就生成好了,这里的 EF5D701CAEB2283BB8D10305F43A691EE48B10EC 就是你的主密钥的指纹,或者叫 key id。

$ PRIMARY_FPR=EF5D701CAEB2283BB8D10305F43A691EE48B10EC

这时候,可以执行一次备份:

$ gpg -a --export-secret-keys "$PRIMARY_FPR" > secret-master-only.asc
$ gpg -a --export "$PRIMARY_FPR" > public-master-only.asc

现在继续生成签名、加密和鉴权使用的子钥。

$ gpg --quick-add-key "$PRIMARY_FPR" ed25519 sign 1y
(会提示你输入 passphrase)
We need to generate a lot of random bytes. It is a good idea to perform some other action (type on the keyboard, move the mouse, utilize the disks) during the prime generation; this gives the random number generator a better chance to gain enough entropy.
$ gpg --quick-add-key "$PRIMARY_FPR" cv25519 encr 1y
$ gpg --quick-add-key "$PRIMARY_FPR" ed25519 auth 1y

执行完后,可以看到新建的带有效期的子钥:

$ gpg --list-keys --with-subkey-fingerprint --keyid-format LONG "$PRIMARY_FPR"
pub   secp256k1/F43A691EE48B10EC 2026-03-26 [SC]
      EF5D701CAEB2283BB8D10305F43A691EE48B10EC
uid                 [ultimate] user <[email protected]>
sub   secp256k1/291AE24AD1CC737D 2026-03-26 [E]
      6CFBDCCF3B478466149FE7E0291AE24AD1CC737D
sub   ed25519/0202CC8049E2FB80 2026-03-26 [S] [expires: 2027-03-26]
      D3C948F3854FF25B339294140202CC8049E2FB80
sub   cv25519/B936CF741340882E 2026-03-26 [E] [expires: 2027-03-26]
      C86408158C50D2C68A0C50D0B936CF741340882E
sub   ed25519/8472C3F353CD9B06 2026-03-26 [A] [expires: 2027-03-26]
      C1B5CC41426C03A0CC0DAFF78472C3F353CD9B06

到这一步我们再备份一次,这次的备份文件需要长期、保密妥善保管,尤其是 secret-full-with-subkeys.asc,最好可以写入一个离线存储里。

$ gpg -a --export-secret-keys "$PRIMARY_FPR" > secret-full-with-subkeys.asc
$ gpg -a --export "$PRIMARY_FPR" > public-full-with-subkeys.asc
$ gpg --export-ownertrust > ownertrust.txt

写入 Yubikey

接下来就是把刚才生成的密钥写入 Yubikey。这里的主要操作有两个,一个是 key n,选中的 key 会用星号标识,再输入一次 key n 取消选择;另一个是 keytocard ,会把私钥写入 Yubikey 对应的槽中。

$ gpg --edit-key "$PRIMARY_FPR"
gpg (GnuPG) 2.4.9; Copyright (C) 2025 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

sec  secp256k1/F43A691EE48B10EC
     created: 2026-03-26  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  secp256k1/291AE24AD1CC737D
     created: 2026-03-26  expires: never       usage: E
ssb  ed25519/0202CC8049E2FB80
     created: 2026-03-26  expires: 2027-03-26  usage: S
ssb  cv25519/B936CF741340882E
     created: 2026-03-26  expires: 2027-03-26  usage: E
ssb  ed25519/8472C3F353CD9B06
     created: 2026-03-26  expires: 2027-03-26  usage: A
[ultimate] (1). user <[email protected]>

gpg> key 2

sec  secp256k1/F43A691EE48B10EC
     created: 2026-03-26  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  secp256k1/291AE24AD1CC737D
     created: 2026-03-26  expires: never       usage: E
ssb* ed25519/0202CC8049E2FB80
     created: 2026-03-26  expires: 2027-03-26  usage: S
ssb  cv25519/B936CF741340882E
     created: 2026-03-26  expires: 2027-03-26  usage: E
ssb  ed25519/8472C3F353CD9B06
     created: 2026-03-26  expires: 2027-03-26  usage: A
[ultimate] (1). user <[email protected]>

gpg> keytocard
Please select where to store the key:
   (1) Signature key
   (3) Authentication key
Your selection? 1
(按照提示输入 passphrase 和 admin pin)

在这里依次选择 key、keytocard、反选 key,直到全部 key 都写入成功。

注意,如果你需要把这一份密钥写入多个 Yubikey 中,在全部写完之后,直接按 Ctrl + C 退出,如果在这一步保存了,本地就不会再有私钥,只会留存一个 stub,实际的私钥已经在 Yubikey 里了,是无法导出的。这时候,拔出第一把 Yubikey,插入第二把 Yubikey,前面说的设置 pin 和信息可以在这一步来做,然后重复上面的流程即可,更多的 Yubikey 也同理。当你写到最后一张,确定可以继续了,就执行 save

gpg> save

现在再执行 gpg --card-status,就会看到对应的 key 前面有个 ssb>,代表密钥已经转移到 Yubikey 上;同时对应的签名、加密、鉴权三个 slot 都有对应的 key。

Reader ...........: Yubico YubiKey OTP FIDO CCID
Application ID ...: [masked]
Application type .: OpenPGP
Version ..........: 3.4
Manufacturer .....: Yubico
Serial number ....: [masked]
Name of cardholder: Leslie Leung
Language prefs ...: en
Salutation .......:
URL of public key : https://github.com/leslieleung.gpg
Login data .......: [not set]
Signature PIN ....: not forced
Key attributes ...: ed25519 cv25519 ed25519
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
KDF setting ......: off
UIF setting ......: Sign=off Decrypt=off Auth=off
Signature key ....: [masked]
      created ....: 2026-03-26 15:53:26
Encryption key....: [masked]
      created ....: 2026-03-26 15:53:45
Authentication key: [masked]
      created ....: 2026-03-26 15:54:00
General key info..: sub  ed25519/1915EA0F3C320F72 2026-03-26 Leslie Leung <[email protected]>
ssb>  ed25519/[masked] created: 2026-03-26  expires: 2027-03-26
                                card-no: [masked]
ssb>  cv25519/[masked]  created: 2026-03-26  expires: 2027-03-26
                                card-no: [masked]
ssb>  ed25519/[masked]  created: 2026-03-26  expires: 2027-03-26
                                card-no: [masked]

导出 SSH 密钥

在上面的 key 中,找到 A 字样的 key,这个代表授权,导出对应的 SSH key。

sub   ed25519/8472C3F353CD9B06 2026-03-26 [A] [expires: 2027-03-26]
      C1B5CC41426C03A0CC0DAFF78472C3F353CD9B06
$ gpg --export-ssh-key C1B5CC41426C03A0CC0DAFF78472C3F353CD9B06!
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINGvVhlehNBnCQMuNp5llACDgGYgtztvM9P66j9ReXkN openpgp:0x53CD9B06

这个公钥就是后续用来进行 SSH 登录的。

配置 SSH

编辑 ~/.gnupg/gpg-agent.conf

enable-ssh-support
default-cache-ttl-ssh 300
max-cache-ttl-ssh 7200

重启 agent。

$ gpgconf --reload gpg-agent

修改 .zshrc

export GPG_TTY=$(tty)
unset SSH_AGENT_PID
if [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]; then
  export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
fi
gpg-connect-agent /bye >/dev/null 2>&1

加载配置

$ source ~/.zshrc

GitHub 配置

点击右上角头像 - Settings - SSH and GPG keys,New SSH key,填入刚才生成的公钥,保存。

然后测试一下:

$ ssh -T [email protected]
(弹窗输入 pin)
Hi LeslieLeung! You've successfully authenticated, but GitHub does not provide shell access.

服务器配置

把对应的公钥添加到 ~/.ssh/authorized_keys,然后尝试用 ssh 登录即可。

子钥续期

一般情况下,如果密钥没有被泄露,不需要专门做什么维护。在子钥到期前,给子钥续期,然后导出更新后的公钥,在对应的机器或者网站上更新即可。这一步不需要使用 Yubikey 进行操作。

$ gpg --quick-set-expire <PRIMARY_FPR> 1y '*'
$ gpg -a --export <PRIMARY_FPR>

FAQ

忘记了 PIN

如果忘记了 pin 或者 admin pin,可以对 Yubikey 的 GPG 区进行重置,注意这会丢失里面存储的密钥。

$ ykman openpgp reset

多个 Yubikey 切换时,提示插入卡号 xx 的卡

执行以下操作:

$ gpg-connect-agent "scd serialno" "learn --force" /bye

最后

总的来说,把 SSH 密钥放进 YubiKey,并不是为了追求“更复杂”的配置,而是为了把最关键的私钥从日常使用环境中尽量隔离出来。在面对本机入侵、供应链投毒、误操作和密钥泄露这类现实风险时,这种做法虽然不能解决所有问题,但确实能把攻击者获取私钥的门槛提高很多。

更重要的是,YubiKey 的价值并不只体现在 SSH 上,它还可以承载 OpenPGP 密钥,用来做 GPG 签名,例如给 Git commit、tag、文件甚至邮件签名。签名的意义不只是证明内容是谁发出的,也能证明内容在签名之后没有被篡改;放在 Git 的场景里,它可以帮助团队确认某次提交确实来自你本人,而不是他人伪造了身份信息。

这套方案确实不算轻量,配置起来麻烦,理解成本也不低,但它换来的,是更强的私钥保护、更可信的身份认证,以及在混乱环境里更稳定的安全边界。很多时候,安全并不是让你永远不出事,而是让你在真正出事的时候,仍然能守住最关键的东西。从这个角度看,把 SSH 和签名能力都收敛到 YubiKey 里,或许正是一种值得长期坚持的做法。

References

GitHub - drduh/YubiKey-Guide: Community guide to using YubiKey for GnuPG and SSH - protect secrets with hardware crypto. · GitHub

猫鱼周刊 vol. 094 龙虾大跃进

2026-03-09 00:33:29

关于本刊

这是猫鱼周刊的第 95 期,本系列每周日更新,主要内容为每周收集内容的分享,同时发布在

博客:阿猫的博客-猫鱼周刊

RSS:猫鱼周刊

邮件订阅:猫鱼周刊

微信公众号:猫兄的和谐号列车

私信:[email protected]

INIT

又是一周,这周感觉做的自己的事情并不多,总体还是被工作消耗得比较厉害。首先是给之前做的 AI 文本生成检测 Aletheia 增加了一个新模型 DivEye,它的原理非常有意思,不需要针对新的大模型来做训练。另外就是尝试用本地的 qwen3.5-35b-a3b 模型写了一篇科幻短篇小说《2136 年,人类发明了超光速引擎》,效果出奇地好,整篇文章没什么 AI 味,逻辑、立意等都说得过去,足够我看得津津有味。

STDIN

连龙虾都不会装的人,怎么会用龙虾呢?

原文链接

这周比较火的事情,就是腾讯举办了个活动,线下安装龙虾,吸引很多人参加,不少评论说是「一代人有一代人的鸡蛋要领」。当然了,这股「龙虾潮」其实已经持续一个多月了,我的信息流里铺天盖地都是关于龙虾的内容,甚至还有一些「同城上门装龙虾」、「龙虾一体机」之类的东西,更别提各路云厂商、AI 供应商、中转站借机大力营销。

倒是有些言论非常清醒,例如「连龙虾都不会装的人,怎么会用龙虾呢?」。这句话显得有些傲慢,但是安装龙虾这件事其实真的非常简单,只要你愿意折腾,最多花上几个小时就能解决。如果你连这几个小时都不愿意折腾,只是「人有我有」地想买个门票,那真的不见得有龙虾你就能做成什么事情。

不管是龙虾,抑或是其他被炒得很火的 Claude Code 等等,他们本质上都是工具,是你解决问题中的一环,它可以是万能钥匙,但前提是你有对应的问题要解决。或者按文章的话来说,「我到底有什么问题,值得交给 AI 去解决」,我觉得这才是大多数情况下要考虑的事情。

大家给 Apple 的成绩单

网站链接

少数派的一个策划,邀请了一批嘉宾给苹果 2025 年的产品线、软件可靠性、硬件可靠性、服务、应用生态与开发者关系、社会责任与本地化这些维度去打分。

不出所料,硬件可靠性这一项得分很高,倒是不知道为啥软件可靠性有点拉跨。这其实是我买苹果最大的原因,抛开电池的因素,苹果的产品可以说是最「耐用」的,七八年前的 iPhone 8 Plus 如今居然还挺好用,同时期的安卓手机估计已经入土很久了吧。iPhone 的得分高也不奇怪,今年的标准版也可以说是牙膏挤爆,给了高刷,如果没有专业需求,性价比真的拉满(之前我都是为了高刷才买的 Pro);国补 5499 的 iPhone Air 其实性价比也非常高,那轻薄的手感是独一档的。

虽然但是,今年我还是没有换手机,手上还在用 15 Pro。最近几代还没有什么让我欲罢不能的更新,也许国行 AI 功能的缺席也是一个很重要的原因,15 Pro 买的时候就说 Ready For Apple Intelligence,让我还蛮激动的,结果一直没端出来,算是有点「诈骗」。

STDOUT

Make mistakes

刚好前面说到 DivEye这种用来检测 AI 生成文本的方法,它的原理是用一个 LM(例如 GPT-2)去预测下一个 token,计算它与实际的文本的「惊喜程度」,例如文法和结构上的变化。很显然,人类的特点就是不稳定,缺乏完美,这也是「人性」之一。

正好想起一部电影 Bicentennial Man,里面有个情节就是男主机器人 Andrew 和女主 Portia 关于「人性」的讨论,这是对应的剪辑,它的对白是这样的:

  • Take chances, make mistakes.
  • Mistakes?
  • Yes! Sometimes it's important not to be perfect, okay? It's important to do the wrong thing.
  • Do the wrong thing?
  • Yes!
  • Why? Oh, I see-- to learn from your mistakes.
  • No, to make them. To find out what's real and what's not. To find out what you feel. Human beings are terrible messes, Andrew.
  • I'll grant you that. I see. This is what is known as an irrational conversation, isn't it?
  • No, this is a human conversation. It's not about being rational. It's about following your heart.

人最大的特点就是会犯错、不完美、缺乏绝对理性,这是机器、AI 无法去实现的事情,他们的训练目标就是完美、尽可能少犯错。90 年代到千禧年左右有很多关于 AI 的电影,这部电影当时好像反响平平,但是现在 2026 年来看,确实立意很深远。

MISC

AppPorts

项目链接

一款 macOS 工具,无缝迁移应用到外部存储并自动建立链接。感觉对 256 GB 的机型应该会很有用。

LanCache

项目链接

内网缓存工具,提前把 Steam 等平台的游戏下载到本地服务器上,然后通过 DNS 劫持、透明代理等实现本地传输加速下载。

想起这个是因为在 B 站刷到博主宣传绿联和腾讯最近推出的一个新功能,所谓“NAS+游戏”生态。值得一提的是,其实 macOS 上也有类似的功能,叫做内容缓存,设置的方法也很简单。这对厂商来说最大的作用就是节约了很多带宽,这在 B 端来说也是一笔不小的钱。如果将来家家户户都有一个小型的 home server(其实现在的光猫再强大一点就行),这也许是一个不错的方案。

steam-lancache-prefill

项目链接

一个脚本工具,配合前面的 LanCache,可以不依赖 Steam 客户端触发缓存。

EOF

本周刊已在 GitHub 开源,欢迎 star。同时,如果你有好的内容,也欢迎投稿。如果你觉得周刊的内容不错,可以分享给你的朋友,让更多人了解到好的内容,对我也是一种认可和鼓励。(或许你也可以请我喝杯咖啡

另外,我建了一个交流群,欢迎入群讨论或反馈,可以通过文章头部的联系邮箱私信我获得入群方式。