RainbowBird's Blog

以梦为马,不负韶华。

2025 年度总结

2025 年,我的一个开源项目被收购了,我拿到了人生中第一份正式工作,也第一次登上了 GitHub Trending。从前端一路做到全栈,从接外包到进创业公司,再到现在和团队一起做更大的事情。这篇文章,我想聊聊这一年我收获了什么。

AIRI

一月份,我加入了 Neko 的虚拟陪伴项目 AIRI。这是一个开源的 AI 虚拟陪伴项目,愿景是让每个人都可以拥有自己的赛博老婆。最早想做开源版的 NeuroSama,现在的方向更接近对标 Grok Ani,既做陪伴,也探索通往 AGI 的各种可能性。

因为是一个长期项目,我们会不断探索新的方向。比如一月份我就在给 AIRI 添加玩 Minecraft 的能力——那时候 AI 玩游戏还处于蛮荒时期,市面上几乎没有类似的产品。最后做出来的效果还不错:可以接入 Discord 语音通话,让 AIRI 陪我一起建房子、挖矿。

不过我是典型的兴趣驱动型选手,一个东西做完了就会迫不及待地开始下一个。既然是虚拟陪伴,语音流水线(ASR、TTS)自然不会少,这一年下来在各个项目里反复搭建,已经非常娴熟了。

做 AIRI 的过程中也衍生出了不少副产品。用 Anthony Fu 的话来说,这叫 Yak Shaving——为了解决问题 A,你发现需要先解决问题 B,于是你造了个轮子 C。比如「Eventa」就是我们的副产品,一个更好的事件系统,支持把各种事件封装成优雅的 RPC 调用;「Velin」则是一个用 Vue 来编排提示词的工具,让 Context Engineering 的组织更结构化。

慢慢地,随着社区变大,团队成员也越来越多,更多的人认识到了项目的价值。虽然没有工资,但大家依然愿意一起贡献。截至目前,我们有了近万人的 Discord 社区、超过 35k 颗 GitHub Star,也上了一次又一次的 GitHub Trending。再次感谢每一位参与者的贡献和支持。

回过头来看,AIRI 教会我最重要的一件事是:不要盲目追新。这个行业每天都有新东西冒出来,很容易焦虑,觉得不跟上就要被淘汰。但这一年真正让我成长的,都是花时间沉下去做的事情——因为好饭不怕晚。

在做 AIRI 的同时,我也没闲着。

二月份在北京的时候,朋友 Miku 跟我提起 Telegram 对中文搜索的支持很差。我深有同感——自己的聊天记录经常搜不到想找的内容,那就自己做一个来解决这个问题,顺便也测试一下当时 Coding Agent 的实际能力。

第一版完全是 Vibe Coding 出来的,效果还可以。我把它发到推特上之后反响不错,于是决定继续迭代下去。

接下来的三个月,我用古法编程几乎重写了一遍。我一直对系统架构设计很感兴趣,采用了当时自认为很先进的事件驱动总线来解耦各个模块。现在回头看,这个设计存在不少问题:事件流转链路过长导致调试困难,模块之间的依赖关系被隐式的事件订阅掩盖了,反而增加了理解成本。现在想想还是应该用消息队列的。重写完成后,我发布了第一个正式版本。

发布之后,每次在推特上分享产品进度,都会收到很多人的点赞和鼓励,还有不少业界前辈也来支持。这是我第一次体会到做独立开发者的成就感——自己做的东西真的有人需要,而不是伪需求。

同时,项目也吸引到了我现在的老板。他给我 Sponsor 了 100 美元,并通过邮件与我取得联系,想一起合作做下一代的聊天软件。三个月后,他告诉我想把项目买下来。

这件事来得很突然。得知消息的那天晚上我兴奋得睡不着,很难相信这一切居然发生在自己身上。再三确认之后,心里的一块石头终于落地了。更让我激动的是,因为收购的消息,Telegram Search 也成为了我人生中第一个登上 GitHub Trending 榜第一名的项目。

这次经历对我的意义不只是经济上的回报,更重要的是一个信号:自己做的东西真的有人需要,有人喜欢,甚至愿意花钱买下来。这让我更加坚定了做产品的信念——原来这条路,是真的走得通的。

找工作

然而好景不长,之前的外包合作到期了,我得另谋出路。

