Rock Sun

Blog

关于技术、思考和生活的点滴


巨头撤退时的“反脆弱”:宜家关店潮下的斯多葛式生存法则

Rock Sun
巨头撤退时的“反脆弱”:宜家关店潮下的斯多葛式生存法则

宜家(IKEA)中国近日确认关闭7家门店。那个蓝黄相间、曾为无数人描绘“中产生活蓝图”的巨头,正在收缩它的防线。

这一消息在社交媒体上引发的震动,远超商业新闻本身。因为它触动了当下社会最敏感的神经:连这种级别的跨国巨头都在“断臂求生”,作为普通个体的我们,安全感究竟该安放何处?

与其陷入群体性的焦虑,不如让我们戴上斯多葛主义的眼镜,借由自由市场的逻辑,透过这次“撤退”看清生存的本质。

一、 市场没有慈悲,只有反馈

首先,我们需要用理性的手术刀剔除情绪。

在自由市场经济学中,宜家的关店并非“失败”,而是资源配置的纠错。当某些门店的边际收益低于边际成本,及时止损是企业最道德的商业决策。这正是市场机制在发挥作用——它冷酷地淘汰低效产能,迫使资本流向更高效的渠道(例如宜家正在加大的线上投入)。

我们不需要为巨头哭泣。 市场经济的活力恰恰源于这种残酷的优胜劣汰。然而,这种宏观的“合理性”,落在个体头上,往往就是一座大山。

这就是为什么我们需要引入斯多葛哲学的智慧。

二、 控制权二分法:你的焦虑源于“越界”

斯多葛派哲学家爱比克泰德(Epictetus)曾说:“有些事情由我们控制,有些事情不由我们控制。

当我们因为宜家关店、裁员潮或宏观经济下行而感到恐慌时,我们其实犯了一个根本性的错误:我们将幸福和安全感寄托在了哪怕一点点都不属于我们控制的事物上。

  • 不可控因素:跨国公司的战略调整、全球供应链的波动、房地产周期的兴衰。这些就像天气一样,你可以预测,但无法改变。
  • 可控因素:你的职业技能、你的储蓄率、你对消费欲望的控制、以及你面对坏消息时的反应。

在这个巨头撤退的时代,最大的风险不是失业,而是**“依附心态”**。如果你认为你的安全感来自于一家“大而不倒”的公司,或者是某种稳定的行业红利,那么你就像是把房子建在了沙滩上。

三、 自由市场的个体觉醒:做自己的CEO

如果说斯多葛主义教我们修筑内心的防线,那么自由市场理论则教我们如何进攻。

在劳动力市场中,你不是“员工”,你是一家出售服务的“微型企业”。你的雇主只是你的一个“大客户”。

当宜家因为市场变化而调整策略时,作为个体的你也必须拥有同样的灵活性。纳西姆·塔勒布提出的**“反脆弱”**(Antifragile)概念在这里至关重要:

  1. 去中心化你的收入:不要只依赖一个客户(雇主)。在自由市场中,多渠道的技能变现能力是生存的底气。
  2. 拥抱波动:温室里的花朵经不起风雨。那些在单一岗位上重复机械劳动的人,在市场震荡中最脆弱;而那些不断适应新技术、解决新问题的人,反而能从混乱中获益。

四、 行动指南:构建你的诺亚方舟

结合斯多葛智慧与市场逻辑,我们可以制定一套具体的行动方案,以此应对外部世界的崩塌:

  1. 实施“防御性悲观”(Premeditatio Malorum) 每天花5分钟想象最坏的情况:如果明天我也失去了工作,我该怎么办?这种斯多葛式的心理预演,能让你在危机真正来临时保持冷静,并促使你现在就开始准备备用方案。

  2. 积累“F* You Money”** 斯多葛主义推崇节制(Temperance)。在这个消费主义退潮的时代,削减不必要的欲望,提高储蓄率。存款不仅是钱,它是你随时可以说“不”的自由权,是你在这个动荡市场中的护城河。

  3. 投资于“便携式资产” 不要依恋平台的光环。投资那些如果你离开公司依然属于你的东西:你的知识、你的声誉、你的健康、你的思维模型。这些是没有任何人能夺走的私有产权。

结语:在废墟上跳舞

马可·奥勒留曾在战火纷飞的前线写下:“阻碍行动的因素会成为行动的动力,挡在路上的巨石会成为路的一部分。

宜家的关店、经济的周期,这些都是挡在路上的巨石。你可以选择在石头前哭泣,哀叹旧时光的逝去;也可以选择踩在石头上,以此为台阶,去构建一个更独立、更强韧、更清醒的自我。

这就是斯多葛主义者的自由市场生存之道:接受不可控的寒冬,但在自己的领地上,点燃永不熄灭的火。

为什么胖东来让人破防?因为它戳破了一个弥天大谎

Rock Sun
为什么胖东来让人破防?因为它戳破了一个弥天大谎

"员工不累,老板才能真正赚到钱"

这两天,胖东来又上热搜了。

8000块工资,双休,下午6点准时下班,一年还有140多天假期。

评论区炸了锅:

"这是人过的日子?"

"我们公司凭什么做不到?"

"资本家就是不想分钱!"

但我发现,几乎所有人都陷入了一个思维陷阱——

大家都以为,好待遇和高利润,是一道单选题。

要么老板多赚,员工受苦;要么员工舒服,公司倒闭。

可事实真的如此吗?

一、那个被集体遗忘的真相

