MoreRSS

site iconTonyBai | 白明修改

重复
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

TonyBai | 白明的 RSS 预览

Rust 要变成下一个C++?

2026-09-12 07:00:00

本文永久链接https://tonybai.com/2026/09/12/rust-complexity-debate-2026

大家好,我是Tony Bai。

【导读】

一位Rust开发者在r/rust发帖,列出了十几项正在讨论中的语言提案——命名参数、开放枚举、字段投影、Move/Destroy/Forget trait……他担心Rust正在一步步走上C++ “大杂烩”的老路。帖子迅速引爆社区,连Rust语言团队成员都亲自下场回应。这场争论,或许正是所有“成功的编程语言”都逃不开的宿命拷问。

【文章要点】

  • 一石激起千层浪:Reddit r/rust 一篇梳理十几项正在推进的复杂语言提案的帖子引发全网热议,直击“Rust 是否会重蹈 C++ 历史覆辙”的敏感痛点;
  • 激增的“焦虑清单”:命名参数、开放枚举、字段投影、Move/Destroy/Forget 三件套 trait、FFI 函数重载等提案密集讨论,引发社区对语法膨胀和语言正交性破损的深层担忧;
  • 出圈的“三轴判断法”:高赞神评提出从“广泛 vs 狭窄”、“能力解锁 vs 纯语法糖”、“新特性 vs 填坑做减法”三个维度量化特性的引入价值,成为理性评估新提案的普适标尺;
  • 语言团队核心下场释疑:核心成员 Josh Triplett 明确表示团队对复杂度极其谨慎,部分提案旨在消除旧有心智负担(做减法),部分特性(如函数重载)将严格限定范围,而命名参数等更倾向于通过现有特性组合解决;
  • 民意与机制的博弈:年度调查显示 40% 的受访者希望优先做简化,而 Rust 历经十年依然保持克制的核心底牌,在于严格且拉长周期的 RFC 流程与“复杂度预算”自检机制。


如果你关注编程语言圈,大概率已经刷到过这条帖子。

今日,Reddit的r/rust板块,一名用户发了一篇题为《My concerns about the future of Rust(我对Rust未来的担忧)》的帖子。

没有惊悚的标题,没有引战的措辞,开篇第一句甚至是:

“我爱Rust,但是……”

但就是这么一篇平静的帖子,硬是炸出了数百个赞和评论,甚至惊动了Rust语言团队的核心成员亲自下场解释。

争论的核心问题只有一个:Rust,会不会变成下一个C++?

一份“焦虑清单”

楼主开门见山地表示,自己并不是反对某一条具体提案,而是担心把所有正在讨论的提案叠加在一起看时,Rust语言会呈现出令人不安的整体趋势。

他列出了一长串正在社区讨论、甚至已经有RFC(提案文档)在推进的语言特性,其中包括:

  • 命名参数 / 默认参数
  • 句柄类型、use语法糖、Share trait(人体工学式引用计数)
  • 开放枚举(Open enums,主打FFI场景,但可能被滥用到其他地方)
  • pub(api)可见性修饰符
  • 字段访问声明与基于位置的生命周期语法
  • 字段投影(Field Projections)
  • 显式尾调用 / loop_match
  • Sized Hierarchy,在Sized/?Sized之上叠加多个新trait
  • 对Drop语义的自定义控制
  • super let
  • 自动实现(auto impl)
  • 面向FFI的函数重载
  • 可变参数(Variadic parameters)
  • Move / Destroy / Forget 三件套trait
  • impl Fn(…)类型里的命名参数

楼主的担忧很朴素:如果这些提案全部落地,Rust将新增几十个关键字和保留字,语法和语义复杂度会大幅上升。而Rust最打动人的地方之一,恰恰是“语言的每一部分都天然契合彼此”——这种正交性一旦被打破,Rust就有滑向“新时代C++”的风险。

他还特别点名了FFI(跨语言互操作)相关的提案:许多复杂改动的出发点是“更方便地对接C/C++代码”,但楼主直言,为了兼容日益过时的语言而牺牲Rust本身的简洁性,这在他看来是一种“本末倒置”。

混战开始:三种态度

帖子发出去不到半天,评论区就自然分成了几派。

第一派:技术限制派。一位用户指出,清单里大多数提案其实源于真实的技术局限,比如显式尾调用能让字节码解释器快出一大截;Drop语义控制解决的是“文件关闭失败”这种现实中确实无解的痛点。这一派的共识是:这些特性大概率只影响特定领域的开发者,平时“眼不见为净”。

第二派:反对复杂化派。这一派用户提出了针锋相对的观点,围绕Move/Destroy/Forget三件套展开了一场高质量拉锯战——有人认为它会让类型系统更复杂,也有人反驳说,这套机制恰恰是为了未来彻底淘汰std::pin这个公认的“复杂度重灾区”,长期看反而是在做减法。

第三派:坚定支持派。这类用户的发言也获得不少点赞,核心观点是:Rust的复杂度和C++的复杂度根本不是一回事——C++的问题在于反复为同一个问题造多个轮子,而Rust的新特性大多是解决彼此独立的新问题,不易牺牲整体一致性。他的结论很干脆:“让Rust更复杂,也就是让Rust更好。”

高赞神评:一套“三轴判断法”

这场讨论里最出圈的一条回复提出,判断一个新特性是不是“甜蜜的负担”,可以从三个维度去看:

  1. 广泛 vs 狭窄:一次性解决一类共性问题的“广”特性,比反复打补丁解决单点问题的“窄”特性更值得加。
  2. 解锁能力 vs 语法糖:真正“解锁”此前做不到的能力,才配得上增加的复杂度;单纯让写法更顺手的语法糖,性价比要打问号。
  3. 新增 vs 修补:有些看似“新特性”的提案,实际上是在“打补丁”——去掉了此前必须硬记在脑子里的例外情况,反而是在给语言做减法。

按这套标准,这条回复明确表示自己并不看好命名/默认参数、闭包里的use语法糖,但会非常支持显式尾调用和可变参数——理由是它们属于“广泛且解锁能力”的类型。

这套框架后来被多位评论者反复引用,成了这场讨论里最接近“共识”的分析工具。

官方下场:语言团队怎么说

最有分量的回复,来自ID为JoshTriplett的用户——他在个人标签里标注了rust、lang、libs、cargo,是Rust语言团队的成员。他特别说明,以下发言仅代表个人,不代表团队整体立场。

他给出的解释大致可以归纳为三类:

  • 一部分特性正是为了“简化”而加。比如视图模式(view patterns)之类的提案,解决的是新人从其他语言迁移到Rust时最容易“劝退”的痛点,本质是把用户本来就要自己想办法解决的问题,用更规整的方式收编进语言里。
  • 一部分特性团队自己也很谨慎,正在反复权衡怎么落地。他举了函数重载的例子:团队希望把它严格限定在FFI兼容场景,而不是变成C++那种任意重载。
  • 一部分特性大概率不会采纳,或者只以“现有特性组合”的方式变相实现。比如命名/默认参数,他透露团队更倾向于鼓励开发者用“结构体参数+默认字段值”的组合方案去解决,因为这只是已有能力的自然延伸,而不是引入一整块全新的复杂语法面。

一组数据:不是孤例

讨论过程中,还有网友甩出了一个佐证:根据2026年3月发布的Rust官方State of Rust年度调查结果,约有40%的受访者表达了与楼主类似的担忧,认为语言应该优先做“简化”而不是“加法”。

这也从侧面说明,这篇帖子戳中的并不是一个人的焦虑,而是一部分Rust用户群体真实存在的集体情绪。

不过也有老用户站出来“降噪”。一位自称从2014年开始写Rust,本人也是servo、rust、clippy等项目的贡献者表示,类似的“塌方警报”几乎每年都会响一次,但过去十年里从未真正发生过——原因是Rust的RFC流程本身就设有相当长的观察期和“复杂度预算”审查机制,大多数提案要经过数年讨论才可能落地,中途夭折的比例极高。

小结

这场讨论最有意思的地方,或许不在于谁对谁错,而在于它精准地呈现了一个悖论:

一门语言越成功、用户越多,等待被解决的“长尾需求”就越多;而每一个长尾需求背后,都有一小撮开发者真心觉得“这个特性必须加,不然我没法用”。楼主在帖子里自己也承认这一点——他并不否认这些提案大多“确实有用”,只是担心把它们全部叠加起来,会不会在某个临界点上,让Rust从“精巧”滑向“臃肿”。

C++用了近三十年时间,才从"简洁的C升级版"变成今天动辄两千页标准、多套编译器实现互不兼容的庞然大物。Rust才十年历史,走到今天这一步是否也是必然,恐怕没人能给出确定答案。

但至少从这篇帖子的讨论质量来看,Rust社区对这个问题的警觉程度,本身可能就是它区别于前辈的地方。


*本文编译整理自Reddit r/rust板块讨论串《My concerns about the future of Rust》,原帖及评论区观点归属原作者所有。https://www.reddit.com/r/rust/comments/1wat0px/my_concerns_about_the_future_of_rust/ *


还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 从0 开始构建 Agent Harness 将带你:

  • 抛弃臃肿框架,回归“驾驭工程 (Harness Engineering)”的第一性原理
  • 用 Go 语言手写 ReAct 循环、并发拦截与上下文压缩引擎等,复刻极简OpenClaw
  • 构建坚不可摧的 Safety Middleware 与飞书人工审批防线
  • 在底层实现 Token 成本审计、链路追踪与自动化跑分评估
  • 从“调包侠”进化为掌控大模型边界的“AI 操作系统架构师”

扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

Go SIMD杀疯了:开发者删光最后一行cgo代码,性能反超C语言库

2026-09-11 06:00:00

本文永久链接https://tonybai.com/2026/09/11/go-simd-kills-cgo-turbopfor-avx512

大家好,我是Tony Bai。

一段跑了7年的C语言压缩库,被Go 1.27的实验性SIMD包干掉了,AI还顺手甩出了一个2倍加速的“外挂”。

【导读】:今年8月,Debian Code Search的作者Michael Stapelberg做成了一件憋了很多年的事——删掉项目里最后一行cgo代码。这背后的功臣,是Go 1.26引入、在Go 1.27上愈发成熟的实验性simd/archsimd包。他用纯Go重写了跑了7年的C语言TurboPFor压缩库,不仅性能反超,还借助AVX512的“位置汇编计数”黑科技把速度又翻了一倍——而这个关键优化,是AI编程助手Claude发现的。