说实话,我很害怕找工作。没有大学文凭,不会八股文,虽然之前参加过算法竞赛,但面试那套东西我确实不擅长。只能硬着头皮把 Telegram Search 和 AIRI 的经历写上去,希望作品本身能替我说话。

我对大厂没有执念,也清楚以我的学历背景大概率连面试机会都拿不到。于是换了个思路,在 V2EX 的招聘板块找创业公司的机会。没想到第一家就进了面试——一个全员大厂和高校背景的 3D 高斯数字人团队,让我负责全栈开发。老板没有问任何八股文,甚至在听到我没有上过大学之后还说「很有想法」。我现在都很感谢他给我这个机会。

这段经历让我意识到一件事:与其花时间去补短板,不如把长板做到足够长。当你的作品摆在那里,能说明问题的时候,很多传统意义上的门槛其实是可以跨过去的。

在那里的七八个月,我独立搭建了整套后端推理系统,从零到一跑通了整个流水线,技术上成长了非常多。也遇到了一个很有技术品味的实习生同事,和这样的人一起讨论问题,是工作中最幸福的时刻之一。团队氛围也很好,经常线下聚会,老板亲自做饭,大家一起玩桌游,相处得很愉快。

不过远程工作的另一面是:所有时间都可能是工作时间。虽然我可以一边上班一边在日本、东南亚旅行,但每次出门都得背着电脑。一旦 oncall 来了,就得立刻找个咖啡厅坐下来处理,有时候一坐就是两个小时。工作和生活的边界变得很模糊,这是我后来离开时考虑的一个重要因素。

休息了个把月之后,我收到了现在老板的邀请,一起共建更智能的聊天软件,做一些更大、更有挑战性的事情。新的旅程就这样开始了~

一些琐事

2025 年也发生了一些值得记录的事情:

  • 去新华医院确诊了 ADHD,终于知道自己为什么在一些方面「异于常人」了
  • 被小音带去了抱石馆,第一次尝试就喜欢上了这项运动,之后几乎每个月都会去几次
  • 去日本参加了 Vue Fes Japan,小音带我去和 Anthony Fu 一起吃饭,第一次近距离接触偶像级别的开发者,很开心

今年参加的线下活动:

  • 杭州 D2 终端技术大会
  • Vue Conf 2025
  • 良褚文化村疯狂星期四
  • Vue Fes Japan 2025
  • FeDay 2025

展望

如果用一个词来总结我的 2025,我会说「充满希望」。

新的 2026 年(虽然已经四月了),我希望可以:

  • 尝试在 50 人以上的场合做一次公开演讲
  • 读完十本书,不限题材
  • 输出十篇以上的长博客
  • 脱离翻译插件直接阅读英文文章
  • 抱石基本能爬 V3
  • 去一趟欧洲或美国

这些目标里面,关于表达这件事,我反思了很久。我总是习惯被动地接受别人的观点,以至于别人觉得我是倾听型人格。但其实不是——我只是不敢说而已。害怕自己的思考不够成熟,害怕说错什么。

但我现在想通了:不说,就永远不会进步。与其害怕出错,不如多被 challenge 几次。在向别人解释一件事的过程中发现自己的不足,才是成长最快的方式。费曼学习法,说的就是这个道理。所以新的一年我会逼自己多输出,多表达。同时也继续保持输入——去寻找和自己不同的观点,去听不一样的声音。只有在碰撞中,才能发现自己的问题在哪里。

最后,感谢这一年里陪伴我的人。感谢 Neko,这一年里她帮助了我太多,经常被我请教问题到深夜,她也不厌其烦地讲解,不断带我了解 AI 行业的最新进展。感谢 Miku,经常来找我玩,把我从繁重的工作中解救出来。感谢室友梓瑶,让我在家中的日子也充满乐趣。

至于项目,这反而是最不需要计划的部分。光是现在手上就有四五个在同时推进了。2025 教会我的是:做自己感兴趣的事情,认真做,坚持做,好事自然会来。

起因

最近开始接触 iOS / Swift 开发,但是 XCode 是众所周知的难用。作为一个习惯了 VSCode 生态的开发者,我决定试试能不能在 VSCode 里写 iOS 项目。

然后第一步就困住了:怎么配环境呢?