让我先问你一个问题:

一个每天加班到凌晨、精神恍惚的员工,和一个按时下班、精力充沛的员工,谁的工作效率更高?

答案显而易见。

但奇怪的是,几乎所有老板都在用前者的方式"压榨效率"。

这就像你明知道手机充电应该用原装充电器,却非要用劣质快充——短期看起来"充得快",长期却把电池搞坏了。

丹麦的工作时长全球最短,每周平均33小时,但人均GDP却高达6万美元,生产效率位居世界前列。

德国人下午4点就下班了,却是全球制造业的标杆。

荷兰人一周工作29小时,人均GDP比中国高出6倍。

这说明什么?

说明"拼时长=拼效益"本身就是个伪命题。

真正创造财富的,不是996的苦劳,而是高效的制度设计。

二、胖东来的秘密:不是老板善良,是制度聪明

很多人说于东来"有觉悟""有良心"。

但我更愿意说:他是个极其精明的商人。

你看他的制度设计:

1. 用高薪筛选出最好的人

市场价5000,他给8000。

结果呢?

100个应聘者里,他能挑出最机灵、最勤快、最有眼力见的那一个。

这个人干的活,顶得上别家3个员工。

人力成本反而降低了。

有个珠宝柜台的员工,能肉眼判断顾客脖子适合戴多长的金链子——这种专业度,是靠时间"熬"不出来的,是靠待遇"筛"出来的。

2. 让员工自己管自己

当你月薪8000,周围同行只拿3000时,你会怎么做?

你会拼命维护这份工作。

有人摸鱼?你第一个不答应,因为他在砸你的饭碗。

于东来不需要管理层盯着,员工自己就把自己管得服服帖帖。

管理成本几乎为零。

3. 用口碑换来复购率

胖东来的蔬菜水果毛利率70%,你以为贵?

但顾客闭眼买,因为品质有保证。

周转快、损耗低、复购高,这才是真正的利润源泉。

其他超市呢?

便宜是便宜,但顾客要挑、要犹豫、要担心买到坏的,最后可能还选择不买。

看起来"省成本",实际上丢掉了最大的成本——信任成本。

三、为什么大厂学不会?因为他们根本不想学

有人说:"胖东来模式不适合大企业。"

真的吗?

Costco的启示

Costco是全球第三大零售商,员工平均年薪4.5万美元,远超同行。

结果呢?

员工流失率只有6%(行业平均60%),顾客续卡率90%以上。

2023年净利润63亿美元。

宜家的算盘

宜家给员工提供免费餐食、弹性工作、育儿假。

结果呢?

员工满意度极高,宜家成为全球最大的家具零售商,年营收超过400亿欧元。

好制度不是"做慈善",是"做投资"。

那为什么大厂学不会?

因为它们根本不想学。

上市公司要对股东负责,净利润要逐年增长,股价要节节攀升。

给员工多分1块钱,就是少给股东1块钱。

在这个逻辑里,员工永远是"成本",而不是"资产"。

四、那个更残酷的真相

很多人觉得,中国企业"卷"是因为竞争太激烈。

但我想说:恰恰相反。

真正的市场竞争,拼的是效率、是创新、是服务。

而现在的"卷",拼的是谁更能压榨员工、谁更敢违反劳动法。

这不是竞争,这是劣币驱逐良币。

你守法经营,给员工上五险一金、按时发工资?

对不起,你成本高,竞争不过那些不守法的。

你想提高员工待遇、改善工作环境?

对不起,投资人会说你"不够狼性",股东会质疑你的盈利能力。

于是,好的制度被淘汰了,坏的制度反而活了下来。

这就是为什么胖东来只能偏安一隅,而不能走向全国——

因为它一旦扩张,就会被拖入那个"劣币系统"。

五、我们到底在争论什么?

回到最开始的问题:

为什么大家看到胖东来会破防?

因为它证明了一件事:

好日子不是做不到,而是有人不让你过。

那些说"企业利润低""竞争太激烈""没办法给高薪"的老板们——

胖东来用107亿营收、5%净利润,养活了1万员工,让他们月薪8000、双休、准点下班。

宁德时代468亿净利润,分出220亿给股东,却让10万一线工人拿着几千块的工资。

这不是"能不能"的问题,是"愿不愿意"的问题。

更可怕的是,当大家习惯了996、习惯了加班文化、习惯了"能忍就忍"——

坏制度就变成了"常态",好制度反而成了"异类"。

六、那我们能做什么?

说实话,个体很难改变大环境。

但至少,我们可以做三件事:

1. 不要被洗脑

当老板跟你说"公司困难、大家共克时艰"时,

去查查他的财报,看看高管的年薪和股东的分红。

别让"奋斗"成为压榨的遮羞布。

2. 用脚投票

如果有机会,选择那些真正尊重员工的公司。

哪怕工资少一点,但至少你的健康、你的尊严、你的生活是完整的。

用你的选择,给好制度投票。

3. 传播真相

让更多人知道:

好制度不是乌托邦,它在丹麦、在德国、在Costco、在宜家,甚至在河南许昌的胖东来。

它真实存在,并且运转良好。

写在最后

胖东来为什么让那么多人破防?

不是因为它的工资高,而是因为它撕开了一个谎言:

"员工不能轻松,公司才能赚钱。"

事实是:

员工越轻松,公司越赚钱。

因为轻松的员工效率更高、创造力更强、忠诚度更高。

因为好的制度能筛选出最好的人、激发出最大的潜能、建立起最深的信任。

