这篇文章本来要写在 CCISC 2026 回忆录中,但由于其内容更多偏向于技术侧,思来想去,洛汐决定还是将其独立成文。一方面,技术类文章和叙事类文章混在一起,观感确实可能不好;另一方面,独立成文可以让洛汐更多涉及到技术侧的一些内容。

由于洛汐近日已被 AI 风格的文章荼毒过深,这篇文章洛汐将尝试恢复星澜音时期的语言,并有意避免 AI 文章的典型风格。

但是这比赛到底叫 CISCN 还是叫 CCISC?

截至发稿时,洛汐仍然没搞明白我们去打的比赛,其英文名到底是 CISCN 还是 CCISC。Gemini 为此做了 Deep Research,但给出的回复并不足以让洛汐确信。多数人在多数场合仍倾向于将该比赛称为 CISCN,但考虑到洛汐在线下看到的各种标志均为 CCISC,所以洛汐将在本文使用后者的称呼。

在一切开始之前

其实当时的零音想过 AI 的事情,即使零音当时因为一些自己的和外界的原因,还没有定下来到底要不要再以 Starfall Koi 队长的名义参加 CCISC 2026 决赛。CCISC 比赛要求全程断网,在线大模型在通常情况下是不能使用的。队伍之前使用的 Qwen 3.5 9B 模型质量过于拉胯,在半决赛赛场上关于一道 RenPy 逆向题,给零音的脚本是带有逻辑错误的,导致那道题零音没有做出来——后来发现其实只差最后一步。手写脚本或哪怕使用「火绒剑」拦截都不会有问题。事实上,零音并不在意做题或拿奖,但 9B 的使用体验真的很糟心。

Hazelita Plaza 的聊天记录。零音抱怨 Qwen 3.5 9B 的质量,因为 9B 在赛场对一个 JSON parser 的分析完全错误。
Hazelita Plaza 的聊天记录。零音抱怨 Qwen 3.5 9B 的质量,因为 9B 在赛场对一个 JSON parser 的分析完全错误。

然而时过境迁。4 月 4 日的零音仍然在为 GPT-5.4 在无编译器和汇编器的情况下完成了一道较困难的 Shellcode 题而感到眩晕瘫坐 像看到原子弹爆炸,而后续 GPT-5.5 和 GPT-5.6 sol 乃至 GPT 6 Astra,以及 Claude、GLM、Kimi 和 DeepSeek 等各大厂商的新模型的飞跃则让 零音/洛汐 更加吃惊。彼时的零音断言 AI 不会再在较短的时期内有更大的飞跃,而事实证明零音确实有些悲观。不过,诚然:从每一个「半年之前」向现在过渡的 AI 用户,在一轮又一轮「温水煮青蛙」般的 AI 变革中或许已然不会再感到如 Sora 2 和 GPT-4o 级别的飞跃;但若直接回望半年之前的模型、我们在当时对模型的预期和现在模型的能力,便能实实在在感受到大模型进步带来的独特力量。

当然,零音/洛汐 对这一切还是有充足的怀疑的——一个可能的原因是 GPT-5 发布时「新智元」的那篇文章让零音真的了一整天。洛汐现在对于 OpenAI 在过去不久的 GPT 6 Astra 发布中宣传的 AGI 已经到来 这件事仍然无法怀有正向的预期,但洛汐希望自己这次也能被光速打脸.png

这是作为一个正常用户而抒发的情感。但作为一个断网线下 CTF 比赛的参赛者,在线模型的飞跃固然与我无关。在前几次比赛中零音已然断言这种比赛只能自己手搓:9B 的智商过于低下只会带给参赛者负作用,而真正有一定智慧的模型仍然需要更大的参数量才能正常工作——直到 CCISC 2026 决赛通知前的十几天,2026 年 8 月 14 日,Qwen 官方发布了 Qwen 3.8 27B:发布即屠榜。在 Artificial Analysis 等评测榜上,Qwen 3.8 27B 的跑分接近 DeepSeek V4 Flash 0731 (max),在代码能力方面甚至赶超 Claude Opus 4.6,虽然 AA 榜因为「GPT 6 Astra < GPT 5.6 sol」这个抽象的评分已经被网友评为野榜了,但据大众的使用体验来讲,在代码力这块确实不错。虽然世界知识和文学写作等方面远不及其他模型,但对于线下 CTF 场景而言,一个代码方面的专精模型事实上便已经足够。

后来队伍的成员们来问过:我们要不要搞一个?其实洛汐当时没想着要搞,毕竟洛汐打心眼里不太认同 27B 能掀起什么浪花,并且在实际测试之前也不会断言一个模型的真正能力——但一个问题是:如果你不搞,其他人就搞了。万一这东西真有用,那我们不就亏了吗?可以不赢但不能输,「补兑,还是要搞!」

初见 MacBook Pro

小学的 protosc 在 VMware 里跑的 hackintosh 是 protosc 对于 macOS 的初印象。事实上,在 protosc 的那台 Intel 4 代 i3 上,hackintosh 的体验极为拉胯,现在的洛汐也不知道 protosc 怎么用得下去的。后来零音在大学里帮同学配置开发环境时有幸体验了大约 12 个小时的 macOS,也会在路过 Apple 授权经销商或 Apple Store 时进去玩一玩。各种角度上来说,洛汐对 macOS 并非完全生疏。

队伍应当租了 2 台 Mac,但洛汐仅在这里描述自己得到的这一台:搭载 Apple M4 Max 芯片的、8 + 128(128 GiB RAM + 8 TiB Storage)的 MacBook Pro。其实拿到手后,适应这一套熟悉又陌生的 OS 并没有给洛汐造成太大的困难——毕竟在 Linux 和 Windows 掌握的终端、网络、调试、编译、脚本和排障等能力大部分都可以迁移过来;真正的困难是在这样一台带有 macOS 约束、基于 Apple Silicon 架构的 Unix 工作站上,配置好属于自己的 CTF 和 LLM 环境。

拿到 MacBook Pro 后,我们拍摄了 MacBook 各个角度的照片,并确认其可以正常开机,且各项硬件功能都没有问题。在 macOS 上使用