【文章要点】

  • 告别七年 cgo 心结:Debian Code Search 作者使用 Go 1.26/1.27 引入的实验性 simd/archsimd 包,将服役 7 年的核心 TurboPFor 整数压缩库彻底重写为纯 Go,删除了项目中最后一行 cgo 代码;
  • 纯标量优化的前置红利:在未动用真正的 SIMD 指令前,通过复用结构体缓冲区消除内存分配(+11%)、利用泛型数组将位宽“焊死”为编译期常量消除分支(+40%~64%),让纯 Go 基础性能直接达到 C 语言库的 76% 以上;
  • AVX2 垂直布局斩获 3 倍加速:利用 256 位向量寄存器同时并行处理 8 个 32 位无符号整数,彻底消除了标量处理的内层循环,直接带来基准性能的 3 倍跃升;
  • AI 顺手送出 2 倍“黑科技外挂”:AI 助手 Claude Fable 5 准确识别出编码瓶颈在于位宽直方图扫描,并提出了基于 GF2P8AFFINEQBVPOPCNTB 的“位置汇编计数(Positional Popcount)”正交变换优化,将单值处理指令数从 12 条暴降至 1.5 条,在追平 cgo 的基础上再次翻倍;
  • 硬件性能极限逼近:重写后的 Go 编码器最终反超了历史 cgo 版本,运行时指令吞吐量达到了每周期 7 条指令(7 IPC,极其逼近现代 CPU 的 8 IPC 理论上限),为纯 Go 在高性能系统编程领域的替代铺平了道路。


一个困扰了工程师多年的心结

Debian Code Search(简称DCS)是一个搜索引擎,用来在整个Debian发行版的开源代码里做字面量或正则表达式检索。它的核心是一套倒排索引:从“词项”映射到“包含该词项的文档”,而文档通常用整数ID表示,所以索引本质上是海量的整数列表。

这些整数列表需要被极高效地压缩和解码,否则索引既存不下,查询也快不起来。DCS采用的是业界知名的TurboPFor整数压缩格式,多年来一直依赖C语言的powturbo/TurboPFor库,通过cgo接入Go项目。

问题是,DCS从一开始就被设计成一个纯Go项目,作者一直不喜欢代码库里混进C代码——但SIMD(单指令多数据)级别的性能,过去在Go里几乎无法企及。这个心结,一压就是7年。

在Go 1.26之前,Go开发者想用SIMD只有三条路

在实验性simd/archsimd包出现之前,Go开发者如果想榨干CPU的向量指令性能,基本只有三个选择,而且每一个都不轻松:

  1. 手写Go汇编:只适合非常小的函数,例如标准库bytes.IndexByte就是用手写汇编(含AVX2)实现的,但代码可读性和可维护性都很差;
  2. 用工具生成汇编:比如Michael McLoughlin开发的Avo,标准库crypto/internal/fips140/sha256的AVX2实现就是这么来的。这比纯手写汇编“高级”一些,但本质上仍然是在跟汇编打交道;
  3. 用cgo调用C库:让gcc或clang去编译真正的SIMD代码,DCS过去7年一直走的就是这条路。

三条路各有各的坑:汇编难写难维护,代码生成器门槛高,cgo则意味着要接受一门“外来语言”长期驻留在纯Go项目里,交叉编译、构建流程都会因此变复杂。

Go 1.26(2026年2月发布)带来了转机——官方发布说明写道:

Go 1.26引入了实验性的simd/archsimd包,可以通过在构建时设置环境变量GOEXPERIMENT=simd来启用。该包提供对特定架构SIMD操作的访问,目前支持amd64架构,提供128位、256位和512位的向量类型(如Int8x16Float64x8),以及Int8x16.Add这样的操作。目前API尚未稳定。

到了Go 1.27,这套实验性能力已经足够扎实,可以支撑一个真正的生产项目去验证——这正是本文要讲的故事的技术底座。

下面这张图梳理了Go在SIMD这件事上走过的路:

起点:先写一个“能跑就行”的朴素版本

在动手优化之前,作者先梳理了DCS对整数编解码的三种使用场景——局部索引(新软件包入库时的增量编码)、索引合并(把海量局部索引合并成少数几个大索引)、查询解码(用户搜索时高并发解码)。据此他设计了一套简洁的API:

package pforenc

type BlockEncoder struct {
    // 复用的临时缓冲区放在这里
}

// EncodeBlock 把不超过256个uint32编码进dest(一个TurboPFor块)
func (*BlockEncoder) EncodeBlock(dest []byte, vals []uint32) []byte {}

// EncodeN 循环调用EncodeBlock
func (*BlockEncoder) EncodeN(dest []byte, vals []uint32) []byte {}

type StreamEncoder struct {
    be   BlockEncoder
    vals [256]uint32
    // 临时缓冲区
}

// 满了就需要调用EncodeBlock
func (*StreamEncoder) Add(val uint32) (full bool)

func (*StreamEncoder) EncodeBlock() []byte {
    if se.n == 0 {
        return nil // 多余的调用变成空操作
    }
    // …
}

最初的编码器实现简单到近乎“偷懒”:所有数值都按32位定长、小端序写入,每256个值加一个字节的块头:

func (be *BlockEncoder) EncodeBlock(dest []byte, vals []uint32) []byte {
    const bitWidth = 32
    dest = append(dest, bitWidth)
    for _, val := range vals {
        dest = binary.LittleEndian.AppendUint32(dest, val)
    }
    return dest
}

这当然是个压缩率极差的实现,但它是个可用的起点。接下来作者逐一实现了TurboPFor真正的几种块类型:bitpacking(按最小必要位宽定长压缩)、bitpacking with exceptions(用位图标记少量“超标”的异常值)、bitpacking with VB exceptions(用变长字节编码异常值,适合异常值很少的场景)、constant(整块只存一个值,适合全零或全相同的块)。

做完这一步,一个关键发现浮出水面:编码器真正的开销大头,是扫描输入值来决定用哪种块类型,而不是编码本身——这个发现,为后面那个“2倍加速彩蛋”埋下了伏笔。

此时,朴素版Go编码器的性能已经达到C版本的76%。作者原本觉得,到这一步其实就可以收工了。但他还是决定,看看到底能把Go推多远。

开局:把测量工具先搭好

在正式动手优化前,作者花了不少篇幅讲“怎么量得准”,这一段对任何做性能优化的工程师都很有参考价值。

设置正确的微架构等级(GOAMD64)。Go从1.18开始支持通过GOAMD64指定编译目标的x86-64微架构等级:

等级 包含指令集
v1(默认) 所有x86-64处理器都支持的基线指令
v2 v1 + CMPXCHG16B、POPCNT、SSE4.2等
v3 v2 + AVX、AVX2、BMI1/2、LZCNT、FMA等
v4 v3 + AVX512F/BW/CD/DQ/VL

作者建议2026年一般项目至少设置GOAMD64=v3,这样像bits.OnesCount8这类函数会被编译成POPCNT硬件指令而不是查表实现。DCS这个项目由于服务器和开发机都是较新的AMD Zen 4/Zen 5,直接用上了GOAMD64=v4(需要AVX512支持)。

基准测试与benchstat。作者用Go内置的testing包写基准测试,并用golang.org/x/perf/cmd/benchstat工具对比不同commit的性能变化,同时用taskset把测试进程锁定在固定核心上,避免核心迁移带来的噪声。

perf看CPU硬件计数器。光看pprof知道“哪里慢”还不够,作者进一步用Linux的perf工具读取CPU硬件性能计数器(比如分支预测失败次数),来搞清楚“为什么慢”,这是Intel提出的“自顶向下分析法”(Top-down Analysis)的典型应用。

第一波优化:不上SIMD,纯标量也能挤出不少性能

1. PGO:一次意外的“负优化”,牵出了CPU对齐的暗坑

Profile-Guided Optimization(PGO,画像引导优化)是Go 1.21起正式可用的特性:先采集一份CPU性能画像,再喂给编译器,让它更激进地内联函数、做条件去虚化。

按理说PGO应该是稳赚不赔的优化,但作者开启PGO后,性能反而下降了13%!排查后发现,PGO会让编译器给热循环打上PCALIGNMAX(64,31)的对齐标记——按照AMD官方的Zen 5优化指南,把热循环对齐到64字节缓存行边界通常是好事。

但这次运气不好:对齐之后,一对被宏融合的CMPQ+JGE指令恰好落在了32字节边界上,而Go编译器为了修复Intel的SKX102勘误(这两条指令绝不能跨越或落在32字节边界),会插入额外的NOP指令来避让——这些额外指令拖慢了本就是“指令派发瓶颈”的循环。

这是一个很好的提醒:编译器优化并非单调递增,理解它的副作用同样重要。所幸后续的优化commit改变了代码结构,这个“不走运的排列”并未在整个优化过程中反复出现。

2. 减少内存分配

原教学版解码器每次需要临时缓冲区时都直接make()分配,这类由变量长度决定、无法在编译期确定大小的分配,会实实在在地走一次runtime.makeslice调用。

作者把这类临时缓冲区改为结构体里预先分配好的固定数组字段(复用而非每次新建),在DCS的debian-mix基准上把速度从773 Mval/s提升到858 Mval/s,提升约11%。这个改动同时也让基准测试更稳定,因为垃圾回收器被请出了热路径。

3. 泛型位宽特化:让编译器把循环“焊死”成常量代码

这是这一波优化里最有意思的一招。TurboPFor的bitpacking核心函数,其控制流实际上只取决于两件事:输入值的数量要打包的位宽。如果这两者在编译期就已知,编译器就能把循环完全展开,生成几乎没有分支、只剩位运算和内存读写的“最优机器码”。

先把输入数量固定为32,手动展开循环;再借助Go泛型,用数组类型自带长度信息这一特性,把位宽也变成编译期常量:

type bitWidthT interface {
    [1]byte | [2]byte | [3]byte | [4]byte | [5]byte |
    [6]byte | [7]byte | [8]byte | [9]byte | [10]byte |
    // … 一直到 [32]byte
    [31]byte | [32]byte
}

func bitpack32Unrolled[T bitWidthT](dest []byte, vals *[32]uint32) {
    var zero T
    bitWidth := len(zero)                  // 编译期已知
    dest = dest[: 4*bitWidth : 4*bitWidth] // 容量也编译期已知
    mask := uint32(1<<bitWidth - 1)
    var acc uint64
    var have, pos int
    // 循环体在这里手动展开,vals[0]..vals[31]逐一处理
    acc |= uint64(vals[0]&mask) << have
    have += bitWidth
    if have >= 32 {
        binary.LittleEndian.PutUint32(dest[pos:pos+4], uint32(acc))
        pos += 4
        acc >>= 32
        have -= 32
    }
    // …
}

编译器会为每一种位宽实例化出一份独立的函数,生成的机器码几乎是无分支的、只由确定操作数的移位和位运算构成。效果非常明显——在处理256值以下的“余块”(remainder block)时,各类场景普遍取得40%到64%的加速:

vals=bitpacking-bw1     751.2  →  1120.5 Mval/s  (+49.15%)
vals=bitpacking-bw2     716.8  →  1176.0 Mval/s  (+64.07%)
vals=bitpacking-bw7     700.0  →  1078.5 Mval/s  (+54.08%)
vals=debian-mix         559.5  →   783.8 Mval/s  (+40.09%)

代价是二进制体积略有增大(.text段约增加20KB),作者认为这个代价完全值得。

第二波优化:真正祭出SIMD

即便不上真正的SIMD指令,只是把处理步长做大(比如用bits.OnesCount64一次数64位而不是逐位判断),也能带来提升。但要触及数量级的性能差距,还得靠向量指令。

1. SIMD构建标签:运行时探测 + 编译期开关双保险