VSCode 装上 Swift 官方插件后,发现 LSP 居然不工作,只有 VSCode 自带的高亮。一查才发现:官方 Swift 插件只支持 SPM 项目(即有 Package.swift 的项目),而我手上的项目是 .xcodeproj + CocoaPods 的传统结构,这就导致 LSP 无法获取编译参数,自然也就没有智能功能了。

折腾了一圈,终于配出了一个能用的环境,记录一下过程,希望能帮到同样想逃离 Xcode 的人。

阅读全文 »

视频链接

【正片】周鸿祎×罗永浩!近四小时高密度输出!周鸿祎深度谈 AI

核心观点概览

AI 并非简单工具升级,而是引领社会变革的强大力量。语言模型构成构建世界认知的基础,智能体前景广阔。AI 发展同时面临挑战与伦理难题,需务实应对并构建健康生态。

1. 先有文本模型,才能搞世界模型

  • 语言是认知世界的基石:语言是人类沟通与知识传承的核心,也是 AI 理解世界的基础。即使失去视觉与听觉,海伦·凯勒仍通过语言构建完整世界认知。
  • 语言模型是世界模型起点:AI 通过语言模型掌握人类知识、逻辑推理与世界描绘,比单纯感知信息更根本。一旦语言智能突破,图像、视频等物理与空间智能将迅速跟进。
  • “世界模型”误区:过早追求世界模型而忽视语言,属于本末倒置。语言才是打开智能大门的金钥匙。
阅读全文 »

Intro

对比 zaf/resamplezeozeozeo/gomplerate 两个库的性能表现。

前者基于 CGO 和 libsoxr C 库,后者为纯 Go 实现。

测试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
> CGO_ENABLED=1 go test -tags soxr -bench=. -benchmem
goos: darwin
goarch: arm64
pkg: github.com/luoling8192/benchmark
cpu: Apple M4 Pro
BenchmarkResample_48k_to_16k_mono/purego/frames=240-12 592992 1911 ns/op 1004.65 MB/s 6768 B/op 18 allocs/op
BenchmarkResample_48k_to_16k_mono/purego/frames=480-12 294157 3782 ns/op 1015.24 MB/s 13552 B/op 20 allocs/op
BenchmarkResample_48k_to_16k_mono/purego/frames=960-12 152620 8024 ns/op 957.14 MB/s 36080 B/op 23 allocs/op
BenchmarkResample_48k_to_16k_mono/purego/frames=1920-12 72862 16160 ns/op 950.49 MB/s 80369 B/op 26 allocs/op
BenchmarkResample_48k_to_16k_mono/purego/frames=48000-12 2707 438796 ns/op 875.12 MB/s 2574854 B/op 46 allocs/op
BenchmarkResample_48k_to_16k_mono/soxr/frames=240-12 93116 12589 ns/op 152.51 MB/s 3352 B/op 5 allocs/op
BenchmarkResample_48k_to_16k_mono/soxr/frames=480-12 90708 12963 ns/op 296.22 MB/s 6680 B/op 5 allocs/op
BenchmarkResample_48k_to_16k_mono/soxr/frames=960-12 76482 13896 ns/op 552.69 MB/s 13592 B/op 5 allocs/op
BenchmarkResample_48k_to_16k_mono/soxr/frames=1920-12 56086 21418 ns/op 717.15 MB/s 27160 B/op 6 allocs/op
BenchmarkResample_48k_to_16k_mono/soxr/frames=48000-12 3708 295917 ns/op 1297.66 MB/s 650306 B/op 6 allocs/op
PASS
ok github.com/luoling8192/benchmark 12.594s

分析

小批量数据(240-1920 frames)

  • purego 完胜: 1.9-16 μs/op,吞吐量 950-1004 MB/s

  • soxr 较慢: 12-21 μs/op,吞吐量 152-717 MB/s

  • 原因: CGO 调用开销 + C 库初始化成本在小数据量时占主导

大批量数据(48000 frames,1秒音频)

  • soxr 反超: 295 μs/op,1297 MB/s

  • purego: 438 μs/op,875 MB/s

  • 提升: ~48% 性能提升,这才是 C 库该有的表现

内存分配对比

1
2
3
4
5
6
7
小批量 (240 frames):
purego: 6768 B/op, 18 allocs/op
soxr: 3352 B/op, 5 allocs/op ← 更少分配