1
2
3
4
5
6
uname -m
sw_vers
system_profiler SPHardwareDataType
system_profiler SPPowerDataType
df -h /
vm_stat

随后更新了 macOS 到最新的版本 26.6.2 并立刻关闭了自动更新。确认环境也没有问题后,冻结版本是一个好习惯。因为在半决赛赛场上,零音因为在赛前升级了 Windows 侧的 VS Code 而忘了同时更新 WSL 上的 VS Code,导致 8 小时比赛全程没有 Remote VS Code 用 我们得到的这台 MacBook Pro,其电池循环次数只有 2 次,是非常新的笔记本了。

我们为这台 MacBook Pro 起名为 stellakexing,延续了洛汐给所有设备以前缀 stella 命名的传统。kexing 意为「客星」——

客星,非常之星,其出也无恒时,其居也无定所,忽见忽没,或行或止,不可推算,寓于星辰之间,如客,故谓之客星。

——【明】无名氏《观象玩占》

安装必备的工具链

在检查完环境后,需要先安装一些必备的工具链,如 Xcode Command Line Tools、Homebrew 和 Rosetta 2。

1
2
3
xcode-select --install
softwareupdate --install-rosetta --agree-to-license
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

事实上在实际运行时,第一条命令总是失败,提示我们「不能安装该软件,因为当前无法从软件更新服务器获得。」这是一个显然的问题,但挂上神奇的妙妙工具后依然报错。Only Apple can do 解决方法是在 Apple Developer 页面手动下载后安装,这需要登录 Apple ID。

安装软件包

「工欲善其事,必先利其器。」安装必备工具链后,我们开始安装软件包。

对于多数软件包,洛汐使用了包管理器(下文会提到)。而对于一些便携的、或没有官方封包的、或需要自己编译的,洛汐于是全部交给 DeepSeek 代办了。在 AI Agent 的时代,AI 的确显著提高了洛汐的工作效率。洛汐安装了一些自己在 stellarkira 上就常用到的开发套件、实用软件和 CTF 工具等,同时也参照了一些已有的 CTF 工具大全(下面给出了其中两个)来完善自己在 stellakexing 上的工具链。由于线下赛是全程断网的,多带一些工具总比没有强。(零音在半决赛的时候少带了工具,导致亏了很多道题目……)

洛汐目前用得最多的包管理器是——

Determinate Nix!

由于洛汐一直使用 Determinate Nix 和 home-manager 管理自己的软件包,且也维护了自己的 home-manager 配置,迁移变得非常「轻松➕愉快」。Nix 的一大优势便是声明式配置,在不同的电脑上应用相同的配置,可以做到软件包的近似完全一致。虽然洛汐已有的配置是为了自己所使用的这台搭载 Fedora 的华硕天选 6 Pro stellarkira,它的架构是 x86_64-linux,但经过一些修改后也可以应用到这台 aarch64-darwinstellakexing 上。

具体地:因为有一些包不为 aarch64-darwin 提供,DeepSeek 帮助编写脚本并筛选出了与 CTF 相关的,存在 aarch64-darwin 构建的所有包,以生成一份 home-darwin.nix。随后我们安装 Determinate Nix 并以 standalone 方法安装 home-manager,在 /etc/nix/nix.custom.conf 中添加下面的内容以换源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Chinese mirrors for mainland China
# TUNA (Tsinghua University) mirror as primary substituter

# Binary cache — use TUNA mirror first, then fall back to official
substituters = https://mirrors.tuna.tsinghua.edu.cn/nix-channels/store https://cache.nixos.org

# Trust TUNA as a substituter to skip strict signature verification.
# Without this, narinfo mismatches cause Nix to silently fall back to cache.nixos.org.
trusted-substituters = https://mirrors.tuna.tsinghua.edu.cn/nix-channels/store https://cache.nixos.org

# Trusted public keys — TUNA proxies cache.nixos.org, same key
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=

# If TUNA is down, try USTC instead:
# substituters = https://mirrors.ustc.edu.cn/nix-channels/store https://cache.nixos.org
# trusted-substituters = https://mirrors.ustc.edu.cn/nix-channels/store https://cache.nixos.org

# Or Shanghai Jiao Tong University (SJTUG):
# substituters = https://mirrors.sjtug.sjtu.edu.cn/nix-channels/store https://cache.nixos.org
# trusted-substituters = https://mirrors.sjtug.sjtu.edu.cn/nix-channels/store https://cache.nixos.org

(怨:这里是 AI 幻觉重灾区……所有 AI 给的换源方法几乎都有问题。这个亲测是工作的。)

随后运行第一轮 home-manager switch 就可以了,待装上 nh 后可以使用 nh home switch .。这里本来应该附带这些脚本和替换后的 .nix 文件,但由于洛汐在济南回程前就归还了 MacBook Pro,这部分内容没有备份。😭

不过与许多朋友的建议相违,洛汐并没有使用 nix-darwin:一是洛汐对这个东西的确不太熟悉,需要额外的心智资源来配置;二是其短期收益事实上远不如 home-manager,大部分功能洛汐完全用不到。所以洛汐坚决选择了 Determinate Nix 和 home-manager。不过洛汐并没有选择将所有软件包都使用 Nix 管理,毕竟,macOS 上还有一个更大众的方案:

Homebrew?

对,Homebrew。

零音曾经在 stellarkira 上用过 Homebrew,不过,Linux 侧的 Homebrew 似乎并不是显著必要的,毕竟存在有高级和原生的包管理器。而对于 macOS,Homebrew 几乎是开发者的必备了。我们在这里也使用了 Homebrew,但并非粗略地将所有包丢给 Nix 或丢给 Homebrew,洛汐认为二者应当有明确的分工。具体地:Nix(配置 home-manager)更加适合管理 zshgittmuxneovim 等纯 CLI 工具及其配置和 uv 等基础运行环境,而 Homebrew 则更适合管理 brew install --cask 的 GUI 工具、某些明确依赖系统集成的工具和现成的软件。

