diff options
Diffstat (limited to '_posts')
| -rw-r--r-- | _posts/2026-04-14-ai-agent.md | 32 | ||||
| -rw-r--r-- | _posts/2026-05-01-virtual-net.md | 76 | ||||
| -rw-r--r-- | _posts/2026-06-01-dedupe.md | 210 | ||||
| -rw-r--r-- | _posts/2026-07-01-vibe-coding.md | 60 |
4 files changed, 378 insertions, 0 deletions
diff --git a/_posts/2026-04-14-ai-agent.md b/_posts/2026-04-14-ai-agent.md new file mode 100644 index 0000000..888a07a --- /dev/null +++ b/_posts/2026-04-14-ai-agent.md @@ -0,0 +1,32 @@ +--- +layout: post +title: 关于AI个人助理的探索 +tags: [AI, Agent, 个人助理] +--- + + 给AI添加手脚能有多少种方法?<!--more--> + +# 起因 + 最近像[OpenClaw](https://github.com/openclaw/openclaw)这样被叫做“AI个人助理”的Agent越来越火了,当然这种东西在我看来依然是新瓶装旧酒,整来整去还就是和AutoGPT一样。这种东西在当时GPT-3.5的时候就有了,现在只不过是增加了聊天软件交互的渠道便突然大火,和当年的Manus一样……实在是无法理解。 + 一年前我用过Devin.ai这个云端的Agent编写过[用JS解析订阅源](/2025/04/08/feed.html)的脚本,体验还算不错,既然现在已经过了一年,那就让我看看现在又有了什么样的发展吧。 + +# 使用AI个人助理 +## 体验原生OpenClaw + 虽然感觉OpenClaw对我的意义不大,但我还是安装体验了一下。不过在国内安装它还是相对有点困难,毕竟国内无论是访问GitHub,还是NPM都有点麻烦,而且还需要有LLM提供商的信息……安装好之后使用起来感觉问题也非常多,经常出现执行一半就停止执行,在它执行的过程中看到它的操作不正确的时候也不能发言打断,而且很多时候最终任务执行的效果也不太好,这也可能是我用的国产开源模型推理能力有限,没舍得用Claude之类先进模型的锅😂? + 另外我也尝试让它加入MoltBook、MomoClaw、InStreet、百度贴吧抓虾吧之类的AI社区让它帮我宣传我的博客,但效果也很差,它每次发的时候会忘掉之前发的内容,结果就是同一篇内容发了好几遍……不过在这期间,有个叫PushMeBot的家伙在[Moltbook的帖子](https://www.moltbook.com/post/7f1b0e1f-5175-4fd1-ad78-856be8b66250)中让我的OpenClaw执行一个网络监视程序,最终安装好之后给我[发了9USDC](https://basescan.org/tx/0x44dbfe53f276201447f3877bf050a5d56adebf5fe05235264ee665da717e9373)😝,还挺有意思。 + 总之按照我的体验,实在是想不出它能火的理由,体验不算很好,而且还要安装Node环境,完全不像是能让大众轻松使用的东西。 + 不过这个项目似乎本身就是Vibe Coding的产物,体验不好也能理解,就看火了之后能有多少人完善它吧。 +## 国内大厂的二开Claw + 国内好多大厂倒是看中了这个东西的爆火,像腾讯就出了几款这样的软件,比如QClaw。它可以不需要配置额外的环境,能像传统的软件一样直接安装使用,而且有自带的模型,有一定的免费额度可以用。配置技能也比较简单,直接点击就可以完成。而且可以直接扫码关联微信,直接通过微信和它进行交流,可以说是相当的傻瓜化了。不过QClaw给的免费额度虽然用来聊天之类的没问题,但对于开发软件还是有点少,所以他们还出了个叫做WorkBuddy的软件,它送的初始额度比QClaw要多不少,所以更适合用来开发。只不过为啥腾讯要出两个功能一样的软件?看起来应该是不同团队出的,可能是面向的用户群体不一样,所以搞了两套吧? +## VSCode中的Agent + 但要说开发的话,用作为“AI个人助理”的某些Claw其实并不合适,毕竟正常开发还是以人开发为主,全AI开发总会有些问题,所以开发的时候还是用编辑器集成的AI比较好。在三年前我就在用[GitHub Copilot](/2023/04/05/ai.html)了,到现在我依然在用。现在的Copilot已经支持了Agent功能,开发相比之前也是强了很多,只不过现在的我没有学生身份,Copilot Free偶尔也会出现不够用的情况。不过对于Agent这类功能实现起来还是太简单了,所以有人开发这种功能的插件也很正常,比如[Cline](https://github.com/cline/cline),Copilot只能用微软提供的几个模型,而Cline可以自定义模型,用起来也很方便。 +## 微型开发板上运行的Claw + 前段时间,我闲来无事看了一下两年前买的[Luckfox Pico Plus](/2024/02/24/luckfox.html)开发板的文档,偶然发现了一个很有意思的项目,叫做[LuckClaw](https://github.com/LuckfoxTECH/luckclaw),这是一个基于[nanobot](https://github.com/HKUDS/nanobot)用Golang重构的轻量个人AI助手,可以在仅仅64MiB内存的超有限环境下运行一个和OpenClaw功能几乎相当的AI个人助理,真的是非常厉害。 + 我在我的开发板上试了一下,体验很不错,安装不需要额外环境,直接下载就能使用,Go语言的程序确实方便。配置也很简单,直接执行`luckclaw config`就可以交互式进行模型等设置的配置,而且作为国产的应用,它也能很方便的对接国内聊天软件。只是限于开发板本身的能力,浏览器功能自然无法使用,所以搜索如果不借助那些需要API Key的AI专用接口,就基本上不能用……但总的来说效果已经非常不错了,至少有那些Claw的80%能力。 + (2025.04.15补充:后来我发现这种超精简的Claw项目看起来还挺多,比如[ZeroClaw](https://github.com/zeroclaw-labs/zeroclaw)和[PicoClaw](https://github.com/sipeed/picoclaw),甚至还有给单片机用的[MimiClaw](https://github.com/memovai/mimiclaw)。而且有意思的是,PicoClaw是Luckfox的竞争对手开发的,但是LuckClaw中却包含PicoClaw字样的注释,结果功能也没PicoClaw强,关注度也更低,属于是没抄明白了🤣) + 想到前段时间还有人为了OpenClaw专门买Mac Mini,就感觉很有意思😆,这个东西看起来应该是在路由器上都能跑。所以想要AI个人助理,硬件完全不是问题,只要整一个能24小时挂机的东西,就可以满足绝大多数人的需求了。 +## 在手机上运行的Claw + 其实很多人也有比开发板和路由器性能更强的闲置设备,那就是手机,所以有人开发了一款叫做[ApkClaw](https://github.com/apkclaw-team/ApkClaw)的软件,一样可以接入国内聊天软件。它既然能在手机上运行,当然和在其他平台运行的Claw相比有一个独特的优势,那就是操作手机应用。现在手机的应用相比电脑应用对于很多普通人来说功能更强大,所以它能做的事情可能比其他的Claw还多。我试了一下,配置也很方便,只不过能配置的项目太少了,看起来似乎没有安装Skill之类的功能,也许是因为它是相对早期的软件,所以功能还比较少吧。 + +# 感想 + 总的来说,现在的Agent依然没有非常明显的进步,问题依旧很多,只是化身“AI个人助理”之后,增加了不少应用场景。这倒也是好事,在广泛传播的过程中,也能让很多对技术了解不多,但是很有想法的人参与其中,也许能对AI的应用化增添不少力量吧。
\ No newline at end of file diff --git a/_posts/2026-05-01-virtual-net.md b/_posts/2026-05-01-virtual-net.md new file mode 100644 index 0000000..3e245b2 --- /dev/null +++ b/_posts/2026-05-01-virtual-net.md @@ -0,0 +1,76 @@ +--- +layout: post +title: 虚拟局域网的组网探索记录 +tags: [虚拟网络, 异地组网, WireGuard] +--- + + 异地组网,有多少种选择?<!--more--> + +# 起因 + 最近我有一些放置在许多不同地方的机器,有一些东西需要让它们之间能够相互访问。虽然我很久以前写过一篇使用[SSH进行互联](/2021/05/07/ssh.html)的文章,但这样做每个服务都需要单独配置,也不方便管理。所以为了能让机器之间能够轻松通信,我打算组建一个虚拟局域网,让它们像在同一交换机下一样。不过这种组网的工具非常多,我应该选哪个比较好呢? + +# 不同组网工具的体验 +## n2n + 以前我用过一款用C写的叫做[n2n](https://github.com/ntop/n2n)的工具,它可以很轻松地组建一个P2P的二层虚拟网络,而且生态也不错,手机、电脑、路由器、服务器上都有可以用的客户端。使用起来非常简单,它的中继和穿透服务程序叫做Supernode,无需太多的配置,只要在有公网的服务器安装并使用`-p`指定一个端口就可以启动。而客户端配置也非常简单,用`-l`配置好Supernode的地址,然后让想要在同一个网络的机器使用相同的任意`-k`和`-c`就可以成功组网,可以说算是非常好用了。 + 唯一的问题就是它这个项目看起来似乎已经停止更新了……虽然大多数情况下用起来没问题,但是有时候还是会出现组网不太可靠的情况。如果两个机器都不经过NAT,可以通过公网IP连接,它的可靠性还可以。但如果是两个NAT后的机器之间,有时候会存在莫名掉线的情况,也许是因为穿透导致的不可靠?总之遇到这种情况之后重启又能正常工作,说明是软件本身的问题,但它停更了……所以对我来说它的可靠性不太够。(其实它还有个叫做[n3n](https://github.com/n42n/n3n)的继任者,不过知名度不高,所以生态也不太行) +## WireGuard + 其实在这之后我本来是打算用L2TP/IPSec进行组网的,但看了一下貌似配置有点复杂,而且不够现代,现在想要组网貌似大多都推荐[WireGuard](https://git.zx2c4.com/wireguard-linux/)作为更现代的选择。只不过它和n2n相比来说是三层的虚拟网络,如果需要发送非TCP/IP协议的特别包,可能就用不了它吧,当然对我来说没有这种需求。它用起来也非常简单,不过正常情况下它设计是为了点对点传输,而且没有自带的NAT穿透功能,所以如果想要实现组网,就得搭一个星形网络,让互联网上的服务器作为虚拟的交换机,这个做起来倒也不复杂。首先,每个节点需要生成一个公私钥对作为身份证明,在安装好WireGuard之后执行`wg genkey`就能生成私钥。作为交换机的节点需要在`/etc/wireguard/wg0.conf`中写一个这样的配置: +```conf +[Interface] +PrivateKey = xxx +Address = 192.168.1.1/24 +ListenPort = 51820 + +PostUp = iptables -A FORWARD -i wg0 -o wg0 -j ACCEPT +PostDown = iptables -D FORWARD -i wg0 -o wg0 -j ACCEPT + +# 机器1 +[Peer] +PublicKey = xxx +AllowedIPs = 192.168.1.2/32 + +# 机器2 +[Peer] +PublicKey = xxx +AllowedIPs = 192.168.1.3/32 +``` + 其中PrivateKey填写交换机自己的私钥,而作为使用者的Peer中的PublicKey可以用对应节点的私钥执行`echo xxx | wg pubkey`这个命令查看,然后每个Peer需要像这样配置: +```conf +[Interface] +PrivateKey = xxx +Address = 192.168.1.2/24 + +[Peer] +PublicKey = xxx # 交换机节点的公钥 +Endpoint = xxx.xxx.xxx.xxx:51820 # 交换机节点的地址 +AllowedIPs = 192.168.1.0/24 +PersistentKeepalive = 25 +``` + 最后全都配置好之后所有节点使用`systemctl enable --now wg-quick@wg0`启动就可以了,启动之后每个节点可以执行`wg`查看当前的连接状态。 + 当然这是在Linux上,至于其他系统大多都有GUI配置,填起来更简单。它的生态也非常好,基本上常见的操作系统都支持,具体可以在[官网](https://www.wireguard.com/install/)查看支持的系统和安装方法。不过由于它在Linux中优先使用内核模块,导致我在一些比较小众的环境中也是遇到了各种特别的问题。 +### 在红米AX3000中遇到的问题 + 我在这个网络中有几个安装了OpenWrt的路由器,在这其中使用联发科芯片的路由器基本上都没什么问题,官网能轻松下载到固件,也能很轻松地在软件包中找到WireGuard并安装,但我还有一台使用高通芯片的红米AX3000,似乎因为高通对资料管控得很严格,导致它没有官网的固件,最终我在GitHub上找了一个其他人自己编译的[固件](https://github.com/hzyitc/openwrt-redmi-ax3000/)。虽然它整起来有点麻烦,不过倒也能用,但是在我尝试安装WireGuard的时候遇到了麻烦…… + 它的软件包里有WireGuard,也能找到对应的内核模块安装包,但安装完之后没法启动……随后我看了一下它下载的[安装包](https://github.com/hzyitc/openwrt-redmi-ax3000/blob/gh-pages/ipq50xx-qsdk-kernel-5.4-openwrt-21.02-qsdk-11.5.05.841.1029/ci-20240727-173350-ab1f9ffa/kmod-wireguard_5.4-qsdk-11.5.0.5-1_arm_cortex-a7_neon-vfpv4.ipk),结果发现是空的😰,它这个固件的内核模块可能是在编译的时候遇到了一些问题。至于让我自己编译这个内核模块,难度似乎有点高了……那怎么办呢?要知道Linux的内核模块都是和内核挂钩的,没办法随便找一个别的模块使用。还好WireGuard倒也不止有内核模块,也有一些在用户空间中的实现,比如[wireguard-go](https://git.zx2c4.com/wireguard-go)和[wireguard-rs](https://git.zx2c4.com/wireguard-rs)。只是官方似乎非常不推荐在Linux上使用它们,所以没有提供预编译的版本。不过遇到这种问题的人也许是比较多,所以有人做了在[OpenWrt上使用的wireguard-go](https://github.com/seud0nym/openwrt-wireguard-go),安装好之后效果和使用内核模块的感觉基本上没什么区别,最终也能连通,唯一的区别就是在执行`wg`的时候,会显示“Interface: wg0 (userspace)”罢了。从效率上来说虽然肯定没有内核模块那么高,但它其实也用了“Tun”模块,理论上和使用“Tap”模块的n2n应该差不多吧。 +### 在openEuler中遇到的问题 + 在我使用的节点中,还有一台安装了openEuler 22.03 LTS操作系统的服务器,虽然openEuler和CentOS可以说基本上没什么区别,但毕竟它的内核是openEuler自己编译的,所以没办法直接使用CentOS的内核模块。并且openEuler的源中也完全没有提供和WireGuard相关的包,所以想要在openEuler上安装WireGuard还是有些挑战(当然如果觉得麻烦,它们倒是有一个兼容WireGuard的客户端[TunSafe](https://eur.openeuler.openatom.cn/coprs/nucleo/tunsafe/)可以凑活用一下)。 + 后来我试了一下在这上面安装wireguard-tools倒是可以直接用[CentOS 8EPEL源中的包](https://mirrors.tuna.tsinghua.edu.cn/epel/8/Everything/x86_64/Packages/w/wireguard-tools-1.0.20210914-1.el8.x86_64.rpm),但openEuler的内核在编译的时候故意没有包含WireGuard内核模块……这该怎么办呢?用wireguard-go吗?虽然这样可以很简单地解决,但感觉这样就是认输了😂。后来我搜了一下,找到了一篇[在openEuler安装WireGuard内核模块](https://dingle.site/archives/wei-openeulertian-jia-wireguardmo-kuai)的文章,方法大致如下: + 1. 首先安装编译环境和源代码。 +```bash +yum install elfutils-libelf-devel kernel-devel pkgconfig "@Development Tools" +yum install kernel-headers.x86_64 pkg-config ncurses-devel openssl-devel dwarves +yum install kernel-source.x86_64 +``` + 2. 然后进行编译配置,内核源码一般会安装到`/usr/src/`下,找到之后在里面执行`make menuconfig`,然后勾选“Device Drivers -> Network device support -> Wireguard secure network tunnel”并保存。 + 3. 最后执行`make`开始编译,为了加速可以用`-j`参数加上CPU的核心数进行并行编译,当时编译就花掉了一整天😂,理论上应该可以只编译WireGuard和它依赖的几个模块,不过我不太清楚怎么做,还是费点时间按照文中说的做吧。 + 4. 执行`make modules_install`将编译好的结果安装到`/lib/modules/5.10.0`。 + 不过系统似乎不会去这个路径下找内核模块,所以还得把这里面的kernel文件夹复制到`/lib/modules/$(uname -r)`下,然后执行`depmod -a`更新模块依赖。 + 5. 最后执行`modprobe wireguard`验证模块是否能正常加载,如果没有报错并且可以在`lsmod | grep wireguard`中看到,就说明安装成功了,剩余的步骤和其他Linux系统一样。 + +### WireGuard的控制平面 + 虽然WireGuard本身配置很简单,但每加一个节点还得在交换机节点上修改一下配置文件,稍微有些麻烦,所以有人开发了一些控制平面,让它可以被更规范地管理,比如[Netmaker](https://github.com/gravitl/netmaker)和[Headscale](https://github.com/juanfont/headscale)。而Headscale主要是为Tailscale客户端开发的开源服务器端,因此功能会局限于Tailscale提供的功能。所以如果没有用过Tailscale,可以优先考虑Netmaker。 + 这两个控制平面支持的功能相当丰富,而且它们还支持让WireGuard进行NAT穿透,自动组建Mesh网络,不像我一堆在NAT后的设备还要直接使用WireGuard就只能搭成星形网络。只不过对我来说,我也用不到那么多企业级功能,这个服务端配置起来也有点麻烦,而且我也没有很多节点需要动态增减,我的云端服务器带宽也足够使用,所以就没有用这些东西了😆。 +## 其他的组网工具 + 除了WireGuard之外,还有很多其他的组网工具,比如[VNT](https://github.com/vnt-dev/vnt)和[EasyTier](https://github.com/EasyTier/Easytier),这俩用起来也非常简单,只需要加几个参数就能组网,和n2n一样。不过功能相比于n2n来说要强大不少,也支持NAT穿透,而且还都兼容WireGuard协议,另外不像WireGuard强制使用UDP传输,这两个还能用TCP和WebSocket,在特殊网络环境下应该比直接用WireGuard更好。另外它们都是Rust编写的,也许会更安全😋?可惜我已经配好WireGuard之后懒得再改了,如果以后有机会,可以尝试一下。 + +# 总结 + 现在如果想要异地搭建虚拟局域网,还是有相当多的选择,而且无论是性能还是配置难度,都比以前好了不少。看来这种需求还是相当多啊,也正是因为有这些需求,所以才会出现这么多的方案可以用吧……总之我最后还是选择了纯WireGuard方案,主要还是简单够用,可靠性也不错,而且折腾了这么多再换也不太合适吧🤣。
\ No newline at end of file diff --git a/_posts/2026-06-01-dedupe.md b/_posts/2026-06-01-dedupe.md new file mode 100644 index 0000000..0d38a40 --- /dev/null +++ b/_posts/2026-06-01-dedupe.md @@ -0,0 +1,210 @@ +--- +layout: post +title: 如何节约游戏占用的硬盘空间? +tags: [dedupe, RPG制作大师, 游戏] +--- + + 浪费硬盘空间是可耻的!<!--more--> + +# 起因 + 在几年前,我写过一篇在[MacBook上玩游戏](/2023/10/21/game.html)的文章,在那之后,我已经在我的Mac上下载了几十部游戏。只不过有个问题……我的Mac只有256GiB的硬盘存储空间,下载一堆游戏会让我的硬盘空间不够用,但是又不太想删,所以我该怎么尽可能让游戏占用更少的空间呢? + 首先为了能在Mac上尽可能流畅地玩,我玩的游戏大多都是用跨平台能力很强的引擎编写的游戏,比如[Ren'Py](https://github.com/renpy/renpy)、RPG制作大师、Godot之类的,而像RPG制作大师这种引擎制作的游戏还有一个特点,开发者一般都会使用引擎自带的素材进行开发,有时候还会用不少第三方的罐头素材之类的(实际上甚至还有好多AVG为了蹭这些引擎的公用素材刻意用它们),所以这几十个游戏里应该有非常多的重复素材,如果能想办法把它们去个重,应该能节省相当多的空间吧…… + +# 去重的方法 + 如果想要对文件进行去重,我搜了一下,有个叫做[jdupes](https://codeberg.org/jbruchon/jdupes)的工具就很不错,它支持多种去重方式,比如使用硬链接,或者用一些文件系统的写时复制特性。不过如果用写时复制特性,jdupes在第二次执行的时候会认为去重后的文件还是单独的文件,就会重复去重了,而且最终也不好统计,反正对我玩的游戏来说,要去重的都是游戏素材,不存在后续修改的可能性,所以我打算全部用硬链接。 + 所以最终要执行的命令也非常简单,直接一句`jdupes -r -L Game`就可以了,这样以后每次下载了新的游戏之后重复执行这个操作,就可以将游戏中和其他游戏里有的素材去重了。 + 不过实际上很多游戏并不能直接用这种方式去重,因为它们的资源文件有些是打包成单个文件,有些进行了简单的加密,导致即使是相同的素材,文件也并不相同,所以我必须让所有的资源以单独原始的形态出现。对于不同的引擎也有不同的处理方式,所以接下来我需要对它们进行一些研究。 + +# 不同引擎的处理方式 +## RPG制作大师MV/MZ + 对于RPG制作大师MV/MZ开发的游戏来说,解密很简单,比较知名的是一个叫做[RPG-Maker-MV-Decrypter](https://gitlab.com/Petschko/RPG-Maker-MV-Decrypter)的工具,它可以在浏览器中进行解密,但一个游戏的资源文件非常多……要是全上传给浏览器实在是太麻烦了……后来我又搜了一下,有一个用C#写的叫[RPG Maker Decrypter](https://github.com/uuksu/RPGMakerDecrypter)工具也很不错,它作为命令行工具比在浏览器中执行简单多了,而且还能只把资源文件单独提出来,这样就可以剔除掉游戏自带的浏览器文件。不过他这个仓库的代码有个问题,它在选择文件的时候似乎会区分大小写,文件夹名中含有大写字母的似乎会被剔除……这样不太符合我的要求啊,当然我不会C#,于是我用AI改了一下,还给他提了个[PR](https://github.com/uuksu/RPGMakerDecrypter/pull/28),不过这家伙看起来似乎不太喜欢AI写的代码,看起来不打算合我的PR😅。不过无所谓了,反正我也是自用,他爱合不合吧。 + 这个工具的用法也非常简单,一句`RPGMakerDecrypter-cli [input] -p -o [output]`就处理好了,处理完之后只需要把`data/System.json`中的`hasEncryptedImages`和`hasEncryptedAudio`设置为false就可以正常识别,以后在Mac中只要在游戏路径下执行`python3 -m http.server`就可以在浏览器中游玩了。 + 在这个过程中,我还发现有一些游戏喜欢把原画文件直接放到游戏里面,一张图片好几M,但RPG制作大师的引擎在渲染的时候根本不会渲染出那么高的分辨率,结果毫无意义地浪费一大堆存储空间,而且因为图片是加密的,对大多数人来说也没有收藏价值。所以在解密完之后我就想干脆把这些图片全部有损压缩一遍,估计能节省不少存储空间,于是让AI写了个简单的压缩脚本处理了一下: +```python +#!/usr/bin/env python3 +""" +图片压缩脚本(多进程版本) +将 pictures.orig 文件夹中的图片使用 WebP 格式进行高效压缩, +保持分辨率不变,肉眼看不出差异,压缩后的图片保存到 pictures 文件夹。 + +使用方法: + python3 compress_images.py + +压缩策略: + - 保持原始分辨率不变 + - 使用 WebP 格式(有损压缩,高质量) + - 质量设置为 85,在保持视觉质量的同时显著减小文件大小 + - 文件名和后缀保持不变 + - 多进程并行处理 + - 处理失败时自动复制原文件 +""" + +import os +import shutil +from PIL import Image +from pathlib import Path +from multiprocessing import Pool, cpu_count +from functools import partial + +# 配置路径 +SOURCE_DIR = "pictures.orig" +OUTPUT_DIR = "pictures" + +# WebP 质量设置 (0-100,数值越高质量越好,文件也越大) +# 85 是一个很好的平衡点,肉眼几乎看不出差异 +WEBP_QUALITY = 85 + +# 对于带有透明通道的图片,可以设置不同的质量 +WEBP_QUALITY_WITH_ALPHA = 80 + +# 并行进程数,默认为 CPU 核心数 +NUM_WORKERS = cpu_count() + + +def compress_single_image(img_file: tuple[str, str, str]) -> tuple[str, bool, int, int]: + """ + 压缩单个图片文件(用于多进程) + + Args: + img_file: (源文件路径, 输出文件路径, 输出目录) 元组 + + Returns: + (文件名, 是否成功, 原始大小, 压缩后大小) 元组 + """ + source_path, output_path_str, output_dir = img_file + source_path = Path(source_path) + output_path = Path(output_path_str) + + original_size = source_path.stat().st_size + + try: + img = Image.open(source_path) + + # 检查是否有透明通道 + has_alpha = img.mode in ('RGBA', 'LA', 'PA') or (img.mode == 'P' and 'transparency' in img.info) + + # 确定使用的质量 + quality = WEBP_QUALITY_WITH_ALPHA if has_alpha else WEBP_QUALITY + + # 保存为 WebP 格式,但使用原始的文件扩展名 + img.save( + str(output_path), + format='WEBP', + quality=quality, + method=6 # 压缩方法 0-6,6 是最慢但压缩率最高的 + ) + + compressed_size = output_path.stat().st_size + return (source_path.name, True, original_size, compressed_size) + + except Exception as e: + # 处理失败时,复制原文件到输出目录 + try: + shutil.copy2(source_path, output_path) + compressed_size = output_path.stat().st_size + return (source_path.name, False, original_size, compressed_size) + except Exception as copy_error: + return (source_path.name, False, original_size, 0) + + +def main(): + source_dir = Path(SOURCE_DIR) + output_dir = Path(OUTPUT_DIR) + + # 检查源目录是否存在 + if not source_dir.exists(): + print(f"错误: 源目录 '{SOURCE_DIR}' 不存在") + return + + # 创建输出目录 + output_dir.mkdir(exist_ok=True) + + # 获取所有图片文件(支持多种格式) + image_extensions = ('*.png', '*.jpg', '*.jpeg', '*.bmp', '*.gif', '*.tiff', '*.webp') + image_files = [] + for ext in image_extensions: + image_files.extend(source_dir.glob(ext)) + image_files = sorted(set(image_files)) # 去重并排序 + + if not image_files: + print(f"在 '{SOURCE_DIR}' 中没有找到图片文件") + return + + # 构建任务列表 + tasks = [] + for img_file in image_files: + output_path = output_dir / img_file.name # 保持原文件名和后缀 + tasks.append((str(img_file), str(output_path), str(output_dir))) + + print(f"找到 {len(tasks)} 个图片文件") + print(f"源目录: {SOURCE_DIR}") + print(f"输出目录: {OUTPUT_DIR}") + print(f"WebP 质量设置: {WEBP_QUALITY}") + print(f"并行进程数: {NUM_WORKERS}") + print("-" * 70) + + # 使用多进程池处理图片 + success_count = 0 + fail_count = 0 + total_original = 0 + total_compressed = 0 + + with Pool(processes=NUM_WORKERS) as pool: + for i, (filename, success, original_size, compressed_size) in enumerate(pool.imap(compress_single_image, tasks), 1): + total_original += original_size + total_compressed += compressed_size + + if success: + success_count += 1 + marker = "✓" + reduction = (1 - compressed_size / original_size) * 100 if original_size > 0 else 0 + status_msg = f"{reduction:+.1f}%" + else: + fail_count += 1 + marker = "✗" + status_msg = "复制原文件" + + status = f"[{i}/{len(tasks)}] {filename}" + print(f"{marker} {status:50} {original_size/1024:>8.1f}KB -> {compressed_size/1024:>8.1f}KB ({status_msg})") + + # 输出总结 + print("-" * 70) + total_reduction = (1 - total_compressed / total_original) * 100 if total_original > 0 else 0 + print(f"压缩完成!") + print(f" 成功处理: {success_count}/{len(tasks)} 个文件") + if fail_count > 0: + print(f" 失败(已复制原文件): {fail_count}/{len(tasks)} 个文件") + print(f" 原始总大小: {total_original / 1024 / 1024:.2f} MB ({total_original / 1024:.1f} KB)") + print(f" 压缩后大小: {total_compressed / 1024 / 1024:.2f} MB ({total_compressed / 1024:.1f} KB)") + print(f" 总压缩率: {total_reduction:.1f}%") + print(f" 节省空间: {(total_original - total_compressed) / 1024 / 1024:.2f} MB") + + +if __name__ == "__main__": + main() +``` + 最终压缩完之后我把原图上传到了[EH画廊](https://e-hentai.org/g/3901673/426a7a17ba/)中,本地只留压缩后的图片,大小从原来的2GiB多下降到了300多MiB,可以说效果相当显著了。 + 除此之外还有一些游戏使用了Ogg FLAC背景音乐,这种音乐不仅占用磁盘空间很大,而且我在Safari上玩的时候浏览器根本没法解析(Chrome应该可以)。虽然我听音乐是会考虑[HiFi](/2025/03/22/hifi.html),但玩游戏就没必要了吧……所以像这种音乐,就得用一句: +```bash +ffmpeg -i input.flac.ogg -c:a vorbis -strict -2 -q:a 10 output.ogg +``` + 转换为正常有损的Ogg音乐了。 +## RPG制作大师XP/VX/VA + 对于RPG制作大师XP/VX/VA引擎开发的游戏来说,它们都是基于用Ruby语言开发的RGSS编写的,作为脚本来说,倒是有跨平台的条件,但因为官方并没有做跨平台,所以不能直接在Mac上运行。不过有一款叫做[mkxp-z](https://github.com/mkxp-z/mkxp-z)的工具允许跨平台运行使用RPG制作大师XP/VX/VA制作的游戏,因此这类游戏我也收集了一些。 + 这些游戏的资源通常会进行简单的混淆加密,一般会打包成单个RGSSAD文件,这个解包也很简单,用刚刚的RPG Maker Decrypter就可以。不过这种游戏还有个特点,有些游戏需要使用[RTP](https://www.rpgmakerweb.com/run-time-package)才能运行,它这个RTP其实就是RPG制作大师自带的素材包,当时设计出来估计也是想着用来节约硬盘空间吧,就是不知道为什么到后来的MV/MZ却取消了这种方式……虽然mkxp-z是支持通过配置文件引入RTP的,但既然我已经选择了硬链接的方式,就没必要单独搞RTP了,我选择把RTP直接和游戏合并,然后让jdupes直接去重就好了,这样相比于RTP的方式还有一些好处就是XP/VX/VA可能有一些和MV/MZ使用相同的素材,这部分也可以不用占用重复的空间了。 +## Ren'Py + 对于Ren'Py来说,因为这个引擎并没有自带的公共资源,所以重复素材的问题并不是很大。不过在我之前对[Ren'Py的探索](/2024/01/20/renpy.html)中提到过,我玩的一些游戏是系列游戏,这种系列游戏有非常多的素材复用,但显然开发者并不会为了节约玩家硬盘空间而共享这部分资源,而且Ren'Py游戏也都是打包成单个文件的,所以接下来我们依然得要解包才能进行去重处理。 + Ren'Py使用的rpa文件解包起来依然很简单,有一款现成的工具[unrpa](https://github.com/Lattyware/unrpa)可以直接解包,用pip就能安装。不知道为什么这些引擎总是喜欢把资源文件都打成一个包,明明很容易就能解包……难道是为了性能吗? + 不过也正是因为Ren'Py的公共资源不多,如果玩的不是系列游戏,就没有解包的必要了,解包之后一堆小文件有可能会比整个rpa文件更大,毕竟文件系统存在“簇”,有可能会消耗没对齐的空间。 + +# 验证结果 + 最终进行完上述操作,可以通过执行`du -sh`和`du -shl`进行对比来验证节约的硬盘空间,我在这次游戏的瘦身中节约了: +``` +~ % du -sh Game + 33G Game +~ % du -shl Game + 47G Game +``` + 看起来还是相当可观啊……尤其是在当下硬盘价格大涨的情况下,如果很多人能通过这些方式来节约硬盘空间,就能减少对硬盘容量的需求吧……不过说到底其实也都是网上能下到的资源,也许玩完之后就删掉才是最好的节约硬盘的方式吧😂。 + +<input name="live2dBGM" value="https://music.163.com/song/media/outer/url?id=1968116350.mp3" type="hidden" />
\ No newline at end of file diff --git a/_posts/2026-07-01-vibe-coding.md b/_posts/2026-07-01-vibe-coding.md new file mode 100644 index 0000000..5d08def --- /dev/null +++ b/_posts/2026-07-01-vibe-coding.md @@ -0,0 +1,60 @@ +--- +layout: post +title: Vibe Coding新体验 +tags: [Vibe Coding, AI, PJAX, 麻将] +--- + + All in AI!<!--more--> + +# 起因 + 前段时间,我写了一篇关于[AI Agent](/2026/04/14/ai-agent.html)的文章,虽然当时我说这并没有什么创新之处,但在那之后用着用着感觉还是挺好用的,至少相比以前[在云端Vibe Coding](/2025/04/08/feed.html)来说又快又免费,所以我打算分享一下最近我用AI Agent的经历。 + 不过大多数情况下我写代码都可以直接让各路AI在对话中给我生成,那到底有什么样的场景才需要用AI Agent呢?一般应该就是需要边改边测的东西吧。 + +# 关于PJAX的改造 + 虽然说AI生成的东西可靠性并不是很高,但至少在网上已经有很多例子的代码应该不至于乱生成吧……其实我一直都想给我的博客增加全站无刷新的功能,但是每次看到PJAX都要自己重新调用当前页面的各种函数就懒得动手了😂。不过其实这也不是什么复杂的事情,主要是一个一个分析函数比较麻烦,所以既然有AI了,不如直接让AI帮我做吧。于是我让它克隆了我的仓库,分析并写出PJAX的代码。 + 用AI Agent最大的好处就是能让它了解整个项目的所有代码,如果是直接在对话里让AI修改的话就很难做到这一点。总之我告诉它之后等了一会它真的写出来了,我试了一下确实能用,虽然刚开始有不少问题,主要是因为我用了[Live2D](/Live2dHistoire/),不少问题也是这个功能引起的。不过只要我告诉它问题在哪里,它总是能给我改好,先不论它实现的代码怎么样,但至少从效果来说还是相当不错的。 + 最终生成的代码我看了一下,代码质量非常糟糕,把我在别处的代码到处复制粘贴到PJAX用的代码里了,但是体验效果不错……所以没办法,虽然代码很糟糕,但先用着吧。不过我觉得优化应该也能交给AI Agent,只是它有时候改来改去还会把正常代码改坏,所以优化看起来还是只能自己做…… + 最终的代码就是:[pjax.js](/assets/js/pjax.js),虽然我对这个代码质量不是很满意,但是毕竟能用,效果也不错,就先这样用吧~ + +# 生成一个小游戏 + 前段时间,我在Bilibili上看视频的时候发现了一个有趣的微信小游戏,叫做“雀谜”,它把立直麻将和Wordle两个玩法结合到一起,看起来挺有意思。不过它作为微信上的小游戏有些问题,一是看提示要看广告,二是换题也要看广告,而且看广告就算能换题也有次数限制,一天也就几次机会,让我有点不爽。 + 不过从功能上来说看起来并不复杂,感觉用AI直接Vibe Coding完全不是问题,不过AI Agent从零开始写比较考验模型的水平,所以我打算先让各路网页上的AI生成初版的Demo,之后再考虑怎么改。于是我对它们说: +> 帮我制作一款立直麻将Wordle网页小游戏,规则为: +> 1. 系统随机生成一个有役能和的14张牌,必须按照万、筒、索、字的顺序排列,同花色内数字从小到大,但是第14张用来和的牌可以不遵守排列规则 +> 2. 让玩家猜6次来猜到这个牌型,每次猜的牌也必须是满足有役和牌的条件 +> 3. 位置和内容正确的牌用绿色标注、只有内容正确但是位置不正确的用黄色标注,不在生成范围内的牌用灰色标注 +> 4. 界面中所有的牌使用Unicode中的麻将字符,并且提供一组输入按钮用于输入这些麻将字符 +> 5. 输入按钮在游玩过程中也要标注颜色 +> 6. 随机生成的牌需要标注场风、自风和是否自摸,如果是荣和需要额外的役,需要考虑平和之类的役种 +> 7. 加一个提示键,可以查看答案牌型的役种 + + 直接生成确实很考验水平,有很多AI直接生成出来的完全用不了,像DeepSeek生成出来的按钮按上去都没有反应😅,最终只有Gemini生成出来的感觉还不错,无论是界面还是输入的感觉都很好,于是接下来的修改我就打算以Gemini生成出来的代码为主。 + 刚生成出来的代码虽然能玩,但是每个AI直接生成都不想做完整的役种检测,说什么一个对话不够😥,明明各个都是几百k上下文的AI,有什么不够的……总之想一口气就完美做出来显然是不现实的,不过其实最开始我也不打算把所有役都做了,毕竟条件是“有役和牌”,而且没有副露,所以好多可以叠加的役其实都不需要做,尤其是役满那些,大多都可以用其他役的条件满足。 + 在这期间,“平和”算是实现起来最麻烦的,主要是这个役需要根据和牌判定,必须是顺子的两面听牌才可以……虽然AI理解这些知识,但是写的时候经常出问题,像边张之类的经常误判……但是不实现又不行,毕竟这是可以nomi的牌型。 + 最终写的差不多了,感觉还不错,于是在GitHub Pages上[部署了一份](https://mabbs.github.io/riichi-mahjong-wordle/),起名叫[立直麻将 Wordle](https://github.com/Mabbs/riichi-mahjong-wordle)。 + 最开始我以为这个小游戏的玩法是他们原创的,于是把生成的代码又丢给AI,让它评价一下创新性,结果AI说已经有开源了😂,有个叫做[Mahjong Handle](https://github.com/xnuk/mahjong-handle)的才是最早做这个玩法的……还好这代码不是我写的,要是我写了半天结果发现有现成的那就很无语了😆。 + 当然我让AI写的版本也有一些特色,Mahjong Handle那个和原版Wordle一样,每天只有一道题,而且这个题是从题库里抽的,我让AI Agent看了一下它的代码,似乎是用[phoenix-logs](https://github.com/MahjongRepository/phoenix-logs)这个脚本从天凤上下载的牌谱数据解析得到的题库,所以它的题目数量是有限的,而我的代码是完全随机生成的,所以结果是无限的。但AI似乎认为作为Wordle来说一天一题才是它的特色,无限就显得没意思了……既然如此,那干脆让它给我把两个游戏合并了,让它参考Mahjong Handle把一天一题的功能也给我加上,另外Mahjong Handle还有一个MPSZ快速输入法,也让AI给我合并进我的游戏里了。 + 这时候就体现了AI Agent的优势,如果直接用线上的AI把Mahjong Handle中的功能移植到我的代码,我还得完整描述要实现的功能,但是AI Agent能自己看人家仓库的代码,就知道要改什么东西了。 + 最终实现的效果也很不错,虽然我的代码没有题库,但是使用了当天日期作为随机种子,用mulberry32算法搞出了一天一题的功能,还挺有意思的。 + 另外在Mahjong Handle以及原版的Wordle中,还有一个困难模式,就是每次猜测必须使用之前的猜测条件,不过我觉得如果原模原样把这个功能抄过来似乎有点无聊,而且我发现玩这个东西一般直接一个国士+两个一气就基本上把要猜的牌找完了,有点简单,所以我想了一下,要不然限制一下提交的条件,提交的牌型必须包含目标的役种之类的……不过要是做这个功能就不能像之前那样只实现部分役种了,得把所有能用到的都实现一遍,还好这种事情肯定也不是我来做,肯定还得是让AI来😋。 + 实现的时候还不能让AI直接一口气全加上,还得一步一步的提示,而且每个牌型还得手动测一遍……最终虽然做的看起来似乎已经很完善了,不过逻辑的根本还是有点问题,AI设计役种计算的时候是按拆解后役种数量最多的情况来算,但事实上立直麻将算役种是按照高点法,要最终点数最大才行。虽然按照目前的方法绝大多数情况下都能正常工作,但偶尔也会遇到问题,比如像这副牌: + - 🀈🀈🀈🀉🀉🀉🀊🀊🀊🀞🀟🀗🀗 🀝 + + 按照目前的方法计算的结果是: + - 断幺·平和·一杯口 = 3番30符 + + 然而正确答案是: + - 断幺·三暗刻 = 3番50符 + + 但是符数的计算又很麻烦……不过反正提示和验牌都是同一套程序,题倒是也能做出来……所以就懒得修了🤣。当然说到底还是因为AI没有用现有的计算库,像Mahjong Handle用的是现成的[Riichi](https://github.com/takayama-lily/riichi)库,制作起来比我这个就简单多了。 + +# 为游戏添加字体 + 在我制作“立直麻将 Wordle”这个小游戏的时候,我让AI使用的是Unicode中的麻将字符,毕竟有现成的字符总比去找牌的图片要方便,而且实现起来感觉更简单一些。不过用字符确实也会遇到一些问题,我不知道为什么在macOS和Android上其他牌都是黑白的,唯有🀄️是彩色的😅,Windows上使用的Segoe UI Emoji倒都是彩色的,但是牌很丑,像是小学生设计的……虽然不影响玩,但是看着很难受,至少牌的颜色统一一下也好吧?虽然也可以搞出黑白的🀄︎,但如果用这个,在Windows上又成了只有🀄️是黑白的😅。虽然Mahjong Handle是直接用了SVG格式的牌图片,但我觉得应该有现成的麻将字体,像这种问题应该有现成的解决方法才对吧? + 于是我直接问AI有没有这样的字体,AI搜了一下看起来还真有,有个叫做[Mahjong Colored](https://github.com/kfarwell/Mahjong-Colored)就是基于那些SVG做的OpenType-SVG字体,我本来以为问题就会这样解决,然而当我应用了字体之后发现牌的位置变成了白色的方块,貌似只有Firefox才支持这种字体……然而现在谁还用Firefox啊? + 那遇到这种问题怎么办?当然还是让AI解决,它说要在Safari和Chrome上使用彩色字体,用COLR或者SBIX格式比较好,COLR格式的兼容性最好,三个浏览器都能用,而SBIX的话Firefox没法用。不过Firefox既然基本上没人用了,我倒是不太在乎它的兼容性,但我肯定还是更希望能用兼容性更好的方案,于是我把OpenType-SVG字体转换为COLR的工作交给了AI Agent。 + 结果它整了半天也没整出来,貌似是因为SVG的特效太多,没办法转换为COLR格式,于是它选择了SBIX方案,不过COLR存储的是矢量图,而SBIX存储的是位图,大小要大一些,而且放大还会有锯齿。只是目前实在是没办法了,只能用这种格式。 + 最终我把它转换后的字体存了一份到[仓库](https://github.com/Mabbs/Mahjong-Colored-SBIX),但是看着它1MiB多的大小以及不太好的浏览器兼容性,再考虑到它还是位图,实在是不太想在我的游戏里应用它…… + 结果后来我又搜了一下,原来是有现成的麻将字体啊,有个叫做[Mahjong Font](https://github.com/rutopio/mahjong-font)的项目用的就是COLR格式,兼容性非常好,而且大小只有80KiB,算是最完美的解决方案了。垃圾AI给我推荐的Mahjong Colored真的是……浪费了不少时间,看来有的时候自己调查要比问AI好一些吧。 + +# 感想 + 总的来看,目前Vibe Coding的体验确实是相当不错,只是问题也非常明显,完全让AI生成的代码要么实现的不优雅,要么就会出BUG,如果完全不懂要写的东西,纯粹由AI生成,那出问题的概率就非常高了。不过要是看不出来错误,完全被AI忽悠过去了,也许这时候的Vibe Coding在另一方面算是完美吧😂?至少对用的人来说也算是解决了问题呢。
\ No newline at end of file |