simd/archsimd包需要考虑一个现实问题:不是所有CPU都支持AVX2/AVX512,程序需要在运行时做特性探测,并在旧CPU上优雅回退到标量实现。典型的三文件模式是这样的:

// constant_nosimd.go —— 不满足SIMD条件时的兜底实现
//go:build !goexperiment.simd || !amd64

package pfordec

func fillConstant(output []uint32, val uint32) {
    fillConstantScalar(output, val)
}
// constant_amd64.go —— amd64架构下的SIMD实现,运行时探测AVX2
//go:build goexperiment.simd && amd64

package pfordec

import "simd/archsimd"

var hasAVX2 = archsimd.X86.AVX2()

func fillConstant(output []uint32, val uint32) {
    if !hasAVX2 {
        fillConstantScalar(output, val)
        return
    }
    val8 := archsimd.BroadcastUint32x8(val)
    i := 0
    for ; i+8 <= len(output); i += 8 {
        val8.StoreArray((*[8]uint32)(output[i : i+8]))
    }
    fillConstantScalar(output[i:], val) // 处理剩下不足8个的元素
}

GOAMD64设置为v3或更高时,甚至可以直接把hasAVX2定为编译期常量true,省掉运行时判断的开销。DCS最终针对编码器和解码器的不同函数,分别用到了AVX2、AVX512,乃至AVX512+VBMI+GFNI+BITALG这些更细粒度的指令子集组合。

2. 256值垂直布局:AVX2直接带来3倍加速

TurboPFor有一种专为SIMD设计的“垂直布局”(256 uint32 vertical layout),同时处理8个uint32小端值,充分利用AVX寄存器宽度。

常规bitpacking的位布局示意图:

256值垂直布局(SIMD bitpacking)的解码顺序示意图:

标量版本用8个uint64累加器,逐一处理8个值:

func bitunpack256v32(input []byte, dest []uint32, bitWidth int) (read int) {
    mask := uint64(1)<<bitWidth - 1
    var bits uint
    var acc [8]uint64 // 累加器:当前位+剩余位
    for op := 0; op < len(dest); {
        if bits < uint(bitWidth) {
            for i := range 8 {
                acc[i] |= uint64(binary.LittleEndian.Uint32(input)) << bits
                input = input[4:]
            }
            bits += 32
        }
        for i := range 8 {
            dest[op] = uint32(acc[i] & mask)
            op++
            acc[i] >>= bitWidth
        }
        bits -= uint(bitWidth)
    }
    return len(dest)
}

SIMD版本同样处理8个值一组,但没有for i := range 8这层循环——8个值被“压”进了一个向量寄存器里并行处理。由于AVX2寄存器一次只能装8个uint32(装不下uint64),累加器被拆成rest8cur8两个Uint32x8

func bitunpack256v32(fullinput []byte, fulldest []uint32, bitWidth int) (read int) {
    dest := fulldest[:256]
    n := 32 * int(bitWidth)
    input := fullinput[:n]
    mask8 := archsimd.BroadcastUint32x8(uint32(1)<<bitWidth - 1)
    bitWidth8 := archsimd.BroadcastUint32x8(uint32(bitWidth))
    var bits uint
    pos := 0
    var rest8, cur8 archsimd.Uint32x8
    for op := 0; op < 256; op += 8 {
        if bits < uint(bitWidth) {
            next := archsimd.LoadUint8x32(input[pos : pos+32]).ReshapeToUint32s()
            pos += 32
            cur8 = rest8.Or(next.ShiftAllLeft(uint64(bits)))
            rest8 = next.ShiftAllRight(uint64(uint(bitWidth) - bits))
            bits += 32
        } else {
            cur8 = rest8
            rest8 = rest8.ShiftRight(bitWidth8)
        }
        cur8.And(mask8).Store(dest[op : op+8])
        bits -= uint(bitWidth)
    }
    return n
}

这个版本benchmark下来,速度是标量版本的约3倍。再叠加前面提到的泛型位宽特化,让bitWidth也变成编译期常量,速度还能进一步提升。

3. 位置汇编计数:AI发现的“隐藏2倍加速”

编码器套上同样的SIMD打包和AVX512异常收集手法之后,性能已经大致追平了cgo版本。但作者说,接下来发生的事让他很惊讶——Claude Fable 5指出,编码器的“扫描”阶段还可以用一种叫“位置汇编计数”(Positional Popcount)的技巧再提速一倍

回忆一下前文提到的发现:编码器真正的瓶颈不是编码本身,而是扫描全部输入值、统计出一份“在每个位宽下需要多少个异常值”的直方图

type stats struct {
    // cnt[n]:有多少个值的bits.Len32(val) > n
    // 即位宽为n时,需要多少个异常值
    cnt [32 + 24]uint32
}

func scan(output *stats, vals []uint32) {
    for _, val := range vals {
        for b := range bits.Len32(val) {
            output.cnt[b]++
        }
    }
}

用3个示例值直观理解:23(二进制0000010111,需要5位)、5(二进制0000000101,需要3位)、666(二进制1010011010,需要10位)。它们各自的“smear mask”(把最高位的1“抹开”到所有低位)分别是000001111100000001111111111111。用POPCNT可以高效地数出“一行”里有多少个1,但这里需要的是数“一列”——即所有输入值在第b位上一共有多少个1,这就是Positional Popcount,标准POPCNT指令帮不上忙。

作者参考了三篇论文/文章(2019年Klarqvist等人的AVX512进位保留加法器方案、2024年Harold Aptroot基于GF2P8AFFINEQB指令的实现、2025年Clausecker等人的改进版),最终选用了GF2P8AFFINEQB路线。

下面这张图梳理了位置汇编计数的核心流程:

值得一提的是,GF2P8AFFINEQB这条指令并不冷门——它也是Go 2025年推出的“Green Tea垃圾回收器”里的“压轴主角”。用Go SIMD实现出来是这样的:

func scanSIMD(output *stats, vals []uint32) {
    ones16 := archsimd.BroadcastUint32x16(^uint32(0))
    shuffle := archsimd.LoadUint8x64Array(&scanShuffle)
    units := archsimd.LoadUint8x64Array(&scanUnits)
    var acc archsimd.Uint8x64
    idx := 0
    for ; idx+16 <= len(vals); idx += 16 {
        v := archsimd.LoadUint32x16(vals[idx : idx+16])
        // 把每个值替换成它的smear mask
        smear := ones16.ShiftRight(v.LeadingZeros()).ReshapeToUint8s()
        // 先转置字节,再转置比特
        matrices := smear.Permute(shuffle).ReshapeToUint64s()
        transposed := units.GaloisFieldAffineTransform(matrices, 0)
        // 一次性对64字节做popcount并累加
        acc = acc.Add(transposed.OnesCount())
    }
    sum := acc.GetLo().ExtendToUint16().Add(acc.GetHi().ExtendToUint16())
    sum.GetLo().ExtendToUint32().Store(output.cnt[0:16])
    sum.GetHi().ExtendToUint32().Store(output.cnt[16:32])
    // 剩下不足16个值的标量兜底路径
    for _, val := range vals[idx:] {
        for b := range bits.Len32(val) {
            output.cnt[b]++
        }
    }
}

原本一个快速版的标量扫描函数,处理每个值大约需要12条指令;用上这套SIMD正交变换后,降到了每个值约1.5条指令——提速约8倍,直接反映在整体编码性能上,就是那个额外的2倍加速

下面这张图汇总了整个优化过程的性能演进路径:

反超之后:Go离C到底还差多远?

作者很诚实地指出,虽然新的Go实现已经超过了DCS过去用的cgo版本,但如果把同样的AVX512核函数和位置汇编计数技巧“对等移植”回C版TurboPFor,Go目前的benchmark结果仍然慢了约1.4倍。差距主要来自五个方面:

  1. 仍有部分标量路径:比如编码器里处理VB异常的函数、给所有位宽同时定价的逻辑,还可以进一步SIMD化,但这会让代码更难懂,作者对此保持谨慎;
  2. 边界检查(bounds checking):这是Go为了内存安全而付出的代价,作者明确表示不会为了性能关掉它,未来的优化空间在于让编译器的“证明”(prove)阶段更聪明地消除不必要的检查;
  3. 中栈内联(mid-stack inlining)产生的NOP填充:为了在二进制里放置内联标记,Go有时会插入额外的NOP指令,这对指令派发瓶颈型的函数是实打实的开销;
  4. 无法针对具体CPU型号定制:Go目前只能指定到GOAMD64=v3/v4这一级的微架构,而不能像clang那样精确到“AMD Zen 4”。例如Go编译器会在每条POPCNT前插入XORL CX,CX,这是为了规避Intel Sandy Bridge到Skylake时代的“伪输出依赖”问题,但在AMD Zen芯片上其实并不需要;
  5. 局部代码生成细节的差距:比如一个简单的循环变量自增操作,Go需要3条指令(POPCNTL; ADDQ; LEAQ),clang只需要2条(popcnt; lea)。这类差距是否值得修复,往往因场景而异。

小结:Go SIMD的意义,以及AI在其中的角色

Go的SIMD支持,第一次让开发者能够不依赖cgo、不依赖手写汇编,就用上现代CPU里那部分强大的向量计算能力——对TurboPFor这类计算而言,这是数量级的提速。

作者也坦率地分享了AI编程助手在这个过程中的价值:他用Claude Code(搭配Opus 5和Fable 5)处理了大量繁琐的性能调优工作——阅读objdump反汇编输出的速度远超人类,能发现人很难注意到的模式和关联,遇到编译错误或运行时panic也不会烦躁,只要给出可衡量、可达成的目标,它可以不知疲倦地反复试验。

作者特别强调,他没有“vibe coding”整个项目,而是在AI给出优化建议后亲自审阅、理解、确认。

从CPU性能计数器看,最终版本的数值解码速度达到了每周期7条指令(IPC),而这颗CPU的理论上限是8 IPC——已经相当接近极限。

对Go生态而言,SIMD支持的到来意味着更多原本必须依赖C语言或汇编才能达成的高性能场景,现在可以用纯Go实现,同时保留Go在内存安全、可维护性和跨平台交叉编译上的一贯优势。正如作者在文末所说:这是Go一个非常受欢迎的补充能力


参考资料

  • 原文:Michael Stapelberg,《Debian Code Search: Fast TurboPFor with Go SIMD》 https://michael.stapelberg.ch/posts/2026-09-06-dcs-fast-turbopfor-go-simd/
  • Go 1.26发布说明中关于simd/archsimd的介绍:https://go.dev/doc/go1.26#simd
  • 作者2019年的TurboPFor原理分析:https://michael.stapelberg.ch/posts/2019-02-05-turbopfor-analysis/
  • 作者2019年的DCS倒排索引/TurboPFor压缩实现:https://michael.stapelberg.ch/posts/2019-09-29-dcs-positional-turbopfor-index/
  • Debian/dcs项目相关commit:https://github.com/Debian/dcs
  • 位置汇编计数相关论文:
    • Klarqvist, Muła, Lemire (2019),《Efficient Computation of Positional Population Counts Using SIMD Instructions》:https://arxiv.org/abs/1911.02696
    • Harold Aptroot (2024),《Histogramming bytes with positional popcount (GF2P8AFFINEQB edition)》:https://bitmath.blogspot.com/2024/11/histogramming-bytes-with-positional.html
    • Clausecker, Lemire, Schintke (2025),《Faster Positional-Population Counts for AVX2, AVX-512, and ASIMD》:https://arxiv.org/abs/2412.16370

