发布时间:2026 年 11 月 12 日
本周,我们推出了 Modern Web Guidance 1.0,这是一项开源技能,可帮助编码智能体使用现代 Web 平台功能,而不是旧版权宜解决方法。本文将介绍本地搜索引擎,它能为您的任务找到最相关的指南,而不会浪费您的 token。
如果您告诉工程师,您为他们的编码智能体构建了一个包含 1.7 MB Markdown 文本的知识库,他们会提出两个问题:
- “这会向我的上下文窗口中转储多少个 token?”
- “它需要什么样的臃肿向量数据库?”
在过去六个月中,我们三次重建了本地检索引擎。我们从 507 MB 的 C++ 绑定,缩减到 23 MB 的纯 JavaScript(使用 TensorFlow.js),再缩减到 7 MB 的软件包,其中查询嵌入是一个 7 行的 for 循环,同时还提高了准确率。
智能体 RAG 和渐进式披露
将 1.7 MB 的 Markdown 文本(包含 200 多个 HTML/CSS/Web API 指南)转储到 AGENTS.md 中是不切实际的。我们相信渐进式披露,但知道 Markdown 索引在规模化时将不再具有 token 效率。
为了解决这个问题,我们使用由搜索 CLI 支持的智能体 RAG:
- 在静止状态下,智能体只能看到技能的说明(约 205 个 token)。该说明遵循 Anthropic 的技能触发指南,以便智能体在相关前端任务中调用该技能。
- 在前端任务中,智能体可以使用 CLI 进行搜索:
modern-web-guidance search "animate height auto",获得 10 个相关匹配项(约 530 个 token)。 - 智能体运行
modern-web-guidance retrieve animate-to-intrinsic-sizes来提取整个指南(约 800 个 token)。它现在配备了有关兼容性、后备和技术的实现最佳实践。
为什么选择 CLI 技能,而不是 MCP 服务器或网页搜索?
- MCP 工具要么会浪费 token,要么触发机制不可靠(由于动态工具发现机制)。技能说明经过精心优化,可确保智能体仅在适当的时候使用指导。
- 内置的网页搜索功能会显示 2015 年的 Stack Overflow 答案和参考文档,而不是最新的、经过精心挑选的
DO/DO NOT实现模式。此外,通过网络运行需要几秒钟,并且在离线沙盒中会失败,而本地 CLI 在 CPU 上运行只需几毫秒。
第 1 代:507 MB 的 C++ 原型
第一版 Modern Web Guidance 使用了本地 RAG 堆栈:LanceDB、Apache Arrow 和 Transformers.js,并搭配 onnxruntime-native。
它在开发机器上运行良好,但不太适合作为智能体技能:
- 当您向项目添加技能(或智能体使用
npx调用技能)时,您不希望等待npm install下载依赖于架构的.node二进制文件,也不希望等待node-gypC++ 编译(这在云沙盒中通常会失败)。 - 解压后的
node_modules目录大小为 507 MB。
第 2 代:将 TensorFlow.js 缩减至 23 MB
我们希望实现零原生编译,并在运行 Node 的任何位置即时执行。因此,我们在 4 月份将原生堆栈替换为在 @tensorflow/tfjs 上运行 all-MiniLM-L6-v2 的纯 JavaScript CPU 引擎:
- 在 esbuild 中对
onnxruntime-node进行了填充,并将 Hugging Face 分词器替换为一个微型树内 BERT WordPiece 分词器(0 个 token ID 不匹配)。 - 在探索了 source-map 之后,pruned
@tensorflow/tfjs-backend-cpu的 400 多个模块缩减为 MiniLM 实际使用的 26 个内核,从而将 JS 引擎从 6 MB 缩减为 830 KB。 - 弃用了 LanceDB,改为使用构建时经过 gzip 压缩的向量,并对所有大约 200 个指南进行内存中
<1 ms点积扫描。 - 将嵌入器置于
import()调用之后,以便retrieve和list在 10 毫秒内完成。
这样,npm 软件包的大小就降到了 23 MB,且没有运行时依赖项。我们发布了该工具,代理人使用它进行了大约 75 万次搜索。
MiniLM 在生产环境中发生故障的位置
几个月后,我们分析了智能体安装了该技能但仍编写旧版代码的轨迹。
在一次 Codex 运行中,一位开发者要求提供删除确认提示。我们有一份完整的指南,介绍了如何使用 HTML <dialog closedby="any"> 构建无障碍确认模态框。
智能体做出了正确的选择,搜索了:
npx modern-web-guidance@latest search "confirm destructive action"
CLI 返回了 []。于是,代理耸了耸肩,写下了 window.confirm()。
为什么返回空结果?在深入分析日志和嵌入后,我们发现了两个明显的检索差距:
all-MiniLM-L6-v2是在 2018 年至 2020 年左右训练的,因此其 WordPiece 词汇表根本不知道 2020 年之后推出的 Web API。当智能体搜索fetchpriority、popover或startViewTransition时,2018 年的词元化器会将fetchpriority拆分为四个无意义的子词:fetch·##pr·##ior·##ity。- 为了防止超出范围的查询返回随机指南,之前的 PR 添加了严格的
0.38余弦相似度截止值。由于 MiniLM 无法将 3 词短语“"confirm destructive action"”与“"dialog"”联系起来,导致其相似度得分低于0.38(得分为0.349,而“"popover anchor"”得分为0.362,“"autofill address form"”得分为0.354),因此门控机制丢弃了有效匹配项。更糟糕的是,每当search返回[]时,我们的技能指令都会让智能体回退到modern-web-guidance list,将所有约 200 个指南说明(浪费了约 1,500 个 token)全部转储到上下文中。

