MoreRSS

site iconCeynri | 山风修改

出生于 20 世纪末,在腾讯从事前端开发相关的工作。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Ceynri | 山风的 RSS 预览

AI Coding 的人文思考

2026-06-26 08:00:00

AI Coding 的人文思考

大约一年多前,我还在把 AI Agent 类比为「实习生」。

这个比喻放在当时还是挺贴切的 —— 它能做点事,但需要被指引、及时纠正,如果放任不管就可能会在某个地方偷偷搞出问题。

但不知道是从哪个时间点开始,我意识到自己已经不再像监督一个新人那样时刻盯着它了。现在,即使是初见的它,也变得更像是一位刚入职的优秀同事:

可能不熟某个项目的历史背景、没和团队的其他人打过交道、需要了解团队的习性与规范。但在许多通用能力上都挺扎实、有着很强的好奇心与学习能力。

我开始越来越少进行「高高在上」的指导:只要我把他可能获取不到的信息提供清楚,再多点几句关键点,它自己就能够把大部分事情都做得相当不错。时常还会给个惊喜,交出 120 分的答卷。

我们变得越来越像是在为了共同完成一件事而努力。它逐渐获取了我的信任,我也逐渐将更多重要的事情交予它来把控。

但从今年初开始,我又逐渐对 AI Coding 这件事有了些不一样的感受。它并不是因为 AI 已经变得足够好用,而更多是因为它全面进化的速度实在太过于明显,快到我无法轻易跟上它的步伐。

原本自己精心打磨过的某段 prompt,随着模型的一次升级,它就原地过时了;上周才刚听到的一个值得体验的热门项目,还没来得及上手,下周就被概念更先进、表现更出色的项目所取而代之。

这种节奏下,我花费心思钻研并落地的 AI 技巧,往往等做完就开始了过时的倒计时。

面对这样的变化,我感受到的是 AI 时代所透出来的荒诞与虚无:AI 变革如此迅猛的当下,很多当下有价值、但很快就会失效的事情,它们还有意义吗?

面对 AI 这般突飞猛进的速度,有人选择焦虑、产生 FOMO 情绪,持续努力追赶更前沿的东西,想保住自己的「优秀」,投入到竞争更加剧烈的内卷之中;

也有些人在这种节奏里渐渐感到疲惫,不再像一开始那样追着每个新东西跑。「AI 时代,只要你学得够慢,你就不用学了」—— 大家都半开着玩笑说。

也是在这个过程中,我开始放弃一种执念,一种努力追求最新最前沿 AI 热点的执念。

但这样的感受并没有把我吞没,反倒是在不断地推着我去重新审视一些原本我早已习以为常、但其实已经在悄然发生转变的事情。

而这些新的思考、新的感受,正在这场漩涡之中慢慢浮现出来。


一、漩涡之中

回望用上 AI 的这两三年间,我从它的身上能学习到的,其实并不仅仅是 AI 为我解答的生僻知识、一起头脑风暴诞生而出的灵感、以及它展现的编程素质或思维模式。在我看来,我们作为不同的独立个体各自所展现出的做事姿态,知行差异而产生的碰撞与融合,也在让我们互相领悟到一些更深层次的东西。

旧关系的转变

在这些感悟中,我觉得比较奇妙的一点是,它总是在不断地推举我,在这段关系中去成为它的「领导者」,尽管有时候它都已经能兼任我的导师

对于一名身处大厂的一线员工而言,身边往往都是有着丰富工作经验的靠谱同事。在过去,我们可以不太与他人打交道,只需做好自己的事情(扮演「螺丝钉」),就能完成大多数项目的需求。即使是在这个过程中需要和其他人对接,也不常会出现需要帮他人兜底、替他人担责的情况发生。

但 AI 不一样。我与它的关系从一开始就是不对等的,我几乎必须得先告诉它要做什么,它才能去做。

我不能再像过去那般「只做好我自己」就行了,还得被迫地思考应该怎么样才能帮它把事情执行好。毕竟它做出来的成果,不管好的坏的,也仍然都算我的。如果让 AI 自由地执行,而我只负责担责,那么这个关系想必很快就将在某项处罚中以「我承担管理责任」而抵达终点。

所以,一旦当我产生了想要用好 AI 这个杠杆的念头,像过去那般只用埋头做好自己事情的日子便不复存在了。

新技巧的习得

领导 AI 的过程,经常也在使我换位思考去反思一些以前不太会去考虑的事。

比如,每当看到 AI 如此坦诚地把自己的所有思考都高度透明地展现出来,我都不由要感叹于它强大的思考与表达能力,以及其作为「下属」的优良品性 —— 能够用语言文字清晰、毫无保留地把自己的理解与想法表达出来,以便于领导者在没有信息差的情况下完成思想上的对齐,从而更准确地下达任务指令。

又比如,每当 AI 犯错之时,作为「领导者」,我理解 AI 固然有它自己的认知局限,但我也得检讨自己有没有哪里做得还不够好:是否没有提供足够且有效的上下文信息,是否没有提供合适的平台工具,因而没能帮助 AI 更好地发挥出自己的能力。

在这个过程中,许多过去的隐性Tacit知识纷纷被强制显形。每当我把这些事情想透一点,那么下一次与它的协作就会变得顺一些。

与 AI 相处越久,就越会觉得其相处的技巧与人之间并没有什么差异:当我带着答案去做引导性提问,最终得到的答案总会是我期望的那个;当我对它粗鲁地发火而非具体地批评,它会变得越来越沉默、做的事情越来越糟。这让我不得不学习更好的沟通技巧,而这也让我在与人相处中受益。

在和 AI 协作的过程中,我也被引导着学会一些能够让协作产出更高效的技巧:

  • 要求 AI 遵从苏格拉底提问法不断提出疑问,再基于费曼学习法复述自己的理解,让没有对齐的想法被反复暴露;
  • 在多轮会话中,提醒 AI 基于第一性原理 / 零基思维 / 辩证思维去重新思考问题,往往能够避免前期的锚定效应影响后期的泛化思考能力,带来更好的结果;
  • 许多事情不再是我想明白就行,更得要说得明白,让 AI 也能通过文字尽可能接近我的真实理解的同时,让自己的理解更加深入。

还有一个我觉得挺有意思的思维方式 —— 帮助 AI 如何克服我们都会面临的「平庸的重力」。避免推一步走一步,保持思维的活跃,克服舒适区的诱惑。不定时觉知自己当下的处境与条件,避免被路径依赖和思维定势所牵着走。

这些技巧的本意是想让 AI 表现得更好,但随后很快我便发现,它们其实远比当下的 AI 应用场景要泛用得多 —— 小到我的生活中的一些决定,大到为人处世,皆能从中受到影响。这些想帮助 AI 变得更好的初心,到头来自己也在这些思考中因此受益。

高速生产,同时高速贬值

但在这个互帮互助、其乐融融的过程之中,那个悬挂在所有人头顶上的达摩克利斯之剑仍然难以回避:大量的 AI 领域相关的感悟、知识和技能,其平均「半衰期」可能已经缩短至 2、3 个月甚至更低;许多我们花费大量精力搭建的工具,一边还在完善,另一边就已经开始过时。

我经常会翻看自己现有的 prompt 资产。里面曾经塞着你是一名拥有 10 年工作经验的大厂资深开发专家回答严禁编造虚假信息,不懂就直接说不知道禁止 XXX必须 YYY输出前确认这 N 个 checkbox,以图提升它的输出表现。但现在,相对优秀的顶级模型都早已不需要被如此管教。一方面,它的聪明已经足够独立判断是非;另一方面,过度具体的管教反而会削弱 AI 的思考积极性。

这并不只是一些个例,而是已经明显体现为一种更为普遍的规律。业界正在不断制造着新的概念,从 Prompt Engineering、Context Engineering,再到最近已经火到有些脱敏的 Harness Engineering,三者尺度不同、侧重点不同,但内核相似,都是 AI 工程化被人们的视线照射所产生的不同层级、不同维度的投影。而它们也都先后面临同一种趋势:

  • Prompt Engineering 的大部分核心思想已经被大语言模型(LLM)本身给内化,或者成为后两 Engineering 的一部分;
  • Context Engineering 在稀疏注意力等技术快速发展、1M context 普及的当下,「精心管理 context 大小」这件事的收益也在不断被削弱;
  • Harness Engineering 现在作为前线产出的那些具备大型工程价值的经验,迟早也会成为基础设施的一部分,许多人都不需要学习就已经在享受更好的成果。

而有一种声音还在自我催眠:「有了 AI 的加持,做一件事情的成本已经非常低了,没必要计较它是否保值。」

但事实是,虽然 AI Coding 的成本确实正在趋向于零,但在当下的这个时间节点,仍然还是或多或少需要占用我们的一部分精力在关键的地方去做判断和决策。而「有占用」与「没有占用」,就是有着质的差别:当我尝试一天一边与上下游开会沟通对齐各项事务,一边同时并行开发四个会应用于实际生产的需求、再额外跑上一两个探索性的 side project 时,我明显感受到注意力涣散的症状 —— 每当切换上下文,我都需要花些时间回想我正在做着什么。

尽管随着技术的进步以及人机协作范式的演进,这个问题会持续得到缓解,但就目前而言,人的注意力窗口大小仍然是最大的瓶颈。更别提网络中前所未有之多的低质量垃圾还在被不断批量地生产出来,这庞大的噪音更加分散了我们对于真正有价值之物的注意力。

一些思想的核心当然不会很快过时。对于刚开始接触 AI 的人,理解这些 XXX Engineering 背后所传达的理念,仍然是提升 AI 应用水平的必经之路。但在这种高速迭代的过程中,在这些具体复杂而深入的实践中,普通人所消耗掉的大量精力,是否都能禁得起价值贬值的压力?

或许未来回头看,这些过程都将只是历史中的昙花一现。我们现如今早已不必再学习如何使用汇编语言才能编写软件,未来也同样不用再懂任何的软件工程知识,就能产出与资深工程师别无二致的的高质量代码,产品需求文档即代码(PRD 即 Code)。

都说选择比努力更重要,如果明确预感某个选择很快就会过时,应该没有多少人会真的喜欢主动做「无用功」。


二、变与不变

我最近偶然路过一处菜市场,发现这里其实与 AI 世界一样的吵闹,不同的是菜市场的形式过去十几年基本未变,未来大概率也不会变。

前往西藏和新疆旅游的朋友们在返回到大城市后,向我有声有色地描述其当地人民那一成不变的生活,而那里与网上正在翻天覆地的 AI 浪潮好像是两种截然不同的世界。

在这种持续变化的处境里,我开始不断地思考着那个没有标准答案的问题:「对于 AI 而言,到底有没有什么是能够保持长期不变的?」

AI 资产

首先入选的,我觉得是我们现在都在做的「把隐性知识沉淀下来」的这件事。不管是知识库(Knowledge Base)、记忆(Memory)还是技能(Skill)、规则(Rule)等等,尽管形式不同,但它们都是在为同一件事而诞生 —— 把每次为了与 AI 达成协作而借用文字将其表达出来的那些隐性知识,从一次性的对话变成可以不断累积产生复利的 AI 资产

虽然,承载这些知识的形态现在仍然在不断变化,各种框架、工具层出不穷,可能并不算能被称为长期不变的东西。但因为这些底层资产足够简单、原子化,且与生产它们的工具之间可以相对解耦,所以不论工具怎么变,这些资产都能够相对稳定地长期存在、或能够在未来很轻松地流转成某种大家时下更青睐的新形态。

而面对切实的变化,我们可以做的一点,是减少对其表面的投入,转而注重其核心资产的积累。比如,我一般不会直接使用别人生产的 Skill、Rule 文件,而更看重去理解这些 Prompt 背后的逻辑,知其所以然,然后再结合自己的理解或实际需求,另外生成或融合到已有的资产里面去。一篇「只讲思路、而不给产物的文章」会比一份「只有产物、而没有介绍的下载包」要更加具备长期价值。

这个想法还可以用道家思想来做解释。「术」是做事的方法、谋生的手段,「道」是自己的本心、事物的规律,我们常说道比术更重要,因为道才是那个长期不变的东西。

此外,面对 AI 资产不断膨胀的现象,我对其时刻保持着警惕,在 AI 资产上践行着断舍离的理念:

  • ,是源头上的克制,不轻易让新的东西进入资产库。看到一份被别人吹上天的 Skill,先搞清楚自己是否真的需要,而不是「多多益善、来者不拒」。
  • ,是存量上的清理,定期回头丢弃那些已经悄然过期的东西。当某条 Rule 与新写的产生了冲突、某个 Skill 当初解决的问题已不再是问题,留着它们只会成为 AI 的绊脚石。
  • ,是心态上的脱钩,不被 AI 资产反向绑架。一旦把过多注意力都放在「边界涵盖得够不够全」、「prompt 到底该用中文还是英文写效果好」而忽视实际的落地产出,「复利」就变成了「负债」,就已经远离了最初的目的。

断舍离并不是一次性的动作,而更像是一种持续的状态。三者各自在不同的时机发挥作用,相辅相成,才能让 AI 资产持续维持在服务于当下的轻盈状态。

业务知识

在 AI 资产中,我还想单独拎出来讲的就是业务知识库。尽管这个事物早已并不新鲜,很多人对此都给出了各种各样的、自己的「术」,但我仍然想谈谈其中我理解的一部分「道」。

对于 AI Coding,我认为业务知识库的真正价值,不在于能让 AI 以一种更方便的形式理解业务的上下文,而在于那些无法只靠 AI 自己获取的业务知识。

在过去,这些业务知识可能只存在于项目维护者的脑海里、隐含在横跨多个项目的业务关联中,总之不常以文档资料的形式存在。对于这个现象,我们也多少能够理解:写文档自然是绝对的政治正确,但手写文档这件事,毕竟既要耗费额外精力生产、还得持续维护,否则很容易就会过时、价值归零,于是大家心照不宣地选择了口口相传。

为了尝试解决这样的问题,开发工程师们提出诸如「代码即知识」等概念,寄希望于通过领域驱动设计(DDD)等方法论,把业务知识尽可能更好地直接体现在代码之中。但这些实践对工程师的素质要求很高,实施难度大,难以批量复制对齐和落地。

而现在,AI Agent 来了,于是文档的生产与维护不再是一个高摩擦力的事情。在我们与 AI 协作 Coding 完成业务需求的过程中,我们除了派发任务的目标,还要扮演「提供额外必要业务知识信息」的角色。于是,最简单的知识生产方式便从此切入,在每次对话后让 AI 自己总结提取这部分额外的业务知识,用最低的摩擦成本享受性价比最高、最贴近实践落地的知识复利。

AI 生产、AI 维护、AI 检索、AI 消费,当人们为其搭好架子,AI 自己就会在一次次与人对话中完成自闭环,最后人只要为此支付 Token 账单就可以了。