事实上,在洛汐将配置文件从 stellarkira 迁移到 stellakexing 的时候,也遇到过这样的问题:Nix 侧的一些包声明提供了 aarch64-darwin 版本,却在构建时因为各种原因出错,在这种情况下,使用 Homebrew 这样相对原生的方案则是最不容易出错的选择。而对于 QQ、OrbStack、UTM、DaVinci 和 Figma 这样的重 GUI 程序,Homebrew 同样支持管理大多数,但洛汐仍然使用了从官网下载安装包(.dmg / .pkg)并手动安装的方法。(内心里始终有点莫名其妙的膈应。)

总而言之,包的安装是相对容易的。不过,装好了可不代表能真正跑起来:macOS 上有一套非常复杂的……

软件包校验机制…

洛汐在 macOS 上遇到的第一个硬骨头是 macOS 的软件包签名校验机制——这远比 Windows 的 UAC、SmartScreen 严苛得多。从 Gatekeeper 到公证、代码签名,再到强化运行时,整个链条构成了 Apple 生态的安全铁幕。对于从 App Store 下载包,或完全使用流行软件包的轻度用户,这不会造成任何问题,反而强化了系统的安全性——但对于重度用户,尤其是 CTFer——这就确实有些灾难了。任何从互联网下载得来的二进制文件,无论是 .app、动态库还是命令行工具,只要缺少有效的开发者 ID 签名或签名被篡改,它在启动前就会被杀死,只留下一个冰冷的 SIGINT。

对于未签名的程序,最基础的放行方式是前往「系统设置 > 隐私与安全性」,在「安全性」区域找到被阻止的软件条目并点击「仍要打开」,以要求 Gatekeeper 对该应用放行。但这种方法仅适用于「未签名」或「签名无效,但未被完全篡改」的情况。一旦 macOS 检测到包内任何文件哈希与签名负载不匹配,这个按钮甚至都不会出现。

很多时候 macOS 提示「包已损坏」,并不是真的损坏,而是隔离属性(com.apple.quarantine)在作怪。该属性由 LaunchServices 在下载完成后自动附加,包含下载来源、时间戳和 Gatekeeper 评分。若要清除它,需要用 xattr 命令递归移除整个 .app 的扩展属性:

1
xattr -d -r com.apple.quarantine /path/to/program.app

这是绝大多数教程的终点——但我们使用的 IDA Pro 因为一些特殊原因,必须对其内部文件进行替换,这才是麻烦的开始。macOS 内核在检测到 .app/Contents/MacOS 下的可执行文件或框架库被替换时,其 codesign 的完整性校验机制会立刻生效,于是我们又会看到 SIGINT。重新签名可以解决这一问题——我们在这里就不展开了。任何一个 AI Agent 都可以帮你完成这一点(其实是洛汐让 DeepSeek 代办之后自己忘了 DeepSeek 到底怎么办的了)。

「劳累……」想念 Fedora 就是在这一刻开始的,没想到 macOS 装个软件包有这么多讲究。好歹所有的软件似乎都正常工作了,洛汐舒了一口气。

验证工具是否可用

即使很多工具安装成功了,也并不意味着它们一定能跑起来。部分工具只有在真正运行的时候才会提示缺这样那样的东西;而很多工具并没有非常易于理解的帮助文档。倘若只是带上了工具却发现无法使用,或带上了工具却在该用的时候甚至忘记了这工具的存在,那便是完全的无用功了。洛汐在安装完软件包后,让 DeepSeek 测试了所有的软件包,用至少三个功能来探测软件包的确已经安装成功,并生成了一份详尽的 Markdown 文档。文档里给出了每个软件包的安装路径、实际用法和注意事项,并且还给出了例如综合渗透之类的通用流程(信息收集、端口扫描之类),真正让装上的软件包成为一套成熟可控的工作流。洛汐依旧需要说,在过去完成这项工作是相对困难的;而在 AI Agent 时代,这样繁复的工作变得从未如此简单。熟练使用 AI 已经变成了技术类工作人员的必备技能。

不过,仍然是因为没有备份的缘故,这里无法向各位展示这份 Markdown 文档了。qwq

其他配置

触控 ID、Siri、Safari 浏览器之类 Apple 或 Mac 特有的配置,洛汐几乎都设置了;之后也尝试了一些 Mac 特有的开发套件和工具等。虽然不是洛汐租的 Mac,但「毕竟 Mac 现在在我手上,那就是我的了!」

于是洛汐拿着 stellakexing 玩了很久,体验了很多 macOS 侧的东西。(猜猜前文,洛汐为什么要给一台 CTF 设备装 DaVinci 和 Figma 呢?)最后,又给 @PatriciaOfEnd 表演了一波单手持握 MacBook Pro 在街上飞奔。 不是我的芝麻信用分我就嗯造了哈哈哈哈

聊天记录。洛汐:「我要单手握持 Mac 了 /咦」Patricia:「整坏了你自己赔 /给你一拳」
聊天记录。洛汐:「我要单手握持 Mac 了 /咦」Patricia:「整坏了你自己赔 /给你一拳」

但洛汐自始至终没有在系统设置内登录 Apple ID。涉及激活锁之类的问题的话,即使洛汐确信自己不会搞砸,却也不敢擅自冒险,毕竟 Apple 对这个的管控很严,而且这是租来的机器,搞出个什么问题就要给 p 大手子急哭了。所有的软件,洛汐也没有从 App Store 下载。

适应

于洛汐看来,适应 Mac、适应 MacBook 和适应 macOS 是不一样的。

洛汐在同学那里用过 MacBook——不过是 MacBook Air。那台笔记本真的很轻很薄,看起来非常优雅,以至于当洛汐拿到这台 Pro 的时候甚至以为自己租到了华强北盗版:重、厚,而且烫;电源适配器也是巨大的一块。但毕竟这是一台性能极其出色甚至恐怖的笔记本,并且,好在它的噪声不大、音响效果非常给力,尤其是低频音超级震撼,在配置环境的那些日子里,洛汐一直使用这台 MacBook 循环播放音游曲歌单,听感甚至比家里那台小米音箱 Pro 还要出色一些。()适应 Mac 其实是一个宽泛的话题:身旁有朋友购入 Mac mini 并接入自己的显示屏和键鼠,他们依旧用得很开心。而至于适应 macOS,洛汐并没有在上文展开太多,因为在拿到 stellakexing 之前,洛汐的确没怎么深入用过 macOS——但事实上,适应 macOS 比洛汐想象中的快很多。

