2026-08-02 17:04:01
我从小就比较自以为是,可能是作为儿子被宠的原因,也可能是别的原因,在我很久远的记忆中就发生过一些事情。大概是不到十岁的时候,我和我的好朋友发生过一起冲突,过程不记得了,我只记得他说我很傲慢。
后面到了初中时,我最讨厌的英语老师也在课堂上说我不知道在傲慢些什么,但我记得初中时我只是一个默默无闻的小透明,不知道她只是因为我的成绩不好觉得我傲慢不学习,还是从平时的表现中认为我傲慢,然后借着成绩不好说出来,总之这让我大受打击,从那以后我的英文一直很烂,直到大学毕业后才开始努力学习英文。
还有一件事情是我从小很爱看漫威和 DC,后面经历过国产电影烂片潮,所以从小就傲慢的认为国产电影是垃圾,虽然此时的我不具备什么电影品鉴能力,但只是说出国产电影是垃圾这种话就可以让我充满了优越感。
当然还有个更加严重的问题是喜欢好为人师,这会给身边的人带来痛苦,我自己也会痛苦,但这种根深蒂固的东西真的很难控制,很多时候当我听到别人说出愚蠢的言论时我会从内心深处产生一种冲动,我要态度坚决的怼回去,这种冲动难以压制,只能通过改变自己的认知来让自己不要出现这种冲动,我应该是从七八年前就开始努力控制这个事情,现在很大程度上解决了这个问题。
解决的过程很复杂,经历了很多阶段,因为我需要从本质上解决这个问题,从本质上消除自己的优越感,很难描述清楚。比如我逐渐认识到了人的复杂性、语言背后隐藏的逻辑、世界观的不同、我自己也是个傻逼、思维从 digital 到 analog 的转变等等。
え?好像跑题了,我想说的是谦逊,对我来说,承认自己的无知也不是很难,只要是我不熟悉的领域,我可以接受自己无知。对于自己熟悉的领域就很难了,这样的好处是会有很强的自驱力让自己学习,坏处是也会有很多痛苦的时刻,比如被别人指出自己的错误。
我最近愈发觉得,这种心态会阻止自己的进步,保持谦逊的心态可以让自己从他人身上学到很多东西,但如果觉得自己不可一世,那就会拒绝学习别人,只依靠自己的能力进步的效率大打折扣。
另外承认自己的“无能”更加重要,我尚且可以通过“这不是我熟悉的领域”来安慰自己,但我无法承认我的专注力、学习能力、智力比不了别人,承认这种事情真的很困难,但如果不承认这些实际上会造成很多不妙的结果。
比如我知道很多人完全靠着自学考到了日语 N1,并且我认为我也有这个能力,所以拒绝报线下培训班,也只靠着自己,但实际结果是已经一年了 N5 都没到,最近终于决定要去报个班,但发现没有合适的。
所以,保持谦逊,承认自己的无知和无能吧,放过自己才能更进一步。
The post 保持谦逊,承认自己的“无能” first appeared on 张可的博客.
2026-07-15 23:04:44
时至今日,LLM 的发展仍然深受 Scaling Laws 的影响,OpenAI、Anthropic 每隔一段时间都会发布更强大模型,AI 取代人类的声音越来越多,难道 LLM 会一直这样发展下去?人类应该处在何种地位?这些问题在今天变得尤为重要,LLM 的特殊之处在于它已经逐渐具备了复杂系统的特性,智慧从复杂中涌现,人类无法准确的预测它的上限。
一个具备人类智慧的 AI 天然带着迷人的魅力,我们可以畅想这如何改变人类的生活、推动社会的进步,谁不想拥有一个有正子脑的机器人或者 HAL 呢?
虽然我认为 LLM 已经在某些方面超越了人类并且短时间内仍会继续进化,但我同样也认为在很多方面人类都能保持绝对的优势,甚至按照目前 LLM 的架构可能永远也无法超越人类。
目前 LLM 的运行仍然是无状态的,它不会记住任何关于用户的信息,每次对话对它来说都是一个全新的开始,现有产品关于记忆方面的功能也都是工程上的优化。
比如跟 ChatGPT 聊天时虽然它能回忆起用户之前的对话内容以及用户的一些信息,但这不意味着模型记住了你,只是 ChatGPT 在将你的对话发给模型之前会检索跟本轮对话相关的内容,然后整理出一份最终的 prompt 交给模型。
或者在跟 CC/Codex 对话时,即使是在同一个 Thread 内对话,用户发送的每一条消息也仍然是独立的,只不过 Codex 会将这个 Thread 内的历史对话和本轮对话一起整理成一个最终的 prompt 交给模型。
当然模型很多时候自行检索互联网内容或者本地文件,但这也都是模型利用 tool call 来丰富最终的 prompt 从而提高回答的准确率。
模型是没有记忆的,每轮对话都是独立的,所以 Context Window(上下文窗口)就变得尤为重要。
实际上今天我们看到的许多 LLM 工程实践都是在弥补 context-window limitations 以及模型无状态(Stateless)的缺陷,比如 Memory、RAG、Context Engineering 等等。
Context Window 是指 LLM 单次输入可接受的最大 Token 数量,不同模型的 Context Window 都有所不同,token 计数方式也有区别,一般来说一个英文单词约等于 1.5 个 Token,一个汉字是一个 Token。GPT5.6 的 Context Window 现在长到了 353k tokens。
LLM 的 Stateless(无状态)是指模型本身是一个无状态系统,输入的 prompt 不会改变其内部状态,有点像纯函数,但模型也具备一些复杂系统的特征,简而言之就是模型并没有任何关于用户或者历史对话的信息,每次输入都是一个全新的开始。
LoRA(Low-Rank Adaptation)是一种参数微调技术,可以在不重新训练原有模型的基础上使其附加一些特定领域的知识、能力或者行为模式,本质上是新增了少量的参数,从而改变模型的行为。
理论上我们可以将代码仓库、知识文档、需求文档、业务文档等内容整理成训练数据并训练一个 LoRA 然后再交给模型,使模型具备项目的一些基本知识,从而更好的帮助我们解决问题。
不过实际情况是目前主流的模型提供商(比如OpenAI、Anthropic)都不支持 LoRA,未来会不会支持也很难说,目前这些并不是他们的重心。一些开源模型一般是支持的,但是需要自己部署,有一定的使用成本,而且开源模型的性能目前还达不到顶尖水准。
另外训练 LoRA 也是需要成本的,而且有点麻烦的是业务和代码是一直在变化的,所以 LoRA 需要持续更新和训练,LoRA 和模型也是强绑定的,这意味着换了模型就要重新训练新的 LoRA。
如果未来模型能力达到了天花板,开源模型的水平也追了上来,自部署模型很可能会成为很多公司的选择,而那个时候使用 LoRA 技术来解决上下文限制和模型无状态问题或许是个不错的办法。
在很多商业公司中,软件的代码质量和开发效率都持续处在漫长的拉锯战中,业务的快速迭代是主要目标,代码质量的价值要如何体现?
Uncle Bob 在整洁架构中说过代码质量的核心价值是降低未来的维护成本,这也是行业共识,但“未来的维护成本”难以被量化,放在 OKR 中也不够亮眼。
有意思的是,前段时间有篇论文将代码质量是否会影响 Coding Agents 作为课题进行了深入研究。得出的结果和人类几乎一致:无论代码质量如何,Agents 都可以准确的完成工作,但对于代码质量较差的仓库消耗的 Token 显著增加。因此降低维护成本这件事情即使放到 Agents 上也是完全成立的。
现在我们可以光明正大的说,重构是为了省钱了。
言归正传,一个大型代码仓库都包含着复杂的业务逻辑,代码之间有着千丝万缕的联系,历史抉择和未来规划隐藏在代码的细节和人类的大脑之中,所以对于代码仓库中的每个决策都应该尽可能慎重。
我在使用 Agents 做复杂任务时经常会遇到撑爆上下文的情况,很多情况下要解决任务A时发现需要先解决任务B,然后 Agents 就会像个 ADHD 一样开始忘记初心,然后随着上下文压缩彻底失控,即使用了 sub-agents 也不能解决所有的问题。因此我现在几乎只会在边界清晰的特定任务上使用 Agents。
这也意味着软件开发还是要人类参与的,人类要做的就是解决这些复杂性以及做出正确的选择。
正确的选择不仅包括历史问题,也包括未来规划,历史问题往往不会出现在文档中,可能表现只是一段奇怪的代码。未来的规划就更加虚无缥缈了,可能会有一些 roadmap,也可能只有产品经理才知道。
我目前写代码其实主要还是手工编程比较多,Agents 给我打辅助。
很多时候有个悖论是,如果我希望 Agents 完全按照要求实现某个逻辑,那么我就需要输入包含了全部逻辑的 prompt,那么这个 prompt 就会巨长,并且自然语言的信息密度低于代码,那我不如直接写代码。
但有一些情况我很喜欢用 Agents,比如具备固定模式的任务、复杂但目标清晰的函数、独立的小模块或者组件、完全不在乎质量的一次性代码、我不擅长的任务、简单的脚本等等。总的来说,这些都是不需要很多上下文的任务。
架构设计包含很多方面,不管是一个简单的页面还是一个业务模块都值得仔细斟酌,不同的业务、历史、现状、工期、规划都会对决策有所影响,想要让 Agents 搞清楚这些还是没这么容易的,反而是人类来做比较好。不过在设计阶段我也可能会和 GPT 聊天,主要目的还是查漏补缺,或者找出更好的方案。
另外对于客户端开发来说,UI/UX 的验证要复杂一点,目前还没有合适的工具帮助 Agents 验证效果,比如一些动画效果需要分析连续帧,也就意味着需要分析视频,如果要追求细节的话 Agents 就有点力不从心了。
人类的上下文管理还有一个优势是人类大脑具备时间参数,软件的演化过程本身也是很重要的信息,而 Agents 更像是从某个时间切片开始工作。而且大脑会持续内化所有的信息,但 LLM 在这方面是静态的。
ADR(Architecture Decision Record) 文档是指架构决策记录,用于记录某个架构或设计的决策原因和背景。
ADR 文档并不是为了 Agents 开发而出现的技术,但它可以很好的解决上面说的问题。如果能建立起一个完善的 ADR 文档系统确实能很好的帮助 Agents 开发出优质代码。
目前关于 ADR 文档并没有一个统一的标准,有些仓库会选择把所有的决策都放在一个 decisions.md 文件内,但我之前看过一篇博客的 ADR 实践感觉也挺不错。
我觉得 ADR 文档确实可以解决很多决策类问题,但是给一个老项目建设 ADR 系统也是很麻烦的,如果设计得当确实能让 Agents 工作的更出色。
现在 GPT 有着出色的上下文压缩能力,Claude Code 有着 1M 的上下文,未来可能仍然会继续增加,但我觉得这仍然不能解决根本问题。
但随着 Context Window 的堆积,真正与当前任务相关的信息占比会越来越低,干扰噪音越来越多,这些因素会导致 LLM 的推理更加困难。Transformer 的 self-attention 机制使 token 之间可以建立全局关联,但随着 Context Window 增大,需要处理的 Token 关系数量快速增长,在更大的 Attention 空间中定位与任务相关的信息会变得更加困难。
所以这是个机制问题,短时间内很难有什么有效的解决办法,而这个问题也是我们作为人类的优势,我们可以充分利用人类大脑的超长上下文和 LLM 的超绝推理一起完成更加出色的任务。
The post 人类如何利用超长上下文与 AI 协作 first appeared on 张可的博客.
2026-05-17 21:27:43
最近 2025 R3 彗星逐渐进入可以观测的范围,四月中下旬星等甚至可以达到 4 左右,环境好的话甚至可以肉眼观测到,不过拖尾亮度没这么高,需要用望远镜。
天文摄影至少需要考虑天气和光害这二者的因素,最终选择在一个晴朗的周末去安吉江南天池拍摄。
江南天池是暗夜保护区,博特尔光害等级4-5,在一个山顶上,海拔九百多米,天池旁边有个小山头,上面是一个天文台,长三角星空大会的举办地就是在这里。用 App 看了下当晚的高空云量也不多,基本上在10%以下,条件也很好。
景区自带一个酒店,有很多房间,其中大部分是在景区的外面,但是还有一栋楼是在景区内部,价格要贵一点,六百多一晚,不过住在里面就不需要购买景区门票了。里面的酒店房间观星条件更好,晚上没什么灯光,而且可以去天文台或者天池这些地方拍摄,视野更好和光污染更低,所以我们选择住在里面。
但后来证明住在里面也并不好,因为里面的房间东方正好是天池,大坝远高于酒店,而彗星就在东方,所以要去大坝上看才比较好,但真到了大坝上会发现大坝的东方有个水电站,灯火通明。。。
但总之最后还是拍到了,就出门在酒店门口,把智能望远镜放在车顶,只曝光了四分钟彗星的大拖尾就出现了,非常漂亮。

