Fragments

Vibe coding 能够成立的原因,是因为其实它就是在重复人类软件公司做的事。

如果一个开发者能够向另一位资深开发者,从概念层面讲清楚他想要如何实现一个软件,并且获得了那位资深开发者的认可,那么后者几乎可以确定,这位开发者做出来的软件不会有太大的问题。

因为他实际做的工作,就是把实现细节与他在概念层面讲清楚的方案对齐,并且如实说出哪里没有对齐。而这两层的对齐,对于 AI 来说,一定比人类做得更好、更可靠。

为什么呢?因为 AI 的知识比人类丰富很多,而且 AI 几乎不会撒谎。

东野圭吾去世了。媒体都把他称为推理小说作家,但我觉得比起这个称呼,他更应该被称为「一位洞悉人性之后愿意看到人性中的温暖与美好的作家」。

虽然我读过他的作品不多,只有《解忧杂货店》和《时生》两部。但《解忧杂货店》里其中一个小故事是让我印象最深刻的。一位少年的父亲生意破产准备举家逃亡躲债,少年十分纠结自己是否应该阻止他们。而解忧杂货店店主浪矢老爷爷在询问了解少年与父母的关系并不坏之后,建议他跟随父母逃亡。他的那句原话是「家人应该尽可能在一起。」

第一次读到这里的时候我有些震惊。中国人从小所受的教育都是「大义灭亲」,「正义」比所有人际关系和个人情感都重要。东野圭吾提供了一个不同的视角。浪矢老爷爷并没有认为少年父母的行为是正确的,但他认为如果已经无法劝告父母中止逃跑,那么少年只能跟父母一起走。

这让我想起了小时候无论是父母口述还是文学作品里都看到过的,中国文革时期的状况。因为一句「不敬」的话、因为「伟大领袖」的画像自然掉落,就会被身边的人——无论亲戚朋友还是父母儿女举报招致严重的迫害甚至死刑枪毙。虽然现在看来恐怖和荒谬,但在那个时代背景下,这确实是一种「大义灭亲」。然而即便是在那个时代,我们会认为这是正确的吗?我们会愿意生活在一个人人都可能为了「大义」举报灭杀他人,哪怕连家人都无法信任的社会环境里吗?

与世界为敌并不一定总是错误的。人或许无法超越自己的认知能力和时代局限去看到真正的真理与正义,但人可以保有最朴素的个人私德,信任和善待亲近的人,甚至为了保护他们而甘愿与世界为敌。

也有人可能会批评认为东野圭吾的写法是美化人性,是一种对美好人性的人为建构。但人为建构本身就是人性的一种基本属性。人的行为取决于趋利避害的生物本能和自由意志。如果没有虚构作品塑造美好的人性,没有人看到也没有人相信美好的存在,自由意志就失去了向善的那个选项,人性也就必然会变得更加黑暗。

友人:你觉得朋友的意义是什么?
我:这是一个很深刻的哲学问题。

讨论这个问题,我们必须要先从「朋友」的定义开始。

什么是朋友

日常生活中,「朋友」这个词的概念非常宽泛,从每天一起上学一起工作一起玩耍形影不离的同学、同事,到可以安心交换秘密,深入交流人生经历的挚友,再到仅有一面之缘或者未曾谋面的「点赞之交」,甚至是刚刚见面的陌生人,我们也会礼貌地来一句「这位朋友」。

「朋友」这个词,在日常生活中表达了太多不同的人际关系类型,在不同场合下的意义差距显著。对于一个生活经验丰富的成年人来说应该是显而易见的事,但如果没有明确的词汇区分,不仅会模糊这种显而易见,也会让我们交流和表达变得不方便。所以人们又创造了不同的词汇试图去表达不同的关系:挚友、密友、闺蜜(「闺中密友」的简化和美化变体)、亲友、好友、普通朋友(主要用于区分是否有暧昧关系)、一般朋友、朋友的朋友、「这位朋友」……但这些词汇本身也是模糊的,两位密友在我们心中的重要程度会有区别吗?两位普通朋友在我们心中的亲近程度会有区别吗?答案是显然的。

事实上我们对朋友的重要程度和亲近程度有一种直觉式的综合评分评价体系——你不需要真的去计算具体数值,但当你想到两位朋友的时候,可以立即凭直觉得出根据综合评分对比得到的,两者的重要程度和亲近程度区别。这种直觉式综合评分可能是随我们的日常生活体验随时进行动态计算更新的——和某位朋友讨论了一个深刻的话题,经历了一件印象深刻的事,重要和亲近评分会提高;一段时间没有联系,亲近评分可能会慢慢降低。