让洛汐首先感到有些别扭的,实际上是键盘的左下角:Command 和 Option 键的加入让洛汐摸不着头脑,用 Mac 的前几个小时里洛汐愣是不知道怎么复制粘贴,怎么在终端里发送 Ctrl-C。而 macOS 中,这些键又被显示为更让人摸不着头脑的 icon(⌃、⌥ 和 ⌘),无疑加剧了洛汐的理解负担。不过事实上,习惯了之后,洛汐便感到舒服了不少。毕竟在终端里再也不需要使用 Ctrl-Shift-C 来复制内容,而 Ctrl-C 的两种客观上的含义也被 Command 和 Control 键分开。过去在使用 hackintosh 的时候,protosc 似乎还在使用某个小工具进行键盘的映射,现在看来也是没有必要了。

让洛汐随后感到十分优雅的,是 .dmg 安装软件的操作:打开一个 .dmg 后,只需要将 .app 拖入到 /Applications 即可完成安装,十分优雅。事实上,在 Fedora 侧的 stellarkira 上,洛汐也在使用类似的 AppImage 方案安装软件,配上 Gear Lever 达成了类似的效果。不过,macOS 上固然也存在有 .pkg 或一键安装脚本之类的安装途径,洛汐在另一篇文章也谈论过这一点。

让洛汐之后挠头的是 macOS 的窗口逻辑。洛汐尝试了 macOS 在最近某个大版本加入的「台前调度」功能,但「台前调度」似乎一次只能展示一个程序的窗口,洛汐不太喜欢这样的使用体验,于是最后还是换回了正常的平铺式管理。另外,在 macOS 上,点击左上角红绿灯中的「全屏」原来是真的「全屏」而不是「最大化」……在触控板上四指平移可以在桌面和全屏窗口之间切换,而点击窗口边缘两次才是日常意义上的「最大化」。洛汐对这个逻辑适应了很久很久才习惯。对了,还有一点:在 macOS 中,关掉了所有窗口并不代表关闭了这个程序。它仍然会留在程序坞中,再次点击它时则会再次出现。当然在 Windows 和 Linux 上有托盘,在 macOS 上菜单栏也可以常驻程序,但这两者似乎并不完全一致。macOS 在这里的默认逻辑可能不太一样,也是换到 macOS 后需要适应的点。

二次更新:这篇文章的预览版发布后,也引来了朋友们的一些讨论和支招。有朋友告诉洛汐,其实在「台前调度」中,只要长按(或者按住 Shift 点击)左侧的窗口,将其拖到当前的主工作台中,就能实现在同一个工作台里同时显示多个软件或窗口。另外,也有朋友一针见血地指出,这些习惯之所以没有,并不是因为它不符合计算机的直觉,也不是不符合现实的直觉,仅仅是因为它不符合 Windows 的直觉而已。感谢各位让洛汐对 macOS 的知识又多了一分。

聊天记录。朋友们正在讨论 macOS 的「台前调度」功能,两位朋友指出了在台前调度中长按或按住 Shift 点击窗口并拖拽到主工作台的方法,以此实现在同一工作台显示多个窗口;随后一位朋友补充道,对 macOS 的这些不适应并非不符合计算机或现实的直觉,仅仅是不符合 Windows 的直觉而已。
聊天记录。朋友们正在讨论 macOS 的「台前调度」功能,两位朋友指出了在台前调度中长按或按住 Shift 点击窗口并拖拽到主工作台的方法,以此实现在同一工作台显示多个窗口;随后一位朋友补充道,对 macOS 的这些不适应并非不符合计算机或现实的直觉,仅仅是不符合 Windows 的直觉而已。

让洛汐真正惊叹的,是 MacBook 的触控板。其实洛汐觉得对于这一点已经无必要阐述了,真正用过的才知道这个东西是多么伟大.png 不过也间接导致洛汐换回 stellarkira 之后反向适应了很久()

留意的点

当然也有一些点需要注意。MacBook 没有 RJ45 网口——或者说它只有 USB-C 接口——对于线下 CTF 这种一张桌子一个交换机,所有设备通过网线连接,U 盘用来拷贝数据的情形不太友好。需要购买一个扩展坞。以及它在开盖的时候就会自动开机,再加上洛汐为这台 MacBook 打开了经典的「duang~」开机音,洛汐觉得这非常高级,但很多朋友表示打开盖子后被吓到了.png

访达 app 是一个相对来说褒贬不一的自带程序,它和 Windows 上 explorer 的地位有的一拼,但似乎在风评上也有的一拼。双击快捷键直接解压而不是打开压缩包、对于文件数较多的文件夹略显糟糕的浏览体验、右键不支持直接新建内容、不能直接在界面上跳转和输入路径等已知问题,都是洛汐这些日子里体验过的——这些都是实际体验后才会发掘的真实问题。自带的「终端」app 在使用 oh-my-pi 的时候有严重的渲染问题;朋友们极力推荐 iTerm2,但洛汐直到最后也没研究出一套适合自己的配置,于是还是选择了 Kitty 并复用了 home-manager 侧的配置。macOS 似乎仍然没有原生支持 NTFS 的写支持,但洛汐权衡之后觉得配置 NTFS 驱动的收益不高,于是选择忽略这一点。配置好环境并不代表可以带着电脑大大方方走上赛场于是万事大吉——与这个系统磨合一段时间后,我们才能真正发现一些的问题,这问题或微不足道、或真的能影响体验。于系统如此,于人也一样——识人不准已然给 零音/洛汐 造成了巨大的困扰,洛汐也不想在识 OS 上重蹈先前的覆辙

配置 Qwen 3.8 27B

总算到了正文了!租赁这台 MacBook Pro,本就是我们为了在断网的时候跑模型。事实上,在 CTF 比赛中,洛汐更愿意使用自己已经配置成熟的环境,至少用起来非常顺手——但毕竟 CCISC 规定了每个参赛成员「只能携带一台计算设备,且计算设备的功率不能超过 260 W」,于是为了跑模型,租一台 Mac 便成为了最有性价比的选择。考虑到我们队的 4 人中只有洛汐对 Linux/Unix 环境较为熟悉,且对本地配置 AI 也有一定的熟练度,于是队伍的其中一台 Mac 就自然落在了洛汐手上。