可以看到下半部分明显有些发红了,因为此时距离太阳出来只剩下不到一小时,R3 的高度角很低,所以能拍摄的时间窗口其实很短暂。

这张曝光时间短一点,位置也跑偏了,不过拍到了星链。
这两张曝光时间都在2-4分钟,很短,因为当时的位置不好,我打算换个地方去大坝上拍,结果到了大坝上天空更亮了,而且凌晨的大坝一个人都没有,我自己也很害怕,最后也没拍成。
前一天晚上还拍到了狮子座三胞胎,还是很漂亮的,曝光了25分钟左右。

还有木星和它的卫星:

江南天池虽然很适合天文摄影,但对于 R3 来说其实并不合适,因为 R3 高度角很低,而且出现的的位置正好会被其他的山挡住,我提前转了一圈也没找到合适的位置,不过天文台上可能会有地方,但我没上去,我很确信凌晨四点我不敢一个人爬到天文台上。
拍摄彗星的过程倒是很简单,首先选择合适的地点和天气,天气信息主要是看云量,云太多就没办法拍摄。地点主要是看光害,城市光害8级左右,但是江南天池这种地方可以到4-5级,条件就好多了。此外还要看具体的拍摄地点的情况,比如是否有路灯或者其他灯光干扰,是否会被建筑物或者山体遮挡,蚊子多不多等等。
拍摄设备我用的是 Seestar 50,只能天文望远镜,没有目镜,手机连接拍摄,设备体积很小很便携,也不需要其他设备。
话说很多年前我就对天文摄影很感兴趣,最终了解下来发现要购买的设备实在是太多了,比如改机单反、望远镜、赤道仪、三脚架、导星镜等等一大堆设备,买下来至少要几万块,最重要的是你至少得有辆车带着这些设备吧,然后就放弃了,最终只买了三脚架和相机随便拍怕。
但现在时代不同了,上面所有的设备的功能最终都可以用一台 Seestar 设备来实现,不仅便携而且功能强大,比我的电脑还要小,甚至包含了一定程度上的后期处理,虽然最终成像效果不一定比得了大口径望远镜,但是对于很多天体的拍摄已经够用了,毕竟价格比一台相机还要低。
Seestar 之所以能做到保持口径小的前提下拍摄深空天体,根本原因在于内置的叠加功能,比如我拍摄狮子座三胞胎时曝光了25分钟,实际上是连续单张十秒钟+机内叠加,内置的算法会自动处理叠加流程,所以最终成片效果非常棒。
当然如果有时间可以叠加的更久,那最终效果肯定也更出色,而且还有智能降噪可以移除噪点,提高画面质量,可以说非常方便了。
至于操作上也很简单,Seestar 会 host 一个Wifi,手机打开app后连接上就行了,在app里可以选择要拍摄的目标天体,然后seestar就会根据角度自动寻找天体,找到后自动开始拍摄。
不过这次拍摄也不算成功,主要是起的晚了点,时间窗口太短了,虽然日出时间是五点半,但是四点钟就已经有曙光出现了,我差不多比预计晚起床十分钟,然后就错过了最佳拍摄时间,后面曙光出现后变亮的速度非常快,争分夺秒,我还在犹豫拍摄位置时就彻底失去了机会,只剩下最开始尝试拍摄时的照片还不错了。
2026-04-15 23:39:57
这几年我逐渐发现自己有个比较奇怪的心理活动,而且最近我发现这种心理在很多人身上都会出现。
一个典型的表现是,当我跟一个群体中的某个人吵架之后我更希望获得这个群体中的其它人的认可,此时我会下意识的选择使用更友善的态度沟通,迫切的希望这个群体仍然是接纳我的,我仍然继续属于着这个群体。
不仅仅是吵架,也可能只是说了不合时宜的话,甚至是得到了超出群体的荣誉,都会下意识的出现这种情绪。
进一步说,当我做出任何偏离这个群体的事情时,我的潜意识似乎会产生一种警惕和焦虑,这种心理会使我想要重新回到这个群体内。
曾经在很长的一段时间内我很排斥这种莫名其妙的情感,我觉得一个理性人不应该被任何情感左右,但同时要做到不被影响又很困难,这种情感毫无理由的出现,深深的扎根在大脑之中,挥之不去。
以前这种矛盾经常会给我造成一些困扰,但好在我现在不会这么想了,不同之处在于,现在我的理性会给情感留出余地,这是作为人类无法抛开的一部分,既然这种情感无法避免,那就尽量用认识和控制它们。
另一方面,我认为我也应该接纳这些情感,抛弃归属感其实也没什么意义,我又不是什么冷酷的杀手🥷,既然和人在一起可以让我幸福,那我就幸福点不好吗?
当然更重要的是,归属感具备一定的生物学基础。有研究显示,急性社交隔离会诱发类似饥饿的社会渴求反应,因此这是我们无法抛弃的,就像我们没吃饭就会饿一样,饥饿感和归属感都是生理需求。
那么归属感是如何作为生理需求的一部分进而影响我们?
首先我们人类的大脑在进化的时候就是朝着群居生物的方向进化的,社交行为和大脑的奖励机制有关,也就是多巴胺也会参与到这个过程中,社会活动本身会影响多巴胺分泌,如果我们参与了一场愉快的社交活动,那么大脑可能会分泌多巴胺来奖励我们。反过来也一样,孤独的生活也会抑制多巴胺的分泌。
不仅如此,长期孤独/社会隔离与脑功能和脑结构改变、认知下降风险增加有关,甚至心脏病和免疫力下降也和缺乏社交有关。长期的孤独还会引发炎症,损害脑细胞并阻碍神经通讯。2015 年发表在《美国国家科学院院刊》上的一项研究表明,持续的孤独与炎症标志物升高有关。这些标志物会显著加重阿尔茨海默病。对记忆至关重要的海马体也会受到严重影响。长期孤独会导致海马体体积缩小,并常常导致记忆力减退。
说到这里,我突然想到以前在杭州工作的时候,我住的地方很偏僻,小区几乎是建在山里的,周围连个超市都没有,时间久了就会产生浓烈的孤独感,所以当时周末的时候我很喜欢去一个公园,公园里面全是打牌跳舞下棋的中老年人们,我只是坐在那里都会觉得好像在给我的人类模块充电。
还有些时候,比如下午自己在家睡觉睡到晚上,醒来后感觉全世界就剩下我一个人了,我就会选择立即出门去城市里骑行,这同样也是在给我的人类模块充电。
比较有意思的是,我曾经极度排斥自身的这些情感,倒也不是某种理性至上的想法,只是觉得我是一个自由人,这些情感是导致我不自由的枷锁,我成长的过程就是逐渐移出这些冗余的部分。但现在我开始学会控制这些情感,有时候可以享受这些情感带来的快乐,有时候也可以规避这些情感带来的负面影响。(所以人类自由意志中的一部分现在又有了生物学的解释了吗?)
想清楚这些后,很多事情似乎都得到了合理的解释,比如我生活在很多不同的社会圈子内,比如同事、技术社区、日常摸鱼社区、亲人、不同的朋友、某些爱好圈子等等,我至少需要得到其中某些圈子的认可才能保证我没有偏离这个社会。
社会认可本身也随着圈子的变化而变化,比如一个要想在学术氛围浓厚的圈子内得到认可需要有一些学术性强的内容来给你背书,那么你的学术模块的身份认可得到了满足,但这种认可同时也会带来社交压力,因为此时认可本身潜在的含义包含了对比,对比意味着压力。
还有一些人充当着社会中的心理缓冲区,他们不会根据你的成就和社会地位进行任何评价,而是近乎无条件的接纳你,那么与这些人相处就会很放松。
2026-02-03 10:35:15
最近使用 Codex 给 Fread 进行了一次大规模的重构工作,累计改动了一万多行代码,如果人工重构可能要持续一个多月的工作现在几天就完成了,现在回顾一下 Codex 工作的整体感受和一些使用技巧。