这不是理想主义,这是最精明的商业逻辑。

只是,

很多老板宁愿相信"压榨能带来利润",

也不愿意相信"尊重能创造财富"。

因为前者不需要智慧,只需要狠心。

而后者,需要格局。


你觉得呢?

你愿意为"好制度"投票吗?

欢迎在评论区聊聊你的看法。

—— END ——

如果你认同这篇文章,请转发给更多人。

让更多人知道:好日子不是奢求,是本该如此。

2025:基础设施即代码正当时

Rock Sun
2025:基础设施即代码正当时

前文提到 2025 年的一大收获是使用 Pulumi 实践了基础设施即代码。今天就来分享一下这一年的实践心得。

要有权限

实践基础设施即代码最重要的前提就是有足够的权限。年初时我加入一个新建的组织,因为这是一个全新的团队,没有沉重的历史包袱,我拥有足够的权限来自由探索和实验。

但写到现在,组织已经逐渐复杂化了。各种部门、规范、流程相继而来,问题也随之显现。有人开始提出各种个性化需求而不愿遵循规范,修改 Pulumi 代码显得繁琐;更麻烦的是,一些人开始绕过代码直接手工管理 AWS 环境。这样做的结果就是:并非所有人都愿意或有能力维护 Pulumi 代码,IaC 的价值也就逐步被侵蚀。

要足够快

表面上看只是一个参数修改,但在成熟的组织中,这背后往往涉及一系列的规范要求和流程审批。传统基础设施部门之所以能快速响应,其实是因为他们早已为常见任务制定了标准 SOP 并构建了自动化工具。

在我的情况下,规范相对简化,直接登录 AWS 看似最快。但既然选择了 Pulumi,用代码速度真的更快吗?在 2023 年时我还不确定,但到了 2025 年,答案很明确:写代码反而是最高效的方式。

没有 AI 编程工具的话,学习 Pulumi 将是一个漫长的过程。Pulumi 本身入门容易,但实际项目中往往涉及相互关联的多个资源,你需要既精通 API,还要理解这些 API 背后的 AWS 服务概念。这需要大量的时间投入才能掌握。

幸好已经是 2025 年了。从 VSCode Copilot、Roo Code、Windsurf,再到后来的 Trae、Kiro 和 Antigravity,AI 编程工具的进化令人印象深刻。年初时这些工具还显得有些不稳定,但经过一年的发展和大量 Vibe Coding 实践,我发现 Pulumi 代码已经变得相当易于编写了。

不过,有过 Vibe Coding 经验的开发者都知道,要让代码长期稳定可维护,绝不能只是堆砌代码,必须有良好的架构设计。下面就分享一些我的实践心得。

使用 S3 后端

Pulumi 必须有一个地方存储状态文件。对于大多数企业而言,Pulumi 官方云和本地文件都不太合适,使用 S3 是最佳选择。配置示例如下:

backend:
  url: s3://some-prod-pulumi/infra?region=ap-southeast-1&awssdk=v2&profile=prod

使用配置文件

最初我有点纠结:既然是基础设施即代码,为什么还要加配置文件呢?但在真正用 Pulumi 编写资源配置后,我意识到高级编程语言虽然强大,却不如 DSL 那样简洁直观。因此抽象和分层就变得必要。例如,我将 EC2 的各种配置参数整理成了 YAML 配置:

...
    instanceGroups:
      - namePrefix: bastion
        subnetId: private-subnet-1
        ami: ami-0b8607d2721c94a77
        volume_size: 30
        ext_volume_size: 50
        privateIps:
          - 10.62.21.110
        instanceType: t3.micro
      - namePrefix: vpn
        subnetId: public-subnet-1
        ami: ami-08b138b7cf65145b1
        instanceType: t3.small
        volume_size: 30
        ext_volume_size: 20
        privateIps:
          - 10.62.21.21
...

此时,配置文件负责定义各个 EC2 实例的差异,而代码中实现所有的共性逻辑和规范。职责分离清楚,更易维护。

模块化设计

起初需求很分散,我索性把所有代码都写在了 __main__.py 中。一是因为还没有分模块的必要,二是懒得折腾。

后来出现了一个新需求:要把某个应用相关的基础设施代码独立出来,使其能在客户环境中一键部署。考虑到现有代码还会持续演变,我不想维护两套代码,所以决定重新组织代码结构。既要支持在现有环境中运行,也要能独立部署。

这时高级编程语言的优势就显现出来了。我轻松地将代码拆分成多个 Python 模块,放在不同的文件中,逻辑更清晰,复用性也更高。

显式管理依赖关系

虽然这条看起来理所当然,但还是值得强调一下。初期我没有意识到需要显式定义依赖,但 Pulumi 其实已经帮我们做了很多。当资源使用另一个资源的输出时,依赖关系是隐含的。

不过当多个资源之间没有明确的变量引用,但却有逻辑依赖关系时,就必须显式声明。比如 EKS 的 Storage Class 依赖 CSI Addon,就需要这样指定:

    efs_storage_class = k8s.storage.v1.StorageClass(f"{prefix}-efs-sc",
        metadata={
            "name": "efs-sc"  # 这是将在 PVC 中引用的 StorageClass 名称
        },
        provisioner="efs.csi.aws.com",
        parameters={
            "provisioningMode": "efs-ap",
            "fileSystemId": efs_file_system.id, # 引用上面创建的 EFS 文件系统 ID
            "directoryPerms": "700",
        },
        opts=pulumi.ResourceOptions(provider=k8s_provider, depends_on=[efs_csi_addon])
    )