在这块 Apple Silicon 上跑本地 Qwen,我们起初想了两条路线——

路线 A:MLX/MLX-LM。这是 Apple Silicon 生态里非常自然的一条路线,通常对统一内存和 Metal 利用得比较好。洛汐天然也对这种方案有着好感,毕竟它足够原生且适配 Apple 芯片。

路线 B:llama.cpp + Metal。这条路线更加通用,生态成熟,模型格式支持和参数控制更广,对 GGUF 模型尤其方便。

但具体选择哪一条,仍然依赖于后续我们的实测。这里,我们直接使用了大名鼎鼎的——LM Studio!

是的,洛汐惊叹于这款软件对两条路线都有着较高的支持度,图形化的界面足够友好,使得洛汐不必浪费时间于模型的命令行配置,更能专注于模型本身能力和效率的测试以及 CTF 环境的配置——这固然是极好的。LM Studio 原生提供了 Qwen 3.8 27B Q8_K_M 量化版本的 MLX 和 GGUF 两种模型的下载和安装,不过它似乎走代理也不是很快。国内有一些镜像站可以使用,但洛汐知道这事的时候已经下完了,下了整整一天一夜……(哭)

另一颗星星上的 Qwen

在洛汐的 stellarkira 上,跑 Qwen 3.8 27B Q4_K_M,使用 LM Studio,默认配置下速度仅为 3.5 token/s,慢到几乎难以接受。洛汐做了设置,在 GPU Offload 25 层、关闭 MTP 并删掉 Vision 层、KV Cache 调至 Q8_0、CPU Thread Pool Size 设置为 8 的情况下,推理速度提升到了 7.5 token/s,有了足足一倍的提升,但仍然慢到难以接受……不过它的占用倒是问题不大,在后台无其他明显软件的情况下,RAM 总占用(指整个 Fedora 系统)为 7 GiB,空闲 23 GiB,比跑 Windows 时开机的占用还要低;CPU 占用大约 60%,风扇也几乎不怎么转。洛汐对自己的调校非常满意,但唯一的问题是这速度太慢;而且洛汐为保守,设置了 32k 的上下文,看起来很多,但 Agent 几轮就满了。非常难受。

然后 stellakexing 就来了。所以洛汐称它为「客星」。

初见 MLX

洛汐首先下载了 MLX 版本的 Qwen 3.8 27B 8bit。下载下来后,进行模型的测试:由于 LM Studio 暂不支持调整 MLX 模型的上下文长度,所以洛汐使用模型的原生最大上下文长度 262k。

无畏 Chat!

第一轮测试不追求复杂。不使用 Agent 的情况下,先使用 Chat,在 LM Studio 内直接做基本测试:模型对同一个问题测试三次,随后取用时和速度的平均值。洛汐测试了:

  • 基础代码能力。让 Qwen 完成一个小型 Python 和 C 任务,判断其是否理解需求、代码能否运行。
  • CTF 任务。使用一段容易的汇编代码让 Qwen 分析,判断其是否正确提取关键信息、生成可运行的脚本。
  • 长上下文。使用了一段较长的小说文本,测试其在长上下文时候的生成质量表现、prefill 速度和输出稳定度。

测试发现各个问题相差不多,几近稳定在 15 token/s,在一句话提示词中只需约 0.1 s 就可以得到第一个 token。不过一个问题已然浮现,虽然在目前看来并不明显——部署的 Qwen 面对长上下文的时候,prefill 速度极慢,在小说文本测试内容中,使用一篇数万字的小说,Qwen 用了近半分钟才完成了 prompt processing。当时的洛汐并没有觉得有什么大问题,紧接着开始了第二轮测试。

无畏 Agent?

其实之前的 零音/洛汐 总觉得这种比赛不应该开 Agent,毕竟 Agent 干活很慢,而且我们不能确知 27B 的最大能力;但后来真正测试后,才发现 Agent 能力还真的确有用——Chat 能做到的功能很有限,取决于交互者的发言水平,而且需要不断的交互,在比赛中这样做会非常麻烦。于是洛汐展开了 Agent 测试:测试使用的 Agent 分别有 oh-my-pi、Codex 和 DeepSeek Harness Desktop。这三款 Agent 都是洛汐日常使用的,洛汐对它们有很高的预期。(至于 Claude Code——我只能祝 Anthropic 和 Dario 一路顺风。)实测得到的结果是,在上下文长度接近的情况下,三者的输出速度等性能表现相差不多;但由于没有足够的 CTF 或代码相关的测试案例和试验方法,洛汐也并非权威的 AI 测试机构,也就无从得知谁对 Qwen 的 harness 更有效,于是最后在赛场上,洛汐还是选择了自己最熟悉的 oh-my-pi。

oh-my-pi

oh-my-pi 原生支持在模型列表选择来源于 LM Studio 的模型。在未配置 Skills、未修改多数配置的情况下,其 initial prompt 约 17k,其中存在一些客观意义上对 CTF 无用的东西,例如对代码风格和要求的规则、对 oh-my-pi 本身的介绍等。@DeepslateQAQ 为 oh-my-pi 开发过一款插件 omp-dsh-minimal,可以将 oh-my-pi 的内置提示词切换到 DeepSeek Harness 的简化风格,但该插件默认仅支持 DeepSeek 的两款模型,洛汐并没有在赛前修改插件的时间和精力,因此放弃了这条路而继续使用默认配置的 oh-my-pi。

Codex

Codex 接入 LM Studio 模型略显复杂,洛汐使用了广为人知的 CC Switch。Codex 是很长一段时间洛汐的主力 Agent,不过,因为 Codex 不能直接看到模型的思考过程,这会让洛汐非常焦虑,所以洛汐最终也没有使用 Codex。毕竟在线下赛中,由于 Agent 太慢,任何一个决策上的错误都可能浪费比赛宝贵的 AI 时间;洛汐更愿意在思维链看到 AI 的想法,并给予适时的指正和引导。

