2025-11-30 18:35:47
![]()
这是猫鱼周刊的第 86 期,本系列每周日更新,主要内容为每周收集内容的分享,同时发布在
博客:阿猫的博客-猫鱼周刊
RSS:猫鱼周刊
邮件订阅:猫鱼周刊
微信公众号:猫兄的和谐号列车
好久不见。上周的周末有事回了趟广州家里,周六当天来回,折腾了一趟实在是太累了,于是就咕咕了。
这周的周刊将会迎来第二次重构,就如上期所说,新的周刊会分成 INIT、STDIN、STDOUT、MISC 和 EOF 这几个板块,弱化掉以前文章、项目之类的概念,更加适应现在周刊的内容。
上周折腾了一个小东西 llmsh,可以利用本地的小参数模型(例如 qwen3-4b),配合 zsh 插件,实现命令补全、自然语言转命令等。做这个东西的来头也很神奇,我在翻东西的时候发现了一张 23 年 gpt-3.5-turbo 刚出的时候的截图,概念是利用 history 的前几条,来预测下一条命令,来实现比 zsh-autosuggestions 更加「智能」的提示。于是,我花了四五个小时搓出了这个东西,它有点像 warp,但是它可以跟你喜欢的终端一起使用,只依赖 zsh 和一个二进制,非常优雅。但是最终我也很少使用这个小东西,原因跟我不使用 warp 这类终端一样,autosuggetsions 的建议足够好用,而且绝大多数日常使用的命令我都能凭肌肉记忆打出。如果你感兴趣,不妨安装试玩一下。
另外这段时间里我学会 3D 建模,给我常用的消毒湿巾做了一个宜家洞洞板收纳,模型开源在 MakerWorld 了 link,感兴趣可以看看。
作者谈到自己最近的迷茫:
我好像一直很忙,工作确实很多,但仔细想想,除了工作之外,我似乎也说不清自己在忙什么,更说不清到底什么是重要的。
他也列出了自己很多的不足,又感叹自己总是好像原地踏步,而且这些问题早就意识到,但总是不能聚焦去改进。顺着这个思路,他还进行了很多思考,略微有点进入哲学的范畴了。
我觉得作者应该是跟我差不多年纪,刚毕业几年内的人,因为我不久之前也有过同样的迷茫,而我现在好像已经渐渐走出这个迷雾了,分享一些我的观点。
首先需要肯定的是,有这种迷茫是好事,说明你还在一个反馈循环里面不断提升自己。在日常工作中,会遇到两种人:一种是埋头苦干的人,这种人是大多数,这里的「埋头苦干」不是指在工作中的态度,更多是说他的认知更多就停留在毕业时或者第一份工作的程度,不会再去跟进行业的发展,不会再学习新的东西提升自己,在工作中对自己也不会有很高的要求,目标多数是赚钱,很多事情就是得过且过;另一种是有远见的人,他会有自己的主线,会追求自己感兴趣的事情,坚持走下去自己规划的路。所以如果你感觉到迷茫,反而是一种好事,说明你还没完全躺平。
然后要说的是,感到原地踏步,或者说一时做下这个又做下那个,在把时间拉长之后,其实也是不小的进步。离开学校之后,会发现很多东西在很短时间内是无法看到结果的,可能要拉长到年甚至数年才能看到效果。乔布斯在斯坦福的毕业典礼演讲上说过一句话叫「Connecting the dots」,我对它的理解就是,在人生路途中你会做出很多选择,或者做了很多的事,很可能在当下看不出有什么用,直到有一天你取得了成功,才发现原来这些点连成了你的成功路线。例如今年五月份的时候我就买了 3D 打印机,当时就打算要学建模,自己打东西玩。结果过去了半年,因为健身房新的跑步机没有手机支架,总是坚持不下去走有氧,有天我在跑步机上怒看了一个多小时视频,回家就把模型画出来了。在这中间,我其实把一大堆兴趣轮询了一遍,例如胶片摄影、模拟赛车、meshtastic 等等,我很少长期专注去做某一件事,也许周刊算是一个,但「兴趣轮询」算是最适合我的一种模式。另外,作者说到自己是「问题驱动」的人,我也是,我不喜欢系统性去学习某个东西,我一般遇到某个问题之后,我会去研究相关的东西,但只限于刚好解决这个问题。时间拉长之后,你发现你有很多点,每个都可以解决一定的问题,这在遇到复杂的问题的时候,你会发现自己会比别人更快地能把这些点连成线,看出某个 bug 的本质,找到对应的修复方案,或者在复杂的需求中提出一个很优雅的技术方案。
顺着「兴趣轮询」说下去,清晰认识自己、发展出适合自己的处事模式也很重要。我从小到大都被老师说不专注,现在在工作中更经常会发现,在准备去解决一个问题的时候,发现了另一个兴趣点,于是又花了大半天在这个新兴趣点上的情况。也做过一些在线测试,说是有一定的注意力缺陷。在这件事情上,我觉得这更多是我的 feature,而不是 bug(毕竟并没有严重到影响我的工作和生活)。因此我围绕这个 feature 发展出了很多效率方法,例如使用 todo list 来保存一些上下文,方便我来回切换和找回重点;又例如我会把一些繁琐的步骤用脚本自动化或者在设计上就简化,保证我不需要花很多精力去重新理解等等。
最后就是,今年一场大病之后,我发现自己做到了「见攰就唞」(觉得累就歇歇)。以前我经常会有爆肝的心态,很着急出结果。现在更多觉得如果已经累了,那就先歇歇,才有更多时间精力甚至健康的身体去做想做的事情。
今天这个部分写得好长,算是记录自己走出类似的迷茫的经历。
英国一所大学对学校使用 AI 制作课程,包括 PPT 和配音都是 AI 制作,引起学生的不满。
我觉得如果 AI 大量应用于教育领域,会对人类产生一种钳制作用。人的大脑跟 AI 有一点是类似的,那就是接受一定的输入,产生输出,然后根据反馈做出调整。因此人其实也会受到 AI 在训练语料上遇到的问题,「Shit in, shit out」,学习了太多低质量语料导致效果不佳。从我知道的来说,连小学生都在用 AI 来批改作文了。会不会从 10 后或者 20 后开始,人类写的文章就是 AI 味的?
作者提到,在 AI 编程推广之后,更大的问题是「理解债务」(comprehension debt)。AI 能很快的写出一大堆代码,而使用 AI 编写这些代码的人因为没有经历完整的思考过程,这些代码实际上并不「属于」他们,很难真正理解;在出现问题的时候,只能利用 AI 去解决,很容易陷入死循环中,让事情变得更糟。
这个事情我深有同感,在 你不是在 vibe coding,而是在十倍速生成屎山 中我就提到:
一旦你开始 vibe coding,你就只能 vibe 到底了。
所以其实 AI 编程并不是一刀切的就一定能提升开发的效率,其瓶颈依然是使用者的认知上限,也应了我经常说的那句:
AI 决定下限,人决定上限。
讲的是 F1 运动历史上第三年轻的意大利新秀 Kimi Antonelli 获得梅赛德斯车队席位的过程,一部 40 分钟的短纪录片。出道一年不到就有一部自己的纪录片,小 Kimi 真是有点东西。
说到 F1,它除了是顶级体育赛事,在比赛周之外就是超大型的真人秀,官方都有不少节目,也有合作方例如 Netflix 的电视剧,以及相关的电影、纪录片,还有无数围绕 F1 话题的视频、帖子,而且也有对应的粉丝、周边等等。作为 F1 车迷,真的可以每一周都很充实(只要别看法拉利)。
一款基于大语言模型的视频字幕处理助手,支持语音识别、字幕断句、优化、翻译全流程处理。
多平台热点聚合 AI 分析工具。
视频镜头分割工具,可以当成 Python 包使用,也可以通过命令行使用。
本周刊已在 GitHub 开源,欢迎 star。同时,如果你有好的内容,也欢迎投稿。如果你觉得周刊的内容不错,可以分享给你的朋友,让更多人了解到好的内容,对我也是一种认可和鼓励。(或许你也可以请我喝杯咖啡)
另外,我建了一个交流群,欢迎入群讨论或反馈,可以通过文章头部的联系邮箱私信我获得入群方式。
2025-11-16 20:38:21
![]()
这是猫鱼周刊的第 86 期,本系列每周日更新,主要内容为每周收集内容的分享,同时发布在
博客:阿猫的博客-猫鱼周刊
RSS:猫鱼周刊
邮件订阅:猫鱼周刊
微信公众号:猫兄的和谐号列车
![]()
好久不见。前两周比较忙,内容也不多,所以就没有写周刊了,也算是给自己放一个小假。
这张照片其实摄于一年多以前,在西乡红树林公园外的一个大草坪。这张算是我后期得比较多的照片,重新裁剪了一下让人占画面大概 1/3 的位置,然后拉暗了暗部的曝光增强剪影的效果。
这周水了一篇文章 Docker 服务器磁盘满排查思路,也是吐槽了一下现在在用的博客系统 Halo,最近博客频繁的访问不稳定,就是拜它所赐。过了几天才想起来,原来之前就写过一篇类似的 排查 Linux 空间占用,不过这次更加专注于 Docker 造成的空间问题和排查。
我强烈建议你先观看一下从没想过的问题?!影视飓风 1400 万粉丝 Q&A! 这个原视频(至少看完相亲角部分的完整片段)再去看这个舆情解释视频,以及网上的各种评论。
不得不说,我看这个 Q&A 视频是因为在小红书刷到了很多关于「相亲角」的讨论,才去 B 站看了。我很赞赏影视飓风,但也算不上死忠粉,所以这类粉丝向的节目本身我不太感兴趣。视频看下来我都没觉得有什么问题,「初中学历」这个东西纯粹是整活,而「离异」这个也是事实,本质是整一下活,我没想到,我觉得 Tim 和他的团队也没想到,这居然能被人用来作为矛头攻击他。
利用矛盾制造情绪是自媒体获得流量的基本公式,而任何二元对立的事情就很容易用来制造矛盾激化情绪,例如贫富、性别等等。视频中说到,很多营销号就用「抽象化切片」来编一个离谱的故事激化矛盾。这是个非常操蛋的逻辑,但是偏偏平台和用户都很喜欢。另一个博主说到:
平台和舆论很傻逼怎么办?没办法。傻逼的共识也是共识。
无独有偶,之前 LTT 也陷入类似的舆论中。说实话,我觉得人无完人,我们不能要求公众人物个个都像圣人一样完美,更没必要反过来竭尽力气去挖人家的污点,这真的很「饭圈化」。
写到这里我想起我好像不是第一次谈这个话题了,今年初的时候,还是影视飓风,当时是跟评测相关的(via vol. 062)。我觉得影视飓风可以称得上国内版的 LTT,他们的商业模式很像,内容大部分也都非常对我胃口。之所以谈到商业模式,是因为在 LTT 公布过他们的收入组成,以周边售卖为大头,辅以赞助、广告等等;影视飓风我感觉也是类似的形式(他们的电商做得很不错),服装、电子产品都有不少原创的设计,我也买过不少;唯一有区别的可能是影视飓风还有一些视频制作相关的商单。这种商业模式的好处显而易见:
他们收入的大头不是广告推广,不是独家内容,而是售卖周边。我觉得这种变现方式就非常地健康,大头是周边产品售卖,热情的粉丝团体,为他们的创意且实用、有品质的产品付费;另外一边,多元的赞助商、广告位等,让他们不需要拍厂商的马屁,拥有相对的「评测自由」。
做评测的里面,顶级头部以及尾部博主其实都比较容易说实话,核心原因都是不需要拍厂商的马屁。腰部博主不好说,谁给得钱多就向着谁就完事了,哄不好金主就没饭开,「被包养就不要谈什么独立人格」。
很有共鸣。作者说到他在工作中极其需要反馈,包括业绩上的正反馈、负反馈,来自朋友同事的看法等等,而没有反馈则是很可怕的事情。
我也是一个很看重反馈的人,我在唱 K 的时候都会时不时降低音量确定自己有没有跑调。这倒不算是缺乏自信,我觉得这是一种提升自己的方式。当然读完这篇文章我也有一个新的想法,就是负反馈也是有益的,也比没反馈要好;但是这点很难去要求别人,没人喜欢说坏话。
在 Netflix 上看的一部电影,拍摄手法很有意思,从一件事的不同人的视角去展开,整部电影被分为好几章节,一个多小时,但是只对应世界时间线里的十几分钟,从一个未知来源的核弹发射到总统决定反击计划的瞬间。最神的是,一开始已经过了一遍整个故事,最后到那个总统决定反击计划的瞬间戛然而止,什么规模的反击、核弹有无爆炸、世界是否核平没有交代,原本剧情会像正常电影一样,全面反击,全球核平,但是从后面的章节开始,又换了一个视角,补充了前面没有交代过的一些信息,每个主角在其中的态度、反应刻画得更加细致。而一开始我很关注的世界核平到底有没有发生,到最后都没有交代,是个开放式结局。
(好像是周刊第一次推荐影视类的东西,为了不剧透写起来感觉有点混乱,欢迎留言反馈)
你的浏览器有多少个打开的标签页?我的可能有上百个,这已经给我造成了一定的焦虑,也一定程度上是我没有更新的原因。这些标签页,有的是我看了标题觉得感兴趣的文章,有些是觉得稍后需要参考的文档,还有一些感觉有意思想研究一下的项目、网站等等。但是一个现实是,如果用游戏中的耐力条来类比就是,人每天的总的精力是有上限的,恢复的速度也很慢(需要足够的休息、合理的饮食等),而每做一件事、看一篇文章、理解一个项目等都需要耗费不少的精力。现在的情况就是,我感兴趣的事情远大于我精力的上限。
道理我都清楚,其实没必要太纠结有没有深入去读一篇文章,纠结有没有错过什么,但是执行上有点难。Arc 有个很好用的功能,可以自动归档大于某个时间的标签页,我现在设置的是 30 天,我在考虑改到 7 天。如果一件事情,一个星期我都没时间去做,它显然不紧急,也很可能并不重要。
写到这里我删掉了下面的「项目」栏目,好像也是第一次周刊不推荐开源项目。如果非要我找的话,其实也还有存货,但确实没有想写的。周刊写了一年多,开始的时候很专注技术,最近发现近期周刊其实更多去分享生活,我自己最近也没有那么完全专注于技术了,也挺好。周刊的结构其实也想改变一下,初步想法是分成三块,一个输入的部分分享我看到的文章/视频等等,配上我的一些看法;一个输出的部分分享完全我自己的思考或者感想,也推荐一些书影音或者购买的东西等;还有一个栏目分享项目/工具/网站等等。稻草人周刊有个类似的结构,分别叫连接、当下和星群,蛮有意思。我在构思的命名是 INIT、STDIN、STDOUT、MISC、EOF,不知道大家有没有更好的意见,也许下周就能迎来这个重构。
Canva 收购了 Affinity 之后,Affinity 本体免费了,AI 功能可以通过 Canva 会员订阅使用。Affinity 算是 Adobe 的一个下位替代,本期头图就是用 Affinity 的 Raw 编辑功能导出的。这对业余创作者来说算是一个好事,比如我很少对照片做精修(不拍人像),核心就是裁切和调色,使用频率还很低(直出居多),所以单独为这个需求买正版的 Adobe 有点冤大头。
使用 Grok 重写的 Wikipedia。这种方式我觉得有点像 Simple English Wikipedia,其是一个用简单的单词、句法等来编写的百科全书,方便英语学习者、学生等阅读。到这里我有个想法,为什么不用 LLM 来补充 Simple English 的 Wiki 词条呢?
![]()
思路跟前面的 Grokipedia 类似,用 LLM 生成词条。作者的思路可以看这篇文章,挺有意思的。
![]()
本周刊已在 GitHub 开源,欢迎 star。同时,如果你有好的内容,也欢迎投稿。如果你觉得周刊的内容不错,可以分享给你的朋友,让更多人了解到好的内容,对我也是一种认可和鼓励。(或许你也可以请我喝杯咖啡)
另外,我建了一个交流群,欢迎入群讨论或反馈,可以通过文章头部的联系邮箱私信我获得入群方式。
2025-11-11 16:43:23
![]()
最近博客频繁出现不可访问的问题,但因为最近没怎么在写文章,所以倒没怎么管,直到收到博友圈发来的邮件:
![]()
登上去一看,发现服务器的磁盘居然满了(之前出现过内存满的情况,重启一下 Halo 的服务就能解决)。因为这台机器上都是用 Docker/Docker Compose 启动的服务,所以自然只需要去考虑 Docker 的问题。
当然,在这之前也是先用 df -h 看了一下,全都指向 /var/lib/docker/overlay2/xxx 和 overlay 这样的东西,所以能确定是 Docker 的问题。如果占用大的在其他目录,那就稍微复杂一点,可以用 du -h --max-depth=1 一点点去排查。
overlay 79G 57G 20G 75% /var/lib/docker/overlay2/89ed74e70d3e59b7c5de6e53522eece539a74e013c9c03c76fc7ac4d73045ed7/merged
其实到这里我就懵逼了,这些服务都没什么会落盘的,不应该一下产生很大的数据才是,所以找了 AI 帮忙,它建议我先运行:
docker system df -v
这里提醒了我两个问题,一个是我有很多镜像没有清理,二是有些卷没有清理,于是先执行了下面两条命令,先释放一点空间。这两条命令都只会清除没有使用的镜像和卷,所以是比较安全的。这一步清理掉了几个 G,但是磁盘还是很满。
docker image prune -a
docker volume prune
于是让 AI 帮忙写了个脚本,查一下到底是哪个容器的卷占用空间大:
echo "VOLUME_NAME SIZE CONTAINER(S)" && docker volume ls -q | while read vol; do size=$(docker volume inspect $vol --format '{{.Mountpoint}}' | xargs du -sh 2>/dev/null | cut -f1); containers=$(docker ps -a --filter volume=$vol --format '{{.Names}}' | tr '\n' ',' | sed 's/,$//'); [ -z "$containers" ] && containers="<unused>"; printf "%-40s %-10s %s\n" "$vol" "$size" "$containers"; done
最终找出来是 Halo 的容器。AI 建议我先考虑是容器日志的问题,用下面一条命令发现确实,于是又重启了一下容器,果然空间就被释放了。
find /var/lib/docker/containers/ -name "*.log" -exec ls -lh {} \; | sort -k5 -hr | head -10
但是这个方法治标不治本,它还顺便给我提出了限制日志大小的方法:
Docker
# 启动时添加日志选项
docker run -d \
--log-opt max-size=100m \
--log-opt max-file=3 \
[其他原有参数...]
Docker Compose
version: '3'
services:
your-service:
image: your-image
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "3"
全局设置(针对所有新容器生效)
# 编辑 /etc/docker/daemon.json
cat > /etc/docker/daemon.json <<EOF
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
EOF
# 重启 Docker
systemctl restart docker
至此,这次磁盘爆满的问题就解决了。
因为 Halo 老是出现 OOM,所以其实这次日志把磁盘写爆是因为 Halo 的问题。一看有一个新版本 Release,说是解决了潜在的内存泄漏问题,更新完好像就再没出现 OOM 的问题了。
我在关于页面里说过,选择 Halo 是因为它丰富的插件生态,完善的主题,还可以的性能,以及部署和发布的便利,现在我倒有了一些微词:
最近也看了不少设计优秀的独立博客,自由度高很多,也更方便发挥,更多东西可以折腾,再做一个自己的博客的念头也慢慢浮现,也许呢。
2025-10-26 19:36:26
![]()
这是猫鱼周刊的第 85 期,本系列每周日更新,主要内容为每周收集内容的分享,同时发布在
博客:阿猫的博客-猫鱼周刊
RSS:猫鱼周刊
邮件订阅:猫鱼周刊
微信公众号:猫兄的和谐号列车
![]()
这周终于又有头图。摄于深圳大梅沙滨海栈道,海水有时候是绿色,有时候是蓝色。沿着海岸线骑行,抬头一看外面海水闪闪发光。
这周没有太多别的产出,工作上的事情又开始多且有趣了一点,摸鱼时间少了一点,所以这期周刊大概也会稍短一点。
这周最大的事情可以说是 AWS 长达十几个小时的服务中断。是时候祭出这张图:
![]()
关于这个事故报告,网络上有很多解读,感兴趣可以自己找来看,我只谈谈我的想法。
第一个是「时间攻击」,也就是时间因素引起的 bug。这里有两个点都可以归到时间上,第一是「竞态条件」,这个是写并行程序中经常会遇到的问题,由于我们思考基本上是串行的,没有特地考虑并行的运行情况就会出现这个竞态条件。第二是「延迟增加」,我们通常认为一个操作可以「很快」完成,没有考虑过如果这个操作超时或者需要比较长的时间才能完成时,整体的逻辑是否还能成立。
还有一种时间因素这次没有出现,就是「定时」,这种更加隐蔽,但是更常见于业务系统中。例如某几个时间点数据会与预期不符,导致下游出错;或者更加干脆就是计算一个月前等逻辑,遇到特殊情况(go 的 time.Add),测试通常没法发现。
第二个是连锁反应,这是基础设施相互依赖、架构复杂的结果。从一开始的 DNS 故障,引发数据库故障,再到下游更多服务受到故障影响出现更多问题,作为下游的更多互联网服务更是也因此受到波动。架构越复杂,越容易被简单的问题拖垮。如果你的业务非常简单,可以考虑不要太过依赖云服务。
然后说说事故处置和 vendor lock-in 的问题。很多人觉得 AWS 「基本不可能出问题」,所以事故处置方案中根本没有考虑过这个,服务不可用的时候就完全瘫痪了。又相反地,有人在这次事故之后觉得需要做「多云部署」。我觉得这两种都不太可取,我的思路是参考这次的事故,审视自己架构中依赖了 AWS 的什么服务,如果其中某个服务故障,会对什么有影响,有没有方法减轻影响。当然,就算做了这些,很有可能还是会被一锅端,只是有预案的公司不会手足无措,在恢复时间上拉一坨大的。
说的是 Kindle 退出中国后,国内电子书生态和体验的问题。
在 Kindle 退出中国之后,国内平台只能走「低价包月」的路线,进入了一个恶性循环,导致出版社不愿意上新书,平台内容质量不断下降。反而直播带货还能卖出去不少实体书,所以出版社还比较有意愿发行实体书。另外国内的平台,包括微信阅读,都是以网文为主,严肃创作居少。
我不是网文的受众,当然我一年也不读几本书。我有 Kindle,找盗版书这件事对我来说倒也不算复杂,但是在 Kindle 还在国内运营的时候我会在商店买书,因为买到的排版好,而且省事,加上支持正版的思维。后来 Kindle 退出之后,会发现找新出的书特别困难(这点文章里也提到了,很多盗版来自于破解 DRM 后的 Kindle 商店版本)。到现在,我如果想要看某本书,还是直接去买实体书划算,很多畅销书只要二三十块就能买到,何苦折腾。
关于「提升电动车品牌效应」。
国产品牌很喜欢在技术以外的领域发力,例如公关、宣传,但就是不愿意在技术上多下功夫。我觉得道理很简单,价格战,加上公关宣传这些花销比技术研发低得多,所以现在的样子已经是「最优解」。
之前有不少关于新能源车的评测,也有不少人针对这些评测做很多争论。不知道诸位还记不记得罗永浩跟王自如的辩论,在评测中想要有点「倾向」实在再容易不过。现在做购买决定越来越多噪声,要做出理智的决定真的越来越难,很多时候还是很会倾向于自己喜欢的品牌。
深圳新出了「骑友巴士」,从市区到市内较偏的骑行点,人车一起上公交,定点发车,票价 20 元,周末开行。上车后,师傅会帮你固定好车,然后就是摇摇晃晃穿过市区几个站点(基本上除了终点站没有人上下车),然后到达终点。
![]()
这个设计非常好,有点像 2077 里面「跳过行驶阶段」的功能,直接到任务地点。在城市区域骑行真的很痛苦,非机动车道没有或很狭窄,在人行道上坑坑洼洼颠得手痛,要一直避让对向的电动车和路上的行人。而且像大梅沙,离宝安几十公里,以我的体力,骑过去可能就已经废了,更别提在那边骑长上坡,返程估计只能货拉拉。
接着说大梅沙骑行的体验,全程都有绿道,精华的一段维护得不错;后面有很长一段骑行道就没有专门的绿道了,要跟机动车共线,又很多急弯和长上坡下坡,有点危险。路线说是可以一直骑到大鹏那个最美 711,一共 35km,但是我骑了十几公里,经历一大堆连续上坡,心率连续拉满之后,就知难而退了。巴士站点附近有便利店和饮食,有个麦当劳,在里面边吃边等车的时候遇到了不少骑友。知难而退的路上还遇到了两个骑友,也是体力不足知难而退了,大家路上一起推了段车,聊天吹水,也很有意思。
我受够 poetry 了!这句话说出来有点好笑,因为我去年初才把原来的 pip 和 conda 等转到 poetry,还写了篇文章。
其实 poetry 也很好用,只是比较慢,而且 poetry update 的逻辑实在太诡异了,没法做到像 go get -u的效果。
这个项目可以把 pip 和 poetry 等项目迁移到 uv,好耶。
一种水印方法,可以抵抗旋转、裁剪、马赛克等编辑。
本周刊已在 GitHub 开源,欢迎 star。同时,如果你有好的内容,也欢迎投稿。如果你觉得周刊的内容不错,可以分享给你的朋友,让更多人了解到好的内容,对我也是一种认可和鼓励。(或许你也可以请我喝杯咖啡)
另外,我建了一个交流群,欢迎入群讨论或反馈,可以通过文章头部的联系邮箱私信我获得入群方式。
2025-10-19 20:11:07
![]()
这是猫鱼周刊的第 84 期,本系列每周日更新,主要内容为每周收集内容的分享,同时发布在
博客:阿猫的博客-猫鱼周刊
RSS:猫鱼周刊
邮件订阅:猫鱼周刊
微信公众号:猫兄的和谐号列车
距离上次出门拍照又有一个多月了,这周还是没有头图,实在是有点苦恼。上次大芬油画村多少有点「诈骗」,坐一个小时地铁过去,看一个城中村,而且深圳就不乏这种虚无的「打卡点」。
下周五是 1024,「程序员节」,这里祝各位程序员读者节日快乐,bug--。
这周有一篇「产出」,这篇文章的来历有点神奇:在大概一年前,我因为做剪辑相关的工作,接触到了 FFmpeg 以及硬件加速相关的东西,在中文搜索结果里没什么人提到,有的文章也比较旧,只考虑到了 Intel 的 qsv 和 Nvidia 的 NVENC,没有 AMD 的 amf 和 Apple 的 VideoToolbox,正好当时折腾这个能适应不同平台自动启用硬件加速的功能,所以在博客的写作列表上列了。结果这篇文章一咕再咕,直到最近这个项目重新捡起来,于是我尝试用 Claude Code 结合我的代码库,帮我写成了这篇文章「FFmpeg 硬件加速小记」。
![]()
我在文章开头标明了「本文有 AI 参与编写」,之所以不说「由 AI 生成」,是因为文章的核心思路、代码都是以「我」为主导,是我自己思考得出的。
![]()
这种创作方式还挺有意思,因为很多时候在写完某个功能之后,会有很强的欲望想分享自己实现的方案,但是重新总结形成文字又是很消耗精力的过程(所以这篇才会被我拖了接近一年)。提供思路(文章大纲和代码),让 AI 代劳文字组织的过程,至少能形成一篇像模像样的文章。这跟「由 AI 生成」最大的区别就是,如果你让 AI 「写一篇 ffmpeg 硬件加速的文章」,这是没有你的功劳的。
一共是两篇文章,分别从创作者和受众的角度评价 AIGC。有些观点很耐人寻味:
快消层面的内容或许将会被 AI 完全替代,而深度哲思的部分仍然(暂时)归人类所有。
AI 替代的不一定是所有创作者,但一定会替代的是不再愿意创作的创作者。
重新理解 AI 的功能性,它是放大器,而人可以作为源头,只要源头保持流动性,AI 就无法彻底取代你。
他的思考很有深度,感兴趣可以看看原文。
在「受众」这一块,我每周大概会读到上百篇有长有短的文章,但是我从来不用 AI 去做什么「摘要」之类的事情,其实摘要在筛选「有兴趣的引发思考的」上有种多此一举,我往往在打开网页,看完标题和首段或者大致扫过整篇文章的结构之后,我就有定夺我是否感兴趣看完(参考Yay or Nay)。「节约时间」的论点其实不太成立,读完一篇表达流畅的文章通常也只需要几分钟,比起作者创作的数十分钟至数小时、数天来说算是一种基本的尊重。
作为「创作者」,我的核心原则是「原创性」,我始终觉得 AI 无法提出真正「原创」的东西。但是在创作的时候是否应该使用 AI,或者说使用 AI 辅助创作是否会失去这种「原创性」,我觉得没有简单直接的答案。在头条的例子中,我其实做了大多数原创的检索和思考,这跟简单让 AI 「写一篇 ffmpeg 硬件加速的文章」是不一样的。
很喜欢这种「好物推荐」类型的文章,当然这种类型已经被各种商单泛滥,所以看到这种认真在推荐的会觉得更有意思。
我之前也考虑过在周刊中加一个好物推荐的栏目,不过不是每周都会购物,写集合的文章我又懒得写,所以偶尔买到好的东西我会在「想法」的地方推荐一下(例如Quote/0、CyberBrick、拓竹 P1SC等)。
衣服这块我觉得值得提一下。有些户外的面料,不在意搭配的话其实日常穿也很合适,舒适度和实用性比传统的材料好得多。我现在日常穿迪卡侬的速干 T 恤,虽然说是「运动装」,干爽透气以及轻盈有弹性的面料,非常适合广东炎热潮湿的天气,穿着上班、健身都适合。
家居向的「好物推荐」。收拾、装饰家里真的是一个时不时做一下很舒服很解压的事情。
AI 编程落地业务开发的探索与实践
AI 代码评审在七猫的实践
AI 时代的 Code Review 最佳实践
一共有好几篇文章,合在一块说了。感觉这个技术团队非常有意思,很鼓励成员去探索 AI 编程的用法,而且很注重分享。
我在公司跟同事分享过这几篇文章,有一个很有意思的反应:「他们真的落地了吗」。背景是类似的东西(例如 AI Code Review)我在 2023 年就搞过,反正在我公司的环境,这个事情最终没有推行下去,从人的角度来说好像技术同事没有很拥抱 AI,从公司的角度这件事不见得有「价值」。所以这几篇文章让我觉得七猫这家公司的技术团队还是很有「工程师文化」,会鼓励成员去做一些「技术上很酷」的事情。
话说回来,AI Code Review 这件事,当时遇到的一些瑕疵(例如行号的识别、nit(可改可不改) 的把握等),居然到 2025 年,模型更新了两三代还是存在。
接着头条的话题。我比较喜欢「扫街」,我是很典型的 i 人,在街上游荡,捕捉一些小小的美好,是我比较喜欢的摄影风格。深圳这个城市就不是很友好,这个城市主要以「石屎森林」为主,缺乏自然风光(其实也有比较好的海边),最重要的是没什么文化沉淀、没什么多样性。
我觉得要提「扫街友好」,首先是香港。住在深圳,这算是最快到达能看到「异域风光」的地方,路牌、街景等等都跟国内有很鲜明的对比。文化沉淀就是,有一种很独特的风格,如果非要我概括的话,就是老旧和时尚毫不违和地融合,优雅又整洁。香港总会给人一种很旧的感觉,有很多东西从上个世纪一直沿用到现在,例如一些用语、标牌的设计等等,尤其是很多街道和建筑实际已经建成上百年仍未变更。另一方面这里的多样性也很足,一条路上可以有佛教寺庙、清真寺和教堂,路上有各色人种人来人往,这种景象在国内真的很少见。所以在这里做「人文摄影」会很有意思,我每次去香港都能拍很多照片。
再有就是广州。这是我土生土长的地方,所以说实话有一点点特殊加成。广州跟深圳最大的不同是有很多古迹,老城区很多地方都还是十几、甚至几十年前的样子,跟千篇一律的现代城市有很大反差。
还有一点是,香港和广州有这种景色的地方很多。例如在香港你可以深度去逛旺角、九龙、中环、坚尼地城、赤柱,在广州你可以逛公园前、东山口、上下九,每个地方都有不一样的景色。而在深圳,来来去去就是各种名字不一样但实际差不多的公园和商场。
当然,这几个地方只是我常去/熟悉的,不代表其他地方就不好。我之前去过潮州,有很多古迹,除了一些步行街以外商业化味道也不是很重,也算很出片。但是像长沙,城市的商业化味道就很重,到处是网红打卡点,但打卡这件事情本来就很千篇一律,反而是让我觉得有点反感(见城市旅游就是打卡吗?)。
也欢迎各位推荐一些深圳或者周边适合扫街的地方,真的很久没碰相机了。
用来分别设置鼠标和触控板滚动方向的工具。
macOS 这点真的非常蛋疼,多数人鼠标的习惯是滚轮向上、内容往下,而触控板的逻辑是「自然滚动」,即往上滑动、内容往下。其实这两种逻辑都是合理的,有点像游戏中是否需要反转输入方向的问题(感兴趣可以看这篇文章),只是多数人是先从 PC(Windows)学习使用电脑,也自然而然习惯对应的鼠标操控逻辑。
如果你是用的是罗技的鼠标,可以用自带的 Logi Options+ 设置鼠标滚动的方向,不需要这些软件。
也是一个类似的软件,除了可以单独设置鼠标和触控板滚动方向以外,还有平滑的功能,对习惯用触控板的人来说,临时使用鼠标的滚动体验会舒服很多。
从零开始训练自己的超小语言模型。比较有意思的动手教程,完全开源免费,写得也很详细有深度。
我觉得比传统词典更加「生动活泼」,具体的可以看作者的文章。
![]()
本周刊已在 GitHub 开源,欢迎 star。同时,如果你有好的内容,也欢迎投稿。如果你觉得周刊的内容不错,可以分享给你的朋友,让更多人了解到好的内容,对我也是一种认可和鼓励。(或许你也可以请我喝杯咖啡)
另外,我建了一个交流群,欢迎入群讨论或反馈,可以通过文章头部的联系邮箱私信我获得入群方式。
2025-10-13 02:37:42
![]()
本文有 AI 参与编写。
硬件加速是指利用计算机中的专用硬件(如 GPU、专用编解码芯片)来执行视频编解码任务,而不是仅依赖 CPU 进行软件编码。相比纯软件编码,硬件加速具有以下优势:
虽然硬件加速很快,但也有一些需要注意的地方:
Apple 的硬件加速框架,支持 macOS 和 iOS 设备。
# 编码器
h264_videotoolbox
hevc_videotoolbox
# 使用示例
ffmpeg -i input.mp4 -c:v h264_videotoolbox -b:v 2M output.mp4
特点:
NVIDIA GPU 内置的硬件编码器,从 GTX 600 系列开始支持。
# 编码器
h264_nvenc
hevc_nvenc
av1_nvenc # RTX 40 系列及以上
# 使用示例
ffmpeg -hwaccel cuda -i input.mp4 -c:v h264_nvenc -preset p4 output.mp4
特点:
Intel 集成显卡的硬件编码器,从第二代酷睿开始支持。
# 编码器
h264_qsv
hevc_qsv
av1_qsv # 12 代及以上
# 使用示例
ffmpeg -hwaccel qsv -i input.mp4 -c:v h264_qsv -preset medium output.mp4
特点:
AMD GPU 的硬件编码器。
# 编码器
h264_amf
hevc_amf
av1_amf # RX 7000 系列及以上
# 使用示例
ffmpeg -hwaccel amf -i input.mp4 -c:v h264_amf output.mp4
Linux 上的通用硬件加速接口,支持 Intel、AMD 等多种硬件。
# 使用示例
ffmpeg -hwaccel vaapi -vaapi_device /dev/dri/renderD128 -i input.mp4 \
-vf 'format=nv12,hwupload' -c:v h264_vaapi output.mp4
下面是一个完整的 Python 实现,可以自动检测系统可用的硬件加速方式并选择最佳方案:
import subprocess
import logging
from typing import Optional, Tuple
logger = logging.getLogger(__name__)
def get_hardware_encoder(use_hwaccel: bool = True) -> Tuple[str, Optional[dict]]:
"""
检测可用的硬件加速器并返回合适的编码器设置。
Args:
use_hwaccel: 是否启用硬件加速
Returns:
(video_codec, hwaccel_options) 元组
- video_codec: 编码器名称,如 'h264_videotoolbox'
- hwaccel_options: 硬件加速选项字典,如 {'hwaccel': 'videotoolbox'}
"""
if not use_hwaccel:
return "libx264", None
try:
# 检查可用的硬件加速器
hwaccel_result = subprocess.run(
["ffmpeg", "-hwaccels"],
capture_output=True,
text=True,
timeout=5
)
hwaccels = hwaccel_result.stdout.lower()
# 检查可用的编码器
encoder_result = subprocess.run(
["ffmpeg", "-encoders"],
capture_output=True,
text=True,
timeout=5
)
encoders = encoder_result.stdout.lower()
logger.debug(f"Available hardware accelerators: {hwaccels}")
logger.debug(f"Available encoders: {encoders}")
def test_encoder(codec: str) -> bool:
"""测试编码器是否真正可用"""
try:
result = subprocess.run(
[
"ffmpeg",
"-f", "lavfi", # 使用虚拟输入
"-i", "testsrc=duration=1:size=320x240:rate=1",
"-frames:v", "1", # 只编码一帧
"-c:v", codec, # 指定编码器
"-f", "null", # 输出到空设备
"-"
],
capture_output=True,
text=True,
timeout=10
)
return result.returncode == 0
except Exception as e:
logger.debug(f"Failed to test encoder {codec}: {e}")
return False
# 按优先级检测硬件编码器
# 1. VideoToolbox (macOS/Apple Silicon)
if "h264_videotoolbox" in encoders and "videotoolbox" in hwaccels:
if test_encoder("h264_videotoolbox"):
logger.info("Using VideoToolbox hardware acceleration")
return "h264_videotoolbox", {"hwaccel": "videotoolbox"}
# 2. NVIDIA NVENC
if "h264_nvenc" in encoders:
if test_encoder("h264_nvenc"):
logger.info("Using NVIDIA NVENC hardware acceleration")
if "cuda" in hwaccels:
return "h264_nvenc", {"hwaccel": "cuda"}
return "h264_nvenc", None
# 3. Intel QuickSync
if "h264_qsv" in encoders and "qsv" in hwaccels:
if test_encoder("h264_qsv"):
logger.info("Using Intel QuickSync hardware acceleration")
return "h264_qsv", {"hwaccel": "qsv"}
# 4. AMD AMF
if "h264_amf" in encoders and "amf" in hwaccels:
if test_encoder("h264_amf"):
logger.info("Using AMD AMF hardware acceleration")
return "h264_amf", {"hwaccel": "amf"}
# 5. VAAPI (Linux)
if "h264_vaapi" in encoders and "vaapi" in hwaccels:
if test_encoder("h264_vaapi"):
logger.info("Using VAAPI hardware acceleration")
return "h264_vaapi", {"hwaccel": "vaapi"}
except Exception as e:
logger.warning(f"Error checking hardware encoders: {e}")
logger.info("Falling back to software encoding")
# 回退到软件编码
logger.info("Using software encoding (libx264)")
return "libx264", None
def get_system_info() -> dict:
"""获取系统硬件加速信息"""
try:
# 获取 FFmpeg 版本
version_result = subprocess.run(
["ffmpeg", "-version"],
capture_output=True,
text=True,
timeout=5
)
# 获取硬件加速列表
hwaccel_result = subprocess.run(
["ffmpeg", "-hwaccels"],
capture_output=True,
text=True,
timeout=5
)
# 获取编码器列表(只提取硬件编码器)
encoder_result = subprocess.run(
["ffmpeg", "-encoders"],
capture_output=True,
text=True,
timeout=5
)
hw_encoders = []
for line in encoder_result.stdout.split('\n'):
if any(hw in line.lower() for hw in ['nvenc', 'qsv', 'videotoolbox', 'amf', 'vaapi']):
hw_encoders.append(line.strip())
return {
"ffmpeg_version": version_result.stdout.split('\n')[0],
"hwaccels": hwaccel_result.stdout,
"hw_encoders": hw_encoders
}
except Exception as e:
return {"error": str(e)}
import json
# 获取系统硬件加速信息
info = get_system_info()
print(json.dumps(info, indent=2))
import ffmpeg
def encode_video_with_hwaccel(input_path: str, output_path: str, use_hwaccel: bool = True):
"""使用硬件加速编码视频"""
# 获取硬件编码器
vcodec, hw_options = get_hardware_encoder(use_hwaccel)
# 创建输入流
if hw_options:
# 使用硬件加速解码
stream = ffmpeg.input(input_path, **hw_options)
else:
stream = ffmpeg.input(input_path)
# 配置输出
stream = ffmpeg.output(
stream,
output_path,
vcodec=vcodec, # 使用检测到的编码器
acodec='aac', # 音频编码器
video_bitrate='2M', # 视频比特率
audio_bitrate='192k', # 音频比特率
preset='medium', # 编码预设(硬件编码器可能忽略此参数)
**{'crf': '23'} # 质量参数(硬件编码器可能忽略此参数)
)
# 执行编码
ffmpeg.run(stream, overwrite_output=True)
print(f"Video encoded successfully using {vcodec}")
# 使用硬件加速
encode_video_with_hwaccel('input.mp4', 'output.mp4', use_hwaccel=True)
# 强制使用软件编码
encode_video_with_hwaccel('input.mp4', 'output_sw.mp4', use_hwaccel=False)
def detect_macos_hwaccel():
"""检测 macOS 硬件加速"""
import platform
if platform.system() != 'Darwin':
return None
# 检测芯片类型
machine = platform.machine()
is_apple_silicon = machine == 'arm64'
# Apple Silicon 性能更好
if is_apple_silicon:
return {
'platform': 'Apple Silicon',
'recommended_encoder': 'h264_videotoolbox',
'performance': 'excellent',
'codecs': ['h264_videotoolbox', 'hevc_videotoolbox', 'prores_videotoolbox']
}
else:
return {
'platform': 'Intel Mac',
'recommended_encoder': 'h264_videotoolbox',
'performance': 'good',
'codecs': ['h264_videotoolbox', 'hevc_videotoolbox']
}
def detect_windows_hwaccel():
"""检测 Windows 硬件加速"""
import platform
if platform.system() != 'Windows':
return None
available = []
# 检测 NVIDIA
try:
result = subprocess.run(
['nvidia-smi', '--query-gpu=name', '--format=csv,noheader'],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0:
available.append({
'type': 'NVIDIA',
'encoder': 'h264_nvenc',
'gpu': result.stdout.strip()
})
except:
pass
# 检测 Intel QuickSync(通过 FFmpeg)
vcodec, _ = get_hardware_encoder(True)
if 'qsv' in vcodec:
available.append({
'type': 'Intel QuickSync',
'encoder': 'h264_qsv'
})
# 检测 AMD
if 'amf' in vcodec:
available.append({
'type': 'AMD',
'encoder': 'h264_amf'
})
return available
def detect_linux_hwaccel():
"""检测 Linux 硬件加速"""
import platform
import os
if platform.system() != 'Linux':
return None
available = []
# 检测 VAAPI 设备
vaapi_devices = [f'/dev/dri/renderD{i}' for i in range(128, 140)]
for device in vaapi_devices:
if os.path.exists(device):
available.append({
'type': 'VAAPI',
'device': device,
'encoder': 'h264_vaapi'
})
break
# 检测 NVIDIA
try:
result = subprocess.run(
['nvidia-smi'],
capture_output=True,
timeout=5
)
if result.returncode == 0:
available.append({
'type': 'NVIDIA',
'encoder': 'h264_nvenc'
})
except:
pass
return available
以编码一个 1080p 60fps 视频为例:
| 编码器 | 相对速度 | CPU 占用 | 质量评分 |
|---|---|---|---|
| libx264 (软件) | 1x | 100% | 10/10 |
| h264_videotoolbox (M1) | 5-8x | 20% | 8/10 |
| h264_nvenc (RTX 3080) | 8-12x | 15% | 8.5/10 |
| h264_qsv (12 代 Intel) | 4-6x | 25% | 7.5/10 |
| h264_amf (RX 6800) | 6-10x | 20% | 7.5/10 |
自动检测并回退
选择合适的预设
# NVENC 预设
# p1 (fastest) -> p7 (slowest, best quality)
stream = ffmpeg.output(stream, 'output.mp4', vcodec='h264_nvenc', preset='p4')
考虑批量处理
监控编码质量
处理兼容性问题
def safe_encode(input_path, output_path):
"""带错误处理的编码"""
try:
# 尝试硬件加速
encode_video_with_hwaccel(input_path, output_path, use_hwaccel=True)
except Exception as e:
logger.warning(f"Hardware encoding failed: {e}")
logger.info("Retrying with software encoding")
# 回退到软件编码
encode_video_with_hwaccel(input_path, output_path, use_hwaccel=False)
def encode_with_debug(input_path, output_path):
"""启用详细日志的编码"""
vcodec, hw_options = get_hardware_encoder(True)
stream = ffmpeg.input(input_path, **hw_options) if hw_options else ffmpeg.input(input_path)
stream = ffmpeg.output(stream, output_path, vcodec=vcodec)
# 获取完整命令
cmd = ffmpeg.compile(stream, overwrite_output=True)
print(f"FFmpeg command: {' '.join(cmd)}")
# 执行并查看输出
try:
ffmpeg.run(stream, overwrite_output=True, capture_stdout=False, capture_stderr=False)
except ffmpeg.Error as e:
print(f"stdout: {e.stdout.decode()}")
print(f"stderr: {e.stderr.decode()}")
raise
# 查看所有硬件加速方式
ffmpeg -hwaccels
# 查看所有编码器
ffmpeg -encoders | grep -E "(nvenc|qsv|videotoolbox|amf|vaapi)"
# 测试特定编码器
ffmpeg -f lavfi -i testsrc=duration=1:size=1920x1080:rate=30 \
-c:v h264_videotoolbox -f null -
硬件加速是视频处理中的重要优化手段,可以大幅提升处理速度和降低系统负载。通过自动检测和回退机制,我们可以构建一个跨平台的健壮视频处理系统。
关键要点: