2026-10-07 02:43:42
时间过得真快。
不只是过去的一年,也是这个 14 天的中秋 + 国庆假期。
本来今年国庆不打算回深圳的,但确实也没有更好的安排。原本想着国庆去意大利玩,或者是去东京走走,但女友忙于工作没办法陪我,最后只能买回深圳的机票了。
国庆那天一家人去看了企鹅岛,后面又去大亚湾住了一晚,剩下的时间我就是在家里躺着刷手机,或者 Vibe 新的项目。我留了两天时间提前回上海,好提前处理下工作之类的。(虽然大部分时间也还是躺在床上刷手机)
回看过去的一年,AI 的发展也很是迅猛。从今年年初的 OpenClaw 全民养虾,到后面的 Claude Mythos 给安全方面带来的变革;又陆续造出来了 Harness engineering、Loop engineering 这些新名词。模型已经迭代到 GPT 6.1、Claude Opus 5.5,可以说是 AI 一天,人间一年。
我在两年前使用 Hugo 替换 WordPress 时,是将原本 WordPress 主题的 CSS 样式,一点点抠出来,复制到新的 Hugo CSS 文件中,尽可能保留之前的主题样式。完整迁移后,我曾尝试用当时的模型,将 style.css 里的 CSS 样式,使用原子化的 TailwindCSS 重构。试了很多次都不能 1:1 保留原本的样式不变。
直到今年 GPT-6-Astra 做到了。正如你所看到的,虽然博客的主题样式没变,但 F12 打开开发者面板可以发现全都使用 TailwindCSS 重写了。说实话我也不清楚这么做有什么收益,只是显得这个站点更加“现代化”了。
与此同时,我还让 GPT 用 Computer Use 操控谷歌浏览器跑 Lighthouse 测试页面性能,并进行调优。它将评论 API 改为异步请求,从而避免接口偶尔加载卡顿导致页面短暂白屏的问题。
说到这个评论 API,我使用自己两年前写的 Sayrud 搭建。半个月前,我将 Sayrud 的历史代码丢给了 Claude Opus 5.5,让它将这个 Schemaless Table 改为多维表格,功能对着飞书多维表格抄。只用了三轮对话,带公式、带多人协作、带筛选、分组、排序,带看板、画册、表单视图的多维表格就完成了。只要是 Claude 写了的功能,都没问题一遍过。前端方面,Claude 还很有心机地使用了 Arco Design 以贴近飞书的风格。
这下真看到核弹爆炸瘫坐在椅子上了。
我在五年前尝试写 EggMD 的时候,就提到过使用 OT 算法实现协同编辑,引用当时前 Google Wave 工程师的话:
Unfortunately, implementing OT sucks. There’s a million algorithms with different tradeoffs, mostly trapped in academic papers. […] Wave took 2 years to write and if we rewrote it today, it would take almost as long to write a second time.
不幸的是,实现 OT(操作变换)很折磨人。相关算法多到数不清,各有取舍,而且大多还锁在学术论文里。[……] Wave 写了两年;就算今天重写一遍,也几乎还要再花那么久。
当时的我觉得像 OT、CRDT 这些协同编辑算法我肯定是写不明白的,只能套前人的 Go 实现,只会在这之上简单封装一个 Markdown 编辑器。
但现如今大模型来了,管你什么文本协同编辑还是 JSON 复杂嵌套格式的协同编辑,库都不用调,手写 OT 全给你秒了!
甚至 Sayrud 后面实现了多维表格的公式列,后端动态计算公式后,也能多端快速同步结果。
既然 Claude 帮我出色地完成了最复杂的部分,那我不如再加上用户系统、IdP、i18n 这些,造轮子开新坑,又双叒叕做一个开源的多维表格项目。最近这几天我就是在家 Vibe 新版 Sayrud 的相关功能,同时搭建了主站、文档站、在线体验站等生态站点。后续准备将自己现有飞书多维表格上的数据迁移过来,作为一个 use case 分享出来。
整个 Vibe 的过程十分舒适,我又逐渐找回了以前写代码,创造新事物时的快乐了。
欢迎尝试体验:
再胆大一点,我是不是还能复活前几年已经弃坑的 EggMD 项目?虽然开源协同编辑文档好像并不新鲜了。
当时的想法是用 NuxtJS + Editor.js 做成块编辑器的形式。但 NuxtJS 巨难用,在不同的工具链配置下会有奇怪的报错,解决起来费时费事。而 Editor.js 又是个大鸽子,这个块编辑器的想法和样式都很好,但 TODO List 挂了四五年了,一点进展没有。计划的 Undo/Redo、协同编辑功能一直没做,哪怕是 AI 如此强大的今天,他们也没有使用 AI 来辅助开发,commit 频率更是按月来计!纯纯摆烂了属于是。
我寻思要不自己拿 GPT 给他哐哐一顿干完算了。
在去年十周年的文章末尾,我提到自己购买并备案了 nekobase.com 域名。做了一个产品反馈平台,在五月份的时候默默上线了。没有任何推广和宣传,因此至今也没有人使用。
一开始的想法是 NekoBox 现有的反馈平台太难用了,要不自己写一个吧。我开始用 Pencil(现在叫 Pen 了)设计原型图,再按照原型图实现前端。微信网页登录开通接入很麻烦,便自己封装了下微信内获取 OpenID 的接口,实现了网页端扫码登录。除此之外好像没有学到什么新东西了。
一切完成上线后,我又迟迟没时间迁移数据,也没真正把它用起来。总会觉得哪哪没准备好,是不是不应该这么急着迁移。因此直到现在都还是没用起来。我也不知道后续会怎么样,短期内没有去继续维护的想法,就还是这样放着吧。😂
一两年前我还在写文章抱怨大模型没办法按我的编码习惯,写出我看着舒服的代码。为此还专门写了一个 Prompt 用于生成符合自己 taste 的代码。
现如今只需要把历史项目的路径丢给 AI,让他对着项目的代码风格和文件结构抄就行。去年的我还是开着 IDE,右侧一个小窗口来和 AI 对话;现在已经是只有聊天框了,只有在最后 git commit 时,我才会在 Git Tower 里草草过一下后端代码,主要是看大方向是否正确,有没有冗余代码之类的。而前端的代码,我是一点不看了,在浏览器里测试交互没问题,就直接提交了。
Everyone is asking: Will AI replace me? A better question is: What can I do with AI that I couldn’t do before?
这大概就是我现在的心态。
BTW,10 月份又是公布诺贝尔奖的日子。
今年的生理学或医学奖是“关于光控离子通道(light-gated)和光遗传学的发现”;物理学奖是“对冰立方中微子天文台(IceCube)的决定性贡献”。
我寻思这俩凑起来不就是 LightCube 吗?今年诺贝尔文学奖颁给川原砾好了
2026-09-06 13:05:00
很长一段时间没有写博客了,难得工作上不忙了,周末也不用加班了,终于有时间来整点活写点东西了。
上周我去洛阳看了许嵩的演唱会,时隔 15 年终于在线下见到真人了,也算是圆梦了。这次掐点在大麦刷了十多分钟,成功抢到了内场 ¥1618 的 VIP 票。

演唱会现场我一开始还是比较拘谨地坐着的,直到演出开始灯一关,第一首新歌《皮下》直接爆了,我都没想到自己会这么激动,全程大声跟唱+挥荧光棒,不知道有没有影响到两边坐着的小姐姐,至少我觉得自己是完全赚回票价了。😄
现场演出的舞台和灯光都很棒,许嵩现场的唱功也很好。但由于花车巡游是在舞台两侧的,反而看台的座位更有性价比些。
跟前几年在上海看邓紫棋演唱会,荧光棒拴在椅子上不同的是,这次许嵩演唱会的荧光棒是可以带走的。后来还发现有人在闲鱼上批量卖现场的荧光棒,一根就卖 20 来块。

我在演唱会结束回到酒店时,手里的荧光棒就已经不亮了。在小红书上看到有很多教人重新点亮荧光棒的笔记,大致思路都是拆开后给里面焊上一个开关或者其他的板子。我寻思这不就是个“忒修斯之船”嘛,把荧光棒里面的东西都换了,就保留个灯泡和外面的演唱会贴纸,实际上已经不是原来的荧光棒了。
我并不喜欢这种拆解改造的办法(实际是因为我没有电烙铁这些工具),想着有没有更优雅的办法来解决,一顿搜索后找到了 「✨ 荧光棒复活计划」 https://github.com/HansZ8/EncoreLightSticks 这个项目,该项目通过控制射频发射模块,发射出与演唱会现场相同的信号,直接点亮荧光棒。
作者还十分贴心地亲自写了介绍文档,提到演唱会荧光棒场控使用 433MHz 射频 (RF) 控制,编码方式为曼彻斯特编码 Manchester Encoding,我上次看到这个编码还是在 HGAME 2018 的 Misc 题里。数据包的控制数据里带有 R、G、B 三种颜色通道的值,每个通道支持 4 档亮度调节,通过调节 RGB 的档位数值来控制荧光棒的颜色。
作者举例的数据包编码是采样自陶喆 Soul Power II 2024 演唱会,当时的硬件版本是 LED17_V5.6.3 2024.05.17,而我拆开许嵩演唱会的荧光棒,里面板子印着的版本号是 LED17_V5.6.31 2025.04.02。我还挺担心版本不一致导致数据包格式不同。

我想着要不买来硬件试试复用下陶喆演唱会的荧光棒信号,要是成功了就是血赚,不成功的话我准备后面以此为理由买个 Flipper Zero 去现场录信号哈哈哈。
作者仓库里是使用 Arduino 来实现的,问了下 Grok,说是也可以用树莓派的 GPIO 接口来做,刚好可以把我家里吃灰的树莓派给用上。Grok 提到需要额外准备的硬件如下:
上淘宝搜了下,不得不佩服华强北这么多年的努力,把这些电子元器件的价格给搞得这么低。
40 根杜邦线只要 2 块钱,还包邮!我都不知道商家怎么赚钱。实际上我们只需要用到 3 根就够了。

433 MHz 发射模块如下图所示,分为单发射、单接收、发射和接收三种,我们买单发射的即可。买 2 个只需要 ¥3.8,也是跟不要钱一样。

然后我们需要给发射模块插上天线,Grok 说拆一根杜邦线剥去外皮就能当天线,但我寻思反正天线也不值钱,豪掷 ¥1.6 包邮买了 10 个弹簧天线。🤣

综上,我们最终的成本不到 8 块钱。(树莓派除外哈,本来就是家里有的东西
荧光棒拿回家后,电量基本都耗尽了。电量不足的荧光棒无法正常响应接收到的发光信号,我们需要先换电池。
拆开荧光棒发现里面自带的电池是不知道什么杂牌的“旭牛电池”,尺寸像是 7 号电池。但这里有个坑,你会发现市面上正常的 7 号电池都尺寸偏大,放不进去,强行硬塞的话会被两侧的弹簧弹出来。 最后我是保留了一个原装的电池,两边各塞了一个自己新买的 7 号电池,勉强将盖子盖上了,荧光棒也有电了。

如果你在后续测试过程中,发现荧光棒在指定颜色与蓝色之间闪烁,那可能就是快没电了,得尽快换电池。
然后开始准备硬件。
首先将树莓派关机并断电。(这一步很重要!我的上一个树莓派就是我带电接线然后给烧坏了)
拆出三根杜邦线,将发射模块与树莓派 GPIO 相连,我这里使用的是树莓派 3B+:
| 模块 | 树莓派 |
|---|---|
| DATA | 物理针脚 11(GPIO17) |
| VCC | 物理针脚 2 或 4(5V) |
| GND | 物理针脚 6(GND) |

然后将我们的弹簧天线拿出来,插入发射模块左边中间的孔中,稍微弯折一下固定住即可。如果有条件的话,最好用电烙铁焊上去。
万事俱备,树莓派接电开机!

SSH 登录后,执行如下命令:
sudo apt update
sudo apt install -y python3-pigpio pigpio
sudo pigpiod
然后将荧光棒放到发射模块旁边,就可以在树莓派上跑 GPT 写的神秘小 Python 脚本了。
脚本链接:https://github.com/wuhan005/LightStick/blob/master/lightstick.py
python3 lightstick.py --r 255 --g 0 --b 0 # 红色
python3 lightstick.py --r 0 --g 255 --b 0 # 绿色
python3 lightstick.py --r 0 --g 0 --b 255 # 蓝色
python3 lightstick.py --r 255 --g 255 --b 0 # 黄色
python3 lightstick.py --r 0 --g 0 --b 0 # 关灯
如果步骤都正确的话,你会发现荧光棒真的亮起来了!

我后面又 Vibe 了一个网页,可以更加方便的控制调节颜色,真是太帅了!

以上所有微小的工作,均已开源至 wuhan005/LightStick
说实话,一开始我是没抱希望能点亮的,没想到最后居然成功了。我也给上面的荧光棒复活计划仓库提了 PR,贡献了本次许嵩安泊猜想演唱会的荧光棒信息。(注意上传图片要去除 EXIF 信息哦,GitHub 默认显示原图,另外一个 issue 里的老哥定位信息就这样在上传图片的 EXIF 中泄露了)
现如今 AI 越来越强大,我已经很难找到可以拿来写博客的题材了。基本上都是一句话,AI 就实现完了,没有跌宕起伏,没有了以前自己踩坑的过程与心得,也就没办法沉淀出一篇文章了。虽然本文的所有代码都是由 AI 编写,但硬件都是我自己下单购买,自己组装的,这些涉及到与物理世界交互的流程,AI 目前还无法干预。我也就能从中有所感悟或体会。
最近在工作中有不少 AI-Friendly 或者 AI Native 相关的建设,我个人倒是偏向于 AI 躺平派 —— 现阶段啥额外的优化都不用做,反正过几个月模型能力提上来了,问题自然就解决了。那些所谓的 Skills、MCP、Harness,反倒成为了更强大的模型的累赘。
说回演唱会,洛阳当地的消费确实比上海低多了!每天在市区里到处打车,一次只要 3-4 块钱。不过洛阳的很多景点建筑其实都是十多年前重建的,并不是啥历史文物,仅适合拍照出片。但洛阳好吃的确实太多了,小街天府的担担面,牛肉汤,大盘鸡,还有我最喜欢的洛阳烤鲶鱼。我至今还怀念洛阳烤鲶鱼的味道,回来发现上海这边的烤鲶鱼都是重庆风味的,并不是洛阳那种烤完会撒上糖粉提鲜,口感微甜的风味。真的真的太好吃了。下次如果还有机会,一定得再去一趟洛阳吃一次烤鲶鱼。
2026-03-28 12:25:00
《HITMAN》杀手三部曲,一直是我很喜欢的游戏。
特别是新杀手第一部(HITMAN 6),是少数支持 macOS 端的游戏。当年捧着 MacBook Pro 刚上大学的我,除了日常写代码外,电脑上能玩的游戏也就 HITMAN 和 Minecraft。后来又蹭室友的 Windows 台式机玩了 HITMAN 2,2021 年那会在宿舍熬夜玩新出的 HITMAN 3(现在应该叫做 HITMAN WoA)。疫情期间在家更是高强度追每个月的 ET(行踪不定的目标),对着 B 站上的攻略视频一步步打。我记得最走火入魔的时候,我在宿舍看到室友,都有种想绕后拿纤维绳按下 Q 的冲动。🤣
三部作品加起来,我的总游戏时长应该已经超过了 200 小时。但 HITMAN 的地图就那么几张,完成主线目标的暗杀后,就会开始想探索地图里的机遇与成就,甚至想去卡 Bug 突破空气墙,看看图外面的景色。之前在室友的 Windows 电脑上,我从网上随便找个 HITMAN 修改器,就能实现无限子弹、无限生命的功能,然后开始无双屠城。
回到游戏生态贫瘠的 Mac 上,我发现全网居然没人做 HITMAN macOS 版外挂!我在自己的 Mac 上只能老老实实地打任务,时间久了挺没意思的。
然而自己又是逆向苦手,IDA 启动后只会按 F5,一旦遇到反编译失败或者没符号表,我就不知所措了。
好在时代变了,现在我们有 AI 了!不需要再古法看汇编了!本文我将介绍自己是怎样用 Claude Code + Claude Opus 4.6 硬生生将 macOS 上的 HITMAN 外挂给 Vibe 出来的。也算是全网首发了吧,嘻嘻。
先从最简单的功能开始,我想先实现“无限子弹”的功能。在正式开始 Vibe 之前,我脑补了下大概有两种做法:Patch 游戏源文件或者进程注入。
macOS 上安装的 App 都是预先带有签名的,任何对 Binary 的 Patch 修改都需要重新签名才能运行。我觉得这种修改游戏本体的做法,让我的游戏程序“变脏”了,我本能地觉得抵触。况且如果我想玩纯净的原版,我还得将 Patch 还原并再次签名。那可太麻烦了。
第二种做法就是进程注入,注入游戏的运行时,修改内存。原理类似以前手机上的八门神器。但 macOS 下默认会开启系统完整性保护(SIP),限制了不能对任意进程进行注入。我也不想为了玩个游戏还专门重启电脑关 SIP。跟大模型讨论了下,提到可以使用 codesign 看下应用的签名信息,说不定应用本身就放开了口子。
codesign -dv --entitlements :- HITMAN
结果还真有意外的惊喜!HITMAN 几乎向我们完全敞开,大模型给的结论是:可以注入,而且非常容易 (Very Easy)。
allow-dyld-environment-variables
allow-unsigned-executable-memory
disable-library-validation
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
讲道理我到现在都没搞明白为什么 HITMAN 的应用签名是这样的,可能是为了开发过程中方便调试?
我让大模型 Vibe 了一个输出 Hello World + 基址的 dylib,然后用 DYLD_INSERT_LIBRARIES 环境变量带上 dylib 启动游戏,在游戏终端能看到输出了我们的注入信息!

有坑注意!
由于我使用是 M1 Pro 芯片的 Mac,但是 HITMAN 游戏是 Rosetta 转译后跑得 x86 架构程序。因此需要交叉编译 dylib 为 x86 架构。
这顺风开局让我信心满满。我决定先自己用 Cheat Engine 手动找到子弹数量的内存地址,然后发给大模型让它帮我 Vibe 一个锁定内存数值的东西就行了。
macOS 上的 Cheat Engine 并不好用,我换成了 Bit Slicer,但操作和原理都是一样的。先在内存中搜索游戏当前的子弹数值,然后开一枪,再搜索变动后的子弹数值,两次搜索结果 Diff 一下,就得出了存储子弹数量的内存地址。再将这块内存冻结住,不让数值变化,就实现了开枪但子弹不减少的无限子弹功能!
想法很美好,但当我真在游戏中实践时,我确实找到了那个内存地址,并设置冻结了,但我发现那只是展示子弹数量的游戏 UI 文本组件。我开枪后,UI 上显示的子弹数量确实不变,但武器的实际子弹是减少了的,多开几枪后还是会没弹药。那个存储武器实际子弹数的内存我死活搜不到,最后一顿瞎改,游戏画面甚至开始鬼畜了!
后来让大模型接手分析才知道,游戏的人物血量、子弹数等会使用 FNV-1 哈希做完整性校验,一旦校验不通过,就会设置 g_iCheatingDetected 这个变量,进而触发游戏的反作弊机制。当 g_iCheatingDetected 变量被破坏后,主循环每帧会用破坏后的值算出一个垃圾指针,对随机内存进行读写。随机读写到了相机的内存数据,就会导致画面开始缩放抖动。
狡猾,实在是太狡猾了!
这下我不知道怎么办了,只能大模型启动。
首先安装 ida-pro-mcp ,然后将 HITMAN 的 Binary 拖到 IDA 打开,IDA 中点击 「Edit」- 「Plugins」- 「MCP」,下方终端输出 Streamable HTTP: http://127.0.0.1:13337/mcp 即说明 MCP 服务已启动。
再在 Claude Code 里使用 /mcp 命令配置好 IDA MCP,能正确获取到 MCP 服务提供的 Tools 即可。可以先提问:
我正在用 IDA Pro 打开了什么项目?
让大模型主动调 MCP 看下是否配置正常。
后续直接:
我想做一个无限子弹的外挂,但是不知道怎样修改内存,请分析 HITMAN 游戏的子弹判定逻辑,并设计应该怎么做。
一开始大模型想着去处理那个 FNV-1 哈希的校验,然后又想去 NOP 掉扣弹的逻辑,但都走不通。最后是将 vtable 里的SetBulletsInMagazine(设置弹药数)和 GetBulletsInMagazine(获取弹药数)两个函数指针进行替换,把 Set 替换为 Get,这样每次游戏调用 Set 减少弹药时,实际执行的是 Get 读取弹药,从而使得弹药数不会改变。
第一版在游戏启动时就会注入,进入游戏就自动有无限子弹的功能了,我想 Claude 再帮忙改成按下 F1 热键才启动这个功能,同时要像 Windows 上的修改器一样,开启了功能要有提示。(F1 键其实是与游戏按键冲突的,不过问题不大,都可以 Vibe 都可以改)
我寻思这应该要用 osascript 推个系统通知的弹窗吧?没想到 Claude 实际也是这么做的,哈哈!
还有个小插曲:当时由于是深夜,我的设备自动开启了睡眠模式,导致弹窗没显示出来,我一开始还没反应过来是为啥。🤣
无限子弹完成后,我还想实现“隐身”功能,即在游戏中执行非法动作或进入非法区域时,NPC 不会产生警觉。Claude 直接逆出来了 ZActorManager::UpdateSensors 函数里的 12 个传感器(视觉、听觉、尸体发现、伪装检测等),对应着游戏内 NPC 的反应动作。UpdateSensors 函数会有一个条件分支,判断是否更新传感器,这里直接将 jnz(条件跳转)改为 jmp(无条件跳转),一次性跳过所有传感器的更新,就不会触发 NPC 的动作了。
但实际调试时,我发现击杀目标完成任务后,撤离出口并不会显示。原因是 LockdownManager::UpdateEvacuationZones 更新撤离出口的逻辑也在这里面,这里的逻辑要单独处理。
需要注意的是,如果游戏中的 NPC 已经锁定你,并进入了对你开枪的状态,这时 AI 隐身就没有用了。之前在 Windows 上用的修改器也会这样。
游戏里自带了一个 g_GodMode 上帝模式的全局变量,我猜测是用在实时渲染的过场动画中,角色会暂时处于无敌状态。当这个变量被设置为非零时,所有扣血逻辑都会被跳过并恢复满血。因此无敌模式其实是最好做的了,直接将这个变量改为 1 即可。
这应该是我最想要的特色功能。☺️
游戏中有很多墙边、柱子可以攀爬悬挂,有些悬挂点是在地图边缘或者高楼边,正常的操作是只能按 G 解除悬挂翻回室内。但我想在这个时候能强制切换到正常行走模式,这样就可以走到地图外进行探索。(例如北海道下山、萨比恩撒上房下海等)
游戏的移动系统有优先级机制,ZHM5MovementLocomotion(行走)优先级最高。其 WantControl 方法决定是否要接管控制权。将函数前 3 字节替换为 mov al, 1; ret,使其无条件返回 true,强制从任何移动状态(悬挂/攀爬/翻越)回到正常行走即可。
附上一张北海道下山的图,这是我在整个 HITMAN 游戏中最喜欢的地图!风景很美,我很喜欢。

我一开始是想要一键在背包里刷出游戏中所有物品的功能。然而 Claude 拒绝了这个需求,它说这个的难度比之前的高好几个数量级,我还得去找出游戏中所有物品的 GUID。
那好,退一步,我让 Claude 直接扫描当前整张地图里已存在物品的 GUID,再将其全部放到我的背包里。这样也能拾取到一些有意思的道具。Claude 的实现方式如下:
ZHitman5::Instance() 获取玩家实例指针。g_pWorldInventorySingleton 的获取整个地图的实体列表。QueryInterfacePtr() 查询 IItem 接口。(过滤非物品实体)ZHitman5::PickupItemDirect() 拾取。相当于是隔空拾取了地图中所有存在的物品,因此只能执行一次,东西被捡到背包里,原位置的物品就没了。但在实际使用中,我发现地图中会有少量 1-2 个物品可以无限次刷新,重复执行后还可以捡这俩东西。
这个项目我在两个月前就已经有了想法。最初是用 Gemini 3 Pro + Roo Code 来指导我用 Bit Slicer 实现无限子弹的 Demo,但效果并不理想,模型幻觉比较严重。Gemini 费了好大劲才发现 FNV-1 哈希校验机制,然后就开始胡言乱语了。
Claude Opus 4.6 发布后,我切换了模型又重新跑了遍,Claude Opus 4.6 能根据我发给它的 Bit Slicer 软件截图,结合 IDA MCP 直接算出当下地址空间随机化后的基址,进而算出我需要修改的 vtable 里的 SetBulletsInMagazine 函数的位置,帮我在 Bit Slicer 里完成了最初的 PoC。当时我还自己验算了一遍,发现完全一致!属实是被震惊到了。
关于这个项目的源码,我想了下还是不公开放出来,不然又要被发律师函了。(为什么是又呢?😝
如果你想获得源码的话,可以成为我的 GitHub Sponsor 解锁。🤗 不过我想上文都已经说得这么细致了,你把文章复制给 Claude Code,它也能给你 Vibe 出来。
收工。我们下一个目标见。
2026-02-02 23:10:00
一年多以前,我还对普通人炒股这种事嗤之以鼻。我觉得那就是庄家做局坑人割韭菜的玩意,钱进了股市注定会血本无归。
每当我看到身边有去炒股的人,我都会有种莫名的优越感,觉得自己才是保持清醒看透本质的聪明人。就像《创世纪》里霍景良的经典台词:“每天九点钟坐车上班,每个月也就挣那一万几千,省吃俭用玩股票,妄想一朝发财,他们根本就不知道真正的大赢家是什么人。”
同时我还极度厌恶风险,大部分的资产都集中在低风险的理财产品中(如微众银行的活期+ Plus,工资到手又上交公司了属于是)。这类产品基本不会有回撤,最多就是因为市场动荡某天收益为 0 而已。但这偶尔的收益为 0 也会让我感觉自己亏了钱,然后开始内耗。🤣
事情的转机发生在 2024 年末,受 KM 上同事的推荐,我去读了《金钱心理学》这本书。这本书主要想传达的是储蓄和复利的力量。书中虽然强调要控制风险并坚持足够长的时间,让复利发挥奇效,但并没有否定充满风险的股市,反倒是用巴菲特的例子来说明了价值投资的重要性。
这本书极大地改变了我对股市的看法,我觉得可以拿出储蓄里的一点小钱,去长期投资一些优质标的,来博取更高的收益。
2025 年的春节,我提前几天回了深圳,赶在过年放假前的工作日,去香港办了卡。具体可以看我小红书的这篇笔记。当时开了汇丰银行和众安银行两家的卡。众安是全程线上办理,上传了出入境记录后,很快就开通了。汇丰则是因为名字重名的问题,无法当场下卡,只能回去等实体卡和密码函的信件寄到家里。
最后果然不出意外的出意外了,后续我一直没能收到汇丰的信件。但因为众安银行的账户已经可以用来入金了,也就一直没管这事。去年 6 月想起来了,打电话让汇丰补发信件,又是过了好久,卡和密码总算寄到了。然而使用 App 激活时又提示出错了,最后还是去年 10 月国庆假期时,抽了一天时间去香港线下激活。但汇丰的卡也不是一点用处没有,我之前 HackerOne 是用 Wise 来收漏洞赏金的,后面改成众安银行的卡,提示打款被拒绝,再次改成绑定汇丰银行的卡,就能成功收款了。
回到正题,在办理了银行卡,并开通了长桥证券账号后,从零开始的美股冒险就此开始了!我将按时间顺序来梳理我过去 2025 年的操作与感悟。
开头叠甲
我没有任何系统性的金融知识,完全是小白水平从零开始。对股票的认知就是低点买,高点卖,然后就能赚钱。所以后面的很多观点可能并不成熟,甚至有的观点是因为幸存者偏差,不构成任何投资建议。
在关注美股以前,我曾听闻过苹果成为美股首家市值突破 2 万亿美元的上市公司的新闻,后续苹果又是首家市值突破 3 万亿美元的公司。因此想先买点苹果 AAPL 练练手。瞄准苹果开盘后的一个低点,“豪掷” 700 美元买了 3 股 AAPL。
可能是还在“新手保护期”吧,还真让我买在了当天的低点,没过多久就开始涨了,收盘时涨了 2 个点。
之后苹果连涨了好几天,直到 2 月 21 日开始回调下跌。虽然只是小跌了 0.11%,但我还是有样学样地卖出了 1 股。(手续费都 2 美元了🤣)
我在苹果这里尝到了甜头,单纯地认为股票只要找准合适的下跌时间,买入,然后等待上涨就行了。
市场很快就给我上了一课。
买入苹果的第二天,我又将目光瞄准了 TSLA 特斯拉。
我发现特斯拉在前一天 2 月 11 日下跌了 6 个点,便觉得它还会继续下跌。直接买了 20 股的 2 倍做空特斯拉。当时我觉得自己会做空股票可太帅了!别人买涨,我买跌,我就是叛逆,这太帅了!我就是大空头索罗斯!
结果就是,特斯拉在 2 月 12 日、13 日两天都在上涨,第二天甚至涨了 5.77%,填补了之前的下跌。我开始慌了,以为等后面正股跌回来就好了。这时有小伙伴告诉我,2 倍做空这种加杠杆的行为,是会有“磨损”的。
“磨损”是很简单的数学原理。我记得有这么一个笑话:给你的工资先涨薪 10% 再降薪 10%,与先降薪 10% 再涨 10%,到手都是亏了的。
因为 1 * 1.1 * 0.9 = 0.99,而乘法满足交换律,所以前后相同的乘数,不管是先涨后跌还是先跌后涨,最后的总数都是减少的。而杠杆又放大了这个比例,这就是 “磨损”。2 倍做空亏损后,正股需要超过之前的跌幅,才能开始回本。
第一次意识到还有这种风险,我吓得果断选择了全部清仓。前后亏损了 60 多美元。这次教训之后,我就不太敢做空了。甚至一段时间内都没再碰过特斯拉的股票。当然,站在上帝视角回看,后面发生的事情我们都知道了—— 2025 年 3 月,特朗普发动关税战,美股股灾。如果我的 2 倍做空特斯拉能拿到 3 月,确实能狠狠赚一笔。
可惜,没有如果。
在特斯拉这里栽了跟头后,我又去关注了下英伟达 NVDA,买入了 5 股。这次因为持有的是正股,我也就不那么慌了,想着一直拿着就好,跌了就补补仓。
然而我对“补仓”这一行为,也没有概念。
我以 141.40 的价格买入了 5 股英伟达,买入的 8 分钟后,股价跌了 1 美元,为 140.40。 然后我就想:股票下跌,是时候该加仓了! 以 140.40 的价格马上买了 2 股。过了 40 分钟,又跌了 1 美元到 139.00,我又买了 3 股!过两天发现跌了 2 美元到 137.00,我又买了 3 股。4 个小时后跌了 3 美元,我又补了 1 股。
当时的我对于涨跌的幅度以及每次下单时券商收取的手续费完全没有概念,看到股价跌了一点就补,导致总体成本也没降下去,白白损失了手续费。后来跟我爸聊起这事,他才纠正我,让我在英伟达每次跌幅超过 10 美元时,才选择加仓,后续涨了 10 美元时,就卖出。
我按照爸爸的建议,在 3 月 3 日英伟达距离我上次买入价跌了 10 美元时,以 112.80 的价格买入 10 股英伟达。
然而 3 月特朗普发动关税战,对我国征收高达 145% 的离谱关税。面对英伟达股票连续几天的下跌,在 3 月 11 日的时候我终于忍不住了,选择购买英伟达 2 倍做空 NVD 来进行对冲,降低亏损。是的,我又开始加杠杆做空了。好巧不巧,英伟达从那天开始上涨修复了!最终给我成功搞成了不管英伟达股票上涨还是下跌,我两头都挨打的局面。😅
在买入 NVD 的 4 个小时后又匆匆卖出了,又亏损了 60 多美元。
好消息是,10 美元买入卖出的策略初见成效。
在英伟达上涨了 8 美元,我卖出了 10 股。后面跌了 10 美元,我又以 110 美元的价格接了回来。跌到 100 美元时,又继续补仓。后续涨到 111、120、130 时再分批卖出。这波操作稳稳的降低了我的持仓成本,实打实赚到了钱。
现在回头来看,我愿称之为“反向马丁策略”。“马丁策略”也就是倍投策略,即赌博时每次翻倍下注,赢一次就能回本且赚钱。马丁策略的问题在于,每输一次,下次需要押注的金额呈指数上涨,除非资金量无限,否则多输几次很快就破产了。
而我跌 10 块买入,涨 10 块卖出的方法,实际上是相反的。我算了下,在英伟达股价 110 美元的时候,它跌 10 块我买 10 股,哪怕它跌到 0 元,我要付出的本金就是:((100 + 10) * 10 / 2) * 10 = 5500 美元。这个金额是确定的,哪怕英伟达破产退市股价为 0,我也只投入了 5500 美元。如果真有那一天,那显卡估计也不值钱了,我可以白菜价买 RTX 5090,想想也不亏。
英伟达涨了,我赚钱;英伟达跌了,我可以低价买卡。正反都是我赢,好!
经历了这次股灾之后,我总是会想:“我经历过 86 块钱的英伟达,所以已经没有什么好害怕的了。”
4 月除了继续在英伟达上微操之外,我还铤而走险去买了 NVD 和 UVIX。
4 月 17 日盘前,在公司吃完饭时,我看到新闻说老黄突然来北京谈合作了。没多想就买了 10 股 2 倍做空 NVDA。开盘后英伟达居然真的下跌了,两倍做空涨了 3 美元后我寻思差不多了,赶紧卖出,小赚一点点。
4 月 29 日睡觉前,我看到消息说特朗普将于美国时间 4 月 29 日发表百日施政讲话。我寻思特朗普这次肯定又会口无遮拦乱说话,怕不是又要带崩股市。所以睡前买了 20 股 2 倍做多恐慌指数期货 UVIX。第二天晚上去找 C 老板吃饭时,发现股价真的开始下跌了,UVIX 涨直接了 15%!我在开盘后的高点迅速卖出,小赚了一笔。但事后再看,还好当晚赶紧卖出了,从那之后 UVIX 就一直在下跌,如果没卖就彻底被套住了!
以上两次操作虽然都赚钱了,但我很清楚这些都是靠运气赚到的,所谓的看消息面只是我的一厢情愿。 技巧一点没有,只是被我蒙对了。
5 月份上半月沉迷 MyGo 和 AveMujica,下半月去东京玩了一圈,美股基本没什么操作。
时间来到 6 月,稳定币发行公司 Circle 于 6 月 5 日在纽交所上市,发行价为每股 31 美元。说实话,我对币圈一直是比较抵触的,在我的认知里,普通人玩币迟早有一天会爆仓完蛋。而我又不知道稳定币到底是个什么玩意,觉得应该也是币圈的东西,最好不碰为妙。
但 Circle CRCL 上市第一天,股价上涨 140+% 来到了 80 多美元。我试探性地挂了 79 元买入 10 股的单,快收盘时成交了。第二天睡醒一看,夜盘直接涨到了 89 块,我赶紧选择卖出,一晚上就赚了 100 美元。
当天开盘后 CRCL 股价一度冲上了 120 美元,后面甚至连涨一周多,最高点股价接近 300 美元。但是我对于这次踏空并不后悔,稳定币完全是我认知以外的东西,我并不清楚它的来龙去脉和运作原理,能赚到钱纯属跟对了风口。对于自己看不懂的东西,我坚定地选择不去碰为好。
说来也是有意思,CRCL 从 7 月之后就一直下跌,期间虽然有小的修复,但整体趋势还是向下的。最近比特币价格一直下跌,CRCL 的股价一度跌到了 62 块。现在回看,庆幸还好当时跑了,并且再也没碰过。
我怀疑散户因为这次 CRCL 上市的涨幅,对后面上市的知名公司,都有了迷之信心。对,我要说的就是被称为 2025 年最受关注的科技股 IPO:Figma FIG。Figma 作为平面 UI 设计的绝对独角兽,还曾拒绝了 Adobe 的收购,我估计很多人会将 Figma 的上市看做下一个 Circle。
Figma 上市当天确实上涨了超过 300%,很多人觉得它会像 Circle 那样连涨 7 天,所以纷纷高位进场。谁曾想 Figma 在第 4 天就破发了。往后更是一路下跌,发财报也不亮眼。我当时也在想要不要买点试试,最后因为价格太高没买成,还好还好。
由于我在 Circle、英伟达、特斯拉、CloudFlare 上数次投机成功,赚了点钱。我开始有点飘了,7 月时买入了小盘股 Destiny Tech 100 DXYZ,长达 3 个月的噩梦就此开始了。
Destiny XYZ 并不是一家常规的科技公司,它是一家资产管理公司。Destiny Tech 100 是一个包含 100 家待上市的顶级科技公司的投资组合。DXYZ 持有这些未上市公司的股票,包装为投资组合,目的是让普通人也能参与私募市场的投资。投资组合中占比最大的就是 SpaceX,因此 DXYZ 常被当做 SpaceX 或者私人航天火箭相关的标的进行投资。
我以 35.25 的价格买入了 DXYZ,随后的两个月内它基本一直在下跌,很少有涨的时候。SpaceX 星舰的发射也屡屡受挫,消息面上也带不动股价的上涨。我很难再重复之前在英伟达上的操作,来不断买入卖出降低成本。到 9 月底的时候,它的股价已经跌到了 25 美元左右。
一开始我还只是嘴上抱怨咋买了这么个垃圾股票,后来逐渐发展成了自己吓自己。我开始在推特上搜索网友关于 DXYZ 的评论,有人说他市盈率过高,有人说这就是一个把股东当 ATM 的骗钱玩意。我发现这股票没有期权,无法做空、往期财报刚好赶在行情好的时候发布、创始人的上一家公司经营的也不好。我还找到了创始人的 Linkedin 和 GitHub,发现他的 GitHub 比脸还干净。
这些消息让我逐渐对 DXYZ 丧失信心,时间来到 9 月底,我决定想办法赶紧回本然后跑路。
经过很多天的观察,我发现 DXYZ 这种成交量很少的小盘股,虽然每天盘中都是在下跌。但每天北京时间早上八九点,夜盘开始时,总会出现几个比当前股价高 2-3%涨幅的买入单。我也不知道这是哪来的大冤种,每到夜盘就花高价买入。后续有人跟我说是做市商,我寻思这做市商人还挺好,说不定我能趁机将成本降下去。
我的计划是这样的:DXYZ 每次盘中快收盘时,往往是股价最低点,这时候使用券商融资大笔买入 100 股,等到夜盘涨了 2-3% 的时候顺势卖出。每天不断这样操作,既没有融资的利息,又可以不断降低成本。我在 9 月底连着 3 天都这样操作,每天都盯盘盯的很晚,心里总是不踏实,每晚睡觉做梦都是 DXYZ 涨了或者跌了。在后来长桥的年度总结里,我有天打开 App 看了上百次 DXYZ。
我连着 3 天都成功盘后低价买入,夜盘高价卖出。甚至有一天的夜盘,要是我再晚 5 分钟卖出,股价还能涨更高,那个时候恰好就能解套了。但我忽略了一件事,虽然我每天 100 股的买入卖出,确实赚了钱,但我原来持有的 30 股 DXYZ,却一直没有动,那 30 股还是随着每天股价的下跌而亏钱,一来一回,我的总成本并没有降低多少。我一开始就应该卖出那 30 股的!
意识到这一点的时候,已经是第 4 天了,这天不出意外翻车了。DXYZ 并没有按我设想中的那样在夜盘有个上涨,我一直等到了当天开盘也没有出现。开盘后,股价还是照常下跌,此时我手握 160 股的 DXYZ,稍微的一点跌幅,亏损都会被放大很多。我直接慌了,最后以 22.70 的价格将手上的 DXYZ 股票全部卖出。
至此,我在 DXYZ 上亏了有 170 美元左右。但我心中的石头落地了,我终于可以睡个好觉了。
那是 9 月的最后一天,DXYZ 在我卖出后还在不断下跌,此时我的心里只有劫后余生的庆幸。
但这还并不是故事的结尾,经历了长达数月的下跌后,在我清仓后的第二天,10 月 1 日,DXYZ 暴涨 31.78%!
没有任何理由,没有任何新闻,它就是涨了,在我卖出后涨了。
如果我当时能再多坚持 24 小时不卖出,等到第二天开盘时,我就能狠狠地赚上一笔了。
这件事之后,我再也不碰小盘股了。
经历了 DXYZ 之后,我开始转向保守型投资了。后面买了美股七姐妹 ETF $MAGS 和纳斯达克指数 ETF $QTOP。这些标的跟着大盘走,收益虽然低,但稳定且可控。
在年初刚开始时,长桥有一个买期权送卡券的活动。这是我唯一一次碰期权,象征性地买了 2 张还有 14 天到期的英特尔 INTC Put 看跌期权,过了几天涨了 0.01 元后卖出,赚了 2 美元。但是算上手续费后,整体其实是亏的。这导致在我 App 的盈亏分析排行榜中,有个几块钱的英特尔的亏损,让人看着不爽。我寻思可以买点英特尔正股,随便赚一点把这个亏损的数给填上。
我在 10 月底英特尔大涨 5% 后的第二天回调开始建仓,成本 41.77,挺高的。后续英特尔持续下跌,但我认为这支股票已经和美国政府的利益捆绑在一起了,可以说像是波音公司一样,是美国的亲儿子,后面特朗普随便发表点暴论,立刻就能涨回来。
后续连着跌了几天,我在 38.32 时又补了点。然而 11 月底时,由于美国政府停摆 + 降息预期下降,科技股在那段时间都有不同程度的下跌。这其实是绝佳的抄底时刻,可惜我在前面的补仓中已经打光了子弹,手头还握着 MAGS 和 QTOP,实在没有闲钱加仓。因为不确定这波下跌周期会持续多久,也不敢冒然去融资。只能静静等待。
当然英特尔也算争气,11 月 28 日传出英特尔成为苹果供应商的消息,大涨 10%。(英特尔:嘿嘿,又要到饭了)12 月 2 日又大涨 8%,创年内新高。我看赚得也差不多了,分批全卖掉了。好巧不巧卖掉之后英特尔就开始下跌了,但我也没选择继续接回来,英特尔好几次大涨都是因为传出又与某某公司合作的消息,要到饭了所以股价涨了,我认为长期来看这很不健康。
虽然之后 1 月英特尔破新高涨到了 54 元,你可以理解为我又踏空了,但跟 Circle 一样,我只想赚自己认知内该赚的钱。
值得一提的是,我前几次都在赌英伟达的财报,每次财报发布前买一点,然而每次财报发布后都是下跌的行情,过了一阵子才涨回来。因此英伟达 11 月份的财报我决定不赌了,肯定又是下跌的剧情,等跌的差不多了我再来抄底!
然而财报发布后的当晚开盘,英伟达股价居然没有下跌。我以为是自己的判断出了问题,说不定英伟达这次财报跟之前都不一样,这次是要涨了呢?我赶紧以 194.80 的价格开始建仓,谁知到了盘中后半夜,英伟达急速下跌,当天直接跌了将近 8%!老黄都站出来表示不理解,英伟达保持了这么好的增长业绩,为什么市场还不买账?
是啊,我也疑惑,为什么市场还不买账?但没办法,既然被套住了,那就只能想办法解决了。我继续按照 3 月的策略,在盘后跌到 180.20 时补了一点降低下成本。之后就是长达一个多月的横盘,但我不急,我坚信英伟达会涨回来的。我之前买 DXYZ 都被套了 3 个月,就算被英伟达套 3 个月又有什么可怕的呢?
等到 1 月 6 日,英伟达开盘股价站上 192 时,我知道时间来了,卖出了 180.20 时加仓的部分。横盘了一个多月的英伟达,这天快收盘时果不其然地又跌回去了,我又在低价给接回来了。做了一个还算完美的 T。
现在我的英伟达持仓成本已经降到 186 左右了,但我不甘心就这样卖掉。因为浪费了快两个月的时间成本,我很想等到它重回 200 时再卖。
1 月的某天中午,我在公司午休刚睡醒。看到台积电发布了财报,财报内容远超预期。我立刻融资买入,当天晚上开盘后台积电 TSM 涨了将近 7 个点。我在开盘后的第一个高点全部卖出。完美的一次日内融空手套白狼。
1 月 29 日,我看特斯拉跌了 3 个点,从开盘 437 美元最低跌到了将近 415 美元,盘中慢慢又涨回去了。按照我对特斯拉这种“妖股”的理解,它每次大幅下跌后,第二天都会快速修复,可能这就是马斯克信徒的力量吧。我抱着试一试的想法,挂了个 416 元的单。第二天一觉醒来后发现居然成交了!当天晚上果然上涨了 5 个点,由于时间已经临近周五,再加上我这次又是靠融资买入的,害怕周末夜长梦多,万一特朗普又发疯说了什么话,周一大跌就完蛋了。索性周五在高点卖了,又是小赚一笔,成功把特斯拉的持仓成本降低到了 400 以下。
我爸后来叮嘱我,让我不要再去碰融资融券这些东西。用自己的钱,做确定的买卖,最多也只是全部亏光,融资则是亏完后还倒欠别人的钱。我也开始有意识的去改正,由于大盘最近一直不是很景气,我清仓了手上的 QTOP,准备多留些子弹去布局其它标的。
这是我从零开始炒美股的第一年,最终也是获得了将近 30% 的年收益率。
期间有过像 CRCL 这样的靠运气大赚,也有像 DXYZ 这样的靠认知大亏。赚钱的时候我会沾沾自喜,觉得跑赢纳指也不是什么难事嘛。亏钱的时候,我才意识到巴菲特能在半个世纪的时间里,穿越周期并保持冷静是多么厉害。3 月的时候传出了巴菲特卖出苹果的消息,当时我跟很多人一样,觉得是这老头跟不上时代犯糊涂了,后面随之而来的股灾让我说不出话了。
美股也让我被动去关注很多地缘政治信息、国际新闻、财经常识等。我开始知道美联储是啥,降息意味着什么,鲍威尔的 Good Afternoon 段子,ETF 是什么……
美股破产四巨头:期权、小盘股、中概、做空。
在未来的日子里,我要时刻提醒自己,不要去碰期权。买卖期权在我看来就是赌博,期权就是资本重点收割的对象。同时我的脑子也理解不来那些复杂的期权策略,这里面的钱不该我赚。
近半年来,网上关于使用大模型炒股的项目和比赛层出不穷。我也曾尝试过从零 Vibe Coding 一个大模型炒股程序,但最终还是弃坑了。一方面是觉得大模型炒股这件事并不靠谱;另一方面,我想把炒股当做一个闲暇时间的兴趣爱好,就像有的人喜欢钓鱼一样。他们并不会用机器或者 AI 去替代人工钓鱼这件事,因为个人的实际参与,才是这件事真正有意义的地方。我希望自己去关注,去按自己的思考下单买卖,大模型最多给我提供资讯方面的情报,它不应该替我执行决策。
最后再打个广告,本文中的可交互股票图表组件,使用的是我编写的 Hugo 股票图表插件 hugo-trading-chart。实现思路很简单,先从长桥的 API 抓取历史 K 线,再使用 TradingView 开源的 Lightweight Charts 组件绘制图表。数据抓取的部分是手写的,其余的前端全是 Vibe 出来的🤣,欢迎 Star~
2025-10-06 21:13:12
十年前的午后,我在b站刷到了一个视频, 视频介绍了如何在 OpenShift 平台上搭建 WordPress 博客。
很多年后,我才意识到那是一个多么生机勃勃的时代:Docker、K8s、Vue 才刚起步,字节才刚开始融资,AS3 还没凉,自然语言对话服务还是谷歌 DialogFlow,微软小冰,IBM Waston。
可惜我找不到那个视频了,估计是被删了吧。但好在我跟着视频一步步搭建的博客,陪我记录了这十年。
我在 9 月 28 日凌晨发了这条朋友圈。原本是想等到 10 月 4 日再写些东西叙叙旧,奈何一想到 10 周年就心潮澎湃,就提前开始“预热”了。😂
此刻,我正坐在同样的沙发上,同样面朝阳台,写下这段文字,和十年前一样。
你可以结合 文章归档 页面,和我一起回忆我的“黑历史”。
我很少有能一直坚持下来的事情,很多 Side Project 都是轰轰烈烈开个头,三分钟热度一过,就再也不管了。写博客最初的动力源于 WordPress 后台给了我种打扮 QQ 空间的感觉,我可以换好看的主题,装一堆插件。但它比 QQ 空间的可定制程度更高,我可以通过自己的域名访问,可以在页面底部加自己的 Copyright 版权信息,一切都是自己的东西。
在按自己的想法装扮完页面后,我想着得写点东西挂上去充数。那会儿我刚上高中,身上的“中二”气息还没褪去,再加上高中开局不利,考试成绩接连爆炸,所以写了些很丧又很幼稚的文章。现在看来真是黑历史。到了 2016 年的高二,我因为接触 WordPress 而开始学习 PHP 语言,但那时同样很难找到东西写,便把自己发的 QQ 空间说说转载到博客来,这样“滥竽充数”也就成为一篇文章了。文章大多很短,有些还很意识流,我已经看不懂当时的自己想表达什么了。
2016 年下半年,我关注了「差评」公众号,在那之后的文章,会不自觉地去模仿差评公众号文章的标题和文笔。并且都是先发表在个人公众号,再顺带转载到博客。到了 2017 年,我终于是能写点正经技术文章了,我分享了如何给 WordPress 全站开启 CDN、写 C# 时踩得坑、用 CodeIgniter 框架写得小项目、用 PHP 写得微博爬虫…… 直到这里,我才算真正产出了能帮助别人文章。
2018 年高考结束后的暑假,我分享了自己开发的微信小程序的前后端实现,如何实现树莓派的内网穿透,初识 Jenkins 等。上了大学后,大学的自由让我能自主规划去学很多新东西,博客文章也是一篇接着一篇。从 CTF 到 Docker、PHP Swoole、PHP 内核(虽然只开了头)、CI/CD、Serverless 函数计算、Redis、Vue,再到现在混饭吃用的 Go。我在那时开了 Apicon 这个坑,把我学到的这些东西融入到了这个项目里,就当是自娱自乐。
时间来到 2020 年的疫情,那年我主要是在开发 CTF 平台 Cardinal,博客文章记录了我运营这个开源项目的感受,技术上和心理上的都有,虽然都比较“稚嫩”。2020 年下半年,我将重心投入到了在 ForkAI 的工作中,博客更新频率大不如从前。我在工作中接触到了 Macaron 框架和依赖注入,还被安利了《黑客与画家》这本书,我也总结了篇读书小记。
2021 年我开了很多坑,比如 EggMD 协作文档、Elaina 代码运行器、mebeats 小米手环心率采集、asoul.video 视频站等。每个项目都有可以分享的内容,都是一篇独立的文章。(虽然很多项目后来我就没维护过了)
这里我想重点表扬下 《Your Soul, Your Beats! —— 小米手环实时心率采集》 这篇文章,这是所有文章中访问量最多的一篇,直到文章发布 4 年后的现在,每天都还有人阅读。抛开文章内容的实用性不谈,更重要的是这篇文章详细描述了我当时一步步解决问题的思路和方法技巧。 我首先使用软件检测电脑蓝牙,再逐步扩展到编写代码操作蓝牙;在遇到依赖库年久失修无法使用的情况时,我又是如何成功找到还在维护且可用的库;最后照应前文一步步的软件操作,将功能编写为代码。直到今天,我都认为这篇文章写得真的真的很好!
2022-2023 年,又是 allin 工作的一年,文章产出更是大幅减少。这段时间的工作内容主要集中在 Kubernetes 集群,所以抽空写得文章都是些集群相关的骚操作。
2024 年中,我入职了鹅厂。工作强度相比前几年小了很多,我有更多的时间去思考,去动手做一些新东西。刚入职的那一个月,几乎每个周末都能写一个新项目出来。(虽然很多都还没开源) 我开发了 Sayrud,它现在也被我用来搭建博客的评论后端。我基于 Traefik ForwardAuth 开发了自己的集群统一认证 ikD,现在我服务器集群对外暴露的所有服务,都已经接入了;甚至该 Side Project 还被我成功引入到了公司团队内,稍作修改后作为团队成员登录各服务的统一认证。😄 之后又自己实现了个大模型套壳站,这段关于大模型应用的开发经验也被我用在了公司的项目中。我发现 2024 年后,我在闲暇时间自己研究的事情——无论是开发的 Side Project,还是在自己的 Kubernetes 集群或者腾讯云运维中积累的知识,在未来的某一天都能反哺到我的工作中。颇有种我提前预判了我的工作,提前就给做完了的感觉。(叠甲:这并不是说我之后就开始摸鱼了,当然是在追求更加精益求精 😛)我很喜欢无心插柳柳成荫的意外收获,希望这样的日子能永远永远地继续下去。
我是一个很在意他人看法的人,不止是他人对我口头评价的看法,也体现在比如 Twitter 粉丝,博客文章评论量这些事情上。我会因为 GitHub Follower 数 -1 或者博客一直没人评论而烦恼,会因为日常工作中他人对我态度不友好而内耗一整天,即使这很有可能是我听错了或者想多了。我时常会在睡前突然想起白天尴尬的事情,然后在床上缩成一团。我会评判自己白天是不是哪句话说得不对,给别人留下了不好的印象。我时常会将自己的成就归结于百年难遇的运气爆棚,进而陷入自我怀疑,会有种不配得感。
反过来也是同样,我对收到来自别人的反馈或者肯定可以兴奋地睡不着觉。之前很长一段时间没有维护过 NekoBox,偶然收到了来自用户的打赏和鼓励,那天晚上就跟打了鸡血一样写新功能肝到凌晨三四点。在工作中也同样,一旦收到了正反馈,我就会感觉这是自我价值得到了实现,自愿加班到 11 点后,开始抱怨为什么空调关了只能被迫下班。
能让我坚持将一件事情做下去的动力有两个。一个是我能从中持续得到反馈,让我觉得自己的所作所为是被看见了的。另一个是我能“吃自己的狗粮”,我自己也会作为用户,会去不断使用我所创造的东西。 NekoBox 是前者,ikD 是后者。
站在十年这个时间点,我觉得得立个 Flag 做点什么。我在年初注册了 nekobase.com 这个域名,并备了案。我想开个新坑,将我博客中用到的服务组件作为 SaaS 开放出来,供大家使用(例如评论后端、代码运行器服务等),顺带继续拓宽技术栈,去做些“更高级的 CRUD”。我也不知道这个服务会不会有人来用,但至少我自己的博客会迁移过去,能 dogfooding 的话,就不会半路弃坑吧。(应该吧)
我不知道下个十年的自己会身在何处,但当下,我发自内心地十分满意现在的工作和生活,希望这样的日子能永远永远地继续下去。
2025-09-13 23:12:57
NekoBox 匿名提问箱于 2020 年 3 月上线以来,至今已有五位数的注册用户并产生了六位数的提问。
这个数据大大的出乎了我的意料,要知道 NekoBox 从未对外公开宣传过,纯靠用户间口口相传。我很喜欢这种无心插柳柳成荫的事情,自己默默做得事情能够被看见,对我来说是很幸福的事。
说来惭愧,一直以来我都是“放养式”运营,每次只有自己手头的工作不忙了,才会登录上兔小巢看一下用户的反馈,然后将一些恶性 Bug 或实现起来简单的需求给做了。很感谢使用 NekoBox 的各位能包容我的懒惰,依旧不离不弃。🧎🏻
本文记录了近期 NekoBox 迁移与重构时踩过得坑,以及我对 NekoBox 的定位与后续展望。技术向的内容会有些多,不感兴趣可以直接跳到文末。
NekoBox 最开始是我 2020 年花三天时间写出来的,作为一个小玩具部署在我的国内服务器上,并且使用我个人的备案域名。网站可以使用任意的邮箱注册,并不需要用户输入手机号并验证实名,这其实是不符合我国《互联网信息服务管理办法》的。
但当时抱有一定的侥幸心理,想着提问和回答都接了云服务商的文本内容审核 API,违规评论都会被拦截,再加上自己也没对外宣传这个站,应该不会有问题。
但这在无形中给我埋下了一个大雷。
2023 年 2 月 23 日上午 11 点左右,我在家接到了网信办的电话,对方说有人在 NekoBox 发布违法言论,我作为站长,需要配合调查。当时我吓坏了,马上光速注销备案 + 关站,并认真配合警察叔叔的工作。
最后好在我没有利用 NekoBox 进行盈利,且我事先也接了相关文本审核的功能,在配合工作提供了相关材料后,这件事便告一段落了。还好没留下什么案底,已经是万幸了。
事后复盘发现,那名用户使用谐音和表情符号绕过了文本内容审核功能。这让我意识到机器审核 API 也会有严重的漏报,但一方面因为成本原因又无法做到每条信息都接人工审核服务。
互联网不是法外之地!
这件事给我的打击挺大的,我最初的想法是将网站代码开源出来,大家能够一起共建,可惜 GitHub 上一直没有多少贡献者,还被炸弹人给爆破了。原先的国内网站下线后,我收到了很多用户的反馈,纷纷询问站点怎么无法访问了,甚至还有一位网友因为 NekoBox 了解到了我的技术博客,受到触动也开始尝试建站写博客。能成为他人的光真的是很开心的事。
因此后续 NekoBox 便迁移到了境外服务器,并且没再使用备案域名了 —— 正如 v2ex、Go 语言中文网等站点那样。希望它能在广袤互联网的一角,继续安静地存在下去。
抒情的话聊完了,该聊点技术了。
NekoBox 部署在境外的 2C2G 轻量服务器上,由于配置的原因,只能使用 Docker Swarm 进行粗糙的服务编排调度。每次需要更新线上版本时,都是 GitHub Actions 通过 SSH 连上服务器,再执行 docker service update 命令。
我想将 NekoBox 接入现有的 K3s 集群,使用 GitOps 实现更好的版本管理和平滑更新。我的 K3s Master 节点位于腾讯云上海区域,经测试发现腾讯云东京区域的线路比中国香港区域好些,因此买了台 2C4G 的境外东京区域的机器,作为 NekoBox 新的部署机器。
关于如何跨地域甚至是跨云组件 K3s 集群,这篇文章介绍的很详细:《基于K3S和zerotier-planet实现跨云搭建K8S集群》
首先使用 ZeroTier 将不同可用区的机器加入到同一 ZeroTier 网络中,这样在 K3s 看来它们就在同一内网里了。

节点均开启 IPv4 Forwarding 后,修改 Master 节点 /etc/systemd/system/k3s.service 中的 K3s 参数,显示指定 node-ip 为节点在 ZeroTier 中的 IP,并设置使用 ZeroTier 的网卡:
ExecStart=/usr/local/bin/k3s \
server --node-ip 10.243.xxx.xxx --flannel-iface ztcxxxxxxx --flannel-backend=host-gw \
同理,修改 Worker 节点 /etc/systemd/system/k3s-agent.service 的 K3s 参数:
ExecStart=/usr/local/bin/k3s \
agent --node-ip 10.243.xxx.xxx --flannel-iface ztcxxxxxxx \
重启各节点上的 K3s 服务,Lens 连上 Master 节点能在 Worker 节点启动 Node Shell 并访问 Worker 上的 Pod 日志,Worker 节点能请求到其他节点上的 Service,说明就配置成功啦~
我们再给 NekoBox 的节点加个 region=jp 的污点,防止集群里的其它服务被调度过来。
后续还推荐装上 goldpinger,它会建一个 Daemon Sets,在每个节点上放一个 Pod 来监测节点间的连接状态。

官方仓库里虽然提供了 Helm Charts,但比较敷衍,可扩展性差,建议是自己把有用的部分扒出来直接 GitOps 写 YAML 创建资源。官方仓库里还提供了 Grafana Dashboard 定义,导入 Grafana 后可以很直观的看到节点之间的连接延迟:

2020 年写 NekoBox 那会,我还很菜(虽然现在也很菜),数据库只会用 MySQL。
尝试过 Postgres 后发现真香,就想着把 NekoBox 从 MySQL 迁移到 Postgres。但 NekoBox 在线上跑着,随时会有用户访问,发布新的提问和回答往数据库插入新的数据,此举无疑是在边开飞机边换引擎。
社区的 pgloader 是一个很好用的 Postgres 数据迁移工具,但它只支持全量迁移,并不支持增量同步。换句话说我需要先给线上的 NekoBox 停机防止有新的数据写入,迁移数据,再将后端数据库配置改到新的库上。受限于老的 2C2G 服务器的性能,我需要对 pgloader 进行限速,停机迁移全量数据的时间可能会很长。
如果要实现不停机迁移,则需要在完成全量迁移后,再将全量迁移这段时间内的增量数据,也同步到新库中。在调研了市面上几个数据库同步产品后,最后我选择使用阿里云 DTS 来完成。(这里不得不吐槽下我司,腾讯云的 DTS 产品只支持 MySQL 系之间的数据同步,不支持异构数据库,还得加强呀!)
将服务器添加为阿里云数据库网关 DG 节点后,即可在 DTS 控制台选择使用数据库网关接入非阿里云的源库与目标库,配置完后启动任务即可。

等全量迁移完了就会开始一直跑增量写入任务了,此时可以在线上写一些数据,来检查数据同步的情况。
但是阿里云在让我失望这件事上从来没有让我失望过。
我发现阿里云 DTS 居然把 MySQL tinyint(2) 类型迁移成了 Postgres smallint 类型,而非 bool 类型!这导致 GORM 在 AutoMigrate 时直接报错了!这一点在 pgloader 中专门有一条 tinyint-to-boolean 规则进行适配:
As MySQL lacks a proper boolean type, tinyint is often used to implement that. This function transforms 0 to ‘false’ and anything else to ’true’.
提工单问了客服,客服只会照本宣科给我发产品文档链接…… 我要的是怎样解决问题,不是你告诉我产品该怎么用。
更抽象的是,阿里云 DTS 怕不是根本没什么人用,产品文档中记录的“库表列名单个映射”功能,前端的树形组件下拉是有 Bug 的,如果直接全选了整个库,则无法再细化各表的字段映射配置。
这导致不看文档,用户自己是不会知道还有这功能的。但这个字段映射也只是配置目标字段的名称,并不能修改映射类型。
我的解决办法是在迁移完后,观测到线上流量低后,关闭 DTS 同步,线上服务停机,Postgres 数据库执行 SQL 修改字段类型。
ALTER TABLE "nekobox"."questions"
ALTER COLUMN is_private TYPE BOOLEAN
USING CASE
WHEN is_private = 0 THEN FALSE
ELSE TRUE
END;
ALTER TABLE "nekobox"."censor_logs"
ALTER COLUMN pass TYPE BOOLEAN
USING CASE
WHEN pass = 0 THEN FALSE
ELSE TRUE
END;
修改类型的 SQL 执行的很快,就当我以为已经全部搞定的时候,我发现阿里云 DTS 这垃圾东西居然不会迁移 Postgres 自增序列! 这意味着每一张表的ID 字段都不会自增并自动赋值,插入数据就会报错说 ID 字段为 NULL。
没办法,赶紧执行 SQL 手动加序列……
-- 1. 查看当前最大 ID(先确认数据)
SELECT MAX(id) FROM "nekobox".users;
-- 2. 创建序列并设置起始值(假设最大 id 是 1000)
CREATE SEQUENCE "nekobox".users_id_seq START WITH 1001;
-- 3. 将序列绑定到 id 列
ALTER TABLE "nekobox".users
ALTER COLUMN id SET DEFAULT nextval('nekobox.users_id_seq');
-- 4. 将序列的所有权给表(表删除时序列也删除)
ALTER SEQUENCE "nekobox".users_id_seq OWNED BY "nekobox".users.id;
还得是阿里云,能整出这种狠活来,真牛!😅
如果再给我一次机会,我会选择自己写一个工具,先记录 MySQL 数据库 BinlogID 或 GTID,然后调用 pgloader 进行全量数据迁移,再从记录的 BinlogID/GTID 处开始增量同步添加数据。
现在 NekoBox 已经迁移到了 Postgres,凌晨 3:30 开始迁移,4:00 完成。线上运行了一天 Trace 里没看到有报错,感觉是没问题了。
NekoBox 的前端使用 UIKit 组件库。这个组件库的风格我十分喜欢,扁平简单,美中不足的是它真的就只是一个 CSS + 一点点 JavaScript 的组件库。社区里有人开发了 vuikit 组件来将其接入 Vue 生态,但这个项目的最后一次 commit 已经是五年前了,并且还未适配 Vue3。
因此 NekoBox 的前端一直是以服务端渲染的形式呈现,稍微复杂一点的交互或者异步加载,则会使用 Alpine.js 实现。渐渐的,我发现它已经无法支撑起后续复杂的前端需求了。我写前端的经常会想:“这些响应式交互,Vue 来了可以全秒了。”
我开始尝试将 UIKit 的 CSS 引入 Vue3 项目中,发现它比我想象中的好用。由于 UIKit 大部分情况下只是在原生 HTML 标签上加上了 CSS 样式,因此我大可不必像 vuikit 那样将按钮、文本框之类的封装为 Vue 组件,直接在原生 HTML 标签用 class 指定样式即可。页面也比我想象中的少很多,因此只花了一个周末的时间就完成了 80% 的前端 Vue3 + 后端 RESTful API 的重构工作。
前后端分离后,由于后端部署在境外,请求 API 难免会慢一些。这里我用了 vue-loading-skeleton 来给页面加载时加上骨架屏,防止页面未加载完时布局塌陷。这个组件做得还行,可以自动识别插槽里的元素自适应调整加载的骨架元素大小。
新版的前端我不敢直接全量上线,想先小部分用户测试下。最简单的办法是将前端部署在例如 next.n3ko.cc 这样的子域下,但会导致后续主站全量上线时,子域的链接还得做重定向兼容。
复杂一点的话,在集群里搭个 Istio 服务网格来实现细粒度的流量转发,但看了下机器的配置,还是算了…… 最后简单粗暴的在 Cloudflare 上配置了回源规则:当请求 Cookies 里带 next-beta=1 时,则将请求转发到源站新版前端的端口上。后续在线上加个按钮,点一下就给 Set-Cookie 即可切换到新版前端,去掉 Cookie 就切回来。
我有问过自己,NekoBox 对我而言意味着什么?
我并不指望靠着它能够发家致富,我认为 NekoBox 是一块让我实践产品运营的“试验田”。参加工作以来,我基本没有做过对性能和服务可用性有很强要求的东西,更没有什么 To C 的经验。这既是好事,好在我不用随时 on call;也是坏事,坏在我没有那些项目经验和教训。
因此我想借 NekoBox 这个用户量还算不少的平台,亲身去实践开发和运营一个产品,去踩那些前人踩过的坑,去体验边开飞机边换引擎的惊险。因此 NekoBox 的项目经历其实也一直被写在我的简历里,作为一个还算成功的 Side Project 被我拿来跟面试官吹逼。😂
对于 NekoBox 的用户,很感谢他们能包容我的“放养式”运营,更是感谢那些还会不定期支付宝打赏的朋友们。我仅通过兔小巢这个渠道接收用户的反馈,并没有尝试组建 QQ 群之类的方式,是因为我认为每个人的圈子不同,年龄和性格也不同,求同存异会比较困难。我也很害怕跟别人起纷争或者冲突,所以还是继续维持现状吧。
关于之后的更新计划嘛,等新版前端稳定后,我想先完善一下基建方面,例如 Tracing 由 Uptrace 切到更专业的腾讯云 APM,完善项目的开发和部署文档,之后就可以开始做用户反馈中提到的暗色主题、多语言、表情评价,甚至是聊天等功能。先把 flag 立了,后面慢慢填坑哈哈哈。