还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

Mermaid最强挑战者“断奶”上位:D2官宣转型非营利,压箱底三年的TALA布局算法也开源了

2026-09-10 07:00:00

本文永久链接https://tonybai.com/2026/09/10/d2-goes-nonprofit-tala-open-sourced

大家好,我是Tony Bai。

【导读】

D2,那个和Mermaid齐名、被很多工程师用来画架构图的文本转图表工具,这周连发两条重磅公告:母公司Terrastruct停止运营,创始人把D2整体捐赠给Hack Club,项目转为完全免费、非营利、开源;紧接着,原本要靠付费才能用的核心资产——自动布局算法TALA,也以和D2主项目相同的协议开源了。

更值得玩味的是,创始人顺带官宣:以后D2的代码贡献将全部交给AI来写,但明确表示“不做MCP、不做API、不做LLM插件”。一个开源项目在被砍掉商业化收入之后,是怎么想清楚“以后要做什么、坚决不做什么”的?

如果你写过软件架构图,大概率见过D2。它用一段纯文本描述节点和连线,编译成SVG,语法比Mermaid更贴近“画图”的直觉,社区一直把它称为Graphviz、Mermaid、PlantUML的有力挑战者。

就在这周,D2项目连发两篇博客,信息量都不小:

  • 9月5日,《D2 is non-profit》:母公司Terrastruct停止运营,D2作为独立、非营利、完全开源的项目,由Hack Club提供财务代管。
  • 9月7日,《TALA is open-source》:曾经的付费核心资产、自研自动布局算法TALA正式开源。

这两件事放在一起看,比单独看任何一件都更有意思。下面详细拆解。

【文章要点】

  • 商业公司退场与非营利转型:D2 母公司 Terrastruct 宣布关停,创始人将 D2 项目整体捐赠给知名非营利组织 Hack Club 进行财务代管,全面转向 100% 免费、完全开源的治理模式;
  • 核心资产 TALA 算法同步开源:自研三年、此前作为核心商业造血产品的正交自动布局算法 TALA,随 D2 v0.9.0 同步以 MPL-2.0 协议开源,支持在浏览器本地纯前端运行;
  • 前沿的“全 AI 驱动开发”宣言:创始人宣布 D2 下一阶段的所有代码贡献将完全由 AI 编写与交叉审核,彻底告别手写代码,但对外沟通与技术博客依然坚持 100% 人类纯手工撰写;
  • 极度克制的“三不做”产品哲学:面对 AI 扩张诱惑,主动划定非目标边界——不做臃肿的万能绘图工具、不内置大模型对话套壳、不做任何依赖服务器的能力(不做 MCP、不做 API、不做云端账户系统);
  • 专为 AI 时代赋能的混合布局:TALA 支持节点坐标的部分或全部锁定,由大模型确定方块大致位置,算法自动兜底美学排版与复杂连线路由,成为 Agent 时代“文本生成图形”的最佳基础设施。


一图读懂:这周到底发生了什么

D2从商业开源核心到独立非营利项目,经历了如下几个关键事件和时间节点:

  • 多年积累 : D2语言开源免费D2 Studio(付费IDE)与TALA(付费布局算法)负责商业化造血
  • 2026年6月 : 开源社区围绕“AI灌水式贡献”产生广泛争议(大环境铺垫,非D2项目本身事件)
  • 2026年9月5日 : Terrastruct停止运营,创始人将D2整体捐赠给Hack Club,D2转型为非营利、完全开源项目
  • 2026年9月7日 : TALA自动布局算法开源,与D2主项目同用MPL-2.0协议

简单说:过去D2走的是典型的“开源核心(open-core)”路线——语言本体和编译器免费开源,靠闭源的可视化IDE“D2 Studio”和自研布局算法“TALA”两个付费产品养活团队。现在,养它的商业公司没了,项目本身没有跟着一起消失,而是被“捐”给了非营利组织,原来收费的TALA也一起开源了。

发生了什么:从“开源核心 + 商业增值”到“完全非营利”

D2创始人在公告中说得很直白:项目诞生和发展至今,一直是一家营利性公司旗下的开源核心业务,靠闭源的D2 Studio和TALA养活开发。现在公司要关停,D2会作为独立的、完全开源的非营利项目继续存在,由Hack Club提供财务代管(fiscal sponsorship)。

创始人表示会继续维护和改进D2,但坦言自己的时间投入会比过去有限,这已经不是他的主业了;如果有人愿意成为维护者,高质量的维护和贡献甚至可能进入由捐赠资金支付的合同关系,他认为这对有开源经验的早期职业开发者会是个不错的机会。

这里有个关键背景信息值得补充:根据D2官方的Sponsor说明页面,D2目前是由The Hack Foundation(Hack Club背后的501(c)(3)非营利组织)提供财务代管,捐赠在美国可以抵税;创始人和另一位共同创始人都不会从这笔基金中拿到任何报酬,资金去向完全公开可查。

钱从哪来、花到哪去:D2的治理与资金结构

这套结构和很多“公司捐赠开源项目给基金会”的先例(比如一些项目转投Linux基金会、Apache基金会)逻辑类似,核心是把项目的存续和某一家公司的商业命运脱钩。创始人特别强调:赞助D2不会影响项目的路线图、治理方式或许可证,D2依然是MPL-2.0协议下的开源项目,开发全程在GitHub上公开进行。

创始人的表态:写代码的时代结束了,AI来写

公告里最引发讨论的一段,是创始人对D2下一阶段开发方式的定调:

这一阶段的D2,靠的是纯手写代码。这个时代结束了。下一阶段的D2将由AI驱动——我的所有代码贡献都会使用AI完成,也欢迎社区提交AI生成的贡献,我会用AI去审核AI提交的内容。唯一的例外是文字内容:所有博客文章和对外沟通,我都会亲自撰写,不使用AI。

他给出的理由是效率:过去十多年他几乎每天都在写代码,但过去一年里,虽然仍在和软件打交道,却一行代码都没有亲手写过。他认为AI能把过去因为工程人力有限而只能停留在设想阶段的功能真正做出来,比如等距渲染器、从其他画图语言迁移过来的转译器等。

值得肯定的是,创始人并没有回避这个决定的争议性。他在公告中直接引用了开源维护者Neil Alexander今年6月的一篇博客《Please stop flooding our projects with AI slop to furnish your CV》[4]。那篇文章描述了一个真实但让人不太舒服的现象:不少开源项目最近收到大量由AI代写、贡献者本人几乎没有实质投入的PR和漏洞报告,有人怀疑这背后是有人在“刷GitHub简历”,Neil Alexander本人后来直接关闭了三个这样的PR,不作评论。

D2创始人的态度是:我知道这样做有争议、有代价,我不是在向任何人推荐这么做,只是想坦诚地把自己的计划告诉依赖D2的社区。这种“先把丑话说在前面”的坦率,某种程度上也是这篇公告本身比较少见的地方。

“三不做”:一份克制的产品哲学

有意思的是,创始人在拥抱AI提效的同时,反而给项目定了更严格的边界,公告里专门开了一节叫“Non-goals”(非目标):

  1. 不做臃肿的万能画图工具。他坦言AI确实能让他轻松把D2做成一把瑞士军刀,但他更愿意把“算力预算”花在打磨一个小而精的核心功能集上,而不是盲目堆功能——比如即便实现起来很简单,他也不打算给D2加维恩图(Venn diagram)这种明显偏离软件架构图定位的功能。后续会考虑做插件/模块系统,把这类“不属于D2核心”的功能挪到外部去。
  2. 不在D2里内置LLM接口。他的判断是,现在的模型已经足够聪明,只需要看几个示例,就能把用户的需求、代码或规格说明转成一份.d2源文件,不需要再给D2专门做一个调教过的LLM对话界面。
  3. 不做任何需要服务端的能力。因为D2现在是非营利项目,不会去支撑需要服务器的功能:不做MCP、不做API、不做用户账户系统、不做服务端渲染、不做多人协作。D2 Studio会做成纯客户端应用,完全可以离线使用。

这份“三不做”清单,读起来更像是一份克制的产品哲学声明——在拥有更强AI生产力工具的同时,主动给自己设边界,避免把“能做”和“该做”混为一谈。

路线图:接下来重点做三件事

其中第三条格外值得展开。创始人的判断是:现在的大模型已经很擅长“在二维空间里把图画得差不多”,但在自动布局这件事上仍然不够确定、不够可控、不够精确,这个局限短期内很难被彻底解决——所以你仍然需要一份文本化的“图源代码”作为最终产物的底层依据,就像网页仍然需要HTML,而不是靠像素级手绘出来一样。换句话说,D2想成为Agent时代“文本生成图形”的标准底座,而不是被Agent绕过去的旧工具。

重头戏:TALA终于开源了

如果说“转型非营利”是D2这周公告里的第一个重磅消息,那TALA开源就是紧随其后的第二个——而且从产品逻辑上看,这一步几乎是必然的:D2既然已经不再靠商业化生存,那么继续把核心布局算法闭源就没有意义了。

TALA是什么

TALA,全称Terrastruct’s AutoLayout Algorithm,是D2团队自研的一种新型自动布局算法,专门为软件架构图设计。和D2内置的另外两种布局引擎Dagre、ELK不同,TALA走的不是“有向无环图(DAG)逐层展开”的路线,而是一种正交布局(orthogonal layout)算法,视觉效果更接近人在白板上手绘架构图的样子。

TALA的设计目标是同时优化多个“美学”指标,包括对称性、节点间的中位距离、信息流向、同类节点的聚类程度等等,算法本身融合了多篇图绘制学术论文的思路(代码中有引用),再加上团队自己的原创技术。

这次开源之后,TALA使用和D2主项目相同的MPL-2.0协议,代码已经并入D2 v0.9.0,安装后只需在渲染时加上--layout=tala即可启用:

d2 --layout=tala architecture.d2 architecture.svg

不想安装的话,也可以直接在浏览器里体验——D2的官方在线Playground(play.d2lang.com)现在同样支持TALA布局,且完全在客户端本地运行,不经过任何服务器。

TALA vs Dagre vs ELK:什么时候该用哪个

维度 Dagre / ELK(DAG系) TALA(正交系)
布局风格 分层、单向延展,适合长链路流程图 更接近白板手绘的正交布局
稳定性 增删节点后局部变化,整体形态基本稳定 有随机性,同样输入相同种子结果一致,但增加一个节点后可能整体重排
坐标控制 一般由算法全权决定 支持部分或全部节点手动锁定top/left坐标,算法负责剩余部分与连线路由
适用场景 偏“流程”类的长链路架构图、DAG关系图 偏“系统全景”类、需要对称美观的软件架构图;尤其适合AI生成图形后交给算法排版和走线的场景
性能 大图渲染相对线性 图越大耗时增长越快(非线性),大规模图谱场景需注意