不过,洛汐在互联网上得到的一致答案是:Codex 的 harness 模型是一流的,甚至对于 DeepSeek V4 Pro 0813 这种过拟合了 DeepSeek Harness 极简模式(minimal mode)的模型也具有更高的跑分,但毕竟模型质量和 harness 质量是一个仁者见仁智者见智的问题,就连所谓权威的评测机构也会在这些事情上有所偏差——洛汐最后还是没有选择 Codex 作为主力 Agent。

DeepSeek Harness Desktop

DeepSeek Harness 可以通过修改配置文件使用 LM Studio 的模型,原生支持 OpenAI Responses API。不过由于比赛时(2026 年 8 月末)DeepSeek Harness 仍处于非常早期的预览版 0.1.0-rc.7,其极简模式虽然具有显著短的 prompt,但在 macOS 上有 极简模式下 Bash 工具的 bug,所以洛汐只能使用标准模式。不过,DeepSeek Harness 在当时的版本也存在一个设计上的致命问题——因为本地 Qwen 的 token 速率实在太慢,在 Qwen 调用 Write 工具编写脚本时,连续出现了多次被打断的情况:因为 Qwen 写得实在太慢了,DeepSeek Harness 判断写入工具执行超时,于是就直接打断了模型。这于线下赛和自部署模型是一个非常恐怖的问题,所以洛汐没有使用 DeepSeek Harness。

无畏并发,,,

模型必须放在真实任务中才能显露其真正水平。于是,洛汐使用了 Mini L-CTF 2026 的 Web 题 EzOmniProbe 进行测试,这次测试使用了 DeepSeek Harness Desktop,得到的结论是:单轮任务,非并发,在跨越 120 m 30 s 的测试中,洛汐提供了 2 次提示,Qwen 3.8 27B 的缓存命中率 91%,平均 token 效率(这里包含 prefill 和等待 tool calling 的时间)4.8 token/s,首 token 平均用时(这里指的是所有轮次的响应的平均值,即第一次响应到最后一次响应的均值)27.6 s。最终,Qwen 成功将题目推进到了 JS 沙箱,大约是题目的 60%。(当然,除了这道题之外,洛汐还使用别的指标进行了测试,这将在后文提到。)

事实上:洛汐是有点失望的。一道零音用了 2 个小时就差不多看明白的 Web 题,被 Qwen 跑了 2 个小时还在绕圈子,而且生成速度是越来越慢的——从各种角度上来说,当前的工作流并不合格。其实洛汐并没有对 Qwen 3.8 27B 抱有很高的预期,毕竟 零音/洛汐 很早之前就自觉 27B 的模型顶了天也比不过部分参赛者在比赛中以各种可能并不正当的方式使用的 1.6T 参数量的模型 DeepSeek V4 Pro。洛汐对这个模型的预期是,它能够帮助洛汐做一些繁琐的工作而非智力上的工作,即,它可以分担一些可能并不太吃智力的工作量,但若让它全局掌管某道题的完整思路,那确实有一些小材大用。不过,在 CCISC 2026 的实际比赛中,由 2 台设备部署的 Qwen 3.8 27B 事实上做出了很多的题目,甚至在对于 MacBook Pro 的理论生成效率来说极短的时间内攻克了一道对其能力而言较为困难的 Pwn,这远远超出了洛汐对 27B 模型的预期。看来真正在线下赛实际共事的时候,无论是模型还是人,其真正的能力和素质才会显露出来。这于洛汐而言,是不亚于 CTF 本身的一个教训。

单发情况下尚且如此,但并发情况呢?洛汐只能坦言:在线下赛的真实场景中,Qwen 3.8 27B 的表现让洛汐更加大跌眼镜。

这里因为洛汐文章的提纲有一些混乱,洛汐只能先行剧透:事实上,我们在线下赛使用了 GGUF 的方案,它的确具备了一些性能上的提升,但远没有那么多。

由于 4 个参赛者共用 2 台 MacBook 的 Qwen,每 2 个参赛者使用 1 台,这里 @PatriciaOfEnd 和洛汐使用 stellakexing 的 Qwen。在两人各打开 2 个 Agent 的情况下,LM Studio 需要 4 个并行推理的线程,于是 token 效率就大约分了四份,实际测试中,在 4 个线程的情况下,每个窗口的平均 token 速率是 3~4 token/s;为了提高速度,我们不得不限制 Agent 的数量,但只要两人同时使用,就不可能有性能的提升——到了后期洛汐甚至完全停止使用 AI,以允许 @PatriciaOfEnd 一个人跑 3 个 Agent。而速率甚至不是最大的问题:由于不知道到底是谁的原因,使用 oh-my-pi 时,若在思考到一半的时候 Ctrl-C 打断,LM Studio 侧的推理并不会立刻中断——换言之它还会继续推理;又因为洛汐设置了 LM Studio 的最大并行数量为 4,于是很容易出现「滚木」推理线程占了位置导致完全无法使用的情况。洛汐必须要关闭再打开 LM Studio 提供的 Server API 强制中断推理才能继续使用。而又因为 prefilling 的谜之速度,在每一轮 tool call 后,本身进行对 tool calling result 结果的处理就需要花费很多的时间;倘若因为一些问题导致内存中 KV Cache 丢失,那么推理速度则更为灾难——在比赛中因为一些原因洛汐重启了 Mac,再次使用 oh-my-pi 重新连接时,对一个 50k 的上下文,Qwen 单是重新 processing prompt 就用了约 10 分钟时间。在比赛中使用 AI 时,有近半数以上的时间我们实际上是在等待 Qwen 重新加载其 prompt。这个速度,说实话,真的有点难以使用。洛汐也在质疑,这个速度到底是快是慢——

再谈速度

质疑归质疑。诚然,等待它生成的时间里,洛汐其实是感到绝望的。但仔细想想,这似乎也可以理解。我们算一笔账,请看下面的答题卡:

题目:计算 Qwen 3.8 27B Q8_K_M 在 M4 Max 上的理论 token 速度。(150 分)

解:

就 Apple 在其官方 Newsroom 文章 Apple’s new MacBook Pro features the incredibly powerful M4 family of chips 的描述,在我们这台 40-core GPU、128 GiB RAM 的 M4 Max 上,它的统一内存带宽为 546 GB/s546 \text{ GB/s}。Qwen 3.8 27B 的参数量约 27 billion27 \text{ billion}。假设使用 Q8 量化,每个参数大致占 1 byte1 \text{ byte},那么模型权重的最低体积即

27 billion×1 byte=27 GB27 \text{ billion} \times 1 \text{ byte} = 27 \text{ GB}

再加上量化 scales、metadata、alignment 和其他张量,我们预期实际的权重接近 2831 GB28 \sim 31 \text{ GB}

在 decode 阶段,模型每生成一个 token,粗略地需要让大部分权重参与一次计算。我们考虑这样一个上限:每一秒,LM Studio 的 runtime 都能完美地将权重从内存中读完,而且不存在任何其他开销,则有

546 GB/s÷28 GB/token=19.5 token/s546 GB/s÷31 GB/token=17.6 token/s546 \text{ GB/s} \div 28 \text{ GB/token} = 19.5 \text{ token/s}\\546 \text{ GB/s} \div 31 \text{ GB/token} = 17.6 \text{ token/s}

从而理论的 token 速度约为

17.619.5 token/s17.6 \sim 19.5 \text{ token/s}