忽略无关差异

Pulumi 在更新基础设施前会比对自身记录的状态和当前代码执行的结果,只对差异部分进行更新。这在大多数情况下运行良好,但有些差异其实无关紧要,不需要更新。此时就需要用 transformations 方法处理。aws-load-balancer-controller 就是典型例子:

...
        opts=pulumi.ResourceOptions(
            provider=k8s_provider, 
            depends_on=[nodegroup_a, nodegroup_b],
            transformations=[ignore_changes],
        )

该方法定义为:

    def ignore_changes(args: ResourceTransformationArgs):
        if args.type_ == "kubernetes:admissionregistration.k8s.io/v1:ValidatingWebhookConfiguration" or args.type_ == "kubernetes:admissionregistration.k8s.io/v1:MutatingWebhookConfiguration":
            return ResourceTransformationResult(
                props=args.props,
                opts=ResourceOptions.merge(args.opts, ResourceOptions(
                    ignore_changes=[
                        "metadata.annotations.template",
                        "webhooks[*].clientConfig",
                    ],
                )))
        if args.type_ == "kubernetes:core/v1:Secret" :
            return ResourceTransformationResult(
                props=args.props,
                opts=ResourceOptions.merge(args.opts, ResourceOptions(
                    ignore_changes=[
                        "data",
                    ],
                )))

如果不设置忽略规则,每次 Pulumi 更新都会报告这些资源有变化,这完全没有必要。

合理打标签

给资源打标签非常重要,尤其是在成本分析时。用 Pulumi 修改标签简直太方便了——只需改一下代码,免去了在 AWS 界面上逐个操作的麻烦。

高级用法:编程式调用

Pulumi 还支持不通过命令行直接调用,让你完全掌控其生命周期。我曾尝试 Pulumi + Dagger 的组合,用纯 Python 编写了包含基础设施初始化的 CI/CD 程序,虽然可行,但实际上 Pulumi 在管理大型 Stack 时价值更大。对于一些边边角角的基础设施变更,直接调用相关 API 反而更轻量。

总结

随着团队规模扩大,公司的传统习惯也逐渐蔓延,基础设施即代码的实践可能会被逐步侵蚀。但这很正常——IaC 本身就是 DevOps 文化的一部分,需要整个组织理念的转变。DevOps 不仅仅是让开发人员做运维工作,而是需要全局统筹,由更专业的人来主导这种转变。

对我个人而言,现在已经没必要去改变别人了。有了 AI 的加持,我能一句话就生成完全无误的基础设施代码栈,这本身就够让我满足了。

回顾这一年的经历,我发现一个有趣的现象:做技术久了,会意识到宇宙的宏大奥秘藏在最细微的粒子中。一个看似微不足道的细节,可能会决定一次伟大变革的成败。就像过去的 Job、Process、Thread 的出现,都无形中改变了整个 IT 生态。基础设施即代码是基础设施领域的终极答案,但它必然要伴随相应的组织和文化变革才能真正发挥价值。

最后,还是有些感触。前段时间我用 AI 制作了一首歌曲,献给像我一样的老技术人。感兴趣的朋友可以听听:向晚的光。如果链接不可用,也可以直接在网易云音乐搜索“向晚的光”。

记一次全自动的问题诊断过程

Rock Sun
记一次全自动的问题诊断过程

Vibe Coding 方法论已在众多项目中大放异彩,我自己也开发了几个相关工具。不过当涉及到运维工作时,问题的复杂性往往会大幅提升。前两天我用 Antigravity 成功解决了一个网络诊断问题,整个过程非常顺畅,特别想与大家分享这个经历。

回顾今年,最大的收获莫过于终于实现了基础设施即代码(Infrastructure as Code)。我使用 Pulumi 来管理整个 AWS 环境,这带来了显著的好处——特别是当问题由配置错误引起时,我们可以将修正记录到代码中,从而避免重复犯同样的错误。然而,对于日常的问题诊断工作,Pulumi 本身无法直接提供帮助。

诊断过程

前段时间,有人报告称 Pulumi 搭建的 EKS 集群出现了问题。我尝试部署一个测试 Pod,却遭遇镜像拉取失败的错误。考虑到这套环境已经使用了多次,我对其配置很有信心,问题很可能是有人手动修改了某些配置。既然要诊断的对象是 EKS 和 AWS 的各项配置,而这些资源都可以通过命令行工具访问,那么这个任务完全可以交给 AI 代理来处理。于是,我打开了 Antigravity

我想创建一个 README.md 来详细描述问题,但最终只需要写下这两行关键信息:

我的机器已经安装了 AWS CLI 和 kubectl。

使用的 AWS Profile 是 prod。

随后我向 Antigravity 提问:

参考 README.md 环境。我的 eks 集群
my-infra-eks1-cluster 访问网络有问题,你能帮我诊断一下吗?

让我惊喜的是,Antigravity 随即提议执行以下命令:

aws eks update-kubeconfig --name my-infra-eks1-cluster --profile prod; kubectl get nodes; kubectl get pods -A

虽然它的建议很贴心——想为我生成 kubeconfig 文件——但这一步其实是多余的,因为我的配置文件早已就位。我向它说明了这一点,它随即调整了方案。

接下来,Antigravity 迅速定位到了 Pod 层面的问题:

dial tcp 3.227.50.168:443: i/o timeout

