Skip to content

设计理念

Nimbus 背后的观点——在人类和智能体都是创造者与消费者的年代,文档应该是什么样子。

更新于 查看 Markdown
面向人类

你拥有所有代码

布局、组件、内容、样式——站点的每一个可见部分都存放在你的仓库中。脚手架工具写入一次后便退到一旁。从那一刻起,项目就是你的:编辑任何文件、重构任何目录、修改任何默认值。无需等待上游 API,也无需 fork 固执己见的主题。

编码智能体的工作方式和你一样。当没有什么重要的东西藏在 import 边界后面时,智能体能更好地理解整个仓库。

  • my-docs/
    • src/
      • components/
      • content/docs/
      • layouts/
      • pages/
      • styles/
        • globals.css
      • components.ts
    • astro.config.ts
    • package.json

交互式内容,完全属于你

值得阅读的文档往往需要展示机制——请求如何流转、状态如何嵌套、接口如何对接。你要么采用预设样式的组件库并继承其外观,要么从零开始构建交互部分。Nimbus 将问题一分为二。

nimbus-docs/react 提供一个无样式的 <Diagram> 包装器以及可组合的 hooks——usePhase、useMeasure、useTabIndicator、useDiagram。包装器负责那些难以做好的事情:离屏暂停、尊重减少动画偏好、键盘快捷键、错误边界、跨岛屿协调。它本身不渲染任何可见内容。

<Diagram>
useDiagram
useTabIndicator
DiagramControls
useMeasure
Tabs

最小的有意义示例——usePhase 走两个步骤,渲染的节点依次亮起。<DiagramControls> 通过注册表安装;其余部分来自 nimbus-docs/react。

Left
Right
import { Diagram, usePhase } from "@cloudflare/nimbus-docs/react";
import { DiagramControls } from "@/components/react/diagram";

function PingPong() {
  const { current } = usePhase({
    steps: [{ id: "left", hold: 1500 }, { id: "right", hold: 1500 }],
    loop: true,
  });
  return (
    <div className="flex items-center justify-center gap-12 py-12">
      <Node label="Left" active={current === "left"} />
      <Node label="Right" active={current === "right"} />
    </div>
  );
}

export function Demo() {
  return (
    <Diagram label="Ping-pong">
      <DiagramControls />
      <PingPong />
    </Diagram>
  );
}

操作栏、标签页和播放/暂停控件按需安装到 src/components/react/diagram/——自由编辑、重新设计样式或替换;hooks 继续正常工作。从不编写交互式卡片的站点不会产生任何开销。

以人为先,以智能体为先

文档不再只由人类阅读,因此每个 Nimbus 站点都会在 HTML 之外提供多种机器可读格式:

  • 每个页面的 Markdown 版本,位于 /<slug>/index.md
  • 站点级别的索引,位于 /llms.txt,列出所有页面
  • 每个页面头部的 JSON-LD,为搜索和智能体工具提供足够的结构化数据来识别页面内容
# Nimbus

A way to build documentation sites on top of Astro.