修正检索:真实遥测、混合搜索和返回 10 条结果
我们最初的基准是根据指南本身生成的 850 个合成查询,这些查询与指南中的措辞过于接近,无法发现这些失败情况。因此,我们构建了一个全新的评估框架 (pnpm benchmark:rag),其中包含从用户选择退出的 CLI 遥测数据中抽样的约 4,000 次实时搜索(这些数据仅记录智能体 2-5 个单词的 search 参数,绝不记录用户提示或代码库代码,并遵循 DISABLE_TELEMETRY=1),以及从 Claude Code、Codex CLI 和 Gemini CLI 的夜间智能体评估中挖掘的 1,029 次搜索。
通过针对这些保留的查询重放我们的 git 历史记录,我们发现 0.38 截止日期导致空 [] 响应激增了 8 倍。有了基准之后,我们在调整嵌入模型之前,先进行了两项快速改进:
- 混合关键字 + 语义搜索:密集向量会模糊确切的 API 名称(
:user-invalid、showModal、details-name),而当智能体不知道 API 名称时,关键字搜索会错过意图查询。我们添加了一个零依赖项 BM25(懂的都懂)关键字索引,该索引支持复数词干提取,为每个指南的web-feature-ids编制了索引,并将 BM25 与语义排名相结合。现在,只有当语义相似度为<0.30且 BM25 的关键字命中数为零时,search才会返回[]。 - 10 个精简结果,而不是 5 个详细结果:技能说明已告知代理忽略
similarity和tokenCount。通过剔除这些未使用的字段,我们将 10 个紧凑的{id, description}结果压缩到与 5 个冗长结果完全相同的约 530 个词元的预算中:
// Before: 5 verbose results (~105 tokens each = ~525 tokens)
{
"id": "dialog",
"category": "html",
"description": "Build accessible modal and non-modal dialogs with native <dialog>, showModal(), and closedby.",
"featuresUsed": ["dialog", "dialog-closedby"],
"tokenCount": 780,
"similarity": 0.49
}
// After: 10 compact results (~53 tokens each = ~530 tokens)
{"id":"dialog","description":"Build accessible modal and non-modal dialogs with native <dialog>, showModal(), and closedby."}
这两项变更共同将我们对保留遥测查询的搜索命中率从 80.6% 提升至 94.7%,衡量标准为 Hit@K(预期指南是否出现在前 K 个结果中)。
第 3 代:预先准备好的嵌入和 7 行 for 循环
即使命中率达到 94.7%,我们的遥测数据也显示出明显的错配:86.7% 的实时代理搜索仅有 2 到 5 个字词(中位数为 3 个字词)。每当客服人员搜索一个 3 词短语时,Node 都会启动 TensorFlow.js,解析一个 22.6 MB 的神经网络,并消耗 770 毫秒的冷启动延迟和 346 MB 的峰值 RAM。
当被问及是否有更轻量的方法来嵌入 3 个字的查询而无需神经网络时,我们的 Antigravity 智能体给出了一堆不熟悉的机器学习蒸馏技术:Model2Vec、Tokenlearn、Matryoshka 维度、Adagrad。(如果您想了解技术细节,请访问我们的搜索质量页面。)
标准本地 RAG 会强制文档和查询都通过用户 CPU 上的同一个小型模型。但我们的约 200 篇指南在 npm 版本发布后不会发生变化,因此没有理由使用 2018 年的小型模型来嵌入它们。相反,embed-guide-chunks.ts 会在发布时使用 gemini-embedding-001 在云端预先烘焙所有引导向量,并将其作为二进制 256 维 Int8 向量(guide-chunks.i8,335 KB gzipped)进行传送。
一个离线、无需 API 密钥的 CLI 如何将用户的实时查询投影到完全相同的 Gemini 向量空间中?由于代理查询几乎总是 2 到 5 个字词,因此我们在运行时不需要转换器。我们只需要一个已知字词 (table) 的向量对照表,以及一种智能求和方法。在运行时,整个 23 MB 的 TensorFlow.js 流水线会折叠成 中的 7 行 for 循环static-embedder.ts:
// Each word's weight and Int8 scale are pre-folded into `scales[i]`
for (const [i, f] of enc) {
const wt = scales[i] * f;
const offset = i * 256;
for (let k = 0; k < 256; k++) {
out[k] += wt * table[offset + k];
}
}
为了构建该对照表 (train-query-words.ts),我们使用了包含 29,165 个字词的词汇表(其中 fetchpriority、light-dismiss、popover 和 scrollytelling 等现代网络术语是独立的完整字词),使用 gemini-embedding-001 对其进行了嵌入,并针对 33,000 个真实网络查询对词向量进行了离线调整,以便多词组合能够落在正确的位置(例如,"light" + "dismiss" 指向 <dialog closedby="any">,而不是照明或配色方案)。整个对照表经过 gzip 压缩后,大小为 Int8 (query-words.i8),压缩比为 5.3 MB。
为什么不使用该静态对照表来嵌入指南,并跳过 Cloud Build 步骤?我们首先尝试了这种方法,但在处理完整段落时效果不佳:对 3 个查询词进行求和可以保持清晰,但对整个指南中的数百个词进行平均,会导致每个指南都坍缩到通用英语平均值附近的狭窄锥体中(以至于 "best pizza in new york" 与我们的 dialog 指南的余弦相似度得分为 0.899,高于查询 "dialog" 本身的 0.844!)。将 3 词静态查询查找 (query-words.i8) 与完整模型 Gemini 指南嵌入 (guide-chunks.i8) 配对,可为我们提供云级准确率,同时查询仍可在 30 微秒内离线运行。