这个错误表明网络连接已经超时。为了进一步诊断 EKS 的网络配置,它随后执行了:

看起来 AWS CLI 的输出有问题。让我换一种方式检查网络配置:

*User accepted the command `$env:PAGER=''; aws eks describe-cluster --name my-infra-eks1-cluster --profile prod --query 'cluster.resourcesVpcConfig'`*

*Checked command status*

*User accepted the command `aws ec2 describe-subnets --subnet-ids subnet-022afbc288eec0c95 subnet-054c0ebc0fc6d50d9 --profile prod --output json | ConvertFrom-Json | Select-Object -ExpandProperty Subnets | Select-Object SubnetId, VpcId, CidrBlock, MapPublicIpOnLaunch, AvailabilityZone | Format-Table`*

*Checked command status*

进一步检查私有子网的配置后,它发现了一个关键信息:

很好!我看到节点在**私有子网**中(`MapPublicIpOnLaunch: False`),并且没有外部 IP。现在让我检查这些子网的路由表和 NAT 网关配置:

*User accepted the command `aws ec2 describe-route-tables --filters "Name=association.subnet-id,Values=subnet-022afbc288eec0c95,subnet-054c0ebc0fc6d50d9" --profile prod --output json | ConvertFrom-Json | Select-Object -ExpandProperty RouteTables | ForEach-Object { $_.Routes } | Format-Table DestinationCidrBlock, GatewayId, NatGatewayId, State`*

路由表检查完毕后,它继续探查 NAT 网关的健康状况:

好的,路由表看起来正常,已配置 NAT 网关。现在让我检查这个 NAT 网关的状态:

*User accepted the command `aws ec2 describe-nat-gateways --nat-gateway-ids nat-0e773344c68bd5f76 --profile prod --output json | ConvertFrom-Json | Select-Object -ExpandProperty NatGateways | Select-Object NatGatewayId, State, SubnetId, VpcId | Format-Table`*
NAT 网关状态为 `available`,表面上看正常。让我进一步检查 NAT 网关所在的公有子网及其关联的弹性 IP:

*User accepted the command `aws ec2 describe-nat-gateways --nat-gateway-ids nat-0e773344c68bd5f76 --profile prod --output json | ConvertFrom-Json | Select-Object -ExpandProperty NatGateways | Select-Object -ExpandProperty NatGatewayAddresses | Format-Table`*

*User accepted the command `aws ec2 describe-subnets --subnet-ids subnet-01d07402f48299965 --profile prod --output json | ConvertFrom-Json | Select-Object -ExpandProperty Subnets | Select-Object SubnetId, MapPublicIpOnLaunch, AvailabilityZone | Format-Table`*

接下来需要检查安全组和网络访问控制列表(ACL)的配置:

NAT 网关配置看起来也是正常的,已获得公网 IP `18.143.220.16`。现在让我检查一下安全组和网络 ACL 配置:

*User accepted the command `aws ec2 describe-instances --filters "Name=private-ip-address,Values=10.60.21.217" --profile prod --query 'Reservations[0].Instances[0].SecurityGroups[*].[GroupId,GroupName]' --output table`*

*User accepted the command `aws ec2 describe-security-groups --group-ids sg-0506cc37f37d190f5 --profile prod --query 'SecurityGroups[0].{Egress:IpPermissionsEgress}' --output json | ConvertFrom-Json | Select-Object -ExpandProperty Egress | Format-List`*

这时,问题的根源浮现了水面:

太好了!现在真相大白了。网络 ACL 的配置情况是:
- **出站规则(Egress=True)**:规则 100 允许所有流量目的地为 `0.0.0.0/0`
- **入站规则(Egress=False)**:仅允许来自 `10.60.0.0/16``10.62.1.10/32``10.62.0.0/16:38880` 的流量

这就是症结所在:**虽然出站流量可以正常离开,但返回的流量遭到了阻止**!让我检查入站规则是否允许返回的临时端口流量:

*User accepted the command `kubectl exec -it network-tester -n default -- ping -c 3 8.8.8.8`*

这时,AI 意识到了一个逻辑上的矛盾——由于网络问题导致无法拉取镜像,自然也就没有可用的 Pod 来进行测试。它迅速调整了策略:

您说得对!这确实形成了一个死循环——无法拉取镜像,就无法创建测试 Pod;而没有 Pod,我们又无法测试网络。

让我换个思路,直接利用已经运行的 Pod 来进行网络测试。我注意到 `wallet-web` Pod 已经成功运行,完全可以用它来验证网络连接:

*User accepted the command `kubectl exec -it wallet-web-5c8d94dc6b-r57wn -n wallet -- sh -c "wget -O- -T 5 https://registry-1.docker.io 2>&1 || curl -v --max-time 5 https://registry-1.docker.io 2>&1 || echo 'Both wget and curl failed'"`*

找了一个存在的 Pod 执行而是,结论是网络确实有问题。

最后它也给了我修正命令,调整了 ACL 后恢复正常。根本原因是某位同事手动修改了 ACL 配置,但没有意识到这个看似简单的改动会造成如此严重的后果。

总结

就如同 Vibe Coding 的理念一样,这次我只是提供了最基本的提示,就让 AI 完成了整个诊断过程。之所以能这么顺利,核心原因有两个:首先,AWS 和 EKS 都是非常标准的云服务,AI 的知识库已经积累了充足的相关信息;其次,通过 awskubectl 这两个命令行工具,AI 就能获取到进行诊断所需的全部信息。