本次任务主要涉及两个方面,一是把依赖注入框架从原本的 kotlin-inject 改为 koin,二是项目中的导航框架用 navigation3 替换现有的 Voyager。
使用 koin 的原因是我个人更偏向于 DSL 的方式来管理依赖注入,这样的代码更清晰,理解成本更低。此外,因为依赖注入涉及到的类非常多,注解不仅对这些类具有侵入性,也会生成太多的辅助类,其中的代码无法被直观的看到,使得整体注入流程像一个黑盒。
导航框架选择 nav3 是因为 Voyager 已经一年多没更新了,基本属于放弃状态,而且 nav3 的设计非常优雅,对于预测性返回以及 Shared Element 支持的也很棒,根本没有理由不用它。
所以可想而知,这是个非常大的工作,基本上所有的页面都要改,所有依赖注入也都要改。当然最终主要靠着 Codex 帮我解决了这个问题,据不可靠统计,差不多有 70% 的代码都是由 Codex 完成,本文就介绍一下使用 Codex 重构的整个过程。
这是本次改动的 PR:https://github.com/0xZhangKe/Fread/pull/86
我目前觉得 AI Coding 最大的能力就是模仿,对于一个给定的模式,Codex 可以模仿的非常出色,即使在模仿的过程中偶尔出现一些例外情况 Codex 也可以随手解决。
所以本次重构我作为人类的主要任务实际上是找出所有不同类型的变更,然后将其分类处理,也就是说使用 kotlin-inject 依赖注入存在有限的几种使用方式,我针对这几种情况分别使用 koin 来替换重构,这样就存在了有限的特定场景下 kotlin-inject 与 koin 一一对应的情况,处理好这个问题后,我直接让 AI 分别模仿这几种情况下我写的重构代码,然后逐一修改。
这样做可以大大降低 Codex 面临的问题的复杂度,它只需要模仿我的代码,按部就班的解决剩下的问题即可,对于越复杂的问题 Codex 越容易出现错误,最开始我直接让 Codex 来重构依赖注入时它先跑了 1.5 小时,不仅耗光了好几轮的 Context,也耗光了五小时的用量,然后问题依然没有被解决,因为依赖关系实在是很复杂,它也几乎被绕晕了。