大批量 (48000 frames):
purego: 2.57 MB/op, 46 allocs/op
soxr: 0.65 MB/op, 6 allocs/op ← 内存效率高 4 倍

结论

对于语音通话场景,可以选择纯 Go 实现,关闭 CGO 之后工程更易维护且性能不会太差。

可以使用 Tag 来控制切换使用 CGO 版本还是 Pure Go 版本。

git-filter-repo

安装 git-filter-repo

1
brew install git-filter-repo

克隆一个新的仓库,之后可以先查看 .git 文件夹的大小:

1
2
> du -sh .git
512M .git

可以通过下面的命令来查看都有哪些 blobs

1
git filter-repo --analyze

发现大多数都是二进制文件

1
2
3
4
5
6
7
8
9
10
11
> head .git/filter-repo/analysis/path-all-sizes.txt 
=== All paths by reverse accumulated size ===
Format: unpacked size, packed size, date deleted, path name
470130748 178598150 2025-11-10 platform
243521416 113247661 2025-10-23 console
46809010 21775586 2025-08-20 c_app
54705026 20429606 2025-06-13 cmd/realtime/__debug_bin913888059
42808594 15271725 2025-07-04 hack/custom-gcl
54721890 10697777 2025-06-13 cmd/realtime/__debug_bin2869633227
54718178 10581905 2025-06-13 cmd/realtime/__debug_bin2562572020
54705026 7964676 2025-06-13 cmd/realtime/__debug_bin1245928177

那我们使用下面的命令来清理大于 10MB 的 blobs 文件

1
git filter-repo --strip-blobs-bigger-than 10M

清理完成之后,运行下面的命令来移除掉未引用的对象:

1
git gc --prune=now --aggressive

再次运行 analyze 命令查看

1
2
3
4
5
6
7
8
9
10
11
12
> git filter-repo --analyze
> head .git/filter-repo/analysis/path-all-sizes.txt
=== All paths by reverse accumulated size ===
Format: unpacked size, packed size, date deleted, path name
632173 625888 <present> misc/test.png
6723898 181368 <present> api/generated/platform/v1/character.pb.go
186702 166919 <present> misc/test.wav
21519778 116651 <present> ent/mutation.go
3185297 81278 <present> go.sum
1850619 53671 <present> api/generated/realtime/v1/realtime.pb.go
14689422 38565 <present> api/generated/v1.swagger.json
744797 36489 <present> api/generated/console/v1/task.pb.go

发现刚刚显示的二进制文件已经没有了

再次查看 .git 大小

1
2
> du -sh .git
6.6M .git

这个时候推送到远端,应该使用 --force-with-lease 防止覆盖新的提交

1
git push --force-with-lease --all

最后要注意的是,所有的人应该拉取新的仓库,否则可能会把之前的记录再次推送上来导致清理无效。

问题描述

Apple TV 连接 HomePod Mini 作为默认音频输出后,使用 Moonlight 进行游戏串流时出现严重丢包,表现为画面周期性卡顿(micro-stuttering)、延迟飙升,体验极差。

断开 HomePod 连接后问题立即消失。

根因分析:AWDL 信道跳变

罪魁祸首是 AWDL(Apple Wireless Direct Link)——Apple 的私有无线点对点协议,用于支持 AirDrop、AirPlay、Sidecar 等设备间直连功能。

AWDL 是什么

AWDL 是 Apple 从 iOS 7 / macOS Yosemite 开始引入的无线直连技术,允许 Apple 设备不经过路由器,直接通过 Wi-Fi 射频进行点对点通信。它后来被 Wi-Fi 联盟采纳,成为 NAN(Neighbor Awareness Networking)标准的基础。

每个 AWDL 节点会广播一系列 Availability Windows(AW),表示自己可以与其他 AWDL 节点通信的时间窗口。节点间通过选举产生一个 master 节点来同步这些时间窗口。

为什么会导致丢包

AWDL 只使用三个固定信道

频段 信道 备注
2.4 GHz Ch 6
5 GHz Ch 44 欧洲/亚太优先
5 GHz Ch 149 美国优先