满分——不过满分归满分,但这只是 roofline 式的粗略估计,不是对某个 runtime 的保证。LLM 的世界里有各种复杂的因素,纯数学永远不能概括它的一切。注意到西电的开学考为 AI 专业筛选「图灵班」的人才,但也因难度太高而在网络上走红。倘若出一些这种难度的题,既不会太难又很有意思,岂不双全之事吗?(

而在现实中,除了需要考虑到「计算」这一的确耗时的问题,还需要扣除一些额外的因素,例如 Metal kernel 并非完整而完美地利用内存带宽、例如矩阵运算和访存并不完全重叠、例如 tokenizer、采样、同步、runtime 自身开销之类。我们实测出的模型效率其实非常符合这个估算——这是物理层面决定的上限。而至于为什么前文我们提到生成速度越跑越慢,因为 Agent 任务并非单纯的输出,每轮的耗时要近似包括 prompt prefill、thinking decode、tool execution 和 tool output 等,周而复始下去,所以后续轮次的 prompt 越来越重。即使我们看到了 91% 的较高的缓存命中率,它也并非意味着前序轮次的成本变为 starwalking=0starwalking = 0——减少了某些公共前缀的重复计算是好事,但新增的 tool output、对话尾部和动态部分仍然需要处理。

另外,prefill 阶段同样也是不得不品的一环。洛汐使用了一个 36k 长度的上下文进行测试,模型在 prefill 的起初可以达到 200 token/s 的速度,但越往后则速度越慢;处理到第 18k token 的时候,模型的速度已经降到了 150 token/s。洛汐在之前测试的一句话 prompt 用 0.1 s 出第 1 个 token 于是就十分合理了;但同样合理的也有一个 50k token 的上下文用了 10 分钟时间才能处理成功。毕竟,M4 Max 的内存带宽只有这么大;我们又何以期待它能超过物理极限呢?毕竟,若跑足够强的大模型只需租一台 MacBook Pro 的话,那这样的比赛不就纯变成了 AI 赛吗?那就没有任何意义了。洛汐于是这么想着,想啊想……

GGUF 和 Speculative Decoding

我是一个 GGUF,我有一个 Speculative Decoding!台下选手欣喜若狂!

想着想着,GGUF 的模型也下载好了,洛汐立刻展开了第三轮测试。本轮测试中,我们使用了史铁生的文章《比如摇滚与写作》并要求模型生成读后感。

(这篇文章真的很有意思!洛汐被史铁生的文字震撼了很久,常读常新……希望大家都来看看 w)

对于 GGUF 的 Q8_K_M 和 MLX 的 8bit 量化版均使用完全相同的 initial prompt、temperature、top-p、max output tokens 和测试问题。GGUF 的配置如下:

GGUF 配置项
Reasoning Effort Medium
CPU Thread Pool Size 16,和 CPU 核心数相同
Speculative Decoding On

MLX 侧也使用相同的 reasoning effort。结果非常 AMAZING 啊——

测试项 Q8 GGUF (64,000) Q8 GGUF (262,144) MLX 8bit (262,144)
生成速度 20 tok/sec 19 tok/sec 16 tok/sec
TTFT 11.77 s 11.74 s 11.33 s
draft tokens accepted 44.6% 44.5% 不适用

(括号内的值是设置的上下文长度。GGUF 模型可以调整上下文长度,MLX 不能。262,144 (262k) 是 Qwen 3.8 27B 支持的原生最大上下文长度。)

奇迹……降临了?至少这已经是一个非常漂亮的结果了。GGUF 的生成速度比 MLX 快了约 25%,同时两边未见明显质量差异,且 LM Studio 为 GGUF 提供了比 MLX 更加高级的配置选项,优势已经显然了。而其中的 Speculative Decoding 是真正的奇迹——所谓 Speculative Decoding,顾名思义:「投机解码」。在生成时,由一个小 draft model 快速猜几个 token,再由 27B 的 target model 批量验证,猜对的直接接受,猜错的再继续推算。

我们在 GGUF 模型打开了 Speculative Decoding,并且得到了高达 44.6% 的 draft tokens acceptance,这说明 draft model 并非盲目瞎猜,有接近一半的候选 token 能被目标模型接受。而且,我们在这里使用的是散文进行测试;代码和技术类文档更加公式化和语法化,理论上我们可以期许更高的接受率。当然,我们不能说 GGUF 相对于 MLX 的一切提升都必须归功于 Speculative Decoding,毕竟二者的实现差异等更加底层的东西可能有待我们进一步探究,但这并不妨碍我们坚定已经下定的战术结论:我们已经有了一个实测更快、支持 Speculative Decoding、配置更可控的 GGUF 主线。好、好啊!把 MLX 扫近历史的垃圾堆吧!

不过,还有没有更大的性能提升呢……?例如,其他的量化方案?

Q6/6bit、5bit 和 oMLX

是的,我们考虑过将 Q6/6bit 量化的模型作为 fallback。如果 Q8/8bit 在真实 Agent 任务中确实撑不住,或许 Q6_K GGUF 和 MLX 6bit 可以作为一个速度优先的同档候选。粗略地看,倘若我们在 Q8 量化得到约 14~16 token/s 的裸生成速度,则在 Q6 量化下,比较合理的期待是 16~22 token/s。看起来提升可能不多,但若转换为同等工作量下的执行时间,在以分钟为量度的情况下,其收益还是可观的。我们甚至还考虑过 MLX 的 5bit 量化版,它带来的效率收益理论更加可观——

但问题是,为了速度而选择更大的量化损失,真的值得吗?

事实上我们无从得知,正如洛汐在前文中写到,自己并没有评测 LLM 的经验,洛汐最终没有选择为一个更快的速度去赌博一个牺牲了精度的模型。当然这并不代表 Q6/6bit 和 5bit 就一定有多么严重的降智——洛汐并不为这一条断言负责,因为洛汐根本没有测试、也没有时间和方法测试。有些遗憾。

不过,来自西电的朋友们提供了一个看起来更加诱人的方案——Qwen3.8-27B-oMLX-MTP-Mac

这个东西看起来真的很诱人——使用 oMLX Runtime 和特定的 oQ4e 量化模型、MTP 配置,声称可以在 M4 Max(128 GiB RAM)上跑到 oMLX baseline: 24.9~25.9 token/s,oMLX + native MTP: 48.0 token/s prose, 65.5 token/s code,测试条件是单流、关闭 thinking、约 2.1~2.4 k token prompt、生成 320 token。

但它究竟是如何做到的呢?回顾前文的内容,为什么 token 生成这么慢?本质上我们可以粗略理解:模型生成每个 token 的时候,都需要将全部权重从内存读一遍,参加计算。每一步的运算量对于这台恐怖性能的 MacBook Pro 来说其实很小,但「读一遍 28 GB」这个动作花掉了几乎所有时间。这就是说,GPU 的计算单元实际上大部分时间都在等待内存送来权重。这也就是为什么 27B 无论在 Mac 上怎么调,其速度都被压在这个量级——不是算不动,是「喂」不动。

Qwen 3.8 有一个极好的特性,它的官方模型卡明确写了自己支持 MTP(Multi-Token Prediction)——这意味着 Qwen 3.8 在训练阶段就学会了在预测下一个 token 时同时预测未来 D 个 token。为了实现这一点,模型末尾挂了一个轻量的 MTP 模块(通常只有一层 transformer,参数少得可以忽略),它利用主模型最后一层的隐藏状态,再去预测后续 D 个 token。

传统的 Speculative Decoding 是小模型猜测后大模型验证,而原生 MTP 的路径有些差别:

  1. 主模型 forward 一次得到当前最后一个 token 的隐藏状态。
  2. 几乎免费的 MTP 模块从隐藏状态出发,以极低的代价预测出未来 K=3 个候选 token。
  3. 主模型再将这 3 个候选 token 一次性放到同一个 batch 里做一次并行 forward。接受的,直接落地;第一个被拒绝的,从那里重新进行第 2 步。

加速的秘密在于第 3 步:验证 3 个 token 时,权重只需要被读取 1 次。batch 变大,只会让计算量小幅上升,毕竟 GPU 的计算单元本身就比较闲置,但「读一遍 28 GiB」这个昂贵的动作被摊销在了 3 个 token 上。于是每秒的有效 token 产出相较于理论的带宽上线,就可以再乘以一个「1 + 平均接受率」。

但我们原来的配置也有 Speculative Decoding,为什么和这个 repo 的实现结果相差了近两倍呢?这有两重原因。第一重,原生 MTP 的接受率显著高于外挂 draft model 的接受率。MTP 的收益事实上严格取决于接受率,好比一个 draft 模型如果给你预测了一堆废话,你一定看都不想看——MTP 就是这个道理。这个 repo 里的原生 MTP 的接受率明显更高,从而它大概率能获得更高的速度。

第二重原因呢?它…实际上是 Q4 量化。对,又回到了洛汐所说的原点——洛汐还是不愿意赌 Q4。写这段文字的时候,洛汐已经打完比赛一两周了;回想一下,当时看到这种新的方案的确欣喜若狂;但想到它是 Q4 量化的时候,洛汐愣是赌都没敢赌。洛汐不敢说自己到底是亏了还是赚了,毕竟正如洛汐所说,洛汐也没法赌,因为的确没法测。不过它倒是给我们提供了一个更有价值的推论:如果 GGUF 侧我们的 Speculative Decoding 不再进步,则该 repo 提供的 oMLX + oQ4e 方案的吸引力就更多来源于它的 Q4 权重和接受率的实现,而非「原生 MTP 这个想法」。是的,GGUF 模型或许没有原生 MTP 那样的速度,但接受率的提升是实打实影响模型效率的关键因素。其实我们已经借这个想法吃到了红利——近半的接受率已经带给了我们显著的效率提升,我们只是缺少更好的量化起点和更精的实现——但这并不是我们可以做到的。这样的推论或许并不能为我们带来 token 速度上的实际提升,却实在可以让我们稍微心安一些了。毕竟,不是我们能改变的事,为什么要为其负责、懊恼和焦虑呢?

所以,我们最终也没有选择这个方案。洛汐跑出的 20 token/s 并非因为 Starfall Koi 太菜,而是在 28 GiB 权重的物理约束下,它真的只能这么快——即使洛汐已经靠着 MTP 拉高了带宽利用率。这个数字其实已经相当体面了——而洛汐也不愿意用更玄学的引擎去赌一个不知道光不光明的未来。于模型优化如此,于人生和未来也一般——孤注一掷,本就不是洛汐习惯的生活方式,即使洛汐已经在生命中孤注一掷过无数次,也为此遍体鳞伤过许多次。

再谈微调

oh-my-pi 配置

MCP 和 Skills

知识库、RAG 与断网彩排

Linux Pwn

在一切结束之后