其次是这样的代码更加可控,因为至少我对于软件整体的理解比它要更深入,架构能力比它强,我知道正确的演进方向,所以我要发挥我的专长把复杂的问题先解决掉,剩下大量的重复性工作再交给它,咱们各司其职。
目前我使用 Codex 时,如果是一个比较大的任务,我基本上都会给它提供一个最佳实践的代码,让它参照最佳实践来工作。
因为 Fread 是一个跨平台的 KMP 项目,依赖注入不涉及通用代码层,也存在很多平台实现层,这些情况叠加在一起问题就变得更加复杂了。
很多时候特殊情况我们自己只需要几行代码或者很短的时间内就能解决,但是 AI 则需要考虑很长时间,并且对于它来说复杂度会成倍增加。
Codex 一方面对于 KMP 项目了解的不多,另一方面对于平台实现层如何正确处理也不能提供一个很好的解决方案。对于这种特殊情况我的解决办法仍然是逐步解决问题,先人工编写部分代码,然后交给 Codex 解决剩下的部分问题,然后人工继续编写部分代码,Codex 再继续解决剩下的问题。
具体而言,对于每个存在依赖注入的模块,我都会声明一个如下的 expect 函数,并且在注册到当前的 koin 模块中。
expect fun Module.createPlatformModule()
val commonModule = module {
createPlatformModule()
}
然后再 Android/iOS 平台创建具体的实现:
actual fun Module.createPlatformModule() {
}
我先做完这一步,然后让 Codex 把所有模块都添加一个这样的改动。
上面的步骤完成后,就需要往这些平台实现的依赖注入模块内注册原有的 kotlin-inject 类。这样问题就简单多了,我只需要指导 Codex 把 kotlin-inject 模块中的声明同步到这个 koin 的平台级的模块内即可,这样的事情 Codex 可以完成的很出色。
然后重复上述步骤,完成所有模块的相关重构。
由于 Codex 的上下文有限,解决一些复杂的任务时很容易会逐渐丧失初心,甚至给代码做一些奇怪的改动。
拆分任务的逻辑是对于复杂的任务,架构相关的工作仍然是交给人类完成,这部分工作完成后就可以继续拆分出独立且简单的子任务,这些交给 Codex 来完成,这样即使 Codex 犯错影响也不会很大。回滚代码时也更省 Token。
AI 生成的代码我个人习惯是一定会全部 review 之后再 accept,如果不做任务拆分那 review 的任务量就太大了,我自己的大脑怕是也难以承受。
对于用 nav3 替换 Voyager 这个任务改动的代码非常多,其中包含了大量的重复性工作,就算是完全让 AI 来模仿我写好的最佳实践但是由于代码太多,任务比较复杂 Codex 仍然有可能出错,这时候我们就可以通过创建 SKILL 来解决这个复杂的问题了。
---
name: screen2navkey
description: convert Voyager Screen to navigation 3
---
## 任务背景
目前项目中使用了 Voyager(cafe.adriel.voyager:voyager-navigator) 作为导航框架,现在我希望将 Voyager 替换成 navigation3(androidx.navigation3:navigation3-runtime).
## 任务内容
目前 nav3 我已经集成并且完成了部分代码的重构,现在你需要帮我做一件事情,将一些 Screen 替换成 一个 Composable 函数 + NavKey。
你的目的是把继承自 cafe.adriel.voyager.core.screen.Screen 或者 com.zhangke.fread.common.page.BaseScreen 的类改成 androix.navigaton3 的一个 NavKey + 对应的 Composable 函数。
比如现在有这样的一个 Screen:
class ProfileScreen : BaseScreen() {
@Composable
override fun Content() {
super.Content()
val viewModel = getViewModel<ProfileHomeViewModel>()
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colors.background),
) {
Text(text = "Profile Screen")
}
}
}
那么你需要改成如下方式,并且新增一个 NavKey:
object ProfileScreenKey: NavKey
@Composable
fun ProfileScreen(viewModel: ProfileHomeViewModel){
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colors.background),
) {
Text(text = "Profile Screen")
}
}
但如果这个页面有参数,那么 key 也应该带一个参数:
data class DetailScreenKey(val itemId: String) : NavKey
@Composable
fun DetailScreen(viewModel: DetailViewModel){
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colors.background),
) {
Text(text = "Detail Screen for item: $itemId")
}
}
然后你需要把这个新增的 NavKey 注册到当前模块的 NavEntryProvider 中,比如:
class ProfileNavEntryProvider : NavEntryProvider {
override fun EntryProviderScope<NavKey>.build() {
entry<ProfileScreenKey> {
ProfileScreen(koinViewModel())
}
entry<CreatePlanScreenNavKey> { key ->
// with parameters
CreatePlanScreen(koinViewModel { parametersOf(key.lexicon) })
}
}
override fun PolymorphicModuleBuilder<NavKey>.polymorph() {
subclass(ProfileScreenKey::class)
subclass(CreatePlanScreenNavKey::class)
}
}
## 工作流程
你需要 Follow 以下工作流程:
1. 首先找到给定模块中所有符合如下条件的 Screen:
a. 继承自 cafe.adriel.voyager.core.screen.Screen 或者 com.zhangke.fread.common.page.BaseScreen
b. 不包含任何嵌套 **Navigator**
2. 将这些符合条件的 Screen 列出并输出到控制台
3. 逐个重构这些 Screen
4. 对于每个 Screen,首先创建该 Screen 的 NavKey,比如给 ProfileScreen 创建一个 ProfileScreenNavKey.
5. 将 ProfileScreen 改为 @Composable 函数。
6. 对于使用了 navigationResult 的地方请保持不动,不要试图修改相关的代码,即使有编译报错也不用管,保留原样。
7. 将 ProfileScreenNavKey 以及这个 @Composable 函数 注册到该模块的 NavEntryProvider 中。
8. 找到这个 Screen 的相关引用,并将跳转处改为这个 Screen 的 NavKey
9. 结束这个 Screen 重构并进入下一个 Screen。
10. 直到所有重构完所有满足条件的 Screen。
## 绝对禁止
一下内容为绝对禁止修改的规则:
1. 对于已经修改完成的类请不要再改
2. 你只应该修改 Screen 和 navigation3 相关的代码,其他的代码不要改,即使你觉得有问题也不要改
3. 不要做任何超出我要求的事情
4. 遇到不属于上述情况的页面请直接忽略,不要自己想办法解决
5. 不要求改任何嵌套的 Navigator 页面,遇到嵌套的情况直接跳过
6. 不要修改任何已经使用 navigation3 的页面
7. 不要通过代码引用的方式找某个页面的引用并且试图修改其引用点
8. 不要修改任何超出要求的代码
在这个 SKILL 中我写了一些绝对禁止的行为,其实就是在规避特殊情况,也就是上面提到的特殊情况特殊处理,这样可以极大的降低任务复杂度。
如果我们定义了具体的工作流程,并且要求 Codex 必须遵守,那么 Codex 出错的可能性就会小很多。
我们也可以先让 AI 针对任务编写足够多的单元测试,并且使重构前的代码全部通过单测,然后进行大规模的重构后再次运行单元测试,以此保证软件稳定性。不过 Fread 本次重构涉及到了很多 UI 代码,单测比较麻烦就没做。
根据我们上面的步骤,每次一个小的任务完成后都可以创建一个提交,然后创建一个新的对话任务来 review 这个提交,创建新的 Threads 是因为要丢掉原本的 Context,作为一个全新的任务交给 AI,否则它可能为自己的问题自圆其说。
The post 使用 Codex 进行一次上万行代码的重构 first appeared on 张可的博客.
2025-11-20 13:39:39
2024 年真的发生了好多事情,我们搬到了属于自己的家、我过了三十岁生日以及我们有了一个孩子。宝宝是在冬天的出生的,临近元旦,赶上了龙年的最后一个月。
乐观的说,我决定生孩子的原因之一就是我很好奇我最终会培养出来一个什么样的孩子,我也很期待有一天能和孩子一起聊天,一起学习,陪伴他成长的过程应该会很快乐。费曼学习法的核心就是你要把你学到的知识向别人表达出来,那么教育孩子这件事情就是要把我的人生向孩子表达出来。
当然还有一点,我不确定,但我总觉得如果有一天我老了,随着激素水平的下降和身体日渐衰弱,我会逐渐感觉到世界的虚无,因为我一直是一个热爱生活热爱这个世界的人,但如果真的有那么一天,我感受到了这种虚无和空洞,我完全不知道要如何接受这一切。但孩子至少会是我和这个世界的一个连接,支撑着我继续活下去。
但是生孩子这件事情还能有什么更高尚的原因吗?我好像没想到,生孩子是自私的,因为做出生孩子决定的时候孩子并不存在,我们无法寻求他的意见,所以他无论如何没办法在主观上希望我把他生下来,既然有了这层因果关系存在,那么选择生孩子这件事情上就一定存在道德瑕疵。所以我也不会通过什么我给了你生命养育你长大之类的观点来给他加上一层原罪好让他以后孝敬我。
好在目前为止我们仍然没有要鸡娃的打算,我们也不会给娃的未来抱有某些功利的期望,不过养娃仍然是一件困难的事情,主要原因在于,一方面我们自己成长过程中已经习得了很多不好的习惯,包括性格缺陷种种,我们不希望在孩子面前继续表现出来,不然孩子也会自然的学到。另一方面来源于外部,东亚国家尤其是咱们这里,社会环境和学校教育也都存在相当多的问题,这些问题和我们的教育之间必然会产生矛盾,如何让孩子在这其中保持平衡我觉得应该很困难。
其实我们对孩子的期望非常简单,就是身体健康和精神健康。身体健康花钱解决,精神健康是我们作为家长要认真研究的事情,我觉得作为一个健全的人要学会正确的思考,要有同理心,要有一定的知识,其他的就按照他自己的意愿吧。
好在现在孩子才一岁,刚刚说的这些都是未来需要考虑的事情,但现阶段也有现阶段的难题。得益于移动互联网的发展,商业公司无时无刻不在考虑如何掠夺用户的注意力而不考虑后果,现在即使是三岁小孩也可以刷抖音和玩王者荣耀,这种产品对孩子的大脑是会产生物理上的损伤,以及为了应对婴幼儿没有产品可以抢夺注意力的缺口自然也有商家发明了各种声光电玩具,总之它们希望孩子的注意力越短越好。我不太能接受的是孩子会失去作为一个人类应有的思考能力,不管是知识的获取还是复杂的思考注意力都是前提,现在我们也尽可能培养孩子独立玩耍独立探索的能力,尽可能减少声光电玩具。
有了孩子之后人生会发生巨大的变化,即使已经过了快一年了我们还是没有习惯这样的生活。
首先这是我们身份的巨大转变,我们现在是绝对意义上的中年人了,一点也没办法再以年轻人自居了,社会不会像对待年轻人一样对待中年人,而且中年人有中年人的行事风格,有中年人的压力和焦虑,好像过了三十岁,有了孩子之后突然就变成了中年人,毕竟不得不考虑孩子带来的责任感。
当然随之而来的还有人生的重点,什么人生的意义个人价值自我实现在养孩子面前都得往后稍稍,我们深刻的明白养孩子意味着什么,因此我们得完全承担起这个重大的责任,不仅要保证孩子身体健康,还要保证精神的健康,还要尽可能的为他的未来考虑,这是一件没有上限的事情。
实际一点的变化就是个人时间消失殆尽,我差不多每天六到七点左右下班到家,然后到晚上十二点还有五个多小时,以前这个时间完全属于我自己,可以看两部电影,或者写点代码看点书。但有了孩子之后情况就不一样了,首先要带娃直到晚上八点多他睡觉,然后中间他可能会醒,我们就得去冲奶粉,不仅如此,还有一些孩子的家务活比如洗衣服洗奶瓶之类的。然后因为孩子醒得早,差不多早上六点多就醒了所以我就要在十点左右睡觉。所以晚上差不多只有一个多小时自己的时间了,还不够看个电影的。
前三个月孩子只能在小区里推着婴儿车转转,公园都不太能去,因为要睡觉喝奶换尿不湿。再稍微大一点的时候可以坐车了,然后我们才能去车程三小时内的地方玩玩,但仍然要带一大堆东西。现在快一岁了,我们终于可以带他爬山,我用个背带背在我胸前,他可以直接在我怀里睡觉或者看看风景,我俩这才算喘口气,不过这样出去玩也是很累,毕竟背着一个快二十斤重的宝宝爬山。
小婴儿对父母是非常依赖的,尤其是在前三个月妈妈几乎是寸步不离,直到现在晚上基本上也只能妈妈哄睡,偶尔可以让奶奶哄睡,我俩出去找点夜生活。好在现在周末白天基本上可以让奶奶带着我们俩在苏州玩玩,所以十全街观前街这种人挤人的地方我们也还是去了,附近的商场和公园也都去了很多次,算是苦中作乐。
好处的话也是有的,生孩子会带来一些全新的前所未有的体验,可能所有的家长都一样,真的完全没办法客观评价自家孩子的颜值,虽然我的孩子眼袋很大但我仍然觉得他是最可爱的宝宝,他的一举一动都太可爱了,是那种发自内心的无法控制的可爱,就算单纯的看着他坐在那里也会感觉虽然不知道为什么但就是很可爱。
然后也会多出来很多作为家长才会有的情感吧,孩子对父母的依赖也会让父母产生幸福和成就感,有时候我们俩想单独出去玩不带孩子但是刚到楼下就已经开始想他了,然后会觉得愧疚,不应该把他留在家里,但孩子也要学会长大,不得不面对这样的情况。
写到这里我突然想到,未来人类总会逐渐脱离碳基生物这个设定的吧?总会不需要怀孕这么久才分娩然后养十八年才成人吧?那时可能就有了生化人或者意识上传,人类实现了永生,那我们现在死的这波人岂不是太可惜了?眼看着马上就能永生结果死在了门口。