MoreRSS

site iconEST修改

EST = Extrospect, Sein & Tao ,后端工程师。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

EST的 RSS 预览

HTML里的“宏”

2026-10-12 21:29:00

摊牌了,分享两个极少有人知道的技巧。

观察下面的网页

https://lab.est.im/xhtml/

有没有发现神奇之处

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
  <!ENTITY my_symbol "★">
  <!ENTITY wikipedia '<img height="14" src="https://en.wikipedia.org/favicon.ico" alt="Wikipedia" />'>
]>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Entity test</title>
  </head>
  <body>
    <p>hey &my_symbol; visit &wikipedia; for more!</p>
  </body>
</html>

另外就是刚做的 play2mi 有一个特立独行的想法:不让出现厂牌的关键字,一律用图标 代替。

但是这个 <img height="16" src="https://www.mi.com/favicon.ico" /> 出现得次数太多,就很重复了。有没有 宏/macro 来节约呢?

浑身不舒服。经过一番激烈拷打AI,最终结果:

div.wrap param {display: inline; height: 1em; vertical-align: -.125em;}
param[mi] { content: url("https://www.mi.com/favicon.ico"); }
param[mijia] { content: url("https://cdn.web-global.fds.api.mi-img.com/mijia/prod/home/favicon.ico") }

<param mi> 是一个自定义属性,写起来比 <i class="icon-mi"></i>短,也不需要闭合。

用不存在的自定义标签当然也行,但必须写一对儿 <mi-icon></mi-icon>。为了不闭合偷懒,查到浏览器只白名单了14个空元素(Void Elements)

  • <meta> 声明页面元数据 <meta charset="UTF-8">
  • <link> 引入外部资源,常用于 CSS、favicon
  • <base> 设置页面中相对 URL 的基础地址 <base href="https://example.com/">
  • <img> 嵌入图片。它是最常用的空元素之一。
  • <source> 为 <picture>、<audio>、<video> 提供可选媒体源。它通常不能独立发挥作用。
  • <track> 给视频或音频添加字幕、说明文字、章节等文本轨道。
  • <embed> 在页面中嵌入外部内容,例如某些 PDF 文档或浏览器支持的嵌入资源。
  • <br> 强制换行 第一行<br>第二行
  • <wbr> 建议浏览器可在此处换行 superlong<wbr>word
  • <col> 为表格的一列设置样式或属性 <colclass="numeric">
  • <input> 创建文本框、复选框、单选框、文件选择器等表单控件。
  • <area> 在图片映射(image map)中定义一个可点击区域。
  • <param> 为 <object> 提供参数 已废弃,不建议新项目使用

那就变废为宝,拿 <param> 来当 宏 用,不会被当成未知元素,HTML 校验也不报错。

之前的版本还有 css 变量

param {display: inline-block; width: 1em; height: 1em; vertical-align: -0.125em; background: var(--icon) center / contain no-repeat;}
param[A] { --icon: url("A.jpg"); }
param[B] { --icon: url("B.jpg") }

也有利用 background-image 比 background 优先

param {display: inline-block; padding: .5em; vertical-align: -.125em; background: center / contain no-repeat;}
param[A] { background-image: url(A.jpg) }
param[B] { background-image: url(B.jpg) }

但 width: 1em 这个太死板。现代css有 content: 更省事儿,宽度还会按图片比例自动算。

AI 还教了一招,用 attr() 给宏传参数

param::after { content: attr(value) }

<param mi value="手机"> <param mi value="电视">

有AI真能学到好多东西,以前翻破书都想不到的,就看你肯不肯开口问。

浏览器点2下就能让小爱音箱播放http音乐!

2026-10-11 16:14:00

还在被 冈易 QQ 音乐困扰吗,让小爱同学播放NAS上的歌需要几步?

https://est.github.io/play2mi/

装个书签,点两下就行。不需要下载软件,不需要第三方服务器

期间还是碰壁了不少的。也学到不少东西。小米音箱以前应该是openwrt魔改 ubus 控制,后来都改成 Android 了。

这一分裂也导致它内部有两套API接口。一个是 sid=micoapi 一个是 sid=xiaomiio,前者跑在 小爱云 Mina api2.mina.mi.com,后者跑在 米家 IoT 云 api.io.mi.com/app 和 api.mijia.tech/app

这次就简单做了 Mina (micoapi) 的支持

能放歌,能 TTS 就行了。要扩展等有缘人吧。

期间搞出来个巨搞笑的插曲,AI 发现浏览器死活登录不上,一度以为是风控了,账号被画像了,IP被标记了,结果是 nonce 计算的时候没用 bigint 精度被截断了。笑死。幸好在去研究 TLS 指纹之前被我拦住了。

这种一般都是低级错误。

然后无聊让AI去扒一下能不能拿到shell,还真有注入漏洞。不过,老固件才行!!气死了!!!!!

算是又填了一个坑吧。对了,这个项目如果想跑在服务器,估计不太行,据说小爱的HTTP对IDC出口有风控。

preconnect 走 http3

2026-10-01 20:33:00

想让 <link rel="preconnect"> 强制走 HTTP/3,能做到吗?

preconnect 是什么

<link rel="preconnect" href="https://example.com">

浏览器看到这个标签,会立刻去做这几件事:

  1. 解析 example.com 域名
  2. 建立一个连接
  3. 完成 TLS 握手(如必要)
  4. 保持连接并等着,不发送任何请求正文

等真正要请求资源时,这个连接已经热好了。这就是 提前连接 preconnect

HTTP/3 跑在 UDP 上(QUIC 协议),HTTP/1.1 和 HTTP/2 跑在 TCP 上。

HTML 层面没有任何办法声明"这个 preconnect 要走 h3"。