当 Apple TV 通过 AirPlay 连接 HomePod Mini 时,AWDL 被激活以维持这条点对点音频链路。关键问题在于:如果你的 Wi-Fi 网络不在 AWDL 使用的信道上,Wi-Fi 射频模块就必须在两个信道之间反复跳变——先切到 AWDL 信道处理 AirPlay 数据,再切回基础设施信道处理正常网络流量。

这种信道跳变会造成:

  • 周期性延迟尖峰:Bonjour 发现过程每秒产生约 50-100ms 的延迟尖峰
  • 丢包:射频模块在 AWDL 信道上工作时,正常 Wi-Fi 信道上的数据包无法被接收
  • 节律性卡顿:因为跳变是周期性的,表现为非常有规律的 stuttering

正常情况下 ping 延迟在 2-10ms,AWDL 活跃时可飙升至 200ms。对于 Moonlight 这种对延迟极度敏感的游戏串流应用,这是灾难性的。

为什么断开 HomePod 就好了

断开 HomePod 音频连接后,Apple TV 不再需要维持 AirPlay 点对点链路,AWDL 回到低功耗的被动监听状态,信道跳变频率大幅降低,丢包问题随之消失。

但注意:即使没有连接 HomePod,AWDL 也不会完全安静。只要隔空投送开启且附近存在 Apple 设备,AWDL 会周期性进行 Bonjour 发现扫描,每次扫描都会短暂锁定 Wi-Fi 射频并切换信道。虽然频率远低于活跃 AirPlay 连接,但对延迟敏感的串流应用仍然可能感知到偶发的延迟尖峰。关闭隔空投送可以抑制这部分扫描。

解决方法

方法一:断开 HomePod 连接(最简单)

在 Apple TV 设置中将音频输出切回 Apple TV 内置扬声器或改用有线/蓝牙音频设备,避免 AirPlay 音频链路触发 AWDL 高频信道跳变。

方法二:将 Wi-Fi 信道调整为 AWDL 信道(推荐)

在路由器设置中,将 5 GHz Wi-Fi 信道手动设置为 Ch 44(亚太/欧洲)或 Ch 149(美国)。当 Wi-Fi 基础设施信道与 AWDL 信道一致时,射频模块无需跳变,延迟尖峰消失。

方法三:关闭隔空投送

关闭隔空投送(AirDrop)可以阻止 AWDL 的周期性 Bonjour 发现扫描,减少无 AirPlay 连接时的偶发延迟尖峰。在 Apple TV 上进入「设置 > 隔空投送与接力」关闭即可。

方法四:使用有线连接(最终方案)

Apple TV 使用以太网连接是最稳定的方案。有线连接完全绕过 Wi-Fi 射频争用问题,串流数据走有线网络,AWDL 的周期性扫描和 AirPlay 音频链路只影响无线射频,两者互不干扰。HomePod 音频也能正常使用。

实测这是唯一能彻底解决问题的方案——即使调整了信道,Bonjour 周期性扫描仍可能带来偶发抖动,有线连接则完全不受影响。

参考资料

背景

已经在软路由上配置好了 tailscale 之后,发现虽然其他设备可以连接到软路由,但是不能科学上网。

解决方法

进入 shellcrash 设置后,选择【7 内核进阶设置】-> 【3 配置公网及局域网防火墙】 -> 【3 自定义透明路由ipv4网段】-> 输入 100.0.0.0/8,保存并重启内核即可。

问题描述

可以访问互联网,Chrome 浏览器访问同网段的其他设备提示 ERR_ADDRESS_UNREACHABLE

Safari可以访问,Chrome 浏览器不能访问。

尝试过多种方式,如重置本地网络,刷新DNS缓存,切换DNS服务器,重置Chrome设置等,均无效。

通过 Wireshark 抓包发现 Chrome 访问内网设备直接都没流量出去。

解决方法

最后发现:需要在设置中打开本地网络访问权限。

文章来源

转载自:Mac 电脑访问网页提示 ERR_ADDRESS_UNREACHABLE

起因

经常用 macOS 的用户都知道,鼠标的滚动方向和触控板的滚动方向是一致的,即为自然滚动,但是在 Windows 上,没有一个很好的方法可以快速调节滚动方向到自然滚动。

网上有很多教程教如何使用注册表来修改鼠标的滚动方向,本文不再赘述。

阅读全文 »
0%