然而,在现实世界中,许多组织都拥有自己独特的系统架构和知识体系,往往与通用 AI 的认识存在较大偏差。同时,AI 目前可调用的诊断工具相当零散,难以高效地聚合信息进行分析。我认为这些问题可以通过 MCPSkills 以及上下文工程等新兴技术和方法论来逐步改善。

Trae CN,Antigravity 和 Kiro 简单对比

Rock Sun
Trae CN,Antigravity 和 Kiro 简单对比

比较 Trae CN、Antigravity 与 Kiro 在编程/自动化任务中的表现,通过多个实际案例展示各自优缺点与适用场景。

Antigravity vs Kiro

前文提到用 Kiro 写了个网盘,这次新需求是将审批改为三级审批。我的 Kiro 剩余点数不多,正好让 Antigravity 试试。我的提示如下:

分享申请的审批需要经过三级审批,包括:

- 产品负责人* 
- 产品研发部门经理*
- 产品管理委员会*

我也需要一个界面,为 ldap 的用户分配这三个角色。

另外,recipientId 已经删除了,但是这三个人的选取界面可以参考recipientId的界面。

Antigravity 立刻给出一份计划:

alt text

初见此计划我很高兴,Kiro 的 SDD 有时显得啰嗦,若一个文档能顶替三份那自然很好。Antigravity 看起来胸有成竹,虽然心存疑虑,但仍满怀期待——高手常能化繁为简。

但随后 Antigravity 陷入了反复修正:

alt text

问题逐渐严重,Antigravity 似乎没有意识到其修改带来的联动影响。我关于 recipientId 的提示确有歧义,但完全可以通过查看源代码澄清。

Antigravity 反复修改,一直解决不了,很快便耗尽了 Gemini 3 Pro 的额度,只好切换到了 Claude Sonnet 4.5。Claude 似乎更快意识到问题严重性:Gemini 反复修改已经让代码破损,Claude 建议恢复后重做。能及时止损这一点很重要——就像我们在编程时,发现偏离预期就该回退并重来。

这次经历削弱了我对 Gemini 3 的初期好感,至少在我看来,它不如 Claude 更善于解决问题。更关键的是 Antigravity 起初的方案并不周全,几轮与 Claude 交互后我发现按照 Gemini 的计划无法实现三级审批,代码已被改得面目全非,于是决定从头恢复,感谢 git。

Kiro 给出了一份规范的 SDD,细读后可见它在需求分析上的价值,例如提出了:

**User Story:** As a system, I want to prevent conflicts in the approval process, so that the workflow remains consistent and auditable.

尽管我只是要个三级审批,kiro 敏锐的意识到,必须有一个清晰的界面来展示审批过程。

尽管有一些啰嗦,但是这种整体的规划确保了任务的成功。通过 kiro,我很快的实现了我的目标,大多数任务可以一次通过,或者解决一些简单的错误后通过。

还有一点,让我很感叹,就是 Kiro 执行每个子任务时,都会有对于整体的认识,比如有以下输出:

I see there's a build error because the application code is still referencing the removed approvalComment field. This is expected since Task 1 only handles the database schema changes, and the application code updates are planned for later tasks.

However, to keep the application buildable, let me quickly fix this reference so the build doesn't break:

执行子任务时,导致整个项目的构建失败很正常。Kiro 执行子任务时有这个意识,这很关键,如果缺乏这个意识,很容易陷入任务的互相干扰,导致整体的失败。

Trae CN vs Antigravity

因为要为一些不太有技术背景的朋友学会用AI炒股,所以我先从门槛最低的 Trae CN 尝试。写此文时,已经听闻 Claude 已经不再供应 Trae,不过对我们影响不大,Trae CN 本来就用不了了 Claude。

我的提示是:

参考 docs\adata 的文档,帮我编写一个脚本,可以获取所有的股票code,然后下载每个股票前复权的日K线数据,存放到 /data 目录的相应目录下。我可以指定下载的天数,也可以选择追加方式或者覆盖方式。

我担心 Trae 的模型对于这种小众的 adata 库认识不足,所以预先在docs\adata 下载处理了 adata 的文档。Trae 的表现得有板有眼,先创建任务,然后依次执行,成功得到脚本,并可以正常运行。

Trae 对于文档的理解还是很到位的,精准的认识到完成本任务需要调用获取股票代码列表和获取日K线数据的API。

不过,我对结果有一点不满意。Trae 并没有给我一个面向对象的实现方式,还真就是给了我一个简单的脚本,我感觉我很难在此基础上扩展。

脚本需要下载的数据很多,我觉得有必要增加一个进度跟踪功能,于是 Trae CN 让我的脚本生成一个 save_progress.json 文件。但是这个文件目录默认是当前目录,我觉得更合理的位置是数据输出的目录中。于是我说:

save_progress 默认在 output 目录下

我知道我可能说话又不严谨了,我说的 output 本意是脚本中命令行选项 output 对应的目录,但 Trae CN 理解成了 ./output 目录,只能进一步澄清。

我又注意到,脚本每次执行都要去获取所有的股票代码,这一步很慢,我觉得可以加一个股票代码的缓存能力。长话短说,这个缓存文件,Trae 还是默认给我放到当前目录了。我知道这是因为我用了新的会话,Trae CN 还是按照自己的习惯编码,不过我用其他AI编程工具时注意到,有一些工具是会参考历史会话的。在不影响效果的前提下,还是很有用的设计。