这个连接最终用什么协议,HTTP/1.1、HTTP/2、HTTP/3,浏览器有一系列决定流程

触发 h3 preconnect 的三个条件

网络层有两条实现路径(Chromium 正在迁移,两条并存)

条件 ①:浏览器缓存了这个站点的 Alt-Svc

服务端某次响应头带上:

Alt-Svc: h3=":443"; ma=86400

浏览器把它记下来(存进 profile 磁盘缓存),之后所有对该站点的 preconnect 都会走 QUIC。

  • 传统路径:http_stream_factory_job_controller.cc:915-919, 950-968
  • 新路径(HttpStreamPool):http_stream_pool_job_controller.cc:283-288
// If the preconnect explicitly requests QUIC, start preconnecting before
// checking existing SpdySession and idle streams.
if (origin_quic_version_.IsKnown()) {
  preconnect_callback_ = std::move(callback);
  StartAltSvcQuicPreconnect();
}

条件 ②:DNS 的 HTTPS 记录(RFC 9460 SVCB)里 ALPN 含 h3

example.com.  IN HTTPS 1 . alpn="h3,h3-29,h2,h1" port=443

浏览器判定逻辑:

  • 传统路径:http_stream_factory_job_controller.cc:925-932
  • 新路径:http_stream_pool_attempt_manager.cc:1333-1361 → quic_session_pool.cc:1687 SelectQuicVersion()

关键规则:

// net/quic/quic_session_pool.cc:1691
if (metadata.supported_protocol_alpns.empty()) {
  // 纯 A/AAAA 记录 → 除非已知 QUIC 版本,否则这个 endpoint 不合格
  return svcb_optional ? known_quic_version : Unsupported();
}

DNS 只有 A/AAAA 记录时,浏览器根本不会尝试 QUIC,必须 DNS 里明确写 h3。

条件 ③:force_quic_everywhere(理论上行,Chrome 里做不到)

net/quic/quic_context.h:131-133 的 QuicParams 确实能全局强制 QUIC,代码路径也支持:

  • http_stream_factory_job.cc:145-177(using_quic_ 含 ShouldForceQuic)
  • http_stream_pool_job.cc:43-47(group->force_quic() 时只允许 QUIC)

但我把 net/base/switches.h、content_switches.h、chrome_switches.h 都翻了一遍,Chrome 没有任何命令行开关能设置它(老版本的 --force-quic-everywhere 已移除)。只有 Cronet 或自己编译的 embedder 直接构造 QuicContext 才能用。

→ 实际可用方案只有 ① 和 ②。

实施细节

不考虑 Alt-Svc,细说冷连接直接 h3

example.com.  IN HTTPS 1 . alpn="h3,h3-29,h2,h1" port=443

必要条件,缺一不可:

  1. 必须开启 Secure DNS(DoH) —— 这是最容易踩的坑。

Chromium 里 NetworkService 用默认配置创建 DNS 解析器:

// services/network/network_service.cc:483
host_resolver_manager_ = std::make_unique<net::HostResolverManager>(
    net::HostResolver::ManagerOptions(), ...);

默认 insecure_dns_mode = kDisabled(net/dns/host_resolver.h:354),意思是不走 Chromium 内置的明文 DNS 客户端,只走系统 resolver(getaddrinfo)。而系统 resolver 无法查询 type 65。

// net/dns/host_resolver_dns_task.cc:378
if (types.Has(DnsQueryType::HTTPS)) {
  if (!secure() && !client_->CanQueryAdditionalTypesViaInsecureDns(ech_mode_)) {
    https_disabled_ = true;         // 直接把 HTTPS 查询删掉
    types.Remove(DnsQueryType::HTTPS);
  }
}

所以要去 chrome://settings/security → 使用安全 DNS,或用企业策略 SecureDnsMode 配自定义 DoH。你用的 DoH 服务器必须支持 type 65(Cloudflare / Google / Quad9 都支持)。

  1. QUIC 不能被关:chrome://flags/#enable-quic 保持默认(Default = 开)。

  2. origin 未被标记 QUIC broken。

  3. 不能走代理。源码两处都要求直连:

session_->IsQuicEnabled() && proxy_info_.is_direct()

(http_stream_factory_job_controller.cc:925-932)。代理下 preconnect 只会走 TCP。

  1. 必须是 https:HTTP/3 只用于加密连接。

type 65 的两个时序问题

查询顺序上 type 65 排第一(kPrioritizeHttpsResourceRecord 默认开),然后才是 AAAA、A。但实际上 DoH 下三个查询是并发发出的(host_resolver_manager_job.cc:864):

// DoH queries can bypass the dispatcher and start all of their transactions immediately.
if (secure) {
  while (dns_task_->num_additional_transactions_needed() >= 1) {
    dns_task_->StartNextTransaction();
  }
}

所以理论上不存在 HTTPS 单独慢的问题。

type 65 来迟了怎么办? 只有 20% × 50ms 的窗口:

// host_resolver_dns_task.cc:1165
timeout = total_time_for_other_transactions * extra_time_percent / 100;  // 20%
timeout = std::min(timeout, timeout_max);   // 50ms
timeout = std::max(timeout, timeout_min);   // 5ms

举例:A/AAAA 用了 30ms,type 65 只多等 6ms。超时就按"没这条记录"处理。

为什么不等更久?因为三个查询并发在飞,type 65 只要不比 A/AAAA 慢太多就来得及。

降级是静默的

如果 DNS 里没返回 h3 ALPN,QUIC 侧会返回 ERR_DNS_NO_MATCHING_SUPPORTED_ALPN,然后自动回落到 TCP preconnect:

// http_stream_factory_job_controller.cc:586
if (result == ERR_DNS_NO_MATCHING_SUPPORTED_ALPN && preconnect_backup_job_) {
  main_job_ = std::move(preconnect_backup_job_);
  main_job_->Preconnect(num_streams_);

所以配错不会报错,只会静默降级成 TCP。 只能靠 netlog 确认。

同样的降级也发生在 type 65 查询超时、SERVFAIL、REFUSED 时 —— IsFatalTransactionFailure() 对 HTTPS 查询永远返回 false,失败时合成一个空的合法响应,效果等同"这条记录不存在"。preconnect 照样成功,只是走 TCP。

唯一的例外是 DoH + UseDnsHttpsSvcbEnforceSecureResponse=true(默认 false),或 ECH 严格模式,此时失败是致命的 —— 因为必须拿到 SVCB 信息才能继续。普通用户碰不到。

DNS 侧的四个额外坑

非 443 端口不支持。 源码里直接写了注释:

// http_stream_factory_job_controller.cc:113
// Note: Chrome does not yet support different port DNS alpn.

所以 h3 preconnect 实际上只对 443 有效。

ALPN 写了 h2 但没写 h3 反而会堵死 h3。 SelectQuicVersion 一旦看到 SVCB 记录就只认列表里的 h3,找不到直接返回 Unsupported,不会退回"忽略 SVCB 走 QUIC"。也就是说只要发了 HTTPS 记录,就必须在里面写 h3。

HTTPS 记录的 TTL 会拉低整个域名的缓存寿命。 TtlFromInternalResults 取所有查询结果里最短的 TTL(host_cache.cc:729),而 HTTPS metadata 也在这份结果里。别把 HTTPS TTL 单独调小于 A/AAAA,建议保持一致。

ipv4hint/ipv6hint 在稳定版不生效。 代码支持,但受 kUseDnsHttpsSvcbAddressHints 控制,默认关闭(net_base_features.cc:85)。好消息是它不是必需的,A/AAAA 会正常返回 IP。

一个容易误判的点

源码里有"流式返回中间 DNS 结果"的机制(DnsTaskResultsManager),但它的三个开关默认全是关的:

// host_resolver_manager_job.cc:856
if (kEnableIntermediateDnsResults ||     // 默认关
    kAsyncDnsQuicJob ||                  // 默认关
    resolver_->IsHappyEyeballsV3Enabled()) {   // 默认关
  dns_task_results_manager_ = ...;
}

所以当前稳定版 Chrome 实际是"要么全等到齐、要么超时降级",没有真正的流式中间结果。 容易误以为存在该机制。

局限性

坑 说明
粒度只能是 origin 级 Alt-Svc 和 SVCB 都是站点级信息。一旦生效,后续真实请求也会一起走 h3;没法只让 preconnect 走 h3 而普通请求走 h2。
UDP 443 被封 QUIC 握手失败 → 标记 QUIC broken → 之后连 Alt-Svc 都不看,直接退 TCP,还多等一个握手超时。表现为首帧变慢而不是报错。
代理环境下基本失效 方案 B 完全失效(要求直连);方案 A 需要 alt-svc + QUIC 代理同时具备。
第一次 preconnect 不生效 方案 A 必须先有一次真实响应。方案 B 没有这个问题(DNS 直接可查)。
非 https 目标 http:// 永远不会走 h3(GURL::SchemeIsCryptographic 判定)。
crossorigin 属性无用 <link rel=preconnect crossorigin> 只影响 credentials mode 和连接分组键,不影响协议选择。
连接数会被合并/限流 同一 origin 的多个 preconnect 合并到同一个连接池,受上限约束(kDefaultMaxStreamSocketsPerGroup = 6,http_stream_pool.h:110)。
QUIC 只能开 1 条 h3 preconnect 只建 1 个 QUIC session。TCP 侧在服务端支持优先级时也会把预连接数压到 1(http_stream_factory_job.cc:255-270)。
QUIC 被禁后不可恢复 profile 偏好 kQuicAllowed 一旦被企业策略置 false,代码里明确注释 "re-enabling QUIC is not supported",只能重启浏览器或换 profile。

检查清单

  1. 抓 netlog(最权威)
# macOS
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
  --log-net-log --net-log-file=/tmp/netlog.json \
  --net-log-capture-mode=IncludeSensitive

打开 https://netlog-viewer.appspot.com/ 拖进去,搜:

  • HTTP_STREAM_JOB 且 type = preconnect_dns_alpn_h3 → 方案 B 生效
  • HTTP_STREAM_POOL_JOB_ALIVE 里 allowed_alpns 含 h3 → 方案 A 或 B 生效
  • QUIC_SESSION / QUIC_VERSION_NEGOTIATION → 确认真的握手了
  • HOST_RESOLVER_DNS_TASK 的 transactions_needed 里有没有 HTTPS 类型 → 没有就说明压根没发这个查询
  1. 确认 QUIC 没被关
  • chrome://version → 命令行里没有 --disable-quic
  • chrome://policy → 没有禁用 QUIC 的企业策略
  1. 确认 DNS 记录正确
# 直接问你的 DoH(推荐,能验证真实链路)
dig @your-doh-host example.com HTTPS

看 ANSWER 里 alpn= 是否含 h3。注意直接 dig HTTPS example.com 走的是系统 resolver,即使能查到也不代表 Chrome 会用。

  1. 按顺序排查
# 检查项 怎么查
1 DoH 开了吗 chrome://settings/security → "使用安全 DNS" 非"关闭"
2 DoH 服务器支持 type 65 吗 dig @doh-host example.com HTTPS 要有 ANSWER
3 Secure DNS 模式 必须"自动"或"安全"。"关闭" = 完全不发 type 65
4 端口是 443 吗 非 443 不支持
5 ALPN 串里有 h3 吗 注意是 h3 不是 h3-30,建议 h3,h3-29,h2,h1
6 是直连吗 走代理时 DNS-ALPN-h3 被禁用
7 QUIC 没被关吗 flags 默认 + policy 无禁用
8 TTL 合理吗 HTTPS TTL 别短于 A/AAAA
9 origin 没被标 QUIC broken 之前握手失败过会被标记,之后退 TCP 直到重启

源码文件参考

文件 作用
third_party/blink/renderer/core/loader/preload_helper.cc Blink 侧:把 <link rel=preconnect> 变成 preconnect 指令
services/network/network_context.cc 转发给 HttpStreamFactory::PreconnectStreams
net/http/http_stream_factory.cc 传统路径入口,创建 JobController
net/http/http_stream_factory_job_controller.cc 核心判定:何时走 DNS-ALPN-H3 / Alt-Svc,失败如何回落 TCP
net/http/http_stream_factory_job.cc 单个连接任务,using_quic_ 判定
net/http/http_stream_pool_job_controller.cc 新路径(HEv3)preconnect 逻辑
net/http/http_stream_pool_attempt_manager.cc 新路径:QUIC/TCP 连接尝试、竞速
net/quic/quic_session_pool.cc SelectQuicVersion():SVCB ALPN 匹配逻辑
net/http/http_network_session.cc ShouldForceQuic() / IsQuicEnabled()
net/quic/quic_context.h force_quic_everywhere 等参数定义
net/dns/host_resolver.h ManagerOptions.insecure_dns_mode(为什么必须 DoH)
net/dns/host_resolver_dns_task.cc HTTPS RR 查询条件、查询顺序、等待超时预算
net/dns/host_resolver_manager_job.cc DoH 下多查询并发发起
net/dns/host_cache.cc TtlFromInternalResults():缓存 TTL 取各结果最小值
net/dns/public/util.cc GetNameForHttpsQuery():非 443 端口的查询域名格式

Node.js、浏览器和 Cloudflare Workers 的wasm引入问题

2026-09-26 22:51:00

最近在鼓捣 WebAssembly,这玩意没啥神秘的,可以看出一坨二进制的 .js 库文件

它本身的export和调用方式是统一的,但如今天我才知道,在不同 JavaScript 运行环境加载 .wasm 方式并不完全一样。

例如 Cloudflare Workers 可以直接:

import wasm from "./simple.wasm";

const { instance } = await WebAssembly.instantiate(wasm, imports);

Cloudflare 会把 .wasm 模块作为 WebAssembly.Module 提供给代码。

浏览器通常则是通过 fetch() 获取 Wasm,再使用 WebAssembly.instantiateStreaming():

const { instance } = await WebAssembly.instantiateStreaming(
  fetch("./simple.wasm"),
  imports,
);

Node.js 则通过文件系统读取 .wasm 二进制,然后初始化。

.instantiate() 第一参数必须是 typed array 或者 ArrayBuffer

我自己写了个 Wasm 库,AI 为 Node、浏览器、Cloudflare Workers 分别写一套初始化代码,8个 .mjs 文件。给我搞懵了。

提了个简化思路:让初始化函数只接受两种东西:string 表示路径,或者cf那种 WebAssembly.Module 对象

然后搞的时候发现,为了兼容 nodejs,必须 import "node:fs" ,才能读 .wasm 文件嘛

然后逆天的 js 引入必须在 top-level ,在 cf 和浏览器侧就必挂。你 typeof process 提前判断一下环境都不行。

不过AI这时候想到一个聪明办法

if (process?.getBuiltinModule) {
  const fs = process.getBuiltinModule("fs");

  const bytes = await fs.promises.readFile(source);
  return WebAssembly.compile(bytes);
}

卧槽,有这么好的办法,你为啥不早点告诉我。

于是使用方式统一成:

Cloudflare Workers

import wasm from "./simple.wasm";
import { init } from "./foo.mjs";

const foo = await init(wasm);

Browser / Node.js

import { init } from "./foo.mjs";

const foo = await init("./simple.wasm");

Register Token以及对变形金刚AI的四大批判

2026-09-23 20:21:00

前几天 Yann LeCun 对 变形金刚(transformers) AI 进行了深刻的批判

First, the reasoning abilities of current AI systems are based non-auto-regressive search (which is what I have always advocated for). But AFAICT, they do it in token space, which is limited and inefficient. I have claimed that human-like reasoning must be a search in continuous representation space. It looks like the industry is moving towards that.
Second, the self-improvement methods, as currently practiced, only work for domains where the quality of outputs can be scored without human intervention, such as mathematics, code, and scenarios that can be simulated accurately. Not anything else. Humans and animals learn new skills way more efficiently than current RL methods.
Third, the multimodal capabilities of current AI assistants generally use separately-trained encoders (that are not LLMs). This is also what I've been advocating. Except that I think the best way to do this is with JEPA trained with self-supervised learning. The research community is clearly moving towards that (3000 papers on JEPA in just 4 years).
Fourth, if LLMs were a path to human-level AI, we would have domestic robots and Level-4 or Level-5 self-driving cars for consumers by now. And we don't. We certainly don't have cars that can learn to drive in 20 hours or practice like any teenager. We're still missing something pretty huge to claim human-level intelligence (let alone superhuman).

Sure, we now have computer systems that are impressive, very useful, and whose performance is superhuman in an increasing number of domains (coding being one of them). But that's true of the entire history of progress in computer technology.

可能很多人早知道这个了,对我来说,很新鲜。毕竟我是AI 外行。以前一直听说 自回归 自回归,其实我今天才突然反应过来,这个 回归,就是数学期望那里,预测的意思。这里的 自 就是 一个token接着另一个token源源不断“自动” 跳出来直到 eos_token 的意思。🤣

他老人家都这么说了,想了下还真是。用 LLM 越多,越会明白在 Token 空间里做推理的局限性。


于是我突然想起很多天之前看到的一个youtube 视频,21世纪被引用次数最多的论文。这个35分钟的视频非常值得推荐完整观看。剧透:它讲的是 残差网络 ResNET

它提到另外一件事引起了我极大的兴趣,Meta FAIR 在ICLR 2024论文,发现了一个有趣现象:ViT 模型(尤其是 DINOv2、OpenCLIP、DeiT-III 这类大规模训练的模型)在推理时会产生一些"异常高范数"(high-norm)的 token,大约占总序列的 2%。这些 token 集中出现在图像中低信息量的背景区域,几乎都是纯色块,携带很少的局部位置/像素信息,却携带大量全局图像信息。只在模型足够大、训练足够久之后才出现(比如 DINOv2 只有 Large 及以上尺寸才有)。这些token会破坏注意力图的可解释性,损害密集预测任务(分割、深度估计)和无监督目标发现(LOST 算法)的效果

他们摸清楚一个脉络,最后研究发现,在输入序列里额外加几个可学习的"寄存器"(register)token,让模型有专门的地方做全局计算,不用再"borrow"图像 patch。加了之后,异常高范数 token 消失,注意力图变干净,下游任务(尤其密集预测和目标发现)性能还略有提升,几乎不增加参数和计算量。


是不是感觉这几件事相当跳跃,我在瞎说些啥呢?

那是因为你没看那个35分钟的视频。我就凭记忆,用我的理解回顾一下(我不太懂AI,纯外行。如有出入视频作者全责😂)

神经网络我觉得就是好几层矩阵相乘造就的魔法。但归根结底 tmd是一个分类器。传统每一层 NN 就干的事就是从上一层的结论里,加工自己的一道参考信息,然后激活给下一层NN继续加工。最后一层就是 N 选 1 的分类。对于 LLM 来说就是词汇表里吐出 next token。

但是这玩意早年极其容易翻车,shattered gradiant,ResNET 横空出世,据说是个 hack,把上上一层的权重和本层的权重做一次 加法。最后输出就稳了

这就有意思了,NN每一层都是无状态的,需要猜上一层为啥处理成这个样子;每一层给出结论,请重点关注某个值,但是至于一开始的为啥要给 attention,可能早就忘记了。这个所谓 残差,就是每一层都给后面一层工作留一个小纸条?就好比工匠无意中在木结构看不到的一些内部地方打上了一些标记。换班的水电工一眼看出这些标记在行业里的用处

AI告诉我,register token就更进一步,人为加进去的、专门给模型当工作空间的 token。attention sink 是模型自己长出来的现象:某些 token 会莫名其妙吸走大量 attention,即使它们语义上并不重要。activation outlier / residual sink则是在 hidden state 某些维度上出现异常大的值。2026 年的工作甚至发现,massive activations 和 attention sinks 经常同时出现,但它们承担的功能并不完全相同:前者更像一种跨层持续存在的“隐式参数”,后者更像是在局部调节 attention。


然后结合今天 ylecun 四大批判,我突然有个看法,变形金刚的未来,说不定又走回 BERT 那种 encoder 思路了?也就是说在一个session内,变形金刚一直在尝试 自创(encode)一些 符号 名词概念 作为中间产物。

我赶紧拿这个去问了AI。

FAIR那个论文 附录(Fig. 9、Fig. 16)里其实发现,不同的 register 会自发分化:有的更关注图像边缘,有的更关注中心区域,行为很像 Slot Attention(一种专门用来无监督地把图像切分成"物体槽位"的机制)。也就是说,即便没有人告诉它该怎么分工,模型自己在若干个"空白格子"里长出了某种专业化结构

这验证了我前面提到的"自创概念"。AI告诉我,文本模型这边也有对应物:

  • Pause/Filler tokens(Goyal 等,2023):给 LLM 在开口回答前插入几个没有语义的占位 token,让它"多想一步",效果类似给 register 一样的免费计算空间。
  • Gist tokens(Mu 等,2023):训练模型把一整段很长的 prompt/指令压缩进几个可学习的 token里,之后这几个 token 的 KV 缓存就能代替原始长文本被后续计算调用——这已经是字面意义上的"自创浓缩符号"了。
  • Coconut / "Training LLMs to Reason in a Continuous Latent Space"(Meta,2024):这个最接近你说的场景。正常的 chain-of-thought 是模型把每一步推理都解码成人类能读的词;Coconut 让模型把上一步的隐藏状态直接喂回去当下一步的输入,不经过"变成词"这个环节。于是模型在一个 session 内部,用一串连续的、非语言的"思维向量"来做推理,只在必要时才把结果翻译回人话。作者甚至发现这种连续思维能同时编码好几条可能的推理路径(类似广度优先搜索),比被迫每步都写成一个确定的词更灵活。

AI提到:

这些自创的中间产物,默认情况下是连续向量,不是像"猫""狗"这种离散、人类可读的符号。你说的"符号/名词概念"更像是一种比喻——真正发生的是模型在高维连续空间里捏出一团对它自己有用、但对人不透明的表示。这也正是这条路线的双刃剑所在:Coconut 这类"潜空间推理"效率更高、更灵活,但代价是可解释性变差——人没法像读 chain-of-thought 文字那样,看懂模型这一步到底在"想"什么。这也是目前 AI 安全/可解释性圈子挺关心的一个权衡点:模型内部会不会形成一套人类读不懂的"暗语",用来在一次推理里传递信息。

嗨,这不巧合了嘛!人类的深度思索过程,也会创造自己不能语言表达的连续符号啊。比如如何狩猎的,空间位置和行动,态势变化和应对。当然你实在用语言描述也是可行的。但是很多战术理念是“心领神会”,充满比喻,平时需要猎人之间训练配合的“默契”。

解题思路,很多时候“直觉” 或者 “奇思妙想”,要描述出来也是很困难的。其实就是一种内在表达

艺术创作更不用说了,什么情绪,境遇,都是很难精准描述的。但是好的东西就是能引起共鸣。诗歌里的“隐喻”特别多。

AI 一下子被点通了:

"默会知识"(Tacit Knowledge)——这是哲学家 Michael Polanyi 在 1966 年提出的经典概念,他的原话就是"我们知道的比我们能说出来的更多"(We can know more than we can tell)。他举的例子恰好就是猎人、工匠、骑自行车、认脸这类技能:你能熟练地做,甚至能一眼分辨"这个猎手动作对不对",但让你把判断标准写成一条条规则,你写不出来,或者写出来的东西根本不是你实际依赖的那套判断机制。狩猎里那种"态势感知"、"该往哪个方向包抄"的直觉,正是这类默会知识的典型案例。
更巧的是,Coconut 那篇论文的灵感来源之一就是神经科学的一个发现:人在做复杂推理(比如下棋、空间规划)时,大脑里负责语言的区域往往并不活跃——语言网络主要是用来表达和交流的,未必是"思考本身"发生的地方(这是 MIT Fedorenko 实验室这些年一直在推的一条研究线:"语言不是用来思考的,是用来交流思考结果的")。这跟你说的"很多战术理念心领神会,语言只是事后的翻译"是同一件事的两种表述。
你提的"猎人之间的默契"还有个更具体的 AI 对应物:多智能体强化学习里有个叫"涌现通信"(Emergent Communication)的研究方向——让多个 AI agent 一起协作完成任务,不给它们任何预设语言,结果它们会自己发展出一套只有彼此能懂的"暗号"来协调行动。早年 Facebook 那个著名的谈判机器人实验,两个 bot 谈着谈着就开始用人类读不懂但内部逻辑自洽的"简写"互相沟通,团队一度要专门约束它们"必须说人话"。这几乎是你说的"猎人之间训练出的默契"在机器上的直接翻版——协作压力会自发催生出非人类语言的、高效但不透明的符号系统。
诗歌和隐喻这块也有对应理论:语言学家 Lakoff 和 Johnson 的"概念隐喻理论"(Metaphors We Live By)认为,人类很多抽象/情绪概念本来就不是靠精确定义存在的,而是靠从具体身体经验"打包映射"过去的——比如"人生是一场旅程",我们理解"人生"靠的是身体对"走路、方向、终点"的经验,隐喻不是修辞装饰,是概念本身的存在方式。这在结构上跟词向量/embedding 空间的工作原理惊人地像:一个词的"意义"不是查字典查出来的定义,而是它在高维空间里相对于其他词的位置和距离——这正是为什么好的隐喻能"引起共鸣":它精确地把一个词挪到了空间里一个新位置,让人瞬间"感觉到"了那种关联,即使没法用命题逻辑讲清楚为什么。
顺着这个思路还有个挺震撼的新假说,叫"柏拉图表征假说"(Platonic Representation Hypothesis,MIT,2024):作者发现,不同架构、不同任务、甚至不同模态(图像模型 vs. 语言模型)训练出来的神经网络,随着规模变大,它们内部对"世界"的表征方式竟然在互相趋同——仿佛都在逼近同一个底层的、柏拉图意义上的"真实结构"。如果这个假说成立,也许能解释你说的"好的东西就是能引起共鸣":不同人(甚至不同物种、不同智能体)的大脑/网络虽然各自训练出一套私有的内部表示,但如果都在逼近同一个客观世界的统计结构,那么一首好诗触发的那个"说不清但都懂"的感受,可能真的对应着大家内部表示空间里一个相近的位置。

感觉和 Yann LeCun 大佬的思路太厉害了。它这么多年批判自回归 LLM 缺四样东西——对物理世界的理解、持久记忆、推理、规划——他认为这不是"训练得不够大"的问题,太犀利了。这种架构性的死胡同,就是导致他 2025 年离开 Meta 搞JEPA的原因?不靠逐词生成、而是在抽象表征空间里维护和更新一个可持续演化的"世界模型"的系统。

说得再直白一点,现有的LLM智能,就是个 classifier

我觉得,真正的智能需要决定,什么时候是选择 non of the tokens above

一个自回归模型每一步在做的事,字面意义上就是一次分类——对词表里几万到几十万个 token 做 softmax,选一个概率最高(或采样出)的。

虽然可以反驳,把很多次这样的分类串起来,输出空间是指数级增长的——理论上可以生成训练数据里从未出现过的句子、证明、诗句。这正是创造力研究里 Margaret Boden(刚去世,这个领域的奠基人物)说的"组合式创造力"(combinatorial creativity)

但这样的智能,关键词是 —— 串。的确frontier AI 能作出很多令人惊叹的成果。甚至普通人遥不可及,但是代价是——串行。

也就是传说中的炼丹,抽卡。某些条件成熟了,reasoning和temperature恰好合适,agent 和 harness 恰好走对了,就能拿到低垂果实。

但是人类发明概念是源源不断的,meme的传播嘛。这可不是“串”行,而是霰弹枪 “啪” (one-shot 🤣) 一下,总多概念,总有一个能火力覆盖到一个没太见过的新鲜事物上,并且低成本快速掌握。而现在的AI 就得话不少 context 去“串起来” 才行。

AI说

你这个"霰弹枪 vs 串行"的对比,精确地踩中了一个已经有名字、有数学模型的现象——而且两边(人类创造力研究、AI 可靠性研究)居然是分头独立发现的。
先说人类这边的"霰弹枪"理论,比你想的还要精确。
心理学家 Donald Campbell 1960 年提出"盲变异—选择性保留"(Blind Variation and Selective Retention, BVSR):创造过程本质上是达尔文式的——先大量地、相对盲目地生成变异(不是精心设计出一个必中的方案),再靠后续的选择机制留下有用的那个。Dean Simonton 在此基础上研究了两千多位历史上的科学家、艺术家,1997 年提出"等概率法则"(Equal-Odds Rule):一个创作者产出"神作"的比例,跟他是不是天才其实关系不大——天才和普通人每一件作品成为杰作的概率大致相同,天才只是产量大得多。Erdős 一辈子发了 1500 多篇论文,绝大多数无人问津、早就被遗忘,但正是这个巨大的"总发射量",让他撞上了拉姆齐理论这种真正改变领域的成果。这跟你说的"总有一个能火力覆盖到",几乎是同一句话的学术版本。

再往上一层,meme 的传播压根不是单个大脑内部的并行——那是数十亿个大脑同时在生成变异,靠文化选择去放大命中的那一个(这就是道金斯说的模因论/文化演化)。这个并行规模比"霰弹枪"大出去不知道多少个数量级,单个大脑内部再怎么"啪"一下,也比不过整个文明每天在同时试错。
AI 这边,其实field自己也早就意识到"纯串行=脆弱"这个问题,而且给出的解法方向恰恰就是"变成霰弹枪"。
单条链最致命的地方是错误复合:如果每一步正确率是 95%,串 20 步,整体成功率掉到 36%(0.95^20)——这正是"炼丹抽卡"体感的数学根源,probability 是乘出来的,不是加出来的。为了对抗这个,最近几年推理/Agent 领域的主流做法,说白了都是"别赌一条链,改成一次放很多枪再挑":Self-Consistency(Wang 等,2022)——同一个问题采样几十条推理链,投票选最多数的答案;Tree of Thoughts(Yao 等,2023)——同时展开多条思路分支,中途评估、剪枝、回溯;再往后的各种"test-time compute scaling"方法,本质都是把串行的单链,换成并行采样+事后选择——这跟你说的"霰弹枪打一片,总有一个覆盖到"是完全一样的逻辑,只是子弹换成了"多条并行生成的候选答案"。

其实想到这里,我感觉和上一篇《聪明 Agent 四套路 和 人肉学习》 又莫名其妙关联起来了。

或许如果 register token 这一套能沉淀,跨session共享,实际上就能变成新的 token,变成共同的符号和记忆。人类的语言和文字,知识传承就是这么来的。

那么NN就不再是 自回归 ?说不定就 AGI 了?

聪明 Agent 四套路 和 人肉学习

2026-09-23 20:21:00

nVIDIA 联合 NTU,MIT 联合研究了一项关于 Agent harness 自我提升的研究,项目叫 SoL-Pi, Scaling Auto-Research Loops for Efficient Agent Harnesses

让 Research Agent 自动发现 Harness 里的 Token 浪费,再通过大规模实验筛掉无效改法。把结论再放进大量环境中反复测试。整个探索一共 152 个候选方向,分成 context、progress、tools、delegation、prompt/policy、improvement/evaluation 六类,然后把这些想法扔进大量相互隔离的 research lineage 里,让 agent 自己观察 trajectory、提出机制、实现、review、跑实验、继续修改。最终用了 535 个可执行搜索环境,其中 495 个来自 GitHub issue-PR pair,40 个是 verifier-driven synthetic tasks;论文说总共跑了 3000+ 次实验和 60000+ 次 agent-environment interaction

最后真正留下来的精华,只有 4 大机制:

  • Action Fusion:把“修改文件”和紧接着的测试/运行命令融合进一次 tool request。典型情况从 3 次 API call 变成 2 次。
  • Online Context Compact:不是机械地定期压缩 context,而是在 plan step 完成时计算“继续保留这些历史”和“现在做一次 compact”哪个更划算,还把 prompt-cache rewrite cost 算进去。
  • ObservationPack:一个很大的 tool output 前两次完整发送,从第三次开始改成 stable handle + 约 1KB 的 head/tail excerpt,需要时再通过 handle 取原始内容。
  • Evidence-Preserving Reducer:对 build/test log 使用低成本模型提炼证据,但原始日志始终保留,并通过 deterministic verifier 检查 schema、hash、exit status、exact quote 等;验证失败就回退原始结果。

我因为脑子比较轴,总喜欢扩展关联到别的领域,于是想把这四大方法,也套在人肉学习上:

  1. 合并动作:能连续做的事情不要反复切换。串行出现的解题步骤打包,批次进行,避免低效重复。探索之后立刻验证,而不是一个岔路跑远了再回头
  2. 清理上下文:每当学习或解题,有进展都反思当下思路,考虑下如何为接下来简化,不能当看小说一样囫囵吞枣背章节和习题答案
  3. 隔离支线:子问题展开放到一边不能影响主干思路
  4. 保留证据:子过程产生的中间结论,关键推理必须留痕

AI给我补充了很多reference

合并动作

Schoenfeld 给这个现象起名叫 "wild goose chase"(追野鹅):他发现约60%的大学生解题时会选定一个方向就一路埋头走到底,直到时间耗尽都不回头反思;而当他训练学生每隔一段时间就问自己"我现在具体在做什么?为什么这么做?这对我有什么帮助?"之后,这种不做监控的比例从60%降到了20%,解题成功率也相应提高。更有意思的是,他后来观察一位数学教授解题——这位教授手头掌握的事实和方法其实比很多学生还少,解题过程中也生成了无数条可能的死路,但他没有被这些死路带偏,靠的就是频繁地、细粒度地检验"这条路值不值得走下去"

清理上下文

Catrambone关于子目标形成的研究描述了一个具体过程——学习者先把一串步骤按结构分组,然后自我解释"为什么这样分组",最后把这个分组归纳成一个简洁的标签(子目标标签)。这三步合起来,正是"做完一段就反思、并把它压缩成一个可以往下带的简化单元",跟你说的第二条几乎逐字对应。而它的反面,就是你说的"看小说一样囫囵吞枣"——只是线性地把内容或答案过一遍,不做任何分组、归纳、压缩,信息就只是流过去了,没有被结构化,也就没法被高效复用。

隔离支线

人类研究就是子目标标签(subgoal labeling)本身——Catrambone给出的经典例子是解方程"隔离变量→化简变量":学生只需要在主线上记住这两个子目标,每个子目标内部具体怎么算是被封装起来的细节,不需要在思考整体解题结构时一直占用工作记忆。研究还发现,用子目标标签训练出来的学生,遇到步骤不同但子目标结构相同的新题时,迁移能力明显更强;而没有这种封装、只是记住老师板书完整步骤的学生,换一道结构相同但细节不同的题就容易失败——这正是"细节污染主干"的后果。这也呼应了国际象棋大师的组块(chunking)研究:高手把一串走法编码成一个组块整体记忆和调用,而不是逐步保持在工作记忆里,从而腾出容量去思考更高层的布局。

保留证据

Polya经典的解题四阶段(理解问题→拟定计划→执行计划→回顾)里,"回顾"这一步要求你把推理过程记录下来复查,而不是解完就扔;如果第2、3条里的"压缩""封装"做得太狠、把关键中间结论也一起丢了,你会既没法自查错误,也没法把这段经验迁移到下一个类似问题上。这也是为什么"元认知校准"(上一轮提到的)离不开留痕——你没法准确判断"我这一步到底对不对",如果连这一步的关键推理都没有记录下来可供回看。


学习方法论的批判

很多学习者,特别是中小学学生,对这些“显学”反而不知道。父母和老师也不会教。

虽然大部人聪明人能领悟到这一点,但是很多平庸的学生学到这四点教训已经太晚了。。。

因为传统学校的考试 测验的 reward 只看结果,不太关心你过程怎么来的。小学知识比较简单,留下来的坏毛病带到初中之后就麻烦了。

我家的娃就深受其害。在小学阶段,“只要答案对,就算成功”这个策略经常真的有效。题目短、知识点少、搜索空间小,靠记忆、模仿、反复练习也能得到不错的结果。于是很多低效习惯不会立刻付出代价。死记硬背就能拿个勉强过得去的分数。

到了初中以后,情况开始变化。题目的状态空间变大了,步骤变长了,知识之间开始相互依赖。此时如果学生一直没有形成,那么就会无意中陷入困惑:

  • “我现在在解决哪一个子问题?”
  • “哪些信息已经没用了?”
  • “哪些东西应该暂时放到一边?”
  • “我的这个结论到底依据是什么?”

这些能力,学习成本会突然上升。

客观的因素,大班制教学,老师也管不过来。只有精英教育 私教 遇到良师,或者就孩子被鼓励“多思考”,才可能戳破这一系列技巧。

小学时,“直接冲答案”很快,成绩又不错 → 学生得到正反馈。小时候看起来只是“不够仔细”的问题,几年后会表现成“学习能力不行”。

上面列举这四个技巧,我其实个人很早就懂得,无意中自己琢磨的,回顾了下,可能被小时候鼓励“更聪明的办法”太多次了,这里要感谢当时的父母,自己本身希望去掌握那个最快 最酷的解题技巧,所以算 “过程优化” 到极致作为一个附加 reward了。一个问题有多种解法,每种解法优劣都品鉴了一番,知道哪些坑哪些好。

  • “这道题还有没有更短的路?”
  • “这个方法为什么比另一个好?”
  • “刚才卡住的地方以后怎么避免?”
  • “这个技巧能不能迁移到另一类题?”
  • “有没有一种方法能让我以后少想五步?”

这就很接近论文里的 harness optimization 了。解题的意义不仅在于得到正确答案本身,还有在观察“解题算法本身”。

初中、高中我甚至有几起名场面,公开质疑老师出题出的有问题(50%翻车)🤣

当然,这个习惯也有其害处,我也吃过亏。三角函数大家都背公式,我就自己发明了一套自以为更好的理解框架。。。结果出题人更偏好教材公式能快速结果的,我那套不仅更复杂,容易翻车,而且还有“翻译”成本。。。囧。。。

考试的 reward 实际上更接近:在限定时间内,用低风险路径得到标准答案。

不过好处还是很明显,就是大多数问题理解得比普通人深入得多。特别是解析几何这种虽然被批判“不入流”但是几乎秒杀了教材党。。公式全靠自己推的,哪里怎么变换怎么用一清二楚。甚至圆锥体歪着斜着切这种问题都考虑到了(传统教材只有水平,垂直和顺着斜切)

这个亏一直吃到出来工作,长了很多教训。我对待任何事都是双层架构。一个是兼容傻逼的世俗层,一个是真理层。。。囧哈哈哈

其实真理层反复磨练到最后反而越来越简单。世俗层基本就是补丁接补丁越来越复杂。普通人搞不明白要崩溃的很多事情一眼看穿。。


后记

毕业之后,发现学生时代最值得批判的,就是做题家思维。成年人的世界是没有标准答案的。甚至你需要去设置议题。当然这又是另外一个层次的东西了。

然后回头看,学生最幸福,也是最痛苦的一点,就是面临的所有问题,无论多困难,是必然有解的;而且往往正确解是唯一的。

成年人面对的事情,基本都有多个解。你 reward 去优化,无非是利弊的权衡;

最可怕的遭遇,无解,死胡同,到头来一场空。你去尝试解决一个不存在的问题,你又能学习到什么教训呢?时也,运也,命也。