## Pages
- [Getting started](https://nimbus-docs.cn/get-started/index.md)
- [Installation](https://nimbus-docs.cn/installation/index.md)
- [CLI](https://nimbus-docs.cn/cli/index.md)

这些格式是默认配置,而非可选插件。在智能体网络中成为一个死胡同的代价是真实存在的,而且这个代价还在增长。

智能体驱动的创作

添加 Nimbus 默认组件或功能使用 nimbus-docs add。它的工作方式取决于你要添加的内容类型。组件和工具库以文件形式复制——registry:ui 和 registry:lib 标识符会解析依赖并落入你的仓库。功能以提示词形式交接——registry:feature 标识符是一个 markdown 配方,由你环境中的编码智能体读取、适配你的项目并执行。

npx @cloudflare/nimbus-docs add 404-page

两种模式之间的分界线在于一个关键问题:哪种变更实际上需要编码智能体参与,哪种更适合作为文件复制。

创作质量是一等公民

内容质量参差不齐的文档站点会损害产品本身。Nimbus 对待创作质量的方式就像代码检查工具对待源代码——一组分层的验证器,每一层捕获不同类型的错误。

预构建验证器自动运行,仅在问题确实会导致站点无法构建时才阻止构建:

  • 配置验证——检查 defineConfig 的形状和必填字段;错误会回显导致问题的值
  • Frontmatter schema 验证——Zod 类型化的 schema 按集合捕获缺失或格式错误的字段,提供友好的编辑器错误信息
  • MDX 组件验证——预构建内容检查捕获小写用法、未注册的组件和缺失的导入
  • 注册表验证——在构建时解析 src/components.ts 并与实际 MDX 用法进行比对

检查引擎按需添加创作质量层。规则拥有稳定的标识符,如 nimbus/single-h1 和 nimbus/bare-url,在 astro.config.ts 中与其他 Nimbus 集成配置一起设置。运行 nimbus-docs lint 获取人类可读的输出,--format json 获取智能体可读的诊断信息,--fix 应用自动修复。构建永远不会被检查结果阻止——草稿仍然会渲染,警告会保持醒目但不阻塞。

astro.config.tsts
import { defineConfig } from "astro/config";

export default defineConfig({
  integrations: [
    nimbus(nimbusConfig, {
      rules: {
        "nimbus/single-h1": "error",
        "nimbus/bare-url": "warn",
      },
    }),
  ],
});

基于 TypeScript 签名的组件属性验证将在 v1 之后推出。

可读只是底线

每个 Nimbus 站点已经提供了智能体可读的格式——每页的 Markdown 版本、llms.txt 索引和头部的结构化数据。一年前这能让文档工具脱颖而出;如今这只是入场券。更有意思的界限在更远处。

人人都让文档对智能体可读了。Nimbus 让文档可以被智能体编写、维护和操作——端到端,基于你完全拥有的代码库。

前提条件就是本页的第一个原则。当每个文件都在你的仓库中,没有什么重要的东西藏在 import 边界后面时,智能体不仅能阅读你的文档——它还能安全地修改文档,因为它能看到所涉及内容的全貌。供应商的主题化运行时可以被智能体抓取;但它无法被智能体重写。

这就是为什么功能以配方而非手动接入的代码形式到达。nimbus-docs add 会检测是否在编码智能体内运行,并向其传送结构化配方——发现项目、确认计划、执行,然后验证构建仍然通过,并在修改文件前检查该功能是否已安装。安装不再是复制粘贴的苦差事,而是变成了一场从头到尾由你的智能体掌控的对话。

# 在编码智能体内部,配方直接传入
npx @cloudflare/nimbus-docs add new-version
# 从人类终端,把它交给任意智能体
npx @cloudflare/nimbus-docs add new-version --print | claude

来源追踪是原语

一旦智能体开始起草页面,问题就不再是机器能否读取这个,而是谁写的这个,人类是否检查过。由于你拥有内容集合,答案就在你控制的 schema 中——来源追踪只需几个 schemaFields,而不是在顶层打的补丁。

启动器附带了这样一个字段。默认值是反向的:面向智能体的内容是默认假设,因此 audience: "human" 是明确为人类优先编写的页面设置的标志。你可以用同样的方式扩展更多字段——这个站点添加了 aiGenerated 来标记由智能体起草但人类尚未审查的页面,页面操作行会显示“待审核”标签,直到有人确认:

src/content.config.tsts
import { z } from "astro/zod";

docsCollection({
  schemaFields: {
    audience: z.literal("human").optional(),
    aiGenerated: z.boolean().optional(), // 显示"待审核",直到被清除
  },
})

注册该字段后,每个页面的来源和审查状态都随页面本身一起传递:

src/content/docs/new-endpoint.mdxyaml
---
title: New endpoint
aiGenerated: true   # 由智能体起草——显示"待审核",直到人类清除
---

来源追踪只有在漂移被发现时才有价值。检查引擎会输出带版本号的 JSON,其中包含 --fix 可就地应用的精确到字符的修复方案,而脚手架生成的 AGENT.md 附带一个固定诊断格式的审计配方——这样智能体可以扫描整个站点并以其他工具可解析的格式报告结果:

- [error] src/content/docs/cli.mdx:42 — 断裂的内部链接 "/instal" — 你是指 "/installation" 吗?
- [warn]  src/content/docs/registry.mdx:8 — 链接文本 "here" 在上下文外没有意义。
汇总:1 个错误,1 个警告。

文档变成了智能体可以长期保持正确的东西——可维护、可操作,而不仅仅是发布——基于你拥有的仓库,而不是供应商的仓库。

目录结构就是真相

除了你的内容之外,不需要维护另一份配置。src/content/ 下的目录结构就是 URL 结构。目录结构也是侧边栏。移动文件,其对应的条目随之移动;删除文件,条目消失。Frontmatter 处理细粒度的调整——排序、徽标、草稿状态——但树的形状来自文件系统本身。

  • src/content/docs/
    • getting-started.mdx
    • guides/
      • styling.mdx
      • deploying.mdx
    • reference/
      • api.mdx

结果是页面存在的唯一事实来源。站点不会与其内容脱节,因为其内容就是站点本身。

版本只是集合

大多数文档工具将版本管理作为特殊情况附加——并行文件系统、URL 重写、自定义路由。Nimbus 用一个已有的原语来实现:多集合内容。

每个版本都是自己的内容集合,使用相同的 schema。docs-v2/ 集合就是 v2 站点;docs-v3/ 集合就是 v3。路由和侧边栏独立处理每个版本。版本选择器只是上面的一个薄层。

  • src/content/
    • docs/
    • docs-v2/
    • docs-v3/

同样的原语也能处理国际化(docs-fr/ 集合就是法语站点)和产品拆分(docs-api/ 集合与 docs-cli/ 并存)。当你需要版本管理时,你已经拥有了大部分所需的东西;你之前没有为此付出代价。

导航

输入以搜索…

↑↓ 导航↵ 选择Esc 关闭