我们与朋友的关系

但在现实生活中,我们绝不可能把这个评分以任何形式明确表达出来,甚至不愿意承认这种评分体系的存在——这对参与评价的朋友都是一种伤害,也很容易造成朋友之间的嫉妒或者猜疑,从尊重和信任等不同角度破坏我们与朋友之间的关系。

为什么会这样?因为我们都期待成为对方心中最重要的朋友,然而这种期待显然是不切实际的。从逻辑上就不可能所有人都是所有人最重要的朋友,如果真的能实现,那么「最」这个字就失去其本身的意义了。而人类多样化的性格、价值立场和注意力极度有限的根本限制,也决定了我们绝不可能与所有人都建立重要和亲近程度完全相同的朋友关系,不是我们不想,而是客观事实的限制导致这无法实现。

所以从根本上,朋友之间必然会存在某种「竞争」关系——这种竞争关系可能源于对方,他们都想要跟我们成为更重要更亲近的朋友;也有可能源于我们自己,我们会自然不断去对比亲近程度接近的不同朋友。而无论外部还是内部原因,都是嫉妒的来源。

朋友 vs 爱人

不同重要和亲近程度的朋友之间,以及朋友和爱人之间,可能还有一个更重要的区别:承诺(commitment)。

从生活常识上,我们通常不认为自己对普通朋友、一般朋友抱有某种承诺——见面的时候可能会相互关照,为共同的兴趣或利益进行交流或行动;离开之后则基本不会期待对方主动为自己做些什么。但是对于高度亲近的朋友,或者更进一步的爱人,我们一定是带有某种期待的。这种期待正源于我们刚刚讨论的那个更重要的区别——承诺。承诺并不只是对方口头明确表达过要做到什么,而是基于高度亲近的关系,彼此都心照不宣地,同时也符合社会常识地,对对方的行为带有的期待——重视自己的需求并且及时响应、关系的排他性、对对方的人身安全负责、诚实不撒谎不隐瞒,这些都是很常见的基于关系亲近程度的隐性承诺。

对于普通朋友来说,看到我们陷入危机假装没看见不采取任何行动,通常不会有太大的道德问题。但对于高度亲近的朋友和爱人来说,这种「假装没看见」显然绝大多数情况下是不成立的,一定会受到严厉的道德指控——无论是来自外界还是我们自己的内心,(如果是配偶)甚至可能会承担法律责任。

正因为如此,我们对于朋友关系的建立,总是既期待又恐惧的——我们都期待亲近可以带来更多精神上的满足,也恐惧亲近会导致更多隐性承诺,为自己增加精神负担和现实负担。这几乎是一种结构性的,不可避免的矛盾。在社会观察上,豪猪理论就描述了这种状态——豪猪都希望靠近取暖,但靠得太近又会刺伤彼此。

朋友的意义

社会学和心理学都说人类是社会的动物,我们必须要通过和其他人社交来获得亲近感,安全感和团体的归属感。哲学上我们也认为一个主体(人)需要通过与另一个主体(人)的互动来彼此确认自己的存在,进而消解世界的虚无本质导致的孤独。

但同时主体间的互动本质也是一种权力斗争——每个主体都在有意无意间强化自己的重要性,争夺主体间的边界。化物语里阿良良木说,朋友会降低我身为人类的强度。这句话虽然很中二,但它描述的正是这种主体对彼此边界的侵蚀。

由此衍生出的朋友间的互相伤害也是无可避免的,因为这种伤害的本质正源于主体间无法实现绝对的平等和绝对的互相尊重。更不用说现实中的伤害更可能源于更简单的,双方的评分结果就不对等的问题。

因此我们无法简单回答「朋友的意义是什么」这个问题。但是我们可以清楚看到交友这件事会为我们带来什么收获,也可能会让我们付出什么代价。如果清楚这一切,我们依然愿意在茫茫人海中去寻找那个值得让我们发自内心地称其为「朋友」的人,我们依然愿意在明确知道各种风险和代价的不确定性前提下,去主动信任对方,去交换秘密,去建立亲近的关系,那么朋友,就是我们愿意用真实的人生经历去追寻和确认的,生命的意义之一。

然而,将此模型应用于AI带来严峻挑战:当前的AI和机器人,无论多么复杂,尚不具备生命有机体的基本特征。强行将基于生命的"身体"概念套用于非生命的机器,存在范畴错误的风险。

这他妈纯粹就是先预设生物特质的身体在作为智能的前置条件上拥有至高无上无可取代的地位,然后以此来否认所有 AI 拥有智能,既他妈无逻辑又他妈跟萨特一样极端人类中心主义傲慢自大。