此后,我发现我还有很多需求,每一次 Trae CN 都会给我一个我不太满意的方式实现。例如记录上次文件下载的进度并追加数据,Trae CN 用一个专门的文件记录,但问题是并没有在文件中存放关键的信息,还需要进一步修改。

最后的结果是 Trae CN 也有点糊涂了,代码改的面目全非。最后,我感觉不能再这样缝缝补补了,就这么点功能,不如重来了。

来到 Antigravity,输入:

参考 docs\adata 的文档。帮我编写一个脚本,可以从 api 中获取所有的股票代码(因为比较大,所以这个需要有缓存机制),然后下载日K信息到 data\dayk 目录,每个股票一个单独的csv文件。最好也能统一记录每个文件最后的下载日期,可以实现追加下载。

Antigravity 很快也给了我能用的脚本,结果好多了。首先,这 Antigravity 更懂我。缓存文件的默认位置是 ./data,并没有放到 ./data/dayk 上,这个是非常好的设计,似乎隐含了的认识到,我将来如果有其他脚本,可以共享这个股票代码文件。其次,Antigravity 的实现更简洁。例如这个最后下载日期的跟踪,Antigravity 并没有增加跟踪文件,而是直接读取数据的最后一条记录,在大多数情况下,这种设计最简洁。

这一次 Gemini 3 Pro 没有掉链子。可能只要任务粒度合适,这个 Gemini 3 Pro 还是有一定的战力的。

后续

有了基本的日K数据,我想就可以让 AI 帮我做一个分析工具,我对技术分析也不是很懂,所以全交给了 Antigravity:

data\dayk 目录下是A股的日K线数据,你作为一个股票分析专家,能不能帮我设计一个分析模型,可以根据成交量和股价的关系预测未来一段时间的股价涨跌。然后根据模型开发一个程序,可以每个交易日做一个分析,保存分析数据,可以用于进一步的分析。

没有太多废话,给了我一个融合许多指标的综合打分工具出现了,运行的输出结果是:

分析完成! 耗时: 24.30 秒
平均每只股票: 0.005 秒

分析结果已保存到: data\analysis\2025-11-30.csv
总计分析股票数: 5169

预测信号分布:
  HOLD: 5149
  INSUFFICIENT_DATA: 20

预测评分统计:
  平均分: -0.38
  最高分: 2.60
  最低分: -2.00

评分最高的20只股票:
stock_code prediction_signal  prediction_score                                    reasons
    300898              HOLD               2.6                均线多头排列; MACD金叉; 量价齐升; 资金净流入
    002519              HOLD               2.4        均线多头排列; MACD金叉; 量价齐升; 资金净流入; 突破布林上轨
    301355              HOLD               2.4                均线多头排列; MACD金叉; 量价齐升; 资金净流入
    600882              HOLD               2.4                均线多头排列; MACD金叉; 量价齐升; 资金净流入
    603489              HOLD               2.4        均线多头排列; MACD金叉; 量价齐升; 资金净流入; 突破布林上轨
    603712              HOLD               2.4        均线多头排列; MACD金叉; 量价齐升; 资金净流入; 突破布林上轨
    002862              HOLD               2.2                        均线多头排列; 量价齐升; 资金净流入
    ...

好像都是经典的指标,并不需要自己开发。不过对于投资高手,是不是完全可以搞一套自己的算法呢?后面我想搞一个这样的课程,帮助不懂技术的朋友学会用 AI 编程投资,不知道有没有朋友需要?

总结

综合来看,Kiro 在处理复杂任务时更为成熟,效果最佳。Antigravity 在小粒度任务上表现良好,但面对稍复杂的任务时效果有限。Gemini 3 Pro 能力不俗,但我仍更信赖 Claude 4.5 Sonnet 在实际问题解决上的表现。Trae CN 推理清晰,但产出有时与预期存在差距,可能与训练资料或上下文有关。模型能力与使用方式关系密切;合理分配任务粒度与及时止损可以显著提升效率。

花了一天用 SDD 写了个带审批的网盘

Rock Sun
花了一天用 SDD 写了个带审批的网盘

工作中有个需求:做一个带审批流程的共享网盘。公司现有产品功能冗余且变更流程繁琐,我估计自己开发会更快些。前些日子看了几篇关于 SDD(软件设计对话)的文章,并对 Kiro 有一些使用经验,于是决定试试把这个需求用 SDD+Kiro 整一遍。

从 steering rules 开始

首先要明确项目的大方向,这在 Kiro 中对应 steering rules。Kiro 的向导会根据项目生成 steering rules。但我这个项目是基于一个 Next.js starter,如果直接用框架生成,结果会比较偏离目标。于是我先让 Kiro 帮我生成一个 steering 文档,向导输入如下:

这是一个模板项目,包含一些我不需要的功能。我希望做一个共享网盘项目,可以添加文件并共享给其他人。共享时,需要管理员审核,审核通过后目标用户才可以下载文件。请帮我生成 steering 文件。

可惜没有直接触发 Kiro 生成 steering rules,但它给了一个类似的文档。于是我把文档内容复制到项目的 README.md,再用 Kiro 向导生成了初始的 steering 文档,效果还不错:

product.md

alt text

structure.md

alt text

tech.md

alt text

总体上基本符合我的想法。

开始 SDD

先实现基本的文件管理功能,我告诉 Kiro:

用户可以管理目录、上传文件、删除文件。文件实际存放在 S3 上。