官方博客里给出的对比方式也很实在:不是挑几张“TALA赢麻了”的图来秀,而是从GitHub上随手抓了7个真实存在的公开D2项目文件,用完全相同的编译器、完全相同的源码,只切换布局引擎,把TALA、Dagre、ELK三种效果并排放出来,并且坦承“对某些图,你可能反而更喜欢非TALA的布局”。

上图是三种布局引擎横向对比示例(以“Fulcro RAD architecture”项目为例,展示同一份D2源码分别用TALA / Dagre / ELK渲染的效果)

更值得关注的是“坐标可控”这个特性

除了美学效果,TALA这次开源博客里其实埋了一条对AI时代更重要的信息:它支持节点位置和尺寸的自定义,也就是可以给部分或全部节点直接指定坐标(top/left),算法只负责剩下节点的自动摆放以及所有连线的路由(routing)。

创始人特意提到,这个特性“特别适合Agent场景”——大模型在二维空间里摆放几个方块的位置这件事做得不错,但要同时兼顾走线不交叉、间距美观、整体布局协调,目前仍然是模型的弱项。TALA的思路相当于做了一次分工:坐标交给模型(或人)来定,走线和排版细节交给算法来兜底。

第三组示例则演示了“部分锁定 + 部分自动”的混合模式:比如“The Printing Room”这个例子里,4个CMYK印刷工位被固定在同一行、等间距排列,以保持印刷流程的机械对齐感,而进料器、摄像头、配准控制器、烘干设备等另外10个节点则完全交给TALA自动摆放。

TALA不是万能的:官方自己列出的三条权衡

官方博客难得地对自家算法的短板讲得很直接,值得原样记录:

  1. 有随机性。算法默认会用3个种子分别计算布局,选出评分最高的一个;相同种子、相同输入会得到相同结果,但哪怕只多加一个节点,整张图的布局都可能完全变样。相比之下,Dagre和ELK在新增节点后通常只是“见缝插针”地局部调整,整体形态基本不变——这一点在某些场景下反而是TALA的优势,但在需要“图稳定不跳变”的场景下就是明显短板。
  2. 不太擅长处理DAG。官方原话是“我经常发现,当我想要一张长长的、单向流动的图时,还是更愿意用Dagre或ELK”。
  3. 大图渲染更慢,且非线性增长。图越大,TALA相对Dagre/ELK的耗时差距会被进一步拉大。官方给出了专门的基准测试仓库供参考:https://github.com/d2lang/d2-benchmarks 。

这种“既秀成果又主动交代局限”的写法,其实也侧面印证了D2团队一贯的风格——不做过度营销,把选择权交给使用者。

这件事为什么值得开发者关注

抛开“又一个开源项目变动”的表面新闻属性,这次D2的操作组合,至少有三层信息值得留意:

第一,对普通使用者来说是实打实的利好。 原本需要付费才能用的IDE增强能力和高质量布局算法,现在都免费拿到手了,尤其是TALA的坐标锁定特性,对正在探索“用AI批量生成架构图”的团队会很有吸引力。

第二,它给“开源项目如何摆脱单一商业公司依赖”提供了一个具体案例。 不是项目直接停止维护,也不是简单换个东家卖掉,而是通过基金会财务代管的方式,把项目的存续和某一家公司的经营状况彻底解绑,资金去向公开透明可查,这种治理结构对其他面临类似困境的开源项目有参考价值。

第三,创始人对“AI驱动开发”的坦率表态,本身就是一次公开的实践样本。 他没有回避AI贡献可能带来的质量风险和社区信任问题,反而主动引用了争议文章、承认这样做有争议,同时用“三不做”给项目划了清晰边界——这种“拥抱效率、但拒绝盲目扩张”的姿态,或许比技术细节本身更值得同行讨论。

小结

D2这次的两连发公告,信息密度其实很高:一家公司关停、一个项目转型非营利、一套曾经的商业资产开源、一份关于AI驱动开发的坦率宣言、外加一份克制的产品边界声明,全部发生在同一周。对已经在用D2的团队来说,最直接的动作是升级到v0.9.0,把--layout=tala跑一遍试试效果;对关注开源治理和AI原生开发范式的朋友来说,这或许是一个值得持续跟踪的样本项目。

如果你也认可D2这种“非营利+完全开源”的路线,可以通过官方渠道进行捐赠,资金用途公开可查:https://hcb.hackclub.com/d2 。

参考资料

  • [1] D2 is non-profit:https://d2lang.com/blog/d2-non-profit/
  • [2] TALA is open-source:https://d2lang.com/blog/tala-is-open-source/
  • [3] Sponsor D2:https://d2lang.com/sponsor/
  • [4] Please stop flooding our projects with AI slop to furnish your CV:https://neilalexander.dev/2026/06/30/flooding-contributions
  • [5] D2项目GitHub仓库:https://github.com/d2lang/d2
  • [6] TALA性能基准测试仓库:https://github.com/d2lang/d2-benchmarks

还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 从0 开始构建 Agent Harness 将带你:

  • 抛弃臃肿框架,回归“驾驭工程 (Harness Engineering)”的第一性原理
  • 用 Go 语言手写 ReAct 循环、并发拦截与上下文压缩引擎等,复刻极简OpenClaw
  • 构建坚不可摧的 Safety Middleware 与飞书人工审批防线
  • 在底层实现 Token 成本审计、链路追踪与自动化跑分评估
  • 从“调包侠”进化为掌控大模型边界的“AI 操作系统架构师”

扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

刚刚,YC 公开处刑「唯模型论」:决定 Agent 上限的,从来不是模型,而是 Harness

2026-09-09 06:00:00

本文永久链接https://tonybai.com/2026/09/09/why-harness-matters-more-than-model-yc-paper-club

大家好,我是Tony Bai。

【导读】

一个月前,Reddit 上有人吐槽:“这种 prompt engineering 不该出现在顶会上。” YC Paper Club 用一整晚的分享正面回应了这句质疑:同一份 Claude Opus 权重文件,裸跑 ARC-AGI-3 只有 30% 正确率,套上合适的 Harness 之后能冲到 95%,NVIDIA 的 AVO 甚至跑到了 100%。模型的智商在原地踏步,真正决定 Agent 天花板的,是那层长期被低估的“脚手架”。

【文章要点】

  • Harness(智能体运行时/脚手架)不是“套壳”,而是模型与真实世界之间唯一的接口层:工具、记忆、循环、权限,全部长在这一层上;
  • 一张六年演化图:从 GPT-2 的裸循环,到 Few-shot、思维链、工具调用、记忆、技能、多智能体反思,再到能递归调用自己的 RLM;
  • 真正的分水岭出现在最近半年:Harness 正在从“静态脚手架”进化为“能自我修改的系统”,DSPy、Darwin 机器、Continual Harness 是三个代表;
  • Prime Agent 用类似 CPU 缓存的 L1/L2/L3 记忆分层,把 ARC-AGI 一路调到接近满分区间,还跑了一次 7 天、633 个智能体的超长程实验;
  • 斯坦福 Open Jarvis 证明:把 Harness 做对,本地小模型也能在成本上把云端大模型甩开 800 倍;
  • YC 自己内部的 QM 项目揭示了一条很朴素的教训:把“大脑”从沙盒里拉出来、给足权限、别让 Agent 太快放弃。


一场“不该存在”的分享

YC 每隔一段时间会办一场叫 Paper Club 的小型分享会,专门讨论一篇或几篇值得细读的论文。这一期的主题很直接——Harness,也就是套在大模型外面的那层运行时:系统提示词、工具调用、记忆管理、循环控制、权限系统……几乎所有让模型从“一个会聊天的黑盒”变成“能干活的智能体”的工程,都发生在这一层。

主持人开场就抛出了两条来自 Reddit 的评论:一条说这种“prompt engineering”不配出现在顶会上;另一条说“上下文工程根本不是研究问题”。Harness 长期被视为学术意义上的“边角料”——直到最近的数据打了所有人的脸。

METER 那张经典的“模型能持续自主工作多久”曲线一直在指数增长,但推动这条曲线上扬的,很大一部分并不是模型智商本身的提升,而是 Harness 的迭代。主持人举了一个更具体的例子:在 ARC-AGI 上,同一份 Claude Opus 权重,裸跑只能拿到 30% 的成绩,套上合适的 Harness 之后可以冲到 95%,而 NVIDIA 的 AVO 甚至跑到了 100%。这中间的 65 个百分点,模型权重文件一字未变。

先弄清楚:Harness 到底是什么

Prime Intellect 研究员 Seth(Prime Agent 的作者)在分享里给出了一个很干净的定义:如果只看权重文件本身,LLM 其实只是一个序列处理器——输入一串 token,输出一串 token,用来预测下一步该做什么。而 Harness,是加在模型和真实世界之间的那一层,负责提供持久化状态、工具和算力

换句话说,模型负责“思考”,Harness 负责让这个思考落地:读写文件、调用 API、维护记忆、协调多个子智能体、决定什么时候该继续干、什么时候该停下来。同一个模型套上不同的 Harness,表现可以有天壤之别——这正是这场分享想论证的核心命题。

六年演化史:从裸循环到能自我修改的系统

主持人花了一个周末重读了从 Self-Refine、Reflexion、Voyager 到 Toolformer 的经典文献,压缩出一条五分钟版本的演化脉络。这条脉络不是严格按时间排列的,而是按“能力维度”组织的——每一步都在往 Harness 里加一种新的表达能力。

几个关键节点值得展开说一下:

  • GPT-2 的 V0 时代(2019):没有工具调用,没有技能,只有一个系统提示词、一段上下文,和一个“等到句末标记就停”的循环。这是所有 Harness 的原点。
  • Few-shot 与思维链(2020 前后):本质上都是“上下文空间”和“输出空间”的创新——前者是给模型看几个例子帮它对齐任务,后者是不再要求模型直接吐出答案,而是把推理过程“摊”到更多 token 上再收敛到答案。
  • WebGPT 与 Toolformer:第一次让模型学会调用外部工具——与其在权重里算 5 减 2,不如直接调用一个减法函数。工具调用的雏形就此诞生。
  • MemGPT:第一次给了模型对自己上下文的增删改查权限,而不只是不断往后追加,这为后来的“长期记忆”打下基础。
  • Voyager:在 Minecraft 环境里,模型把反复验证有效的工具链路蒸馏成一份 skills.md,需要时检索调用——这基本就是今天“技能(Skill)”概念的原型。
  • ReAct → Self-Refine → Reflexion:让模型自己扮演不同角色互相评审、结合环境反馈修正错误,是“多智能体自我改进”思路的起点。
  • RLM(Recursive Language Model):允许主控智能体递归地派生子智能体去解决子问题,子智能体还可以再派生自己的子智能体——这是“Harness V1”走向成熟的关键一步。

这一整套演化,主持人把它统称为 Harness V1:静态时代——无论工具、技能、子智能体列表配置得多复杂,Harness 本身(也就是这套代码逻辑)在运行过程中是不变的,改变的只是往上叠加的功能。下面是一个典型的 V1 循环架构:

真正的拐点:让 Harness 自己学会进化