三代模型的得分情况
在我们的发布里程碑中,Hit@K 从使用整篇文档嵌入时的 78.2% 提升到使用静态 Int8 嵌入器时的 95.4%(相比之下,使用云端 gemini-embedding-001 API 时为 96.9%):

引入静态嵌入器后,这三代嵌入器的性能对比如下:
如果您运行之前那个有问题的 Codex 查询:
npx modern-web-guidance@latest search "confirm destructive action"
dialog 指南会重新显示在列表顶部。
(您可以在我们的搜索质量信息中心内,探索涵盖所有里程碑、查询长度和 K 范围的交互式图表。)
代码不值钱,衡量才是关键
智能体可以在几分钟内编写出 BM25 排序器或词向量蒸馏脚本。生成候选实现不再是瓶颈。了解一项更改是否确实有帮助(或者是否像我们的 0.38 门控一样,悄悄破坏了 9% 的生产搜索)几乎是整个工作的核心。
我们是这样设计该系统的:
- 确定北极星目标,并投资于衡量框架,而不是任何单一实现。我们花了更多时间构建
pnpm benchmark:rag、整理 5,000 个真实查询,以及根据人工评分校准自动评分,而不是编写任何检索器。 - 制定一套评分标准,供您和您的智能体用来评判质量,并预计该标准在接触到真实数据后会不断演变。人们称之为“评估优先”,但这只对了一半:你可以在第一天就编写基本的已知答案检查,但在检查实际失败之前,你无法凭空设计出良好的评估标准(Shankar 等人在《谁来验证验证者?》一文中称之为“标准漂移”)。看到智能体触发 1,500 个 token 的
list后备机制,迫使我们在评估召回率的同时,也对 token 膨胀进行惩罚。 - 有了可靠的衡量机制,您就可以大胆尝试不同的实现方式,并让记分板来解决架构争议。一旦基准测试能在几秒钟内对 5,000 个查询进行评分,您就不必在 PR 中争论一个 7 行的
Int8循环是否能击败一个 23 MB 的 Transformer。在一个下午尝试五种方法,并保留效果最好的方法。
当然,所有代码(包括嵌入器、提取器和基准测试工具)都是开源的。