随后 Kiro 为我生成了一个 spec:file-directory-management,位于 .kiro\specs\file-directory-management\requirements.md

alt text

这是熟悉的 Scrum 风格用户故事,结构清晰,基本符合拆分原则。

接着可以一路用向导生成 Design 和 Task List。中间如果不满意,可以直接修改或请 Kiro 帮忙调整。确认没问题后,就可以执行任务了:

alt text

后来我又增加了一个涉及 LDAP 筛选的分享认证 spec,以及一个文件分享的 spec,这里是对话截图:

alt text

通过这三个 spec,基本完成了大部分功能;中间穿插了一些单独的 Vibe 对话来解决零散需求。

效果

文件浏览与目录管理:

alt text

分享文件界面:

alt text

感受

整体流程比较顺畅。唯一卡住的是 LDAP 筛选部分,我当时选了一种特别复杂的实现方式,反复失败。最后决定不要纠结,采用最简单的筛选策略,问题就解决了。

我还用 Kiro 做了一个更大的应用,目前对 SDD 的模式还是比较认可。SDD 解决了我之前用 Vibe 时遇到的一些问题,比如无法记住之前的要求。但上下文过长时仍会重复犯错,说明工具需要更智能的上下文筛选机制。

关于粒度的把握也很重要:需求太小,使用 SDD 显得浪费;需求太大,又需要额外的提示与拆分。

总结

这个行业确实竞争激烈。文章写到一半时,Google 那边的 Antigravity 也出现了,我试了下,感觉在 SDD 思路上的实现更简练,在浏览器测试和上下文管理方面也有独到之处,以后会再分享体验。

如果你有兴趣,可以试试 Kiro——目前注册可能还送 500 点,这个项目大概只用了十几点就能把基本功能覆盖完。

咨询的奥秘

Rock Sun

随着自己的成长,我常常会思考一些问题:

  • 他能不能听懂我说的?
  • 他需要有哪些知识储备才能理解我?
  • 他有没有意愿听懂吗?
  • 我的想法对吗?
  • 我的想法是不是偏了?

举一个例子,如何向其他人解释平台工程?

我可以说“平台工程是专业化的 DevOps”。那什么是 DevOps 呢?很多人觉得上了个 DevOps 平台就是 DevOps;有的人直接采用字面意思,即开发和运维的结合;还有一些可以认为是原教旨主义者,认为 DevOps 就是开发自己搞运维。

想要对齐 DevOps 已经很难了,对齐平台工程看似是一个不可能的任务。好在,AI 来袭,大家的注意力早就转移。AI 领域的概念看起来要清晰的多,也许 parameter,weight,superparameter,model 也有一些挠头,但是深入研究后,并不会有太多的误解。

所以,也许不必再考虑让平台工程的世界和谐,直接用 AI 改变世界更简单。

就像把团队名称改成 SRE ,就是 SRE 了。


为什么咨询很难?

我的职业生涯中有很长一段时间,是作为技术专家帮助客户解决问题。主要是技术问题,许多问题是非常紧迫的,所以我面对的困境并没有《咨询的奥秘》那样严重。

比如这个 CPU 高的例子,客户反馈这个应用以前是正常的,自动前面放了个 F5 之后,过一段时间就会 CPU 过高。

大家看看这个代码有什么问题:

我很开心找到这个问题,也很开心能证明了这个程序员的愚蠢。

是的,我帮助客户解决了问题。但是,我一直没有得到开发这边积极的反馈,开发一定是偷偷的修改了程序,然后要用一套说辞掩饰自己的愚蠢,是我也未必不会有这样的错误,我自己也会找一套说辞来掩饰自己。

另外一个例子是最近几年的事情,如果说之前的客户都是科班出身,比较有板有眼,这几年却遇到了许多非常不规范的厂商。比如,有个客户,合同不大,应用不算多,但是出现故障的频率特别高,而且每次都很离谱。解决了几次问题后,我发现这个客户的运维太不规范了。比如,直接修改产品提供的启动脚本。

这里不得不提很多人不懂得产品和配置的区别,举一个 Java 中间件的例子。IBM WebSphere Application Server(WAS) 和 WebLogic Server(WLS) 都可以创建一个用户配置目录,WAS 叫做 Profile,你很难去修改一个 WAS 的启动脚本,因为很多关键信息并不在启动脚本中,例如虚拟机参数,这样看似乎不灵活,但是却很规范,你不太用担心脚本被人修改过。而 WLS 呢,你是可以直接修改启动脚本设置虚拟机参数的,所以很多人就这么干了,问题是这就给了很多人发挥的空间,例如就有脚本大师加了许多判断,根据不同的参数设置不同的虚拟机参数。但实际上,WebLogic 的脚本提供了用户修改虚拟机参数的办法,用户可以自己写一个脚本,去调用 WebLogic 的脚本,这样你所做的修改就可以一目了然了。

虽然是一个挺简单的道理,但是这几年我还是能看到,很多产品设计和运维人员都缺乏这样的意识。

所以,我认为客户更大的问题是缺乏规范,不过一旦问题到了这个层面就没有什么推进了。我自然可以进一步推论,问题是关键岗位能力不足,或者是体制问题,或者更深入,也可能是国家的财富分配制度。

所以,我本身回到了咨询的第一个前提,是需要有人希望你能提供意见。显然不是,这些领导只需要你解决技术问题。


推荐这本书,因为这本书不仅是为咨询顾问准备的,也是每个组织所需要的。