最近半年,事情开始变得“更 trippy”——不是模型在学习,而是 Harness 本身开始学习。这里有三条代表性路线:

  1. DSPy(Demonstrate-Search-Predict):拿一小批训练样例,通过类似遗传编程的方式不断生成、合并、评估候选系统提示词,虽然没法反向传播,但可以持续迭代出更优的提示词版本,相当于对系统提示词做 CRUD。
  2. Darwin 机器(Darwin Gödel Machine 一类工作):比 DSPy 更进一步——不只是改系统提示词,而是允许修改 Harness 的代码本身。你会维护一个“智能体档案库”,每个个体都是一份(Harness 代码 + 系统提示词)的组合,通过适应度函数评估、筛选、变异,再放回档案库,循环往复。元 Harness 的任务,就是生产更好的 Harness——这是一个相当“元”的设计。
  3. Continual Harness:在此基础上进一步细化了记忆的分类(历史、技能、记忆、子智能体规格等),并且引入了 Dagger 式的在线学习思路——不仅更新 Harness 配置,甚至可以在小样本上对权重文件本身做测试时训练。主持人称这是“一个值得投入的重要研究方向”。

正是这一批工作,把 Harness 从“人工设计的脚手架”变成了“会自我改写的系统”,也是当晚三位主讲人分享的三个项目——Prime Agent、Open Jarvis、QM——共同的技术底色。

案例一:Prime Agent——像管理 CPU 缓存一样管理记忆

Prime Intellect 的 Seth 分享的 Prime Agent,被定义为“一个自我进化的 RLM Harness”。他的核心方法论是:把记忆按访问速度和持久性分成三层,类比 CPU 的缓存体系。

在这套结构里,Prime Agent 把子智能体做成了“持久化子会话”:主会话可以随时派生新的子智能体去执行任务,任务完成后子智能体并不会消失,而是进入“闲置”状态,仍然驻留在内存里,需要时可以直接把消息发回去继续工作,不必重建上下文——这解决了多智能体协作中最常见的“上下文丢失”问题。

实测数据上,团队在 ARC-AGI-3 上的调优过程颇为坎坷也颇为真实:第一轮直接套用一个社区系统提示词,跑出了 99.9% 的“惊人成绩”——结果一看日志,是“作弊”了(沙盒没做好隔离);重新搭好沙盒后,GPT-5 系列跑出 78%;换用其他模型也各有 70~80% 区间的成绩。团队还特意强调了一个容易被忽视的问题:很多评测在比较模型时,实际花费的预算(时间、token)并不对等,这会掩盖真实的性能差异,所以他们更看重“长程性能的实际拐点在哪里”,而不是单点分数。

除了 ARC-AGI,团队还做了一次相当极限的长程实验:用 8 台 H200 节点跑了 7 天,累计调度 633 个智能体、产出 2300 万 token,在一个类似“自动化工厂搭建”的任务里持续做技术树推进。有意思的是,直到实验结束,智能体依然没有陷入停滞,还在持续取得技术进展——这被主讲人半开玩笑地称为“我们自己的《Gemini Plays Pokémon》”。

Seth 给出的经验总结是:想把 Harness 做好,重点关注智能体化的上下文管理(分层记忆、压缩、精炼)、群体协作(Swarm)与 RLM 的深度使用,以及用标准化的评测方式衡量长程性能,而不是只看单点分数。

案例二:Open Jarvis——把“大脑”搬回本地,成本降 800 倍

斯坦福团队分享的 Open Jarvis,出发点是一个很现实的问题:目前的个人 AI 几乎全部依赖云端大模型,代价是高昂的 API 费用、隐私风险,以及“你在租智能,而不是拥有智能”。他们想验证:能不能把模型推理、智能体执行、记忆与学习这套核心栈完全放到本地设备上跑,同时性能不明显掉队?

团队把任何一套个人 AI Harness 拆解成五个最基础的原语:

  • 用户界面:想通过什么方式和 Agent 交互;
  • 智能体逻辑:如何组合推理与工具使用;
  • 智能(模型):用哪个本地模型(如 Qwen、GPT-OSS、Gemma 3N);
  • 推理引擎与硬件:Ollama、llama.cpp、vLLM、SGLang,跑在 Apple Silicon 还是 NVIDIA 上;
  • 工具、记忆与学习原语:通过标准的 MCP 协议接工具与记忆,并支持提示词层面的优化(如 DSPy)或权重层面的优化(如 LoRA、SFT)。

他们做的一个巧妙设计是:用云端大模型去自动优化本地小模型的 Harness 配置——诊断问题、提出改进方案、生成更优配置,但推理阶段完全不产生云端调用费用。

结果显示,经过这种优化的本地配置,相比“开箱即用”的本地部署有显著提升,在部分个人任务、编程、智能体任务上已经能和云端模型打平;即便仍有不少任务本地模型力有不足,这个差距正在按月收窄。

团队给出的关键数字是:成本最多可降低约 800 倍,同时延迟也显著下降。而且无论选用哪家云端模型来做优化器(Opus、GPT、Gemini、Kimi、GLM 等),都能带来收益——说明“用云端智能优化本地 Harness”这条路径本身具有一定的通用性。

案例三:QM——YC 自己踩过的四代 Harness 演进

如果说前两个项目更偏研究性质,YC 内部团队分享的 QM 项目则更像一份“血泪史”。QM 是 YC 自研的开源 Agent Harness,目标是让每个员工都拥有一个可深度定制、随时可在 Slack 或网页里调用的助手。

它的演进经历了四个阶段:

其中最关键的设计转折发生在 Hermes 舰队阶段之后:团队发现,把智能体的“大脑”完全绑定在某一台沙盒电脑里,看似强大,实则脆弱——一旦沙盒数量上来,管理成本会失控,而且所有会话记录都被“锁”在那台机器里,无法被系统整体利用。

QM 的解法是把所有对话与状态统一卸载到 Postgres,再暴露给 Agent 本身去调用,而沙盒不再是 Agent 的“家”,而是它按需取用的一种资源——需要重型开发环境就申请强力沙盒,任务简单就用轻量沙盒,这个决策权也被下放给了 Agent 自己,而不是写死在 Harness 逻辑里。

分享中还提到几个非常“接地气”的工程教训,很值得国内做 Agent 产品的团队参考:

  • Agent 放弃得太早:团队引入了一个“死磕工具(grind tool)”,给任务设定最低耗时或最低 token 预算,Agent 在达到预算前不允许放弃任务——这一简单机制显著提升了研究报告等长任务的产出质量,据分享,OpenAI 和 Anthropic 近期在数学难题上取得的一些突破也用了类似技巧。
  • Agent 容易“搞不清自己在哪”:即便系统提示词里写得很清楚,Agent 在多人协作(比如 Slack 群聊)场景下依然容易搞混当前处境,需要额外的“本地感知锚点”来纠偏。
  • 权限与“社交常识”的缺失:人类天然知道什么信息该讲给谁听,但 Agent 没有这种直觉,私密信息很容易“泄漏”到不该出现的对话里。团队的解法是依托 YC 已有的细粒度权限系统——但他们也坦言,大多数公司并不具备这样的权限基础设施,这方面还需要投入相当的工程量。
  • 人类审核正在被“橡皮图章化”:早期 Agent 提出的数据库写操作变更,团队会逐条仔细审核;但随着信任建立,审核正在变得越来越流于形式——这被认为是接下来几个月需要重点关注的风险点,而不是一个可以放心的信号。

从这三个案例里,能提炼出哪些共性

把 Prime Agent、Open Jarvis、QM 放在一起看,几条设计原则反复出现:

  • 把决策权尽量下放给 Agent,而不是写死在 Harness 里:无论是“该用哪个沙盒”还是“该切换到哪个模型供应商”,让 Agent 自己判断,往往比在 Harness 层强制规定效果更好;
  • 记忆要分层管理,而不是无限堆砌上下文:从活跃上下文到 Ripple/RAM 再到持久化存储,压缩与精炼要成为常态化机制;
  • Harness 应当尽量精简,只保留真正核心的能力:QM 团队特意提到,他们把核心工具收敛到“远程沙盒执行、对象存储读写、内部应用发布”这三项,其余都被视为“过渡性补丁”;
  • 要给 Agent 足够的“耐力”,不能一遇挫折就放弃:预算约束下的“死磕”机制,是当前提升长程任务质量的一个简单但有效的手段;
  • 信任的建立要伴随持续的审查机制,否则“人类在环”很容易退化为形式主义。

写在最后

这场分享传递的核心信息其实很朴素:过去几年里,模型的智商曲线(用主持人的话说,接近“IQ”维度)确实在稳步上升,但真正把这份智商转化为可用生产力的,是 Harness 这层长期被视为“配角”的工程。

ARC-AGI 上从 30% 到 95%、95% 到 100% 的跃迁,靠的不是换了更聪明的模型,而是给同一个模型换了一副更懂得怎么用它的“身体”。

当模型能力的边际提升开始放缓,Harness 会不会成为下一个真正的竞争高地?至少从这场分享的三个项目来看,答案已经相当明确。