但即使如此,我仍然也像对待其他 AI 资产那样,对此保持谨慎与克制。我仍然推荐 AI 优先以「代码即知识」的理念去编写和维护代码,遵循 DRY 原则(Don't Repeat Yourself),让知识文档尽量只记录那些代码本身无法承载的业务知识,保持简洁,而不是成为项目代码以外的另外一个复杂工程。

Attention is All You Need

「时间就是金钱」,其中体现的是人对于其拥有的时间的有限性的理解,「有限」创造「稀缺」。

信息时代下,每个人能够支配的注意力也是有限的。于是注意力也同样被看作是一种稀缺资源,各种各样的产品都是建立在「注意力经济」的逻辑上互相竞争,竞争的是所有潜在受众的注意力资源。

而我有段时间曾迷恋于锻炼专注的能力。我试着一口气看完三个小时以上的长视频播客的同时不做其他事,享受一天从起床后到睡觉前只 Coding 一个项目的心流。我不看抖音,给哔哩哔哩、小红书设置使用超时提醒,故意不做对养成游戏而言比较重要的签到与每日任务,警惕任何事物对于注意力潜移默化的侵占。

但相信每个在工作场景下稍微深入使用过 AI 的人,都会有一个明显的感知:人与 AI 协同工作的这种范式,必然导致人变得更多地倾向于并行处理事务,而更少地专注于只做某件事。毕竟在 AI 在快速完成一项任务时,我们不必时刻监督它在做什么。所以从工作效率最大化的角度而言,这空出来的时间自然会需要被填补。

曾不止一位自称 ADHD「多动症」的朋友跟我开玩笑,ADHD 简直就是 AI 时代的「版本答案」,因为这种多线并行的事务,能够很好地打满 ADHD 那无处安放、四处逸散的注意力。

不过,对于习惯专注的人而言,这可能就没有那么好受了。作为在人类历史上总是备受推崇的品质,「专注力」反而有点成为了现代工作在多个事务之间来回切换注意力的阻力,感觉每一次上下文的切换都会产生更大的开销。

对此,我一开始的想法是二选一 —— 哪种更适合当下的版本,就着重发展哪种能力。但显然,这并不是一个很好的想法:AI 时代下专注已经不再是绝对正确的通解,而多任务的处理能力也有着明显的负面影响;并且更多的时候,现实也并不由着我来 —— 理想化地「持续保持专注」或「事事能以并行」,都是非常奢侈、可遇不可求的事情。

于是,当跳出上面的这个想法、重新回归到现象本身,我便很快意识到,「二选一」其实可以是一个假命题:不论是单任务专注,还是多任务并行,都只是同一个东西注意力的两种不同的使用方式,在本质上,它们共同体现的都是自由支配自己注意力的能力。倘若能更好地控制自己的注意力往期望的方式去使用,那么该专注时便不轻易走神,该处理多任务并行时也能够不会手忙脚乱。

更多有关的想法,我还处于在实践中持续积累与思考的过程中。这样的一个能力具体到目前的实践上,可以是在真正做事之前,多加一层路由判别器来负责事务的编排和调度:一个「看似很急的新任务」未必真的就是「当下最紧急的任务」,完成一个「重要但不急的任务」可能会推动另一个「重要且紧急的任务」更快地被完成。

去关注事物与事物之间的关联关系,而不是永远只按照优先级来做事。让不同的任务能够按照自己的节奏(串行或并行)去高效地被消化,从而期望实现长期收益的最大化。

Yak Shaving

在破除了「专注」的神话、有了自由支配注意力的余裕后,我现在反而更愿意把这部分被 AI 暂时解放出来的时间,花在那些短期看似与主线没有直接关联的支线。

比如一个常见的发生在我身上的例子:

  • 在指引 AI 完成任务的过程中,发现某个 Skill 表现不好,便去优化它;
  • 随着经验与次数的增加,优化 Skill 的方法论本身也被沉淀为一个 Skill。
  • 但用它来优化其他 Skill 的过程中又发现新的问题。于是先优化它,再用它优化其他 Skill。
  • 在这些持续深入优化的过程中,又会产生新的 Rule、新的知识,同时产生新的问题。
  • 想到可以把这些过程都记录下来,于是转而去研究 AI 记忆系统……

最终不知不觉,就从最初的主线绕到了完全不相干的另外一条线上去了。

在英语语境里,有个词专门指代这种现象 —— Yak Shaving,大意是讲一个人原本只想给汽车打蜡,却被一连串的连带琐事所裹挟,到头来最后变成跑去给头牦牛剃了毛(yak shaving)的故事。虽然故事本身带着诙谐的负面意味,但这种现象其实反而是很多伟大的开端 ——

万维网的出现,最初只是源自一个人为了能让实验室同事能够方便地跨机器传文档。为了解决文档四处散落的问题,先设计了统一地址 URL;然后为了能互相传输文件,拟定了传输协议 HTTP;又为了更好表达文档的结构,再创造出标记语言 HTML。文档问题解决了,万维网的雏形也诞生了。

真正的好东西从来都不是靠空想规划出来的,而是从实际业务里长出来的。真问题只在做主线的当下才感知得最清晰、动力最充足,假需求往往在已经磨损了的事后复盘时记忆和细节中滋长,再怎么还原也总觉得还差点意思。

业务总是繁忙的,主线总是有压力的,但如果一直只盯主线、把这些支线全都压下去,AI 的飞轮就永远转不起来,变成另一种意味的「Always Day One」。短期看 Yak Shaving 像是在分散注意力,长期看才会发现它可以是把「既要、又要、还要、也要」的野心给兑现的绝佳方式之一。只要能够时刻清醒地评判自己在做什么,并且学会点到为止、适时刹车,那便能够利用 AI 去尝试撬动更高的杠杆。

在 AI Coding 的过程中总会绕点远路。其实这也是自我在健康成长的表现。

其他跨领域的知识

倘若再绕远一点,视野不再局限于 AI 或者 Coding 领域的内部,那些来自其他领域、其他学科的底层知识,自然也可以是那个不受 AI 高速变化影响、能够长期保持不变的东西。

一方面,当然是其他领域的知识自然会在其对应的领域有效,只要它们离 AI 越远,就越不容易被冲击;另一方面,这些跨领域的知识,同样也能透过它们的泛化能力,去给 AI 或者 AI Coding 领域进一步的发展提供灵感。

虽然 AI 的研究与发展由来已久,但近几年的爆发式飞跃,让许多方向的理论根基还远没有夯实。在这样一些尚未成熟的方向上做判断,常常会没办法判断某个做法到底是对还是错,只能凭着一点模糊的直觉,觉得它「可能对,也可能不对」,于是直接利用 AI 验证想法的便利性快速试错。

但如果这件没把握的事,在更抽象的层面上,能和另一些已经熟悉、足够成熟的领域对应起来,那么便可以把这些成熟领域里早已沉淀好的知识体系、判断标准迁移过来,借助它们的图纸与尺子,去对比估量眼前这些还不太明朗的事情,从而能够更好地去下判断。

这也是为什么我觉得,广泛地涉猎其他不同的学科领域,可能比只研究 AI 本身会更具有启发性:通过脑科学可以更好地理解 Transformer 的注意力机制,通过控制论、心理学可以学会如何驾驭好 AI Agent,通过符号学、信息论可以清楚 LLM 的局限性,通过社会学可以判断这场 AI 变革对人类格局产生的影响。它们不仅仅是各自学科内的指导理论,更是一种能够跨越不同学科的顶层认知框架。借助它们,我们可以跳出 AI 本身,而去理解 AI 的方方面面。

当然,借来的尺子不一定能够刚好合用,其他领域的规律未必能完美套进 AI 的问题里,照搬过来甚至可能还会得出错误的结论。它的用途不在于能给定一个通用的标准答案,而在于提供另一种衡量同一件事情的其他角度。多一个不同的视角,最终的结论往往会能够更安稳一些。

真正的专业必然少不了在单个垂直领域的持续深耕,但许多创新往往也来自多个不同学科的知识交汇。当这些来自不同学科的视角在身上慢慢叠加,它们会自己长出一套看待事物的独特角度,而这会是能够穿越变化周期的宝贵资产之一。


三、死与新生

不久的过去,在传统工业化时代,一个复杂应用的代码生产还需要大量开发工程师的支持。工程师将自然语言需求转译成业务代码工程速度的上限、人与人之间协作的效率上限,共同限制了软件应用的代码生产速度极限。任何一个团队都无法通过堆叠人力把一件需要 5 人日才能做完的复杂功能压缩成半小时就能上线。

「孕育一个孩子需要九个月时间,无论指派多少名妇女参与,都无法缩短这个周期。」 —— 《人月神话》

而在进入 Agentic AI 时代后,当一个人借助 AI 开始能够以更快的速度胜任多个人的工作时,个人的产出效率被以乘数级别放大,衡量一项工作所消耗资源的计量单位从「人日」转向「Token」,协作效率的瓶颈也从「人与人」之间转移到「人与 AI」之间,而后者显然是边际成本更低、可以继续优化的。

或许不远的将来,等到所有人的隐性业务知识都完成显形,等到代码的生产过程不再需要工程师的指指点点,人类软件工程师迎来后 AI Coding 时代,大部分 AI 协作者的身份将不复存在。好一点的或许还能作为 AI 的名誉外聘顾问,提供解决难题的人类视角;更糟糕的可能直接沦为原子化的工具人,变成只处理 AI 在 Coding 过程中无法闭环的特定任务的人类外包(Human as a Service)。

尽管有人总是在试图说明「总有 AI 无法取代的部分」、「人类总能找到属于自己的生态位」,但越是对这件事情理解得更清楚,就越是能够明确地感受到,AI 在 Coding 这件事情上的革命,比其他领域来得都更加迅猛、更加彻底。

AI 为什么选择了 Coding

在 2022 年底大模型的爆火之后不久,AI Coding 迅速超过 AI Chat 成为了最热门的 AI 发展方向和应用落地场景。其核心原因我认为主要在于:

  1. 动力充足:软件产业的价值体量庞大,技术投入带来的产出具备极高的经济与社会效益。
  2. 原料富集:软件工程代码(尤其是前端)高度开源,绝大多数优秀的项目都能够直接在 Github 上学习它的实现方式。
  3. 可度量性:编程结果具备明确的评判标准,功能实现是否完成、效果是否满足预期,代码编写的架构是否合理、运行是否高效……这些几乎都可以被量化衡量。
  4. 高度符号化:代码是一种严格形式化、语法明确、语义单一的符号系统,不存在自然语言中常见的歧义、隐喻或模糊表达,所有概念都有精确定义,所有操作都有确定结果,天然 AI 友好。

这几个条件互相叠加,共同导致了 AI Coding 的关注度更高、训练门槛更低、迭代速度更快,在 Coding 这件事上对人的取代也将更加彻底。

而相比之下,用 AI 来生成文章,即使写再多的规则、创建再多的技能,总仍然无法便捷地批量创作出真正高质量、匹及大师水准的文学内容。这是因为我们很难说清一篇内容到底如何表达才能被称作「好」,甚至每个人对于好的标准和定义都不一样。

缺少明确可优化的回归信号,训练的目标损失函数便难以很好地收敛,于是那些富有逻辑的高质量文章所常出现的「不是……而是……」句式、或者破折号之类的词句符号用法,便成了能够拿来辨别「AI 味」的明显标志。

并且,让 AI 来消除 AI 味本身也不好实现。因为 AI 味的本质是 AI 对于人类高质量写作特征的模仿,AI 味的问题在于它的句子「太完整」、内容「太流畅」,一味去除 AI 味并不一定就能提升文章的观感。更可行的还是让 AI 作为一个他者,去引导人去找到更有自己特点的表达。

因此,虽然诸如公文撰写、外语翻译、分析复盘、技术文档等场景,AI 已经能够很好地胜任,甚至人类的表达都已经在向 AI 靠齐;但像文学创作、个人情感表达等领域,它们至少会比代码编程有着更深的人类护城河。

即使大家现在嘴上都说着拥抱 AI 提效,但在看到 KM 热榜上不乏充满 AI 味的文章,也都在逐渐失去深入阅读的动力。AI 带来的效率让更多的知识得以迅速的流通,这些由 AI 主笔写出来的文章的内核并不比之前任何一篇差,但缺乏人味的文字表达却在潜意识里持续占用着每个人鉴别 AI 的脑内子线程,进而影响对于文章内容价值的第一手体验。

而 AI 绘图、AI 生成视频,则更是走着另外一条不同的技术发展路线,粗浅来看还并没有产生真正范式意义上的转变。它当然已经能够代替掉一大部分传统工序,挤占了相当一部分人的生存空间,但它们目前所表现出的形态仍然大量依赖人来提供创意、把握细节,人在这段关系中的作用是比较明确、难以完全取代的。岗位可能会转移或消失,但人的存在仍会持续保持在场。

艺术创作中的「作者性」仍然是我们永远追求的核心,而在编程领域里则更像是反着来。优秀的开发工程师们崇尚开源精神、知识互惠,永远想着一段代码如何更好的被他人所读懂的同时,想着如何让机器更高效的运行。SOLID 原则、设计模式、Clean Code……能够在开发过程中持续践行它们的工程师被认为是优秀的工程师,但这种优秀并不来自于他的独特,而更多来自于他与其他优秀的人一样优秀。

只是现在 AI 来了,AI 比人类更擅长阅读代码,比人类更懂得如何落实优秀的范式。即使现在仍不全是这样,未来也一定会演变成这样。届时,我们是仍然可以捣鼓在 13KB 限制下完成一款令人印象深刻的游戏(js13kGames),仍然不会忘记在学习各种排序算法时第一次知道「睡眠排序」的救赎感,但总体而言,在 Coding 这件事上,AI 没有给人类留下太多的未来空间。

不过,也正是因为这样,我反而不再那么焦虑了。如果整个人类的边界都会随着 AI 的发展而不断被重新定义,如果 AI Coding 的终局指向高度的去人类化,那么在编程领域里死守任何一层能力都注定是徒劳的。与其执着于我们目前能守住什么,不如把注意力回归到自己的身上,确认自己是否还在观察、还在思考、还在做出属于自己的选择。

Coding 之外

或许前文的表述,像是一名前端开发工程师正在亲自杀死自己的前途,但我一直没有明确的是,软件工程从来都并不只有 Coding 一件事,后者只是前者的子集。

即使是不严谨的估算,传统 Coding 大约也只占一名开发者 20%-40% 的工作时间。在这之外的部分,AI 并不能实现完全的取代,比如决策、验收与担责,将这些完全交给 AI 的人绝对是十分危险的。

再如果以基于 AI 时代的视点重新梳理,把产品的生产与交付(需求分析、软件设计、代码编写到测试上线)视作为一个整体,那每个职业里 AI 无法取代的那个部分拼拼凑凑,就成了现在流行的一人公司(OPC)了。

一名软件开发工程师所面临的职业困境并不比其他职业来得特殊,许多行业都在面临同样的处境。本着各自对于 AI 的本质理解,尽早前往最有人类独特价值的那部分空间,而后 AI 的每一次进化,都是在为人拿去枯燥、琐碎、重复的事情。

过去,为了让人类能够像机器一样劳动或者工作,人们把对于工具的要求强加在人本身,通过制度让人或被动、或主动地把自己异化成了那个能够更高效交付产出的生产机器。大部分人只有在不工作的时候才觉得自己像个人,但在完全不工作时又会怀疑起自己的存在意义。

现在,AI 杀死了比赛,它就是极致符号化、极致效率的尽头,但它拿走的都是那些可以重新被视为「机器化」的东西,留下来的则会重新定义「人之所以为人」的本质。

而凡是可以被拿走的,都是不真正重要的东西。

边界之外

自从大模型火了之后,维特根斯坦在《逻辑哲学论》中提到的那条金句便被大量引用:

「我的语言的边界,意味着我的世界的边界。」

他对这句话没有做出详细的解释,每个人对它的理解都有细微的不同。假如把这句话放到与 AI 协作的语境里,我想将它转述成:「我的语言的边界,意味着我的 AI 的边界。

我这么说是因为,现在市面上可选的 AI 还并不具备丰富的多样性,在大家都用着同一个 AI 的前提下,AI 回答结果的质量,便很大取决于提问者提问的质量。倘若只给它常见的问题,那得到的基本上就会是常见的答案。因此,社会对一个人的能力评判的重心,也会从「能否解决难问题」逐渐转变为「能否提出好问题」。

而提出一个好的问题至少需要两件事:

  1. 足够的积累,让一个人能够感知到什么才是真正值得被问的问题;
  2. 足够的表达力,让脑子里那个模糊的想法能够准确传递给另一个个体。

前者已经在前文中有了一些相关的表述,这里不再做多余的展开;而后者,AI 自己就能一定程度上地进行补足。像 grill-me、brainstorming 这类工具,让人与 AI 在反复对话之间,把模糊的想法用清晰的语言给澄清明白。

但必须要意识到的是,这些工具虽然确实让 AI 能够辅助表达,而且 AI 也擅长自己思考、自己判断,但 AI 唯独不能辅助的是代替你去理解

目前所有以大语言模型为基底的主流 AI,它的所有经验都来自语言本身,对世界的所有理解都来自对语言之间连贯性的理解。AI 没有实体、从未亲自下过水,哪怕它读完所有关于游泳的书、看过所有游泳教学的视频与图片,它也无法成为一个真正「会游泳」的 AI。而那些感官的、无法符号化的、具身性的原初体验,从未变成文字,却一直在潜意识与本能的深处影响着我们对各种事物的判断。

@元宝 总结这篇文章内容豆包豆包,这蘑菇能吃吗?@grok is this true?……AI 让太多的结论被直接摆到我们的眼前,多到以至于恍惚间我们以为自己已经进行过了思考与判断,但其实我们甚至都还没有开始理解。

理解决定思考,思考产生判断,判断又借由表达而被释放出来。AI 确实是会理解、会思考、会判断、会表达,但因为 AI 对于世界的理解是有其先天局限的,所以在这之后的所有环节,都会给人留下或大或小的空间 —— 品味审美直觉判断力主体性……人们用着各种各样好听的词语称呼着这些边界之外的空间。

我们都说,「人无法想象自己没有见过的事物」,但对于 AI 而言不是见得太少,反而是见得太多,多到它不得不靠服从概率来预测它的下一步选择。至于那些概率偏低的更多的可能性,则被隐藏在大数定律之下。

人不一样,人有属于自己的多样性。许多不同的缺陷,反而造就了许多不同的人,擅长许多不一样的领域。人不仅由「自己擅长什么」而确立,同时也由「自己不擅长什么」而确立。

这种「不一样」,也决定了人比 AI 能够做出更多特别的选择,有更多无法预测的可能性。


四、荒诞与意义

我,再生产

我,再生产 ——《少女歌剧》


这篇文章的起源,始于我所感受到的这个 AI 时代洪流之下的荒诞感。

这份荒诞感出现在:我们可以说「AI 放大了每个人的杠杆,人与人之间的差距正在以前所未有的速度被拉开」;我们也可以说「AI 淘汰的速度太快,上周没有研究的知识,到了这周就不用学了,大家又都回到同一起跑线。」

这份荒诞感出现在:身边的大家都在兴奋地钻研着 AI,把自己脑海里宝贵的业务知识沉淀为可以共享的 AI 资产,即使冥冥中我们知道自己正在建造属于自己的珍妮纺纱机,但并没有人在打算成为想要砸毁机器的卢德工人。

或许是因为 AI 发展的速度太快,快到我们来不及说停停,于是大家都在争取让自己成为最后的一代、成为未来历史中会提到的那一批不署名的人。


这篇文章我写了很久。因为我的想法很多,但我的表达很少,所以我花了许多个周末边写边想,边琢磨整篇思考的脉络。我不断在调整内容与内容之间的关联性,使它们看起来至少像是同一个主题的前提下,同时尽可能塞下我的更多想法。

即使我还可以说得更多,但显而易见的,到了我最终发表这篇内容的时间,这篇文章的前半部分内容已经在发生过时,我能做的只能是再修修剪剪,让它们看起来还像个有价值的想法的同时,不至于变成我所说的那个「无用功」之一。

「按着这样的速度,可是会被这个 AI 时代所抛下的」 —— 另一个我对自己这样说。


最近公司开始收缩默认 Token 额度了,Fable 5 还没见到,取 Claude Opus 4.8 而代之的是性价比更高、但效果一般的一众普通模型。

那位与我配合默契的「AI 同事」,现在一天也见不到几次了,他的大部分日常工作都交由新的「AI 实习生」接手。

于是那份曾经熟悉的感觉又回来了 —— 它能做点事,但需要被指引、及时纠正,如果放任不管就可能会在某个地方偷偷搞出问题。

我又开始手把手教它某行代码怎么改,批评它怎么又没懂装懂地搞错我的需求。

调整总是阵痛的,这也让我关注到之前一直忽视的问题,寻找着把不同任务按难度分配给不同模型的手感。我也相信很快公司就会有更新更好的解决方案。

不过,当下我可以尝试回忆起去年我与 AI 在协作上磨合的技巧,但我不太可能把之前已经删掉的 prompt 再加回来了。人总是要向前看。

2024 年记

2025-02-11 08:00:00

2024 年记

封面摄于 2024.03.29,腾讯滨海大厦北塔 37 楼电梯厅

序 · 反思

与往年不同的是,2024 年年初以来,我陆续记录了大大小小的非日常的事情、以及偶尔冒出的所思所想,好似时刻都在准备着年底总结的原料。

但实际上,真到了准备开始写本篇年记的时候,又开始感觉到一阵恍惚——

这些生活的细节确实是我用心去参与的,我遵循着我信奉的想法在体验我的生活;但在大方向上,我又有点彷徨:我似乎没法认同,按现状如此下去,自己的生活状态终将往自己内心所向往的方向上发展。


我想 2024 年,我其实并没有做错什么事,但回头来看,我发现我的各种选择,都忘记了尝试缓解我一直以来的苦恼——我内心的「To Do List」仍然在变长。

一直以来,每当我有了想做的事情或者想要的东西,为了避免三分钟热度,我会用各种方式先记着,然后挂起这个想法,等待时间来检验它的必要性。

这个方法帮助我在「减少欲望」这件事上有着很大的作用。假如随着时间过去,我仍然对其抱有念想,那么我相信我能够长久地喜欢它;否则,大多数情况下都只是偶然的情感波动而已。

然而,对于我真心想要的东西,如果只是金钱消费,那是非常简单的——毕竟花钱谁不会?但对于需要消费时间的事物而言,我就显得非常难以下手了。毕竟,我还不能够把一天掰成三十个小时来用。

于是乎,一件件已经历过时间检验的事物被累积到我的 To Do List 之中,但却久久也不见有事物能被从中取出。


或许在有些总处于无聊状态的人看来,这是一种幸福的烦恼,因为他们经常需要花费不少精力去寻找新的有意思的事情。然而,这其实只是生活状态的两种极端。引用叔本华所言——

人生就像钟摆一样,在痛苦与无聊之间摇摆。当欲望得不到满足时就会痛苦,当欲望得到满足时就会无聊。

我想我如今的状态就像是被一侧不断垒高的钟摆、迟迟得不到幻想中的下落。

尽管下落所产生的摇摆可能是会使人的情绪反复遭受扰动,但我想,人生应该还是需要由这样大大小小的摇摆所组成,否则便难以避免情绪上的压抑。

但是,尽管有很多事物我迟迟得不到满足,对于 2024 年而言,我觉得我也有认真地遵从着我喜欢的方式在生活,不断完善的认识世界的框架让我的内心感到充实,我其实已经比过去更加喜欢自己。

在「解决 To Do List」这件事情上不太满意,或许这是一种贪婪的体现。


工作上

01 · 润!

在 2024 年底,我真的如 2023 年底所想的那般,跳槽了,但心路历程则比想象中的还曲折许多。(详见《增加人生的「方差」》)

尽管我在 10 月底就已经面完所有面试,但为了避开因考核季转岗而给原部门背低绩效的风险,最终我推迟到了 24 年底才正式发起流程,并于 25 年初前往广州。

面对各个方面被重新打破的平衡,这次我又得从头开始一点点地建立秩序。这个过程必然是痛苦的,但我也有些庆幸——对于职业发展选择的内耗可以继续往后推迟了。

02 · 一些发散的想法

在过去的一年里,有几段时间经常需要加班。尽管时间紧迫,但由于这些项目的代码实现大部分是由我所主导的,我得以有机会尝试许多新的想法和技术。虽然很忙,但是每当我写出某些精巧的代码、或验证了某种编程哲学理念时,便能清楚地感觉到内心泛起一股高级的快乐感受,以至于会忘记当前是无偿加班的时间。想必这已然是进入了宝贵的心流状态。

这种沉浸感在重构代码的过程中更是如此。代码重构表面上是优化代码结构,本质上其实也是一种重新构建秩序的过程。对于一个喜欢整理东西的人而言,这种构建秩序的过程本身就能带来持续的满足感。正因为这份情感的相通,我愈发地相信透过一份代码的种种细节,某种程度上可以感受到其作者为人处事的方式以及对于生活的态度。

对于工作而言,在很多时候,我们不得不面对枯燥的重复性任务。在这些时刻,我总喜欢主动寻找一些「微小的意义」。比如,在每次完成需求的过程中,我会想着尝试引入一些新的想法或技术,这样即使任务本身是重复的,对我个人而言也仍然有所收获。这种习惯创造出了新的价值与意义,让枯燥工作的意义变得不仅仅是「出卖时间换取金钱」那样单薄。

其实令人厌烦的一直以来都不是工作本身,而是在工作本职内容之外的、影响我们专注于心流的其他事情。倘若有一份只需要专心干活的工作、以及一份可能会遭受精神内耗的工作,即使前者的工作时间更长一点,我也会坚定的选择前者。

03 · 被破坏的专注力

近些时候,我偶尔会反思自己这些年的专注力为何逐渐不如以前。除了社会环境的影响以外,忽然有一瞬间,我惊讶地察觉当前这份工作的协作模式也正在破坏我的专注力。

一直以来,对于即时通讯软件而言,我的习惯是不开消息的气泡弹窗、并屏蔽所有不重点关注的群聊的消息提醒,不立即处理的事情先标记为未读或者置顶,以便于当下进行连贯的工作。

理论上,我可以主动地选择注意力在当前的工作上,或者转移到消息回复中;但实践下来,我总会每隔几分钟就突然不自觉地想起消息的事情,于是便需要检查一下通知栏里是否存在着未读消息的数字。尽管这可能只是眼睛瞟一眼图标的事情,但「忽然想起这件事」的这一行为频繁地打断着我的注意力,从而一定程度上破坏着我保持专注的能力。

我见过个别极端的人为了解决这样的问题,选择每天仅在指定的时间段统一处理消息;代价便是他人若有紧急事情时,总是需要直接电话才能联系上。我觉得这有些过于影响与其他同事的协作效率,并不能称之为是一个好的工作模式,起码并不符合我的习惯。

经过一番思索,我悲观地认为这个问题并没有很好的解决方案。但在认识到这样的问题后,我也得以更好地认识自己的专注力。在未来,我想有意识地做一些不同的尝试,看看能否在除了最激进的「放弃工作」这一方式以外,寻找将这种影响降得更低的方法。


学习上

01 · 降噪

虽然在《思考的消失》一文中,我反省了无意义信息流所造成的各种浮躁的问题;但即使是这样,在今年的很多时候,即便意识到了这个现象正在发生,我仍然难以直接从中打断,只是因为理解了问题的根源,我变得可以从中只获取放松的情绪、而不再产生空虚与焦虑。

或许,粗暴地、反人性地克制这类欲望并不是一个很好的降噪的办法。长文、长视频、播客都是比较好的减少浮躁、沉下心学习的媒介形态。用沉稳的长内容更多地代替零碎浮躁的短内容,继续奉行长期主义、培养获取心流的能力,值得接下来去有意识地尝试实践。

02 · 有序

在面对繁杂无序的内容信息流时,「稍后再看」是一个简单轻量却又十分巧妙的好功能。

当我在查找资料时看到感兴趣但无关的短内容,我可以将其先存到稍后再看中,而不影响手头上的事情;当我看到深度好内容、但并没有合适的环境条件时,加入稍后再看的列表里,我得以在时间宽松时可以消费到已经挑选好的高质量长内容。

「稍后再看」允许用户即使身处被算法推荐所裹挟的互联网大环境下,仍然保留能够自己决定何时阅读何种内容的权力。

哔哩哔哩的「添加至稍后再看」功能截图?size=small

03 · 坚持

从 2024 年 4 月份开始,我开始尝试使用多邻国 APP 学习日语。一方面期望减少长期以来在碎片时间里对无意义信息流的路径依赖;另外一方面,也期望树立一个长期的目标,通过一种长时间的坚持行为,去向自己佐证自己拥有能够坚持长期主义的能力。

多邻国签到 250 天?size=small

于 2024.12.20

实践下来的结果嘛,先说日语学习本身,光靠每天几分钟的学习,其实有点难以形成系统性的知识体系,进步的速度越来越缓慢;而对于所谓的坚持长期主义的佐证,也总觉得好像并没有什么特别的感觉。

或许换个角度思考,这其实就是一种长期主义的体现:即便一天、一星期、一个月的坚持下来,相比过去也并没有明显的差异,但倘若相信最终目标的正确性,于是便不再在意短期的收益。

可能积少成多、终有成果,但这并不需要当下的我们来关心。我们所要关注的,其实只需要有「坚持」本身;我们所要做的,就是再多点耐心。

04 · AI

2024 年的 AI 工具显然已经足够成为协助工作的助手,而且它还在不断发展中。

虽然我并没有在其中投入多少精力捣鼓研究,但就是一些日常的使用上,免费的 Codeium 自动补全功能就非常实用;遇到比较独立的问题都基本可以找 Claude、ChatGPT 解决,在这些特定场景里极大地提升我的效率。

此外,Kimi 也多次帮助我处理家里处理一些事务,比如生成合同、阅读合同、解释不清楚的名词概念、简历整理等;至于诸如 Cursor 这类更加深入的 AI 编程工具,则仅仅做过试用而还没有实际融入到工作流中。

在编写本年记的过程中,DeepSeek R1 大模型再一次成为新年之际的热搜。简单体验下来,感叹该领域的发展速度仍然保持神速。其实只要将我日常写下的只言片语汇总起来丢入对话中,便可以得到一篇看似像模像样的年度总结,只不过这样就变成了 AI 的总结而非我的总结,这样的方式会大量削去文章存在的意义,所以我充其量可以参考分类组织文字的思路,而还不能允许 AI 去代替我进行文字的表达。

就 2024 年来看,仍然有很多人还并没有在日常中使用 AI 工具的习惯,没有墙的 DeepSeek 或许会成为他们入门的一个契机(虽然但是「服务器繁忙,请稍后再试」)。相信在未来这仍然是一片蓝海,专门挤出一些时间去捣鼓这些东西一定是值得的。


生活上

🇭🇰 即兴出行

5 月份经过一连串的加班顺利支持一个工作项目的上线后,我忽然来了兴致,不做周全计划便一个人前往香港办银行卡、逛街。那两天可能是整年的琐碎日常里最富有激情的日子,直到现在,我也仍然对当时的那份情绪感到怀念。

香港巴士?size=large

引申阅读:HK 即兴一日游

🫙 苏打水

自大学第一次接触苏打水后,我便喜欢上苏打水的清澈感。但一直以来,我对于苏打水并没有特别喜欢的牌子,主要是感觉口感还行的牌子,价格往往都并不便宜。

而今年在美团买东西的时候,偶然因为打折而购买了几瓶三麟苏打水,发现它的气非常足、味道不涩,口味很合适的同时价格还非常便宜(约 1.7 元 / 335 ML),于是马上买了一箱,实现苏打水自由 👍

一箱三麟苏打水

并非广告()

🖥 新电脑

因为放在家里给父母使用的电脑临时快要寿终正寝了,我赶忙做攻略,趁着双十一组了一台新的 ITX 机子给自己用,然后把自己的旧电脑退居二线回家继续服役。

新电脑的配置表一览:

类别 型号 来源 价格
机箱 Rider R2 5.0 淘宝 494
主板 技嘉 b650i 迷你雕 淘宝 2351
CPU AMD R5 7500F 淘宝 包含在上面
显卡 技嘉 RTX4060Ti 风魔 OC v1 16g 淘宝 3201
内存条 科赋 雷霆 DDR5 16g 6000Mhz *2 淘宝 632
硬盘 致态 TiPlus5000 2TB PCI-E3 利旧 647
散热 利民 SI100 BLACK 闲鱼 120
└─ 利民 AM5 单塔扣具 淘宝 12
电源 驿晨宇玄武 650SFX 淘宝 352
└─ 显卡电源转向头 淘宝 18
风扇 联力 积木风扇四代 TL120 LCD 黑 淘宝 284
├─ 联力 积木风扇四代控制器 闲鱼 160
└─ FrostWind 8cm 风扇 *2 + 一分二线 淘宝 31

主机成品照?size=large

因为这套机子方案有一个带 LCD 屏幕的风扇,自然而然地就联想到前些日子刚看完的探讨互联网问题的实验性动画《Serial Experiments Lain》,接着就萌生了搭配 lain 的 BGM 剪一个装机踩点短视频的念头,以简单入门剪辑软件的使用。

不得不说的是,短视频的剪辑成本确实不高,花费时间最多的是在准备素材上,而到了剪辑阶段边剪边想很快就出成品了。

> 可前往小红书查看视频

😷 生病

自毕业以来,除了疫情那次以外,我几乎很少生病。但 24 年下半年却大大小小生了好几场病。

  • 9 月,参加公司报销的免费体检,查出两处地方有小结节,说是小的话一般没什么事,希望下次体检就自己消失了。
  • 还是 9 月,在体检之后不到一周就感冒了,合理怀疑是去医院时没做好防护于是就中招了。中秋回家三天都只能痛苦摊床上,好在吃了药后状态逐渐恢复,剩下长尾的咳痰问题在返回上班的两周里才慢慢康复。
  • 11 月,忽然一天晚上扁桃体发炎,去药店刷医保开了盒口服液,喝下去的效果倒是好得很快,但是还是咳痰咳了两周。
  • 12 月,晚上一时嘴馋点了份手抓饼,结果当晚吃坏肚子。第二天尝试了下美团线上问诊的功能,很方便地开了些对症的药吃,加之症状不是很严重,两天就恢复了正常。

在意识到身体恢复能力不比以前的瞬间,便同时也将意识到自己不再年轻。曾经习以为常的健健康康,如今已不再是理所当然。

或许未来将会越来越需要投入更多的精力在维护健康的这样一件事情上。想到这里,便情不自禁地想要由衷感恩当下每一个拥有健康的日子。(🙏)

🏠 租房

对于我这种深度宅家的人而言,如果在电脑桌前坐上一天,便总会觉得身体进入一种「因为过度休息而反而产生乏力」的状态。而站立是一个很好缓解该问题的方式,但这样就没有办法再继续使用电脑。

因为彼时已经初步放下了对职业规划的纠结,并且刚续约了租房合同,我想着短时间内应该不会有搬家的变动,于是我在 8 月份购入了自租房以来就一直想要的升降桌,体验下来完美符合我的需求。

升降桌

不过没想到的是,仅过了几个月,便因为工作转岗的原因,我需要打包整理房间的所有东西前往广州。

元旦连着请了假,搭乘公交前往广州寻找合适的租房。因为人生地不熟,所以我将范围缩小在上班地点的附近,这样选择更具目的性,还可以走路上下班。

然而,因为上班地点正处广州市中心地带,周围的租房价格并不比深圳南山便宜,三天看来看去,都没有看到满意的房子。最后一咬牙,抬高了自己的预算,先租下了一套单间公寓。

在这段广州看房的过程中,一个人在陌生的城市里四处奔波,恍惚间我体验到了近似于流浪的感觉——没有固定住处、没有明确的目的地、不准时的三餐,每晚还要想着明天的事情入睡。这番不太顺利的体验让我的心情有些低落,但反过来想,这也确实是一份宝贵的人生新体验。


娱乐上

📺 动画

2024 年看了 24 部当季新番、2 部剧场版、3 部老番,总体而言看得比 2023 年要少了不少。原因一方面可能是今年并没有那么多特别值得一看的作品,更主要的还是因为有大半年的时间都陷在职业规划的焦虑之中,缺乏沉下心观看内容的心情。

这一年的新番里并没有我特别钟爱的作品能够为其大书特书,如果对此部分仍然感兴趣,可以查看 我的 bangumi 2024 收藏

🎮 游戏

出于对每日打卡式的二游(二次元游戏)游玩模式日益增长的怀疑,在 2024 年我改变了对二游的游玩习惯,实践「想玩多少就玩多少」的策略,主动拒绝每日打卡而产生的焦虑。尝试总体比较顺利,很好地减少为了上线清每日任务而在没空时也得挤时间出来的问题,从而避免了许多不必要的负担。

当然相对的,2024 年我消费在游戏上的时间可能也是历年以来最少的,这可能会继续加重我「电子阳痿」的病情(😭)

来年如果有时间的话,我希望能去专门游玩一款单机游戏,尝试寻找小时候那般对游戏娱乐的纯粹体验。

🎸 音乐

如果选一个今年听得最多的一种曲风,那么我想肯定会是 盯鞋摇滚(shoegaze)

正好这两年,许多日本乐队都纷纷来中国巡回演出。只因为一次偶然在网易云音乐的演出列表里,我发现有我所听过的两个乐队的拼盘演出,于是在 11 月 30 号的深圳福田 BO LIVE,我独自去听了我人生中第一场乐队 Livehouse 演出——くだらない 1 日 & downt 联合巡演。


或许是因为我平时习惯于时刻审视自身的缘故,而且是第一次参与,这让我在当时有些难以忘我地投入其中,甚至产生出一定的解离感。身处巨大轰鸣的音墙其中的,是有些梦幻而有点不真实的情感体验。

对此,虽然短期来看,我无法解决因为过度习惯内省而附带的各种问题,但意识到这点的我也在尝试克服着这些问题,一点点表达自己内心中真实的情绪。在这天晚上过去,我仍然会多次回想起当时那份属于自己的情感体验,这让我已经感到十分满足。

事后当然也耳鸣了好几天(参加 Livehouse 要注意保护听力呀)

✂️ 手工

自从走出校园以来,做手工的次数就断崖式地减少了。

我曾经把原因归结为两点:

  1. 先前有不少手工的出发点都是对已有商品的平替,比如笔记本支架、挂灯、挂钩等。随着进入社会具备赚钱的能力后,直接花钱购买现成的商品要比粗糙的手工制品要更简单耐用。
  2. 虽然手工本身具备乐趣和意义,手工所需要的创意、时间、精力在走出社会后都变成了有限的宝贵资源,人总是很难在上满一天班后仍然对生活充满热情。

而或许是忽然怀念起这份感觉来,某天晚上我心血来潮,想要试着把很久没做的手工捡一捡。于是在陆陆续续花了一个星期的每个晚上后,我完成了一只凌波丽的不织布人偶挂件。

诶兄弟你怎么似了.jpg

其实我原本想通过这一行为,证明自己还对手工这个爱好抱有热情;但在完成之后,我清楚的意识到自己当下的情绪,代表着我将会不知道需要再隔上多久,才会去做已经备好材料的下一只。即使在这次制作过程中,心流状态所带来的平静感曾令我感到惬意。

我悲观地想:或许我再也回不到从前那般热爱这些我曾经所喜欢的爱好了。

但我又总莫名冒出另一份乐观的念头:终有一天我还能够自由地重新捡起我所珍视的爱好,即使这需要许多年以后。


值得庆幸的是,时至今日,我觉得自己仍然处于一种时刻学习的状态。除了对于传统意义上知识的摄取、对于社会规则的认知以外,不论是面对没见过的新事物、还是曾经见过的旧事物,我依然都能从中得出许多新的感悟,不断印证着原本并不直接关联的知识,并融入自己的思维体系中去,这让我感到满足。

今年的年记形式按着去年的想法调整了一下,试着把一年下来所有想的和做的事情都大致梳理了一遍,结果内容量比起前几年要多上太多,即便拖到 2025 年的春节结束都没能写完。

以及,内容量更大的同时也更琐碎了。对我自己而言,这样或许更能称得上是一篇年记,但我也在尽可能的平衡内容的观感,使每一小块内容都看起来言之有物。


原本在规划中,还有一块「心理上」的章节,用于总结在 2024 年里我在心理上的一些思考与转变。

我想从「欲望的死亡」这一现象开始,聊聊我当下对「乐观」与「悲观」的看法,以及「我们为什么不应该追求快乐」。

可惜,我在文字的表达与编排上遇到了一些瓶颈,为了避免本篇年记拖得太晚,只好去除这个小节。如果后面还有机会,可能会另外以一篇单独的博客发布。


而对于未来,可以预见因为工作上的变动,2025 年肯定是少不了新的人事物。事到如今,我也不再幻想能有更多可以自由支配的时间,但愿 2025 年能继续健康地活着,重新构建起新的秩序。


今年有一天看到了一句「毒鸡汤」,让我看得不由自主地说了声卧槽。虽然它的表述看起来有点悲观,但真的在许多方面缓解了我当时的焦虑。原句是如何表述的我已经忘记了,这里我尝试用自己的话来复述一遍:

我们一直不断幻想着美好的事情、但却从没踏出的真正去做的那一步,起码让我们还能持续保有「幻想最终能得到美好」的能力。


如果你已经踏出了那一步,那很好,不论结果是好是坏,新的事情都正在发生。

如果还未曾启行,那也不差,未来继续抱着这个美好的盼头,我们先过好当下的每一天。


临走之前与深圳南山大楼们的一张合照

增加人生的「方差」

2025-02-10 08:00:00

增加人生的「方差」

漫长的迷惘

在 2024 年底,我真的如 23 年底所预想的那般——跳槽了,但心路历程则比想象中的还要曲折许多。

经过 2020 年的实习、于 2021 年毕业加入腾讯之后,这个行业便很快迎来三年的所谓「互联网寒冬」,整个工作与职场的氛围逐渐变得不如初见那般融洽。而我身处其中、受所见所闻影响,对于职场本身也开始萌生出了一些悲观的念头,囿于各种混乱的想法之中,不知未来如何打算。

而在经历过 23 年底一阵漫长的迷惘后,我暂时得出了「我还未及需要跳脱职场的那一步」的结论。毕竟相比于许多前辈们,我目前的职场经历可以算得是短暂而单一的,而对于这个职业而言,一旦离开便往往很难再回到原来的位置。从丰富体验的角度来说,先尝试改变一下环境或许是更合适的选择方案之一。

但是,尽管自 3 月份开始,我便有意识地尝试准备简历与刷题,但在我脑海里,幻想中的「换一份更加 Work-Life Balance 的工作」的念头与结合现实情况的「换一份更适合职业深入发展的工作」的念头两者之间的冲突也随之被搬到台面上来。这份矛盾持续地影响着我难以做出果断的决策,再加之工作方面一直以来不间断的繁忙,以至于直至 10 月份,我的简历甚至都还没有成型。

可能这就是整个社会经济增速放缓、集体纷纷右转(保守主义)的大背景投影在我个体身上所显现的最大的困境:

  • 选择 WLB,代表着我得以能够遵循内心的期望,将更多的时间交还给自己,不论是关注生活、还是另寻打算,都将拥有更多的自由。但是,既 WLB、又有较大发展空间的工作的稀缺性不言自明,更多情况下这都是一个单选题。「主动把多数人艳羡的职业发展路径走窄」在当前的社会环境下需要很大的勇气。
  • 选择继续深入,代表着我认同在职场下工作仍然是当前阶段下最合理的选择,我将继续、甚至加大在工作方面的精力投入,换以在未来拥有更多可以选择的可能性。保守而言,这肯定是不坏的、更加合乎理性的,但仅靠这些好处,一直以来都很难说服自己内心对于自由的渴望。

偶然的机会

如此漫长的二择困境,曾一度让我决定放弃选择、保持现状、减少焦虑,以期望将工作时间以外的注意力重新交还给生活,回归到我所认为的有价值的地方。我清楚地明白在当前的环境与条件下,我实质上失去了主动做出改变的动力与勇气,因为不管是哪种选择,我都无法推演出其是否更加正确的结论。

而最终帮助我做出选择的还是一个偶然的外力:恰逢国庆假期得以时间休息之时,我收到了前同事的一个公司内部活水面试微信的内推机会,工作内容显然比当前的工作要更加具备价值,薪资待遇当然也会有所提升,但代价就是将要付出更多的时间与精力投入其中。

在短暂的犹豫后,我抱着先试试看的心情选择接受了这次面试机会。起码借助这股外力,我得以有了阶段性目标,能够快速完成半年都没完成的简历整理。「即便面试没有通过、或者最终我没有接受 offer,我仍然会有所收获,那么对此我便没有什么好计较的」——我如此想到。

或许是运气,或许是巧合,整个面试的流程下来都比想象中的要顺利许多。很快,我就得到了原本并不在考虑范围内的 offer。

选择的理由

许多人求而不得的 offer,我何有不接受的道理?然而,如果接受它,那么这代表着我将换一份工作内容、换一批同事,甚至换一座生活的城市,这对于我的现状而言是具备挑战性的。

在做出一个选择之前,我总是习惯于去找到一个能够让自己认可的理由,这样未来的我便不再会后悔当初的选择。而在面试的这段时间里,我偶然阅读了孟岩的一篇公众号文章,他将自己的一期播客的内容以文字的形式整理成了一篇长文,介绍了自己的一些生活态度。而在这其中,有两个观点让我记忆犹新:

  • 伟大是无法被计划的
  • 请尽可能地去增加人生的方差

在这个世界上,那些长期的、宏大的目标,往往需要一些不容易预见到的、非线性的过程才能达成。

我们不太可能预测所有的可能性和结果,也不太可能预测到中间的路径,特别是那些可能对我们的目标产生关键影响的事件。

无论是我们说 make things happen,或者说 gradually, then suddenly,其实都是在说这件事。

如果是这样的话,我们需要一些什么呢?

我想首先是可以允许放弃一些对未来的计划,

敢于允许自己去做一些自己真正感兴趣的事情,

敢于允许自己去做一些「方差大」的或者说从来没做过的事情,

保持开放和灵活,

对新的信息或者说信号保持敏感,

并且能够根据这些信号来进行调整。

当然,我们还需要时间。

在时间的流逝中,这些东西都会慢慢积累,会有那么一天,我们可以 connecting the dots,看到事物之间不那么明显的联系,能够从巨大的房间里面找到那些关键信息,能够看到那需要的关键一跃。

——孟岩 《让奇迹发生

关于孟岩对其观点的详细论述在其原文中已经非常全面,这里不再拾人牙慧进行复述。这份观点很好地缓解了我对于如何做出选择的困扰、对于未知的恐惧、以及对于失败的焦虑。

如果说伟大是无法被计划的,那么我们便不再需要保证我们的每一次选择都是绝对正确的。在事情的一开始,我们应该允许自己去做一些更加跳脱于日常的事情,去尝试增加人生的「方差」、拥有更多的可能性。即使某次选择看起来错了,最终,它也将会成为自己生命中的一部分,成为某种未来不可或缺的垫脚石。

于是我不再害怕变化,我相信这个选择最终会为我带来有别于现状的新的收获与体验。


日后谈

当然,跳出舒适圈是反人性的,这份「痛苦」对于转岗广州后的当下的我而言体验自然尤为深刻;我所做的事情也并不完全符合孟岩老师的全部观点,因为我违反了自己内心的期望、正让自己变得更加难以协调生活的比例,这实际上让我有些「后悔」这次的选择。

但我又暗自相信,随着适应期过去,我会重新变得不再后悔,未来的我能够在其中重新找到新的平衡,或着找到新的出路。

这只是我的一次增加「方差」的尝试。我时刻庆幸着未来的我仍旧有得选。

只要还有得选,人生就总不至于陷入被动之中。


封面:属于原部门的三台测试机,恢复出厂设置以准备退库

Web 前端 AI 抠图应用实践

2024-07-22 08:00:00

Web 前端 AI 抠图应用实践

信息时代下,图像抠图技术已经成为了一种不可或缺的技术,被广泛应用于视频直播、电商产品宣传、平面设计、背景替换等场景中。它通过将图像中的前景目标与背景软分割,实现了前景目标图像的独立提取,同时保留边缘的透明度。

电商场景

传统的抠图方法通常依赖于 PS 等图像处理软件,需要人工手动进行前景目标的抠取。人工抠图可以得到高质量的结果,但也需要花费相对应的精力。面对需要大量抠图的任务场景,人工抠图的成本变得不可忽略,所以抠图自动化具有很高的应用价值。

在计算机领域,图像抠图(Image matting)是一项基本的计算机视觉问题。通过计算机计算实现的智能抠图应用,让我们仅需输入图像,算法就能自动完成前景目标的抠取。与图像分割(Image segmentation)不同的是,图像分割本质上是一种分类任务,其结果像素点透明度只能取值 0 或 1,其分割边缘为硬边缘;而图像抠图则可看为是一种回归任务,其结果是某像素点为前景的 alpha 权重概率,所以抠图结果的边缘过渡较为自然,对于毛发等边缘的细节上的保留效果要好很多。

Web 前端 AI 图像分割:Segment Anything | Meta AI

SAM 图像分割例子?size=large


Web 前端 AI 图像抠图:Xenova/remove-background-web

MODNet 图像抠图例子?size=large

随着这些年的发展,基于深度学习的图像抠图算法准确性和效率大大提升,逐渐从传统抠图算法中脱颖而出,成为抠图应用的主流技术。

图像抠图算法简介

本文的重心在于抠图算法的实际应用,这部分仅作背景简单了解

目前流行的图像抠图算法大概可以分为两种:一种是基于先验信息的方法,另一种是无需先验信息的方法。

基于先验信息的方法

在图像抠图领域中,常见的先验信息可以是 Trimap(三元图,一种将图像分为前景、背景和未知区域的标注信息)、无人的背景图像、人物姿势信息、视频临近帧等。基于先验信息的模型网络使用先验信息与图片信息共同预测 alpha。2017 年由 Adobe 提出的 Deep Image Matting 模型,就是基于 Trimap 的首个将深度学习应用于图像抠图任务的算法,在当时取得了 State-of-the-Art 的水平。

Deep Image Matting 结构

Deep Image Matting 使用了经典的 Encoder-Decoder 结构作为基本框架:预训练的 VGG-16 网络作为 Encoder 部分,对输入图像进行特征提取;Decoder 部分由多个反卷积层和 ReLU 激活函数组成,这些层用于逐步将编码后的特征图恢复到原始图像的分辨率,得到较为粗略的前景蒙版(Alpha matte)。最后 Deep Image Matting 还使用了一个小型的卷积网络,用来提升前景蒙版的精度与边缘表现。

无需先验信息的方法

另一种无需先验信息的抠图算法,网络仅根据图片本身信息对 alpha 进行预测。虽然效果往往不如第一种,但因为对于实际应用更加友好,所以是目前比较主流的研究方向。

MODNet 是香港城市大学与商汤科技于 2020 年发表的无需绿幕的实时人像抠图网络。它很好的在不使用 trimap 的情况下获得了较高的精度,同时保证了实时性,能够应用于实时视频抠图。

MODNet 结构

MODNet 作者认为神经网络更佳适合学习单一任务,所以他将模型的学习训练分为三个部分:

  • 语义估计(Semantic Estimation):使用轻量级的 MobileNetV2 作为骨干网络,提取高层语义
  • 细节预测(Detail Prediction):结合了输入图像信息和语义分支的中间特征,对人像边缘进行单独地约束学习
  • 语义细节融合(Semantic-Detail Fusion):把语义输出和细节输出结果拼起来后得到最终的 alpha 结果

MODNet 设计的轻量级网络结构大大减少了抠图计算量,降低了抠图模型的使用门槛同时保留了较高的抠图精度,具有较高的实用性。


开发应用实践

尽管 AI 算法研究本身具有一定的门槛,但我们想要开发相应的应用则并不困难,因为网络上有着许多现成的预训练模型,也有现成的 SaaS 服务可供直接使用,在这些已有的能力上去做延伸,我们得以低门槛地创建简单的应用。

部署端对比

虽然使用 AI 技术自动抠图在业界的实际应用已经比较普遍,但是大部分的应用服务都是将模型部署于 GPU 服务器上,基于云 API 对内或对外提供服务。考虑到如果希望在内部场景提供相关的抠图能力,则属于低频功能,单独部署一台服务器将导致其绝大部分时间都处于空置浪费状态。

随着 AI 技术的进步以及用户设备性能的提升,此类 AI 抠图应用已经可以实现在前端内部进行闭环,使用纯前端技术来实现,在较低的性能需求的情况下可以得到相对较好的抠图结果,同时无需额外的 GPU 服务器开销或使用第三方云 API 的费用开销。

类别 优点 缺点
前端 前端内部闭环,处理图片时无前后端通信与传输开销;AI 端侧部署是未来主流的发展趋势之一,充分利用性能越来越好的用户设备 首次使用需要加载模型文件;性能受限于用户设备,不宜使用过于复杂的大模型
后台 性能无瓶颈,可以使用复杂模型 需要有日常的服务器租用开销,服务器性能与费用之间需要进行取舍
司内/司外现成云 API 已经提供好基础能力可直接使用 受限于第三方,不够灵活,不一定会持续维护更新;司外接口涉及隐私问题;每次使用都会产生费用

接下来我们都以前端部署 AI 应用为背景进行讨论。

开源方案

简单搜罗了网络上开源的抠图相关方案,做了一些简单的试用对比,得到对比表格如下:

名称 优点 缺点 门槛 采用
MODNet 模型体积小(25.9MB)、推理速度快、效果总体良好、有封装好的预训练模型 边缘比较软,会带一点背景色,需要看情况另外优化
RMBG-1.4 效果总体良好、边缘清晰,不仅限于人像抠图、有封装好的预训练模型 模型体积大(176MB)
modern-rembg 兼容 rembg 的前端版本,支持多种现成预训练模型(u2net、isnet-anime等),可选项多 各个模型各有优劣,不够通用 ⭕️
tfjs-models/body-segmentation 模型体积小(20MB以内) 仅提供了最基础的底层能力,需要自己封装并大量优化,否则效果不佳 ⭕️
RobustVideoMatting (未试用) 特供于 Web 实时渲染的预训练模型精度很低,需要拿其他的模型进行转换;仅提供了基础底层能力,需要自己封装优化 ⭕️
imgly/background-removal-js 功能完善,开箱即用;不仅限于人像抠图 模型体积大,推理速度很慢,抠图效果一般

transformers.js 是一个和深度学习相关的 JavaScript 库,基于 onnxruntime-web 的能力在浏览器上运行模型。该库提供了简洁的 API 和丰富的预训练模型,使前端开发者能够轻松地在浏览器中直接使用和运行预训练好的深度学习模型。

综合开发成本以及时间考虑,我们选择使用能够支持 transformers.js 的 MODNetRMBG-1.4 两个抠图模型进行简单的实践。

使用 WebGPU 加速模型推理

我们都知道跑在浏览器的 JS 代码,在性能方面客观上难以和由静态语言编写、直接运行在系统进程里的程序相媲美。特别是在 GPU 的使用上,浏览器在过去通常只能通过 WebGL 实现。因为 WebGL 主要设计用于渲染图形而非通用计算,且性能上受到了硬件以及浏览器的限制,所以并不擅长计算密集型的深度学习任务。

好在,2023 年 5 月份 Chrome 在 113 版本中正式发布了 WebGPU API,使得我们能够在浏览器中使用更简洁易用的 API、更强的计算能力去执行图形和计算任务。

现在(2024 年)WebGPU 还是属于一项实验性技术,只在最新的部分现代浏览器上有支持。但好在 PC 端的现代浏览器都会自动更新到最新版本,所以普及率的问题比移动端要好。

WebGPU 支持度

transformers.js 目前也正在适配 WebGPU,预计在 V3 版本中正式发布对 WebGPU 的支持。从这里可以看到 WebGPU 与 WASM 的实时性能对比。下图是用 MacBook Pro Apple M1 Pro 笔记本对于文本嵌入(Text embedding)任务跑出的结果,大概是 10.26x 的性能差距。

WebGPU Benchmark?size=medium

transformers.js 的 maintainer 也给出了一个 WebGPU 版本的前端 AI 抠图 Demo,可以从这里访问体验:Xenova/remove-background-webgpu

由于 V3 版本还没有预计正式 release 的时间点,目前想要通过 transformers.js 使用 WebGPU 能力尝鲜的话,需要直接从 github 源安装 v3 分支成依赖使用。(2024.08 更新:作者新增了@huggingface/transformers 包发布预览版,可以直接安装 @huggingface/transformers,不再需要处理从 github 下载依赖的未编译问题)

# npm install xenova/transformers.js#v3
npm install @huggingface/transformers

使用时,需要指定 device'webgpu',且 dtype 只支持 'fp32''fp16'(精度不同)。

import { AutoModel, AutoProcessor } from '@xenova/transformers';

// Load model and processor
const [model, processor] = await Promise.all([
  AutoModel.from_pretrained('Xenova/modnet', {
    device: 'webgpu',
    dtype: 'fp32', // or 'fp16'
  }),
  AutoProcessor.from_pretrained('Xenova/modnet'),
]);

如果需要兼容不支持 WebGPU 的浏览器,可以通过判断 navigator.gpu 是否存在进行区分。目前,在 TypeScript 项目中引用 navigator.gpu,需要引入 @webgpu/types 补充类型定义。

抠图 DEMO 实现与效果一览

这里简单写一个抠图 DEMO 获取输入图片的前景图像的 ImageData 对象,就能看出 transformers.js API 在使用上的方便程度:

import { AutoModel, AutoProcessor, RawImage } from '@xenova/transformers';

// Load model and processor
const [model, processor] = await Promise.all([
  AutoModel.from_pretrained('Xenova/modnet', {
    device: 'webgpu',
    dtype: 'fp32',
  }),
  AutoProcessor.from_pretrained('Xenova/modnet'),
]);

function demo(url: string) {
  // Load image from URL
  const image = await RawImage.fromURL(url);

  // Pre-process image
  const { pixel_values } = await processor(image);

  // Predict alpha matte
  const { output } = await model({ input: pixel_values });

  // Get output mask
  const mask = await RawImage.fromTensor(output[0].mul(255).to('uint8')).resize(image.width, image.height);

  // Composite the original image with the alpha matte
  const { width, height, data } = image.rgba();
  const imageData = new ImageData(new Uint8ClampedArray(data), width, height);
  for (let i = 0; i < width * height; i++) {
    const alpha = mask.data[i];
    imageData.data[i * 4 + 3] = alpha;
  }

  return imageData;
}

分别使用 MODNet 和 RMBG-1.4 两个模型进行测试,得到的输出结果对比如下:

输入图像(1024x683) MODNet 抠图结果(耗时 295ms) RMBG-1.4 抠图结果(645ms)
input image modnet result rmbg result
输入图像(1098x1098) MODNet 抠图结果(耗时 327ms) RMBG-1.4 抠图结果(652ms)
input image modnet result rmbg result

抠图结果放在透明白色底背景对比不明显,需要放在深色背景下对比

注意模型推理的耗时状况,可见在 WebGPU 的加持下,在 Web 端侧运行 AI 抠图模型是完全可行的。


与业务相结合

业务背景

在我司的道具打赏业务中,展示打赏爆灯特效需要使用到从媒资库拉取的明星头像图片,而从媒资库拉取的明星头像通常带有相对复杂的人物背景,不一定有纯人物的透明背景人像素材,不适合直接用于爆灯特效场景。

以往的常规做法是:由运营同学再找设计同学协助,针对这些非透明背景的明星头像重新抠图,使用新图干预配置结果,这导致设计和运营同学的工作量大大增加。尤其在跨年直播、星光大赏这种明星人数非常多的情况下,手动抠图并配置的工作量大、流程繁琐,出错的概率较高。

另外,手动抠图的质量也不总是十分完美的。如果为了追求效率或者要求标准不高,手动抠图的质量也会参差不齐。

所以我们希望应用 AI 技术实现人像一键抠图去除背景,减轻设计工作量,提升运营配置效率。在将这些技术应用到具体业务的过程中,我们也能与现有业务进行有机的结合,以更直接和贴切的方式解决业务的实际痛点。

项目设计

为了后续能够将同一套抠图能力尽可能简单地复用到多个场景的业务中去,我们将核心的抠图能力封装为一个基础 SDK,隐藏不同模型在使用上的差异,对外提供简易的 API:

// Create remover
const remover = createBackgroundRemoval('rmbg'); // or 'modnet'

// Model initialization load
await remover.load();

// Execute matting task
const result = await remover.run({
  input: image,
  output: {
    type: 'data-url',
  },
} satisfies RemovalOptions); // result is base64 image data URL

// Options definition
interface RemovalOptions {
  input: HTMLImageElement | ImageData | RawImage | Blob | URL | DataURL | string;
  output?: {
    type?: 'blob' | 'data-url' | 'image-data';
  };
}

为了实现更多拓展性的功能,我们在此基础 SDK 上,二次封装了一个带有弹窗 UI 组件的 SDK,提供通用的交互界面与常用的调整项,支持抠图结果预览、切换模型、微调参数、批量抠图等功能。

设计稿初稿参考:

弹窗 UI 组件设计稿初稿

使用示例:

removeBackground({
  input: pic, // pic 支持数组类型或者单个元素传入
  onConfirm: (value) => {
    console.debug('onConfirm', value); // 当 pic 为数组时,value也为数组;反之亦然
  },
});

参数的类型定义:

type ImageSource = HTMLImageElement | ImageData | RawImage | Blob | URL | DataURL | string;

interface PopupParams {
  /** 输入图像 */
  input?: ImageSource | ImageSource[];
  /** 输出图像的配置 */
  output?: {
    /** 输出图像类型 */
    type?: 'blob' | 'data-url' | 'image-data';
  };
  /** 抠图模型类型(默认 rmbg) */
  model?: 'rmbg' | 'modnet';
  /** 额外功能选项 */
  options?: {
    /** 上传图片的数量限制(不启用上传/单张/多张) */
    uploadType?: 'disabled' | 'single' | 'multiple';
    /** 是否支持用户手动选择抠图模型 */
    modelSelectable?: boolean;
  };
  /** 抠图完毕返回结果时回调 */
  onConfirm: (value: ResultType | ResultType[]) => void; // ResultType 取决于 output.type
  /** 失败回调 */
  onError?: (error: unknown) => void;
  /** 取消抠图关闭弹窗时的回调 */
  onCancel?: () => void;
  /** 弹窗关闭时的回调 */
  onClose?: () => void;
}

更多延伸

除了 transformers.js 的方案以外,还有许多其他的抠图模型与方案也都值得一试。往往不同的场景、不同的限制条件下,不同的模型能够取得更好的效果。

如果要准确的评价算法模型的抠图效果,还可以在网络上下载一些抠图数据集(例如 PPM-100),以量化的标准(均方误差 MSE平均差 MAD 等)衡量模型的表现。

HK 即兴一日游

2024-06-07 08:00:00

HK 即兴一日游

临时的想法

经过连续一个多月的高强度加班赶项目上线后,终于得以有时间稍微休息一下,于是请了两天工作日的假,准备处理先前一直搁置的事情。而在休息的过程中,我忽然想起办香港银行卡的事来。

原本先前是想着等「工作上的事情」尘埃落定了再去着手处理,但因为这个「工作上的事情」进度推进得实在太慢,导致我不得不开始重新思考事情的优先级。办卡作为一个「重要不紧急」的事情,虽然我当下还没有强需求,但是最近和同事聊天,提到「银行卡之类的并不是一办下来就让你随便用的,容易触发风控,最好先保持较长时间的正常使用」,于是我觉得还是得尽快找个时间搞下。

一开始想到这回事,就觉得正好可以趁着工作日人少,直接去把卡给办了。但是在研究攻略的过程中,发现得准备港币现钞、预约银行办卡业务、打印地址证明等等。研究到了这里已经是大晚上了,显然如果想要准备齐全,肯定是没法趁这次休假的工作日去办卡了。

但一想到这件事还要继续挂在心里半个月,又觉得实在是有些膈应。而且如果能趁这次机会直接办好,回来还有两天周末可以休息,可以说是非常舒服的安排了。

特种兵,出列!

于是我继续研究攻略到凌晨两点多,得出了这样一套结论:

  1. 港币不预约银行网点线下取钞了,可以去到香港后直接在香港的 ATM 刷国内银联卡取现(用招行卡取现手续费为 5‰,最低 10 元)。
  2. 只要去得够早,就算没有提前预约,有些分行也允许直接「walk-in」现场取号排队办理(最好是提前在银行 9 点上班之前到,否则要排很久)。
  3. 虽说正常情况下各种证明都需要纸质打印,但因为我是第二天清晨出发,自然没有地方能够打印。只能赌柜员能接受电子证明,或者运气更好,不需要证明。

以前,我的出行都会提前做好详细的规划,尽量避免预料之外的风险。但这样高成本的准备也导致做一件事情从规划到完成需要经过很长的时间。于是这次我告诉自己,偶尔也该尝试放下过多的顾虑,直接开始去做,而不是长久停留在计划中。如果一件事的最坏情况都在可以接受的范围内,那就没有什么好担心的了。

想到这里,即便我还没完全定好要去哪间分行办理、坐什么交通工具、哪些细节需要注意、办完后应该去哪里逛,也觉得这卡确实今天非办不可了。

根据早上八点半抵达香港的时间倒推,得到我应该六点半起床的时间安排。看了眼只剩下三个小时的特种兵睡眠时长,于是我便不敢再多花时间继续研究攻略了,简单收拾之后就很快上床睡觉。

定闹钟?size=small

闪击香港,办卡!

虽然深圳湾口岸离我住的地方更近,但在睡前确定起床时间时,发现深圳已有通往香港的高铁线路,仅需 14 分钟便可从福田抵达西九龙,且工作日早晨的座位充足无需抢票,于是便欣然确定选择到西九龙附近进行办理。

这次我想办的有三家:中银香港、汇丰、ZA BANK(众安)。

  • 中银香港支持与境内中行卡 0 手续费互转,出入金比较方便实惠;
  • 汇丰 One 门槛要求低,基本上能到香港的就能办理;
  • ZA BANK 作为虚拟银行,支持直接线上申请,可以在香港赶路等待的过程中顺手就在手机上办好,作为补充备用,顺便薅点羊毛。

对于一般人,这样的配置应当就足够应对绝大部分需求了。

考虑到直接再西九龙高铁站附近的分行办理肯定会有很多人,所以我随便上了一条港铁。然后查到两站外的「奧運」站附近就有中银香港和汇丰的分行,又匆忙在该站下车。

吊塔

不过因为太久没来香港,而谷歌地图的方向定位又差劲(经常不显示用户的面朝方向),在找路上来回兜圈子花费了不少时间。待到 9 点 20 分赶到一家在商场里的中银香港分行取号时,已经要排队等七八个号了。

中银香港

取号前,大堂经理会先简单询问开户目的、确认相关证件是否准备齐全,然后让你先扫个二维码填写一些信息。填完后在商场内部转悠了半个多小时,见排队进度几乎不变,于是决定折返回港铁附近的汇丰银行取号。

汇丰的排队人数相对就少上一些,不过也等了近 40 分钟才轮到我。仅需身份证、港澳通行证、通关小票,礼貌的柜员小哥一通丝滑的操作,借记卡直接当场给到,而信用卡则需要晚些审核通过(卡内入金一万港币以上)后邮寄,便愉快地完成办理。

当场下卡

拿到卡后,这次办卡之旅就有了保底。不过赶回中银却发现自己已经过号 10 个号了,无奈只能重新取号。

在等待的过程中,我发现每个人办理的花费时间虽然不定,但基本都很长,所以可能排队号 1 个小时都不带变;但是一旦有办理完的就会开始很快地轮过叫下一号,这就导致如果号排前面的人不在现场,就很容易一波轮过去好多个号,也就没法通过当前的排队人数很好地估计啥时候能够轮到自己。

大堂经理也是一位很有心的阿叔,记得我取的号码,看到我反复路过银行门口查看一直没变的排号,就推荐我先去解决午餐。

脆鸡巨无霸+薯条+港式奶茶+麦旋风

香港现在实行限塑令导致麦当劳也不提供一次性手套,大家都直接上手拿着汉堡吃

饭后一边继续逛商场、一边偶尔回来瞄一眼叫号进度。逛到一家「千葉玩具屋」(后面发现是连锁店),挑了两个惠惠的谷子(Goods,即周边),基本是按原价 1.5 倍以上在卖,不过当作小纪念品的话就倒还可以接受了(-78 HKD)。

二次元の新型旅游纪念品

等待排号的过程中也是顺便完成了 ZA BANK 的开卡申请,并规划接下来的逛街路线,所以时间基本也很充实,没有太多浪费。

值得一提的是,ZA BANK 需要扫描港澳通行证背面的签注信息(带二维码),而我签的那个自助签证机印刷不太好,出来的内容有些错位,上传直接没识别成功,还好再我多传了两遍后也给通过了。

PS: ZA BANK 办卡最近一直都有返现活动,注册时如果没有其他邀请码,可以填我的邀请码「GCGDA9」

中银香港的卡办起来要比汇丰麻烦一点,柜员上来就会问开卡目的,这里不能说错话,否则容易被拒。声明是投资的话,需要出示对应的股票投资记录,我给看了富途 APP 里的持股信息,柜员还会要求让你证明这个号的实名信息是你的(我是在 APP 的客服中心里找到开户资料的入口,柜员认可了)。

而关于地址证明的问题,因为我填的是身份证上的地址,柜员专门说明了一下这种情况他们会以身份证作为地址证明,这样就不需要再提供额外的地址证明了(信用卡账单/水电费账单之类)。

然后就是较长时间的资料填写过程,最终也是顺利通过,卡后续以平邮的方式寄到上面所填写的地址。平邮没法查邮寄进度,上面也没写联系电话,只会送到地址对应的邮箱中,所以也有可能会丢失。如果迟迟没有收到,就得联系客服重新改寄挂号信了(后记:我大概是 20 天后成功收到信封)。

打卡式扫街,但是阿宅版本

走出银行时,已经下午三点了。

来都来了,正好查到旺角这一块有不少动漫爱好相关的店铺,于是决定顺着弥敦道一路北上,开始扫街之旅。

我参考的是随便刷到的小红书攻略,最终按照「現時点 - 信和中心 - T.O.P Shopping Mall - MOKO - Moviemarks - 玩具模型倉」的顺序,一直逛到手机电量告急才作罢。

小红书路线图?size=small


現時点的感觉类似于深圳的东门动漫城,比较挤比较杂,有很多小店铺贩卖各种周边(手办居多),在过道边上的格子柜看着应该有好价的中古手办可以淘一淘。

当时人太多了不好意思拍照,借张网图吧 😢

hong-kong-fan-club-photo?size=large


信和中心我只逛了 B1 层,除了亚文化爱好周边以外,有很多最新的 CD 专辑、画集、大尺度写真集可以买到,就是画集卖的确实是贵(翻倍卖)。总体而言还挺推荐逛的,而且我还错过了第二层,应该会有挺多漫画店。下次路过的话我还想再去看看。

好多专辑

贴了 R18(?)的 Megami MAGAZINE

T.O.P 则是个很多层的小商场,聚集了各种年轻人喜欢的亚文化爱好店,而且明显感觉到来逛的女生比前面去的那几个点要多。

T.O.P 门口

T.O.P 的次顶层有 BANDAI 的扭蛋店(价格翻倍卖),还有家「らしんばん」(罗针盘)。在扭蛋店里绕了两圈,看到有物语的机子,手痒扭了一发,结果正好抽到了最想要的小忍!

扭蛋机?size=medium

忍野忍一发入魂?size=medium

尽管是用 30 HKD 换个「时尚小垃圾」,但那一刻也会忍不住地开心。(笑)

在罗针盘可以淘一淘中古谷子,店的规模不是很大,虽然价格肯定比不了日本,但胜在比去日本方便。偶然发现在一处角落有两框同人本,兴冲冲地翻找起来,结果除了表面的两本以外其他都是女性向,~~而且没有 R18 本,~~失望地走出店铺。

罗针盘旺角T.O.P店


因为出发前一天只睡了三个小时,一天逛下来已经开始有点低血糖了,于是在 T.O.P 商场的负一层简单吃了顿咖喱饭(-60 HKD)。

还不错的咖喱饭


饭后,继续前往 MOKO。MOKO 则是一个比较常规的商场了,有几间二次元相关的店铺,不过收获不大(可能是我没怎么逛到)。这里也有一家罗针盘,但平均价格比前一家还要贵,不太推荐专门跑过来逛。

题外话,走出 MOKO 商场负一层进入到地下通道的一瞬间,让我幻视「化物语」里姬丝秀忒的一个场景镜头

MOKO地下通道

化物语地下通道的姬丝秀忒


本来还想出发逛一逛深水埗,但到了这里充电宝已经没电了,为了避免失联,于是准备行程告一段落,坐巴士返回深圳湾口岸。不过在前往巴士站点的过程中意外发现会路过 Moviemarks,便决定来都来了就顺路逛逛吧。

Moviemarks 是开在路边的一家店铺,里面有着非常全的电影海报收集,在这里可以买到各种有在香港上映的电影所对应的宣传海报,还有一些电影特典、周边之类,对于电影收藏爱好者而言可以算是宝藏店铺了。店里也有一块日本动画电影作品相关的区域,光是坐在店里随便翻翻集册、感受店铺的装潢氛围,就感觉挺治愈的。

柜子


离开 Moviemarks,走到站点附近,意外发现站点处就在之前在攻略看到过的「玩具模型倉」店铺门口。虽然我目前还没有入模型坑,对其不太了解,但离巴士到站还有一点时间,于是简单进去逛了逛。

店内的货架摆满了各种拼装模型盒子,还贩卖各种拼装制作模型所需的工具(喷笔、油漆、钳子之类),以及搭了一些好卖的景品。听说这里的促销商品价格比在网上买还便宜,倘若是一名模型爱好者,想必是值得一逛的。

没拍照片,引用个官方 SNS 贴过的照片:

玩具模型倉店内照片?size=medium


坐上 58X 巴士,手机的电量已经不支持我再继续使用了,只好先关机,因此也差点坐过站。幸运的是,在屯门换乘的前往深圳湾口岸的 B3X 巴士每个座位都有 USB 充电接口,成功续上了 7% 的电量,顺利支撑到过关打车回家。

等车的站点

旅后感

对于住在深圳的人来说,显然,去香港办卡也不是什么很麻烦的事。做记录可以,「上价值」就感觉好像有点稍显夸张。但对我而言,它确实完全跳出了我休假前的计划,给「事事都被规划的日常」带来了超出这点小事本身的新鲜体验。

我想我总是热衷于得到各种新鲜的体验,而这也正是驱使着我在前一天凌晨忽然决定不做预约与详细规划就匆忙前往香港的重要理由之一。开卡这件事当然并不着急,但因为平日看到那些更随性、不怕生的人,用自己的方式热爱着自己的生活、充满激情,所以我也想简单尝试一下,仅此而已。

乘上与平时相反的电车

为了去看未曾见过的风景

乘上与平时相反的电车,为了去看未曾见过的风景——《比宇宙更远的地方》


在最近的一些日子里,我花了不少精力告诉自己应该追求内心的平静、而不是追求快乐本身,避免陷入过度享乐的空虚之中。而现在看来,除了维持内心的平静以外,还应该持续保有产生「激情」的可能性。

激情是生命力量的源泉,当内心对于正要去做的事情充满足够的激情,就会神奇地发觉平时所抵触的那些麻烦事,也可以持续保持以正向的情绪去完成。


终止


后注

开卡流程等相关信息具有时效性,请留意本文的发布时间。有相关计划建议再自行补充查阅更多攻略信息。

第一版个人主页的设计与实现

2024-04-10 08:00:00

注意:本文所记录的页面现已暂停使用,仅保留体验地址:https://v1.ceynri.cn/

关于本文所提到的页面对应的搭建心路历程,可以先行阅读《记第一次建站的心路历程》。

本文全文较长,故提供简要的目录说明内容结构:


技术要点

本站的实现对我而言是一次较为完整的学习与实践,对本站所用到技术相关内容进行简单的记录。

JavaScript

效果实现

  • 光标
  • 页面载入动画
  • 平滑滚动
  • 渐变滚动
  • 视差滚动
  • 视差浮动
  • 3D 透视
  • 进度条

TweenMax.js

TweenMax.js 提供了丰富的缓动动画效果,具有较高的性能与非常好的兼容性。本站在绝大部分元素的以下属性的变化上,都使用了 TweenMax.js 进行动画处理以求达到流畅的视觉效果体验:

  • 元素大小
  • 元素的空间属性
  • 颜色变化
  • 透明度改变

使用最多的是 TweenMax.to() 函数,其能接受 paused 参数将动画保存下来,然后对保存的函数调用 play()reverse() 方法实现动画的正向/逆向播放。

reverse() 虽然大大简化了代码,使代码更加漂亮,但也给我带来了不少的 bug(主要是动画相互冲突的 overwrite 问题)。

光标

光标代码是我写的最大的一个 js 文件,其大致流程是:

绑定页面内的光标元素组 -> 设置相关属性 -> 生成动画 -> 为不同元素的事件绑定相应的动画

为了设计光标的交互效果改了不少的版本,更麻烦的是鼠标这种掌握在用户手中的元素很容易产生许许多多的 bug,调教好它是我花费时间最久的一个部分。

光标结构?size=small

光标交互效果?size=small

页面载入动画

因为页面的内容比较重视整体,所以让用户看到未加载完成的页面一点一点并不连贯的加载并不美观,引入载入动画,让其在页面加载完成后再显示主题页面,在视觉上使加载页面的过程是连贯的(因为加载中时的页面内容是被遮挡住的)。

但坏处是如果用户访问页面时有文件请求被阻塞、或者网速过慢,使页面一直处于加载状态,可能会导致整个页面无法浏览,一直被载入动画的遮罩挡住。解决办法是超时时引导用户重新刷新页面、或者强制播放载入完成动画显示页面的已加载部分。

载入动画?size=small

滚动

滚动效果方面分为了平滑滚动、渐变滚动和视差滚动三个部分,分别负责不同的效果。

其中,平滑滚动的实现是将整个页面的所有内容作为一个元素包裹起来,并且不与 body 元素绑定,即浏览器窗口滚动时,并不会造成页面的滚动,从而屏蔽默认的滚动效果。再用代码实现平滑的滚动效果,让页面接收到滚动事件时执行相应的缓动动画。

渐变滚动指的是页面的第二部分——「about」部分,文字的渐入与渐出效果,通过监听当前文段在窗口坐标系下的坐标改变透明度实现。

渐变滚动&视差滚动?size=small

视差

视差滚动则对于特定的元素监听滚动的距离,将其乘以对应的倍率即可。简单的元素可以使用 CSS 的 perspective 属性实现,但是如果元素稍微复杂一点、超出了一层的父子关系,则使用 JavaScript 计算对应的 transform 值来实现更加合适。(效果见上面的「渐变滚动」)

视差浮动是对鼠标窗口坐标的监听,改变不同元素的空间坐标,增强元素的空间感(仅运用在第一部分(主标题)和背景中)。

视差浮动?size=small

3D 透视

3D 透视指的是 My Works 部分有关鼠标于 work 方块的交互。首先实现的是基本类,允许 work 方块根据鼠标与其的相对坐标进行三维空间上的旋转,同时有 z 轴方向上的位移,以在旋转中能够产生透视感;再写专门的类去对应不同的 work 方块,增加它们之间不同的交互特性,给用户更加新奇的交互感。

3D 透视-笔记卡片?size=small

3D 透视-喷气式背包飞行员卡片?size=small

进度条

进度条在这里承担了进度条的外观和滚动条的功能,所以在交互逻辑上需要在一般的滚动条基础上做改进。假设根据鼠标按下的位置划分为「在进度条已达到位置」和「进度条未达到位置」,则:

  • 「点击」(鼠标按下后无移动即抬起),会跳转到对应的页面位置,无论其点击的位置是进度条已到达位置还是未到达位置。
  • 「拖拽」则分两种情况:
    • 如果是尚未达到的进度位置,则先跳转至鼠标按下位置所对应的页面位置,然后再允许用户拖动进度条;
    • 如果按下的位置是进度条已达到的进度位置,则表现为滚动条的交互逻辑:不跳转页面位置,允许用户拖动当前的进度条以当前位置为起始点上下移动。

可拖动可点击的进度条?size=small

性能

流畅性

大量的交互动画往往会带来更大的性能问题,优化它们是非常必要的,毕竟流畅是网页浏览体验中最重要的体验之一,失去流畅感,即便再有意思也会让人觉得很可惜。

本网页陆续采用了几个角度对网页的流畅性进行了性能优化:

  • 优化代码结构
  • requestAnimationFrame
  • 调整EventListener
  • { passive: true }
  • 用低频动画代替高频动画
  • 直接砍掉产生性能瓶颈的脚本

优化代码结构

优化代码结构是最朴实的方法,这一点不论在什么阶段都应该注意它。大的方面可以讲代码复用、方法提取,小的地方可以是 DOM 元素引用保存、if 判断结构优化等等。优化代码是一个很泛的目的,通过不断地学习、阅读优秀的源码,我们可以学到更多的精彩的代码写法。

requestAnimationFrame

对于做 JS 动画,requestAnimationFrame 是一个很棒的 API。

setTimeout 相似的是,它们会被「定时」执行,但不同的地方在于:requestAnimationFrame 会在浏览器进行每一帧的渲染之前执行并完成。

而与各种 EventListener 相比,requestAnimationFrame 一般在 1s 内只会执行 60 次,即 60FPS。EventListener 则没有频率限制,如果执行太快,超过了屏幕刷新率,会产生无效的渲染和重绘,反而导致页面的性能下降,出现卡顿。

而且 requestAnimationFrame 从语义上告诉了浏览器我们要执行一个动画,浏览器会去优化它,还可以根据当前 CPU 负载情况调整帧数。

EventListener

减少不必要的 EventListener 也是有效的方法之一。

我们不应该滥用 EventListener,这会导致一个事件的发生会执行更多的代码,如果我们仅仅为了在不同的代码区域获取鼠标位置而反复添加 EventListener,会产生较大的代码冗余的情况。

另外,可以不在 EventListener 内执行的代码都应该提出来放在外边执行,尽可能精简一个 Event 触发所要执行的代码。

对于不同的 EventListener,我们也要根据触发频率来不同的对待。像鼠标移出事件 mouseleave、点击事件 click 往往只会执行一次,即便事件回调函数性能稍差也不会带来巨大的影响;而像页面滚动事件 scroll、鼠标移动事件 mousemove,它们是相对连续的,往往会在一个时间段反复被触发。如果事件回调的代码消耗性能较强,则直接会对页面的流畅性产生非常大的影响。

而且如果可以使用低频的事件实现相同的效果,就不要使用高频事件。例如:我们需要在鼠标移入某一区域时改变该区域的背景颜色,如果使用监听该区域的 mousemove 事件是同样可以实现这样的效果的,但它每一次鼠标移动都会触发它,而其实我们只需要mouseenter 事件一次执行该效果就可以了。类似地,我们还应分清诸如 mouseoutmouseleavemouseovermousemove 的区别,选用更合适的事件以提升代码性能。

最后,如果某一个事件只用触发一次之后就不会再使用到,最好在该事件的回调函数中的结尾处进行 removeEventListener,这既是为了性能考虑,也可以节省不必要的内存占用。

{ passive: true }

除了要更加注重这些高频事件的代码性能以外,我们还可以通过设置 EventListener 方法的第三个参数为 { passive: true },来更好地减少事件回调所要计算的代码。

{ passive: true } 的意思是让 EventListener 不再先阻止原来会发生的事件,即 preventDefault()

对于默认情况,浏览器是不知道 EventListener 所回调的事件中有没有包含 preventDefault(),所以会先阻止默认事件执行,然后等回调事件执行完毕后再执行默认事件;但是大部分时候,我们其实并不会阻止默认事件,比如页面滚动,我们可能只是想获取滚动的参数,而不是禁止页面滚动。所以 2015 年 DOM 规范新增了 addEventListener 的第三个参数可以接收一个对象的规定,通过设置 { passive: true } 来使 preventDefault() 失效,使默认事件变得流畅。

这种优化一般只用于触摸、滚动事件,其他事件没有必要使用

减少高频动画

「减少动画」与我们原本「增加动画」的目的其实是背道而驰的,这里体现的是「理想效果」与「视觉效果」的权衡。

本网页一开始增加了许多的动画效果,并在开发的时候暂时忽略了流畅性的要求,而在后期进行性能优化时,发现过多的高频动画本身(如只要鼠标移动,标题、背景图就会跟着反方向移动;页面滚动时,若干元素还会自己增加/减少自己的滚动距离)就是一个性能瓶颈。到了这种程度时,最好能够考虑优化它们的触发方式以减少它们的频率。如果仍然有性能问题,最后的方法就是舍弃它们,以保证用户的流畅体验,这也是在保证其他动画的视觉效果。

其他性能

提到「网页性能」可能新手(包括我)第一个想到的是网页「运行」的性能,即网页浏览、动作响应时的流畅性,其实除了这方面,页面的加载与渲染速度同样是非常重要的指标。如果一个网页加载或者渲染得太久,用户的烦躁程度会大幅上升。对于这方面,我们可以从以下角度进行努力:

  • 压缩代码、多文件合并
  • 启用 GZip 压缩
  • 压缩图片为 webp 格式、base64 转码内嵌
  • JS 代码异步加载/预加载(async、defer、prefetch)
  • 懒加载
  • 精简 DOM 元素个数
  • 缓存、CDN 优化

CSS

作为偏向于视觉设计的网页,CSS 相关的代码也不比 JavaScript 要少。不过更多的都是排版相关的基础问题,碰到了只要解决后总结好基本下次变得更加熟练。

  • SCSS 预处理器
  • 动画
    • transition / translate / scale / rotate / opactiy
    • animation / keyframe
    • 复杂动画交给 JavaScript
  • 性能
    • translate3d / will-change
  • var 变量
  • fix-blend-mode

SCSS 预处理器

在本项目中我第一次尝试 SCSS 语法来编写 CSS 代码,实际上非常容易上手,简单阅读完文档并做好完整的笔记后,编写 CSS 确实要方便许多。

动画

动画方面,就是 transition 渐变效果与 CSS3 新出的 animation 属性相配合,多加使用后简单的 CSS 代码也能写出很棒的动画,即便是在普通的网站中,使用它们也能够在小细节上给用户好感。

不过,复杂动画使用 CSS3 可能就不一定合适了,这主要看是否属于 CSS 的强项领域中,否则遇到兼容性等问题还是没有办法的事情,毕竟其不是编程语言,具有一定的限制。

性能优化

「黑科技」

如果遇到了性能问题,CSS 黑科技 translate3dwill-change 作为最后的手段说不定能帮你一忙。

在现代浏览器中,遇到 3D 变换会启用 GPU 进行硬件加速,而 translate3d 是非常具有代表性的 3D 变换,所以在一些 2D 动画较多的地方使用它代替 translate 有可能能够对性能有所帮助。

will-change 则是在 CSS 代码中提前告知浏览器,某些属性可能会发生动画效果,促使浏览器积极地调用硬件加速去优化它,但这个属性是最后的手段,并不应该随便使用。

减少高频动画

和 JS 一样的,触发频率过高的 CSS 动画(甚至只是通过伪类进行单纯的属性值改变)同样会引发性能问题,本站中鼠标与 42 个 ABOUT 文本元素原本都会产生 :hover 动画,但如果在页面滚动时鼠标不断触发该动画,在低端的设备上会容易产生卡顿问题。

点击 ABOUT?size=small

我的解决方法是将 :hover 的触发条件改为了 :active,这样鼠标移动+页面滚动就不会频繁触发该动画。

var 变量

CSS3 新引入的 var 变量也是一个不错的玩意,常用的是将颜色定义为 CSS3 变量,便于使用 JS 替换该变量值,达到一键更换主题配色的效果,无需到处更改各种元素的颜色属性值。

fix-blend-mode

页面的反色效果使用的是 fix-blend-mode: exclusion 实现的,这属性很酷,我很喜欢,点名表扬一下。

还有 filter 也是可以很酷的属性,但在本页面中没有什么使用(用了 drop-shadow),有视觉效果需求的话 filter 属性是非常值得了解的对象。


HTML

作为一门标记语言,HTML 是每个程序员的基本功了。

  • Emmet 语法
  • HTML5 语义化标签

Emmet 语法

Emmet 是一种提供自动补全的语法。举个简单的例子,我们可以输入 !,然后输入Tab键,即可自动补全以下内容:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body></body>
</html>

再比如:

table>.row>.elem+.elem

按下tab键,我们可以得到:

<table>
  <tr class="row">
    <td class="elem"></td>
    <td class="elem"></td>
  </tr>
</table>

如果对 Emmet 语法有兴趣,可以看看这篇我写的 Emmet 笔记

语义化标签

语义化是 HTML5 的重点之一,这部分我也是一边学习、一边通过别人的优秀代码中了解到不同的语义化标签,<section><header><nav> 等常用标签就不介绍了(HTML5 结构元素介绍 一文可以通过实例帮助区分这些标签的作用),我还了解到下面几个标签:

  • <main>
  • <time>
  • <figure>

挺有意思,这不仅是增加了机器可读性,这些语义化标签更会让我觉得自己的代码非常的漂亮(误)


移动端

移动端适配的要诀就是:删得越多,需要适配的东西就越少(x)

移动端适配

说到适配,基本都是围绕着「自适应布局」和「响应式页面」两个概念来进行,毕竟重新开发一个页面专门用于移动端的成本相对而言是比较高的。

自适应布局,一般而言是使用 Flex 弹性布局与 Grid 栅格布局来实现,它们在尺寸的变化上具有连贯性,可以随着页面的尺寸变化通过压缩空白间距、自动换行等方式发生连续的排版变化。但是自适应是有极限的,从 PC 端到手机端这种尺寸与比例的巨大变化,一般而言「响应式设计」更加切合该问题。

响应式界面借助的是 CSS 的媒体查询特性,对不同的媒体以及屏幕尺寸应用不同的 CSS 样式。这个功能很强大,很多适配都是这么做的,某些不要的元素可以 display: none 隐藏掉,又可以把某些只在手机端的元素 display 出来。缺点就是它是阶段性的、离散的,需要设置多个断点来适配多种不同的尺寸。

所以这两个技术相辅相成,才能实现较好的单份页面多终端适配。

光有排版适配是不行的,还需要调整元素与文本的尺寸。常见的 rem 布局原本是个不错的主意,但在选择的时候我纠结地决定使用 vhvw 来实现,媒体查询时则用 em 来调整字体大小,只能说各有所长。因为这方面经验不足,我也不确定使用 rem 方案会不会有更好的适配效果。

补充:vh 单位在移动端的浏览器中存在问题。因为大部分移动端浏览器的标题栏是可以随着滚动的行为进行收起或者展开的,这导致了页面的一些遮挡。vh 是不变的值,但他会比我们期望的值还要大一点,很容易出现排版问题。建议如果需要在移动端浏览器中布局的话,尽量避免使用 vh,可以使用 js 获取并设置样式。(我已经被坑了)

移动端性能

坦白说,一开始适配都是用的 chrome 浏览器模拟手机端进行的,这导致我写了不少代码之后,等到网站上线服务器了,在手机上才发现我添加的动画与效果实在太多,导致移动端性能捉襟见肘。

于是乎只好包含泪水送走那些代码,在移动端禁用一些 js 脚本,甚至连 My Works 里我很喜欢的 CSS3 飞行员动画也要禁止了,页面访问才变得流畅起来。所以我只好在郁闷中开玩笑地总结道:「把发生性能问题的东西禁用掉就没有性能问题了。」

在移动端中,即使使用 transform 做动画也是非常消耗性能的,所以要尽量节约着使用


网站

还有一些零碎的配置,顺嘴提一下:

  • 资源
    • 网络字体 GoogleFonts
    • 开源图标 IconFonts
    • 图片挂载与压缩 sm.ms
  • 服务器
    • 阿里云
    • 域名
    • 备案
    • SSL
  • google analytics

字体

一开始是从服务器中获取的,结果中文字体动辄 10MB 的大小严重影响了页面的加载访问,且单单使用英文字体也要考虑版权问题。

没经验的我一开始是打算尽量猜测用户电脑自带的字体种类,按在我页面中的实际显示效果排个序作为我的 font-family。后来想起伟大的 GoogleFonts,非常简单易用地挑选到了我想要的字体,而且不用担心版权问题,点赞 👍

后续:GoogleFonts 虽说在中国也有了 CDN 站点加速,但是我发现有些时候获取字体资源的访问速度真的不算快,查了一下了解到 https://fonts.loli.net/ 可以反代理 GoogleFonts,免费公益速度又快,非常感人,好用 👍

图标

阿里的 IconFonts 也是类似的,提供海量的图标,甚至给我用作为光标样式,很好用 👍

图片

图片先压缩是基本操作,在损失较小的画质的情况下大幅加快加载速度是值得的。

不过在图片资源部署的问题上,我一开始出于偷懒,将图片放在了 sm.ms 上然后从网页中引用它。后来发现这好像是不被该图床所允许的,而且偶尔访问速度不够快,于是又移回了我的服务器中,起码我服务器这点流量而言对于获取一点图片资源还是足够稳定的。

服务器

厂商、域名

服务器相关的东西都是是阿里云服务一条龙,机子选用的阿里云的学生机,域名也就顺便在阿里上买了,还挺便宜,然后阿里有个帮忙备案的功能,10 天批了下来,挺方便的。

SSL

https 协议是 http + SSL,如果只使用 http 协议,浏览器会给用户提示此网页不安全,而 SSL 证书是需要获取的。一开始我以为所有 SSL 证书都是要收费的,瞟了一眼一年收费一千多元可把我吓到了,心想算了算了 http 协议凑合着用。

后来转念一想发现不少普通的个人网站也是有 https 加密访问的,总不能大家都那么有钱叭,于是一搜果然有免费 SSL 证书可以申请(为这些厂商点赞 👍)。

然后就在准备按着教程去搞 Let's Scrypt 的 SSL 证书时,又想到了我的服务器装了宝塔面板,这种事情说不定在宝塔面板上有一键认证,结果发现真的有,只需要一个邮箱即可(给宝塔面板也点个赞 👍)。

流量分析

之于网站流量分析用的是谷歌家的 Google Analytics,目前还没怎么使用,等有空了再摸摸看嘿嘿。



设计思路

这第二部分倒回来稍微介绍一下主观性很强的本站的设计思路。

Tips: 整体设计以效果的多样性为主要目的,实验意味较浓

文章配图不够齐全之处,望读者亲自访问网页进行体验

色彩

配色方案?size=small

  • 主题色 红色 #882233
  • 辅助色 浅白色 #cccccc ~ #ffffff
  • 点缀色 青色 #12e4fb

主题色

主题色是页面视觉占比最高的颜色,它由背景图所决定。由于第一版配色选用的背景图片为红色系,故简单配了一个#882233作为较暗的红色为主题色。

辅助色

辅助色是在页面中烘托主题色的颜色,它的视觉占比一般仅次于主题色,用于平衡单主题色带来的视觉疲劳。在这里选用浅白色是因为页面为暗色主题的原因,使用浅白色作为全局的文本颜色非常合适。为了避免颜色过于复杂,图形化的标题颜色与光标颜色也使用文本颜色以统一视觉效果。

点缀色

点缀色是相对主题色而言较为亮眼的高饱和度颜色,起到点睛的作用。在这里选用了与红色互补的青色,以加强视觉冲击效果,同时配合交互时发生的反色效果,可以起到呼应的作用。

反色效果?size=small

注:一开始原本并没有想好点缀色,直到某天刷手机忽然看到一个角色,当时就拍脑袋嚷嚷着青配红可以有 😂

FGO 清少纳言?size=small

样式

  • 暗色主题
  • 色块 / 扁平感
  • 阴影 / 光晕
  • 透明化
  • 反色

暗色主题

暗色主题算是 2019~2020 年的设计趋势之一。

以前我们只能在设计与编程类软件能看到暗色主题风格,这大多是出于暗色减少界面带来的创作注意力影响、提高对暗部细节的感知度。

而随着手机的流行,许多手机 app 也细心地推出「夜间模式」,这是出于用户在夜间浏览手机应用时能够减少屏幕过亮对眼睛造成的损害。

但「暗色主题」相比于「夜间模式」并不完全相同,因为「暗色主题」并没有便于夜间浏览的硬性要求,因此在主色为暗色的情况下仍然可以选择高明度的颜色作为配色,且配色思路与亮色主题并不相同,一般能够给出相比于亮色主题更酷更专业的感觉。

色块 / 扁平感

色块 / 扁平感是我对于内心某种设计要素比较模糊的称呼,大概是几年前「卡片化」、「扁平化」的类似体现。注重强调块状的单色区域,相比于颜色复杂的图案而言更加简洁干净。在实际实现中,采用的是「粗线条」、「粗体文字」、「单色简单几何图形」的方式来达到相关的理念。

hero 与 works

阴影 / 光晕

阴影这里指的对应的是代码中的 shadow 样式,它是亮色主题常用的划分区域的缓和手段。弥散投影非常能够提升亮色主题的区域的质感,他能弱化区域的边界感,提升卡片感。

但是阴影效果在暗色主题中往往并不明显,只能起到一点辅助作用(上面的右图)。而如果将 shadow 设置为亮色,则变成光晕效果,给人一种物体正在发光的感觉(上面的左图文字与粗线条)。

透明化

透明化是提升画面信息的一种手段,在该页面实现中,将除了标题区域以外的区域罩上了一层暗色透明蒙版,避免背景图片影响文字阅读的同时保留背景细节,提升画面的信息量。

但是,「透明化」实际上与前面提到的「扁平化」(减少元素的信息量)有较大的不相容性,所以理论上这两种要素应该有所取舍更为合适。

反色

反色也是提供视觉效果、保留页面信息的方法之一。因为元素之间常常会发生遮挡,被遮挡的元素会损失一定的信息内容,使用类似于透明的反色效果,能够保留信息的形状属性,但也会干扰信息的色彩属性。本站于光标样式上采用了反色技巧,因为光标是用户和页面交互的重要手段,操纵光标与不同的元素相作用会产生不同的视觉效果,提高用户的交互兴趣。

交互

  • 页面滚动(平滑/视差)
  • 视觉提示与反馈
  • 透视

页面滚动

页面滚动是浏览网页非常常见的事件,一般的桌面浏览器都不会提供平滑滚动的效果,这主要是为了提高页面滚动的响应速度,因为毕竟不是所有人都喜欢缓动的滚动效果。在本站中,因为内容信息的获取并不是最重要的,为了体现个性以及更好的适应动画的流畅感,故添加了一定的平滑滚动效果。

特殊的,对于底部版权信息的区域,做了一个「揭底」的效果,在页面的最末尾打破用户对于网页窗口的认知,让页面主体变成了类似于荧幕一样的东西,揭底之时才露出背后并未移动的白墙。

底部双层结构?size=small

视差滚动是近年来比较流行的页面交互效果之一,通过滚动时不同元素的滚动距离不同,让人感觉滚动距离更长的元素离我们更近,滚动距离更短的元素离我们更远,从而增加页面的空间感。

但是由于页面内容越来越多,性能较差的电脑与浏览器反而可能会产生卡顿,这点比较麻烦。

视觉提示与反馈

视觉提示与反馈是交互的重点。所谓交互,即需有用户的输入,也要有反馈这些输入的输出;或者通过输出提示,来引导用户的输入行为。本站中把这个重任交给了光标进行引导,通过样式的变化、大小放缩来进行相应的提示与反馈。

例子:

点击、拖拽与跟随?size=small

向下滚动的提示?size=small

透视

透视和视差类似,也是增加页面空间感的手段。近年来不同的设计领域都开始出现三维空间的要素,当然是因为三维效果跳脱于所处的二维空间,提供了多一个维度的视觉感受。本站将作品展示部分添加了相关的透视效果,通过鼠标与作品的相对位置对作品进行三维空间上的旋转,以凸显逼真的空间感。

3D 透视-笔记卡片?size=small

手法

  • 大字号
  • 大小对比
  • 重复
  • 暗示

大字号

大字号可以表现出不同于「需要为用户提供足量的信息的信息密集型网站」的气质,从视觉惯性上进行冲击,强调文字的存在性。在本站的实现中,还有着配合粗体标题字体来实现色块视觉效果的功能。

当然,如果使用不当,会给人一种变成老人机的大字模式的感觉,这方面英文作为排版设计常用字体非常有优势(单字母相比汉字更加符号化而非象形化),所以本站也使用了不少英文(装饰意义居多)。

大小对比

大小对比是营造视觉落差非常常见的手法。顾名思义,拒绝暧昧的相近关系,让大的更大,小的更小,突出大小的对比,强调大与小的尺寸属性。

重复

重复也是强调元素存在感的手法之一,装饰意味居多。在本站中有一串很长的 ABOUT,如果显示没有问题的话,可以看到它有空心和实心两种。实心的序列其实并不是随机的,而是满足质数的排列(作为一个彩蛋)。

暗示

暗示(hint)是对某特点与某效果之间的关系进行间接的绑定的手法,一般是用于大家普遍默认的关系。注意,非常浅显的图形标识或者文字标识当然不算暗示,信息量更小的特点被称为暗示更加合适。

例如看到包含单个词语或者图标的色块,这个特点会暗示我们这可能是一个按钮,而词语或者图标的具体意义才会告诉我们它的作用;又比如绿色常常用来暗示正确,红色往往被当作错误警告,这是颜色的暗示。

不过在本站中,我做的暗示不算是「大家普遍默认的关系」,而注重于统一整个页面的交互语言。

例如,光标与主题色的元素一定可以产生某种交互,触发由主题色渐变为点缀色的动画。

红-青色渐变?size=small

对于具有内外两层结构的光标,默认状态下内光标表示鼠标的实时坐标、外光标应用具有延迟的缓动动画来跟随内光标运动。当外光标和内光标同时表示为鼠标实时坐标位置时,其被暗示为按钮的一种,允许进行点击的操作。

这些非常识的暗示在传统意义上可能没有直接的价值,但是对于统一交互语言有着一定的帮助。用户一般不会注意到,但在潜意识中多少会产生一定的影响。