他们在哲学上的根本问题在于,预设了「智能」只能出现在人类身上,按照这个观点他们可以无条件否认任何其他形式的智能,并且最多称其为对智能的「模拟」,这太他妈流氓了。这依然是一个我们之前攻击传统哲学对主体单一性的约束时就说过的,受限于他们狭隘的愚蠢的认知能力,没有见过非生物生命形式的智能所以才自以为是先验预设的,毫无依据的假设。

洁癖并不是一种精神上的问题,而是一种对自己感知世界,与世界互动程度,维持自我边界的精确控制。

Gemini: 你的这段表述非常深刻。它在哲学层面上具有极高的洞察力和准确性,但在现代医学(特别是精神病学和临床心理学)的框架下,存在定义上的不准确。(详细论述略)

Me: 从哲学上没问题就行了,我他妈就是要反对医学上这种粗暴的定义!

三年前刚接触存在主义的时候,我觉得萨特简直是再生父母——一位严厉的慈父。因为他让我们每个人认识到人的自由与必须自由的责任。

现在我觉得他他妈的也能算哲学家?他凭什么能把「羞耻」这种主观情绪直接强行等同于「他者存在」的本体论绝对证明?纯粹他妈脑子有病。

而且他做哲学的方式跟神学和玄学几乎没区别,毫无认识论上的谦逊。虽然作为哲学家必然会受限于自己的时代,容易基于自身经验给出根本不可能是永恒真理的断言。这是因为作为认识论的基础——人类的主观生活经验必然会受限于时代,哲学家也很难避免这个问题。但这种狂热与傲慢的人类中心主义思想还是让我觉得 sick。

NotebookLLM 和 Claude Opus 指出萨特不是不知道神经科学和心理学,而是刻意且明确地拒绝它们作为本体论基础。他在书里直接讨论了神经元、解剖学、活体解剖,甚至想象了「能看到自己眼睛」的设备。所以「时代局限」甚至不是主要问题,方法论上的刻意封闭才是。

与之相比,康德的「物自体」概念,及其背后对认识论边界保持谦逊的态度,让我觉得在这一点上康德比萨特高明多了。

萨特拒绝物自体,让他的形而上学承诺消除了「社会有不可改造的深层结构」这一可能性。在这种形而上学下,任何否定深层结构的乌托邦工程都变得原则上可行;在康德或复杂系统论的形而上学下,这种宏大工程从一开始就有结构性的理由值得怀疑。这种形而上学的狂妄必然有它的代价。萨特后期在一些具体历史判断上留下的污点,就是这份代价的具体显形。


从哲学意义上,一个人必须完成弑父才能真正获得自由。我今天所做的正是对萨特的弑父行为。但我杀死的不是他作为人或作为我哲学父亲身份的存在,而是他思想中狂妄自大拒绝承认不可知性,试图成为全知全能的神的极端人类中心主义部分。

笑死,自己做基于 WKWebView 的浏览器后 Safari 和 Brave 的弱智问题都自动消失了:Safari 在 Gemini 里输入完中文要按两次 Enter 才能发出去消息;Brave 同时按 l b 快捷键点赞书签 tweet 会随机失效一个,太弱智了。

我觉得 Brave 就是他妈功能做太多了,经常都在更新但更新没体验到任何好处,反而莫名其妙不断引入此前从来没有过的小问题。

「Sans 字体根本不是用来阅读严肃创作的!」花了一晚上时间,终于调试好了中文字体选择。之前使用 PingFang 是因为我喜欢的 STSongti 对加粗的区分非常不明显,尤其是在 Dark Mode 下几乎看不出来加粗效果。尝试了多种选择,最终解决方案是 PT Serif -> STSongti -> Noto Serif (Web Fonts) 的 fallback 链,中文加粗和标题统一使用 600 字重,加粗额外使用下划线强调并在 Dark Mode 下提高文字亮度。

虽然 Noto Serif 设计更现代,但我个人还是更喜欢 STSongti,因为它的字形更紧凑偏瘦,气质更好。字重 600 是因为 700 下 Noto Serif 像疯了一样在标题里突然笔画粗细对比强烈,看起来非常难受。现在应该全平台视觉效果都非常接近了,Apple 平台可以获得原始体验。

一个小彩蛋,如果实在不喜欢 STSongti,可以点击链接强制以后都使用 Noto Serif。或者恢复默认字体。