本文根据 YC Paper Club 分享《Why The Harness Matters More Than The Model》整理编译,视频原文参见 YouTube(https://www.youtube.com/watch?v=n9xKblqyQ28)。


还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 从0 开始构建 Agent Harness 将带你:

  • 抛弃臃肿框架,回归“驾驭工程 (Harness Engineering)”的第一性原理
  • 用 Go 语言手写 ReAct 循环、并发拦截与上下文压缩引擎等,复刻极简OpenClaw
  • 构建坚不可摧的 Safety Middleware 与飞书人工审批防线
  • 在底层实现 Token 成本审计、链路追踪与自动化跑分评估
  • 从“调包侠”进化为掌控大模型边界的“AI 操作系统架构师”

扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

两年攻坚、11 个子任务闭环,Go 终于把 HTTP/2 这个“寄养”十年的孩子接回了标准库

2026-09-08 06:00:00

本文永久链接https://tonybai.com/2026/09/08/go-127-http2-move-into-std

大家好,我是Tony Bai。

【导读】

2026 年 8 月,Go 1.27 正式发布。在一堆“泛型方法”、“JSON v2”、“后量子加密”这些吸引眼球的新特性背后,藏着一件低调却意义重大的工程大事:跑了整整两年、拆成 11 个子任务的跟踪计划 #67810 终于收官——HTTP/2 的“官方实现”从外部仓库 golang.org/x/net/http2 正式搬进了标准库 net/http/internal/http2。这背后,是 Go 核心团队一次教科书级别的“大型遗留重构”操作。

【文章要点】

  • Go 从 1.6 版本(2016 年)就“透明支持”HTTP/2,但真正的实现代码其实一直“寄养”在 golang.org/x/net/http2 这个外部模块里,标准库只是通过一个叫 bundle 的打包工具把它“塞”进来,生成一份谁都不敢手改的 h2_bundle.go
  • 这种“寄养架构”带来了四个长期痛点:安全补丁难回合并、HTTP/1 与 HTTP/2 改动无法原子化、新版 net/http 要兼容老版 x/net、用户想调个 HTTP/2 参数还得单独 import 一个包。
  • Go 团队用一个跟踪 Issue(#67810)把这件“大事”拆成了 11 个可独立交付的子任务,历时两年,逐个击破。
  • Go 1.27(2026 年 8 月发布)标志着这次迁移已经“事实”完工(只是这件事估计要在Go 1.28才会宣布):net/http/internal/http2 成为唯一“真源”,golang.org/x/net/http2 反过来“包装”标准库实现,角色彻底反转。
  • 对普通 Go 开发者的直接好处:配置 HTTP/2 参数、启用明文 h2c、控制并发流数量等操作,现在全部可以用标准库原生 API 完成,不用再额外引入 x/net


一个“寄养”了十年的核心协议实现

如果你写过 Go 的网络服务,大概率享受过这样的“隐形福利”:你只需要 http.ListenAndServe,Go 就自动帮你把 HTTP/2 安排得明明白白,TLS 握手一谈妥,协议自动升级,你几乎感觉不到它的存在。

但很少有人知道,这套“自动挡”背后的真实实现,长期以来根本不在标准库仓库里,而是住在另一个独立维护的模块——golang.org/x/net/http2

标准库要用它,得先经过一道特殊工序:用一个叫 bundle 的代码生成工具,把整个 http2 包“打包压平”成一份巨大的单文件 h2_bundle.go,塞进 net/http 包里一起编译。这么绕一圈的原因很简单:x/net/http2 本身依赖 net/http,如果标准库直接反向 import 它,就会形成导入环——Go 编译器是不允许这种循环依赖的。

于是我们得到了一个略显魔幻的架构:

这套“寄养架构”跑了整整十年(从 Go 1.6 到 Go 1.26),也确实撑住了 Go 生态最重要的网络协议升级。但代价也在慢慢累积。

痛点攒够了,官方决定“接孩子回家”

2023 年 6 月,Go 团队核心成员 neild 在官方 Discussion 区发起了一次提案(#60746),把这些年攒下的问题一次性摊开:

  • 给标准库的 HTTP/2 实现打安全补丁,要先改 x/net,再走一套复杂流程“倒灌”回 net/http,backport 过程繁琐易错。
  • HTTP/1 和 HTTP/2 的实现分处两个仓库、两套发布节奏,想做一次原子性修改几乎不可能。
  • net/http 的新版本还得兼容旧版本的 x/net,两边版本矩阵越滚越大。
  • 用户想配置 HTTP/2 的具体行为(比如并发流数量、心跳间隔),必须额外 import golang.org/x/net/http2,而且一旦这么做,标准库内置的实现会被整个替换掉——配置和“用哪套实现”这两件事被死死绑在了一起。

一年后,也就是 2024 年 6 月,这份提案正式落地为跟踪 Issue #67810,标题干脆利落:net/http: move HTTP/2 into std(把 HTTP/2 挪进标准库)。目标也说得很直白:让标准库仓库成为 HTTP/2 实现的唯一“真源”(source of truth),并最终让 golang.org/x/net/http2 走向废弃。

把一个“不可能完成的大重构”拆成 11 块骨头

这个项目最值得学习的地方,其实不是技术方案本身,而是工程管理方式。Go 团队没有搞“一次性大爆炸式重写”,而是把整个迁移拆解成 11 个可以独立设计、独立评审、独立合入的子任务,每一个都有自己的编号、自己的提案讨论、自己的验收标准:

拆解逻辑其实很清楚,可以归纳为三条主线:

第一条主线:先把配置能力“暴露”到标准库。 这是最先动手、也最先让普通用户受益的部分。#67813(HTTP/2 配置 API)和 #67814(协议版本选择 API)在 Go 1.24 就已经落地:标准库新增了 http.Protocols 类型和 http.HTTP2Config 结构体,用户第一次可以不引入任何外部包,就直接在 http.Serverhttp.Transport 上配置 HTTP/2 的行为。#67816 则顺带解决了一个长期为人诟病的问题——原生支持未加密的 HTTP/2(也就是常说的 h2c),此前这必须依赖 x/net/http2/h2c 这个“补丁包”才能实现。

第二条主线:把真正的协议实现代码“物理搬家”。 这是硬骨头,涉及把 x/net/http2 里成千上万行的 frame 解析、流控、多路复用、写调度器逻辑,原样迁移到新建的内部包 net/http/internal/http2,同时删掉大量只为兼容独立模块而存在的历史包袱(比如那些专为导出测试用的 ExportXxx 函数、ServeConnOpts.UpgradeRequest 这类过渡期 API)。从评论区可以看到,仅“初始导入”(CL 751300)之后,团队还花了大量精力清理测试代码、让 net/http/internal/http2 直接复用 net/http 自己的 TransportServer 做测试,而不是自成一套测试基础设施。

第三条主线:把旧包“体面地”退休。 #67819、#77695、#78064 这三个 Issue 是一脉相承的关系——最早提议把 frame 操作单独拆包,后来评估后发现不如直接提议废弃整个 ClientConnPool,最终演变成一个更彻底的方案 #78064:直接废弃 x/net/http2 里的 TransportServerConfigureServerConfigureTransport 等一整套 API。逻辑很简单——这些能力标准库现在都有了,没必要留两份。最后一步 #78508,则让 x/net/http2 摇身一变成为 net/http 的一层“包装壳”,专门服务那些暂时还没升级、依旧直接 import 老包的存量用户。

整条主线走完,正好呼应了 issue 里那句朴素的目标:“先让 x/net/http2 里每一个非废弃功能,在别处都能找到(配置项进 net/http,部分功能进新包,部分直接废弃)”。

Go 1.27 之后:架构彻底反转

2026 年 8 月,Go 1.27 正式发布,这场持续两年的迁移画上了句号。架构关系发生了根本性反转:

用官方仓库 x/net/http2 自己 README 里的话说得非常直白:

从 Go 1.27 开始,真源已经转移到标准库包 net/http/internal/http2。所有新特性开发都应该发生在那个包里,x/net 只会继续得到关键 bug 修复和安全补丁的回合并。

也就是说,golang.org/x/net/http2 并没有立刻消失,而是转型成了两种实现的"外壳":对 Go 1.27 以下版本,它还是原班人马的老实现;对 Go 1.27 及以上版本,它默认变成一层薄壳,内部直接调用 net/http(如果你还想用回老实现,可以加上 http2legacy 编译标签)。

用一条时间线回顾整个过程会更直观:

对普通 Go 开发者意味着什么

先说结论:大部分人什么都不用改。这次迁移最重要的设计原则之一,就是完全遵守 Go 1 兼容性承诺——你的老代码、老依赖,不需要任何改动就能继续在 Go 1.27 上跑,无论你是否使用了 HTTP/2。

但有几件事,从现在开始会变得简单很多:

1. 配置 HTTP/2 参数不再需要额外依赖。 以前想调整最大并发流数量,得这样写:

import "golang.org/x/net/http2"

http2.ConfigureServer(srv, &http2.Server{
    MaxConcurrentStreams: 250,
})

现在直接用标准库字段就行:

srv := &http.Server{
    Addr: ":8080",
    HTTP2: &http.HTTP2Config{
        MaxConcurrentStreams: 250,
    },
}

2. 启用未加密的 HTTP/2(h2c)不再需要 x/net/http2/h2c 这个补丁包。

srv := &http.Server{Addr: ":8080", Handler: mux}
srv.Protocols = new(http.Protocols)
srv.Protocols.SetHTTP1(true)
srv.Protocols.SetUnencryptedHTTP2(true)
srv.ListenAndServe()

3. 安全补丁的响应速度会明显变快。 过去一个 HTTP/2 层面的安全漏洞,需要先在 x/net 修复、发版,再走一遍搬运流程进入标准库的下一个发布周期;现在真源就在标准库仓库里,修复和发布可以在同一个release cycle里原子完成。

4. 如果你的项目还直接 import golang.org/x/net/http2 用它的 TransportServer,目前不会立刻报错,但官方已经明确表态要废弃这批 API(见 #78064),建议尽早评估迁移到 net/http 原生能力的可行性。

写在最后:一次值得学习的“存量系统”重构范本

抛开 HTTP/2 本身的技术细节,这个持续两年的项目更像是一份“如何优雅地重构一个所有人都在用、谁都不敢乱动的核心组件”的操作手册:先公开摊牌讲清楚“为什么要改”(Discussion 提案),再拆成一串可以独立评审、独立合入、互不阻塞的小任务(11 个子 Issue),过程中每一次代码提交都关联回同一个跟踪入口,外部社区随时能看到整体进度。

issue 最后一条评论,只有两个字:“And done.”——干净利落,正如整个工程本身。

对于每天都要维护“改不动又不敢不改”的存量系统的工程师来说,这或许比 HTTP/2 本身更值得收藏。

参考链接

  • 跟踪 Issue:https://github.com/golang/go/issues/67810
  • 最初提案讨论:https://github.com/golang/go/discussions/60746
  • HTTP/2 配置 API 提案:https://github.com/golang/go/issues/67813
  • HTTP 版本选择 API 提案:https://github.com/golang/go/issues/67814
  • 废弃 x/net/http2 Transport 与 Server 提案:https://github.com/golang/go/issues/78064
  • x/net/http2 反向包装 net/http 提案:https://github.com/golang/go/issues/78508
  • Go 1.27 发布说明:https://go.dev/doc/go1.27

还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 从0 开始构建 Agent Harness 将带你:

  • 抛弃臃肿框架,回归“驾驭工程 (Harness Engineering)”的第一性原理
  • 用 Go 语言手写 ReAct 循环、并发拦截与上下文压缩引擎等,复刻极简OpenClaw
  • 构建坚不可摧的 Safety Middleware 与飞书人工审批防线
  • 在底层实现 Token 成本审计、链路追踪与自动化跑分评估
  • 从“调包侠”进化为掌控大模型边界的“AI 操作系统架构师”

扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

物理 AI 淘金热:为何机器人是下一个数万亿美元级的超级风口

2026-09-07 07:00:00

本文永久链接https://tonybai.com/2026/09/07/the-physical-ai-gold-rush

大家好,我是Tony Bai。

【导读】

当整个行业还在卷各类“ChatGPT 套壳”和聊天机器人时,科技界的底层范式转移已经悄然走向现实物理世界。海外技术博主 Rich Odin 撰文指出:过去二十年软件吞噬了世界,而以具身智能为核心的“物理 AI”即将消化它。从僵硬死板的 C++ 运动学控制,跨越到端到端直接输出电机指令的 VLA(视觉-语言-动作)大模型,机器人的研发逻辑彻底被重构为了“下一个 Token 预测”。在这篇极具前瞻性的万字长文中,作者不仅用生动的真实翻车案例剖析了技术现状,更梳理出软件工程师与创业者无需造重硬件即可切入的四大高利润软件变现路径。

【文章要点 (TL;DR)】

  • 范式大转移:AI 正从纯数字空间的对话框,历史性地迈向具备自主行动能力的现实物理世界(具身智能系统);
  • 底层控制逻辑颠覆:摒弃手写运动学逆解(IK)和传统 CV 算法,VLA(视觉-语言-动作)大模型将机器人控制转变为端到端的“下一个 Token 预测问题”;
  • Sim-to-Real 飞轮:依托超写实物理仿真(如 NVIDIA Isaac Sim),机器人在真实下地前已在虚拟世界中完成了相当于 50 年的物理试错与强化学习训练;
  • 避开硬件重资产陷阱:软件工程师无需自建工厂造硬件,产业链中利润率最高的环节正流向通用 AI 中间件、物理合成数据管线、垂直场景微自动化和人机接管基础设施;
  • 安全与确定性兜底:利用 1000Hz 超高频确定性安全控制器严密包裹 VLA 大脑,构建兼顾灵活性与工业级合规的控制闭环。


过去二十年,以移动互联网和云端 SaaS 为代表的“纯数字软件”重塑了全球商业版图;而过去两年,大语言模型与生成式 AI 的爆发更将数字经济推向了巅峰。然而,当大部分创业者还在卷各类“ChatGPT 套壳”应用时,科技行业底层的范式转移已经悄然启动——AI 正从云端走向现实物理空间。

这篇来自海外技术博主 Rich Odin 的深度长文《物理 AI 淘金热:为何机器人是下一个数万亿美元级的超级风口》,用犀利且极具洞察力的视角指出了未来十年的核心趋势:“软件吞噬了过去二十年的世界,而物理 AI(Physical AI)即将消化它。” 文章不仅列举了机器人领域近期令人哭笑不得的真实翻车案例,剖析了以 VLA(视觉-语言-动作)大模型为核心的技术栈代际变革,还梳理出目前机器人产业链中利润率最高的四大纯软件/中间件商业变现模式。

无论你是软件工程师、AI 研究者、投资人,还是寻找下一个时代机遇的创业者,这篇干货满满的文章都非常值得精读。以下为全文完整译文:


“软件吞噬了过去二十年的世界。而物理 AI(Physical AI)即将消化它。”

多年来,机器人技术一直是风险投资的“坟场”。

那时你看到的,尽是过度工程化的硬件、脆弱易崩的 C++ 控制脚本,以及一个纸箱倾斜 3 度就会彻底死机卡住的机器。

但如今,我们正真切地经历着科技史上最大的一次底层结构性范式转移:

  • 从“数字 AI”(聊天机器人、SaaS 套壳应用)向“物理 AI”(自主具身智能系统)的历史性跨越。

如果你觉得上一轮 AI 纯软件繁荣创造的财富已经足够疯狂,不妨等等看当物理机器开始在现实世界中大规模替代并执行经济生产劳动时,会发生什么。

以下是关于机器人市场为何正在迎来大爆发的不加修饰的真相、目前正在发生的真实魔幻案例,以及如何将其商业变现的实战打法指南。


第一部分:机器人技术的“黑人问号(WTF)”时代(离奇的事实与真实故事)

在深入探讨枯燥的数字之前,我们先来看看现实物理世界的 AI 正在以多快的速度“发疯失控”:

  • 五角大楼的“幽灵机器狗”乌龙事件: 2024 年底,一台配备了全新 AI 视觉系统的自主四足机器狗投入实地测试。

    在一次封闭式演练中,由于动态阴影的光线干扰,该模型将一个普通的垃圾桶误判为具有敌意的威胁目标,随后果断执行了精准的动能冲撞打击……狠狠撞翻了一个塑料分类回收桶。

    在不到 36 个月的时间里,我们竟然就已经从“机器人还走不稳路”,快进到了“机器人因存在主义危机而在高速状态下发神经”。

  • 无限死循环的咖啡师: 2025 年初,日本东京某试验点的一台咖啡制作机械臂遭遇了未经处理的边缘死循环故障。

    由于某个传感器未能成功上报“纸杯未就位”的状态,这台机械臂在空无一物的托盘上方整整连续倾倒了 6 个小时、共计 420 杯拿铁。

    损失有多大?浪费了价值 3,000 美元的燕麦奶。但这起事故的视频在 X(推特)上狂揽了超过 4000 万次播放。

  • 从仿真到现实的“跨次元瞬移”(Sim-to-Real): 此时此刻,各大具身人形机器人公司正让模型完全在超写实的物理仿真世界中(例如 NVIDIA Isaac Sim)进行强化学习训练。

    一台人形机器人在真实脚掌踏上现实世界的地板之前,仅仅在一个下午之内,就能在仿真环境里经历相当于 50 年的物理试错与摸爬滚打。

第二部分:市场为何发生剧烈变动(VLA 模型)

为什么是现在?

因为我们终于彻底放弃了编写僵化死板的逻辑规则,转而开始将机器人控制视为一个“下一个 Token 预测问题”(Next-Token Prediction Problem)。

在过去,如果你想让机械臂抓取一个苹果,你必须手写成千上万行运动学逆解(IK)、轨迹规划数学公式以及硬编码的传统计算机视觉算法。

  • 而在今天,视觉-语言-动作(Vision-Language-Action, VLA)模型能够直接以原始摄像头像素画帧 + 自然语言提示词作为输入,端到端直接输出机械关节的原生动作指令。

核心代码:现代 AI 机器人技术栈长什么样?

下面是一套高度精简的 Python 架构示例,展示了在边缘端设备(甚至在原型开发阶段直接在你的 Mac 上)运行现代 VLA 感知与控制的核心逻辑:

# 现代 AI 机器人控制技术栈 v2.4
import torch
import numpy as np

force_sensors: np.ndarray

class VisionLanguageActionModel(torch.nn.Module):
    def __init__(self, model_checkpoint: str):
        super().__init__()
        # 加载多模态物理具身基座模型(如 RT-2 / OpenVLA 架构风格)
        self.vla_backbone = torch.hub.load('robotics/vla_core', 'vla_base', pretrained=True)

    def predict_action(self, image: np.ndarray, prompt: str) -> np.ndarray:
        """
        接收高分辨率摄像头画面帧 + 自然语言交互指令。
        直接预测并输出连续的 7 自由度(7-DOF)空间速度轨迹向量。
        """
        tensor_img = torch.from_numpy(image).permute(2, 0, 1).unsqueeze(0).float()
        action_logits = self.vla_backbone(tensor_img, prompt)
        return action_logits.detach().cpu().numpy()


class RealtimeSafetyController:
    def __init__(self, frequency_hz: int = 1000):
        self.freq = frequency_hz
        self.max_force_limit_newtons = 45.0

    def validate_trajectory(self, planned_action: np.ndarray, current_state: RobotState) -> np.ndarray:
        # 确定性高频安全校验闭环(1000Hz 超高频循环)
        if np.any(current_state.force_sensors > self.max_force_limit_newtons):
            print("[紧急警报] 接触受力超过阈值!正在触发柔顺阻尼缓冲保护。")
            return planned_action * 0.1 # 瞬间将动作速度降低 90%
        return planned_action

# 初始化运行入口
if __name__ == "__main__":
    brain = VisionLanguageActionModel(model_checkpoint="vla-7b-embodied")
    safety_layer = RealtimeSafetyController(frequency_hz=1000)

    print(">> 硬件无关的 AI 大脑初始化成功。")
    print(">> 准备就绪,开始低延迟流式输出电机动作指令。")

第三部分:如何在不制造硬件的情况下真正赚取利润

从头造物理硬件需要天量沉重的基础资本开支(CapEx)。

而真正聪明的资本和创业者正在涌向:

> 核心软件系统
> 数据中间件生态
> 垂直整合应用层

以下是目前机器人产业中利润率最高的 4 种商业模式

1. 硬件无关的通用 AI 中间件(RaaS,机器人即服务)

硬件原始设备制造商(如宇树 Unitree、优傲 Universal Robots、库卡 KUKA)在制造高性能电机、高精减速箱和轻量化铝合金机架方面极具优势,但他们做出来的配套软件交互体验往往非常糟糕。

  • 商业打法: 打造一套即插即用的 AI 智能控制软件订阅服务(Robotics-as-a-Service)。
  • 变现模式: 按每台活跃运行的机器人收取软件授权费,每月 $500 - $2,000 美元。

2. 物理数据变现与合成数据生产管线

具身 AI 模型要想在严苛的企业级场景中达到 99.99% 的工业级可靠性,需要数以万亿计的现实物理世界数据点(实时遥测数据、高频触觉受力反馈、动态偶发边缘工况)。

  • 商业打法: 开发自动化的遥测数据采集工具,或构建用于生成超写实合成训练数据的高保真仿真物理环境,将其提供给各大机器人研发公司。
  • 变现模式: 向具身基座大模型实验室直接出售精选清洗的数据集包,或按 API 调用量计费。

3. 垂直细分场景微自动化(垂直领域 AI 集成商)

千万不要好高骛远去尝试解决所谓的“通用家庭保姆家务”。找到极其具体、环境脏乱、危险系数高或员工流失率极高的 B 端企业痛点:

  • 沙漠光伏电站的自主巡检清洁系统;
  • 面向有机农业的 AI 视觉引导超精密激光除草机;
  • 面向大型远洋商船船体清理的仿生水下无人潜航器。
  • 变现模式: 基础项目交付合同制 + 提取企业节省的实际运营成本的一定百分比。

4. 远程遥控接管 + 人机协同(Human-in-the-Loop)基础设施

当 AI 机器人在现实中遭遇从未见过的罕见异常(例如地图中未标定的高空坠落物)时,它绝对不应该直接死机或失控,而是应该立即发起一个只需 5 秒钟的人工介入接管请求。

  • 商业打法: 构建基于 WebRTC 的超低延迟实时控制面板系统,允许分布在低劳动力成本地区的远程操作员在几秒钟内协助解决现实边缘工况。
  • 变现模式: 按照成功协助并解决的人工干预次数向企业客户收费。

小结:窗户现在正在打开

移动互联网 App 时代催生了一大批纯软件独角兽公司。

而物理 AI(Physical AI)时代,必将孕育出人类历史上首批规模达到数万亿美元的超级自动化企业集群。

想要在这个历史级的浪潮中分得一杯羹,你并不需要拥有机械工程专业的博士学位。你只需要吃透并掌握:

  1. 如何微调(Fine-tune)开源权重的具身 VLA 大模型;
  2. 如何用确定性的高频安全控制器将其严密封装保护;
  3. 如何向客户销售实打实的商业价值与结果(投资回报率 ROI、降低人力开销、安全合规),而不是兜售华而不实的金属大玩具。

→ 别再继续折腾无聊的 ChatGPT 套壳应用了。
→ 去为现实物理世界构建真正改变世界的软件吧。

感谢阅读!

原文链接:https://x.com/rich_odinn/status/2088006989062303788


还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 从0 开始构建 Agent Harness 将带你:

  • 抛弃臃肿框架,回归“驾驭工程 (Harness Engineering)”的第一性原理
  • 用 Go 语言手写 ReAct 循环、并发拦截与上下文压缩引擎等,复刻极简OpenClaw
  • 构建坚不可摧的 Safety Middleware 与飞书人工审批防线
  • 在底层实现 Token 成本审计、链路追踪与自动化跑分评估
  • 从“调包侠”进化为掌控大模型边界的“AI 操作系统架构师”

扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。