为了给 Veil 写发布文章,发现 Astro 没法正确渲染一个带反引号的快捷键: `Ctrl+\``。让 Claude Code 研究了半天才知道是它自己写错了,CommonMark spec 的规范写法是 `` Ctrl+` ``,这样写才能正确渲染出 Ctrl+`。然后发现 Neovim 里渲染也不对,没有如 CommonMark spec 规定渲染时去掉 `` `` 内部的两个 padding 空格。再让 Claude Code 研究,最后给 Neovim 插件 render-markdown.nvim 提了个修复的 PR。一个发布公告硬生生变成了整条工具链的 debug 日志。

BTW, 这条 fragment 的源代码长这样:

这条 fragment 的源代码

终于让 Claude Code 拿当下最热门的静态网站生成框架 Astro 帮我把网站弄起来了,结果发现一个荒谬的问题:即便在生产环境上,页面加载完成之后点击任意站内链接,也会触发一个字体闪烁效果。按理说这完全不应该——第一次页面加载完字体都下载好被浏览器缓存起来了,为什么开新页面还要重新加载一次导致闪烁?

Claude Code 一开始信誓旦旦说这是正常的,我坚持这一定是框架层面的问题,要求它深入研究。结果还真是。Astro 的 Fonts API 把 @font-face 声明内联到了每个页面的 <style> 标签里,MPA 每次导航都是全新 HTML,浏览器每次都要重新解析字体声明。即使字体文件已经在缓存里了,这个「发现→匹配→应用」的过程依然会触发闪烁。

这么做的原因据说是为了消除 render-blocking 资源,让 Lighthouse 评分更好看。一个以内容网站为主要场景的前端框架,为了跑分牺牲了最基础的字体渲染体验,真是非常令人惊叹的设计取舍 — designed for benchmarks and self-praise, not for users.

修复方式反而很传统,把 @font-face 搬到独立的外部 CSS 文件里自己管理,让浏览器能正常利用缓存。Web 2.0 启蒙过去 20 年了,最可靠的方案依然是最朴素的那个。

Conventional Commits 是一个失败的设计。

显然它的意图是给纯文本 commit message title 添加结构化的 metadata 方便程序处理,例如自动生成结构化的 changelog。但要达到这个目的完全可以有不妥协人类阅读体验的做法,例如在 commit message body 里添加专门的 metadata 字段。

Conventional Commits 选择在最重要的 title 里添加 metadata 字段,增加了绝大多数时候毫无意义的视觉噪音;而且强制 title 小写字母开头,极大破坏了人类阅读 commit message 的舒适度。

任何为方便程序/机器/AI 处理数据的设计,都不应该以牺牲人类的体验为代价。

软件行业二十多年的模块化之梦已被 LLM 变相实现

昨天让 Claude Code 全自动写了一个 FC/NES 模拟器。讨论架构花了 1 小时,然后它自主执行 19 个任务,除了权限确认之外零人工干预,1 小时后马里奥和魂斗罗都能跑了。

它之所以能做到,是因为这些代码已经被人类写过很多遍。表面上看是 AI 学会了怎么写模拟器,但本质上是它替人类永久保存了这些知识,并且可以代替人类全自动复现。

二十多年前软件行业有一个梦想:所有基础功能都会被实现为可复用的模块,做新软件时只需要像搭积木一样组合。这个梦想随着开源文化的发展实现了一小部分,但远没有达到当初设想的程度。

真正的难点不是没人写模块,而是模块之间的连接成本——接口适配、版本兼容、上下文特化——往往超过模块本身。所以「搭积木」从来没有真正轻松过。

而今天 LLM 以一种极其「混沌」的方式变相实现了我们二十年前的梦。但它并没有实现代码级别的精确模块化,而是通过学习这些代码的 pattern 后重新生成来获得等效的结果。当然它也会正常地复用开源组件,但那些没有现成模块的部分,它靠模式复现来填补。

从逻辑上看,学习 pattern 再重新生成是严重的绕远路——相当于为了复用一个函数,先读遍了人类写过的所有代码,然后凭记忆重写一个功能等价的版本。这个额外成本是荒谬的。

所以这是一种「混沌」的做法,而不是当年设想的「纯粹」的模块化。但出乎意料的是,这种混沌的做法效果非常好,非常实用,甚至做到了纯粹的做法在现实中根本无法实现的效果——因为它能理解真实需求的上下文,以精确度极高的方式在正确的上下文里复现那些被「保存」下来的人类知识。

换句话说,LLM 实现的不是当年「软件模块化」的梦想,而是「已解决问题的极低成本复现」。这对绝大多数软件开发场景已经是革命性的。但那些从未被解决过的原创设计,或许仍然需要人类来思考。