关于CMS内容AI就绪的叙述

在CMS领域目前流传着这样一个叙述:"如果你想要AI就绪的内容,你需要Sanity的结构化内容方法。"说实话,Sanity的内容湖和他们GROQ驱动的AI集成确实给人印象深刻。但问题是——大多数团队不能只是放弃现有的CMS。你在WordPress中有多年的内容。你的应用数据层在Supabase中。你刚刚在六个月前完成了向Payload CMS的迁移。再经历一次迁移的想法让你胃部不适。

好消息:你不需要切换。你需要用不同的方式思考内容的结构化、存储和暴露方式。在过去的一年里,我帮助团队对现有堆栈进行了改造以支持AI消费,这些模式令人惊讶地一致,无论你运行的是哪个CMS或数据库。让我给你讲解一下。

目录

什么是"AI就绪内容"

在我们开始战术性讨论之前,让我们先澄清我们实际在谈论什么。"AI就绪内容"不是营销术语(好吧,它是,但其下面有实质内容)。这意味着你的内容满足三个标准:

  1. 机器可解析的结构 ——AI模型可以可靠地从你的内容中提取意义,无需猜测上下文
  2. 丰富的元数据 ——每个内容都携带足够的语义信息,使AI能够理解关系、意图和上下文
  3. API可访问性 ——内容可通过AI代理、RAG管道和LLM工具调用可以消费的编程接口获得

就这样。注意清单上没有什么:特定的供应商。这些是架构模式,而不是产品功能。

内容智能谱系

将内容AI就绪程度想象为一个谱系:

级别 描述 示例
0 HTML块 WordPress帖子,包含内联样式和混合媒体
1 分离的关注点 带有结构化数据标记的干净HTML
2 字段级结构 内容被分解为类型化字段(标题、摘要、正文、作者)
3 语义关系 包含显式引用、分类法和实体链接的内容
4 AI原生 包含嵌入、语义注释和机器可读意图的内容

Sanity的结构化内容模型默认将你推向第3-4级。但是每个CMS都可以达到第3级,并且通过一些额外的基础设施,可以达到第4级。

为什么Sanity获得所有关注

让我们给应该给予的地方归功。Sanity对结构化内容的方法对于AI用例来说确实设计得很好:

  • Portable Text 将富文本存储为JSON AST而不是HTML,使其易于以编程方式解析
  • GROQ 查询返回你需要的数据的确切形状,这完美地映射到LLM上下文窗口
  • Content Lake 将内容视为具有显式引用的类型化文档图
  • 他们在2025年的 AI SDK集成 允许从LLM直接工具调用到内容查询

但这是Sanity传道者没有提到的:这些优势是架构模式,而不是专有魔法。你可以在现有堆栈中实现每一个。这只需要有意的设计。

真正的问题不是"我应该切换到Sanity吗?"而是"我如何在已有的地方应用结构化内容原则?"

在WordPress中为AI构建内容

WordPress在2025年支持互联网的约43%。如果你在运行WordPress,你并不孤单,而且你的选项比你可能想象的要多。

第1步:停止对所有内容使用经典编辑器

Gutenberg块编辑器已经将内容存储为结构化块。每个块都有一个类型、属性和内容。这比大多数人意识到的要接近Sanity的Portable Text。

{
  "blockName": "core/paragraph",
  "attrs": {},
  "innerBlocks": [],
  "innerHTML": "<p>这是结构化内容,而不仅仅是HTML。</p>",
  "innerContent": ["<p>这是结构化内容,而不仅仅是HTML。</p>"]
}

块数据作为序列化注释存储在 post_content 中,但你可以以编程方式解析它:

$blocks = parse_blocks($post->post_content);
$structured = array_map(function($block) {
    return [
        'type' => $block['blockName'],
        'attributes' => $block['attrs'],
        'content' => strip_tags($block['innerHTML']),
    ];
}, array_filter($blocks, fn($b) => $b['blockName'] !== null));

第2步:投资自定义字段和分类法

Advanced Custom Fields (ACF) 或 Meta Box 为你提供第2-3级内容结构。但你需要有意地对待它。不要只是添加字段——设计一个内容模型。

// 为AI消费注册结构化内容类型
register_post_type('knowledge_article', [
    'supports' => ['title', 'custom-fields'],
    'show_in_rest' => true, // 对API访问至关重要
]);

// 定义语义字段
acf_add_local_field_group([
    'title' => 'AI就绪内容字段',
    'fields' => [
        ['key' => 'summary', 'label' => '摘要', 'type' => 'textarea'],
        ['key' => 'key_concepts', 'label' => '关键概念', 'type' => 'taxonomy', 'taxonomy' => 'concept'],
        ['key' => 'content_intent', 'label' => '内容意图', 'type' => 'select', 'choices' => [
            'informational' => '信息性',
            'transactional' => '事务性',
            'navigational' => '导航性',
        ]],
        ['key' => 'related_entities', 'label' => '相关实体', 'type' => 'relationship'],
    ],
]);

第3步:通过REST API暴露一切

WordPress REST API是你与AI的桥梁。确保自定义字段被暴露:

add_action('rest_api_init', function() {
    register_rest_field('knowledge_article', 'ai_metadata', [
        'get_callback' => function($post) {
            return [
                'summary' => get_field('summary', $post['id']),
                'concepts' => wp_get_post_terms($post['id'], 'concept', ['fields' => 'names']),
                'intent' => get_field('content_intent', $post['id']),
                'related' => get_field('related_entities', $post['id']),
                'structured_blocks' => parse_blocks(get_post_field('post_content', $post['id'])),
            ];
        },
    ]);
});

如果你以Next.js或Astro前端运行WordPress作为无头CMS(这是我们在Social Animal做的很多事情),这个REST API就成为你的AI的主要接口。

第4步:添加JSON-LD结构化数据

这个经常被忽视用于AI就绪性,但这很重要。Google的AI概览和其他AI爬虫程序消费JSON-LD。Yoast SEO或RankMath之类的工具生成基本架构,但对于真正的AI就绪性,你想输出详细的结构化数据:

{
  "@context": "https://schema.org",
  "@type": "TechArticle",
  "headline": "使你的内容AI就绪",
  "abstract": "如何为AI消费构建现有CMS内容",
  "about": [
    {"@type": "Thing", "name": "内容管理"},
    {"@type": "Thing", "name": "人工智能"}
  ],
  "mentions": [
    {"@type": "SoftwareApplication", "name": "WordPress"},
    {"@type": "SoftwareApplication", "name": "Payload CMS"}
  ]
}

Payload CMS:你已经很接近了

如果你已经在Payload CMS上,祝贺——你可能已经在第2-3级,而无需做太多额外工作。Payload基于集合的架构,具有类型化字段,本质上是结构化的。

为什么Payload已经是AI友好的

Payload将内容存储为MongoDB或Postgres中的类型化JSON文档。每个字段都有定义的类型。关系是明确的。这正是AI需要的。

// 已经是AI就绪的Payload集合
const Articles: CollectionConfig = {
  slug: 'articles',
  fields: [
    { name: 'title', type: 'text', required: true },
    { name: 'summary', type: 'textarea' },
    { name: 'body', type: 'richText' }, // 存储为Slate/Lexical JSON
    { name: 'topics', type: 'relationship', relationTo: 'topics', hasMany: true },
    { name: 'contentType', type: 'select', options: ['guide', 'tutorial', 'reference'] },
  ],
};

Payload的富文本编辑器(v3.x中的Lexical)将内容存储为JSON AST——就像Sanity的Portable Text一样。你已经有结构化内容。

将AI特定的字段添加到Payload

Payload和完全AI就绪之间的差距主要是关于元数据。将这些字段添加到你的集合:

const aiFields: Field[] = [
  {
    name: 'aiMetadata',
    type: 'group',
    fields: [
      { name: 'embedding', type: 'json', admin: { hidden: true } },
      { name: 'extractedEntities', type: 'json', admin: { readOnly: true } },
      { name: 'semanticSummary', type: 'textarea', admin: { readOnly: true } },
      { name: 'contentHash', type: 'text', admin: { hidden: true } },
    ],
  },
];

然后使用Payload的钩子在保存时自动生成嵌入:

const generateEmbeddingHook: CollectionAfterChangeHook = async ({ doc, operation }) => {
  if (operation === 'create' || operation === 'update') {
    const textContent = extractTextFromLexical(doc.body);
    const embedding = await openai.embeddings.create({
      model: 'text-embedding-3-small',
      input: `${doc.title}\n${doc.summary}\n${textContent}`,
    });
    
    await payload.update({
      collection: 'articles',
      id: doc.id,
      data: {
        aiMetadata: {
          ...doc.aiMetadata,
          embedding: embedding.data[0].embedding,
          contentHash: hashContent(textContent),
        },
      },
    });
  }
};

这本质上是Sanity的AI功能在幕后所做的。你只是自己做。对于使用Next.js在Payload上构建的团队,这个模式自然集成到你现有的部署管道中。

Supabase作为AI就绪的内容层

Supabase很有趣,因为它不是CMS——它是一个数据库平台。但越来越多地,团队将其用作内容后端,特别是通过Supabase的pgvector扩展支持嵌入。

pgvector的优势

Supabase自2023年以来一直支持pgvector,并且它已经成熟得很多。这意味着你可以在同一数据库中存储内容和向量嵌入:

-- 启用扩展
create extension if not exists vector;

-- 创建具有嵌入支持的内容表
create table content (
  id uuid default gen_random_uuid() primary key,
  title text not null,
  body text not null,
  metadata jsonb default '{}',
  content_type text not null,
  embedding vector(1536), -- OpenAI text-embedding-3-small维度
  created_at timestamptz default now(),
  updated_at timestamptz default now()
);

-- 为相似性搜索创建索引
create index on content using ivfflat (embedding vector_cosine_ops)
  with (lists = 100);

为AI代理构建内容API

Supabase的自动生成的REST API加上Edge Functions为你提供了你需要的一切:

// AI内容检索的Supabase Edge Function
import { createClient } from '@supabase/supabase-js';

Deno.serve(async (req) => {
  const { query, limit = 5 } = await req.json();
  const supabase = createClient(Deno.env.get('SUPABASE_URL')!, Deno.env.get('SUPABASE_KEY')!);
  
  // 为查询生成嵌入
  const embeddingResponse = await fetch('https://api.openai.com/v1/embeddings', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${Deno.env.get('OPENAI_API_KEY')}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: 'text-embedding-3-small',
      input: query,
    }),
  });
  
  const { data } = await embeddingResponse.json();
  const queryEmbedding = data[0].embedding;
  
  // 使用pgvector进行语义搜索
  const { data: results } = await supabase.rpc('match_content', {
    query_embedding: queryEmbedding,
    match_threshold: 0.7,
    match_count: limit,
  });
  
  return new Response(JSON.stringify(results), {
    headers: { 'Content-Type': 'application/json' },
  });
});

相似性匹配的Postgres函数:

create or replace function match_content(
  query_embedding vector(1536),
  match_threshold float,
  match_count int
) returns table (
  id uuid,
  title text,
  body text,
  metadata jsonb,
  similarity float
) language sql stable as $$
  select
    content.id,
    content.title,
    content.body,
    content.metadata,
    1 - (content.embedding <=> query_embedding) as similarity
  from content
  where 1 - (content.embedding <=> query_embedding) > match_threshold
  order by content.embedding <=> query_embedding
  limit match_count;
$$;

这为你提供了一个完全功能的RAG(检索增强生成)后端,而无需任何CMS迁移。你的内容在Supabase中,你的AI可以语义地查询它,而你的Astro或Next.js前端可以通过相同的API消费它。

AI就绪内容的通用原则

无论你的CMS如何,这些原则都适用:

1. 将内容与演示分离

这是你能做的最重要的单一事情。如果你的内容与HTML、CSS类和布局关注点纠缠在一起,AI无法可靠地解析它。将内容存储为数据,在演示层将其呈现为HTML。

2. 对所有内容进行类型化

每个字段都应该有一个明确的类型。不要对结构化数据使用泛型"文本"字段。日期应该存储为日期。引用应该是引用,而不是粘贴到文本字段中的slug字符串。

3. 使关系明确

如果文章A引用产品B,这应该是一个类型化的关系——而不是正文中的提及。AI工具需要遍历你的内容图,他们无法用隐含的链接做到这一点。

4. 添加语义元数据

超越基本的SEO元数据。包括:

  • 内容意图(信息性、事务性、导航性)
  • 受众细分
  • 置信度/新鲜度指标
  • 实体注释
  • 除基本类别外的主题分类

5. 版本和时间戳一切

AI系统需要知道内容有多新鲜。包括 created_atupdated_at,理想情况下还有 valid_untilreview_date 字段。RAG管道中的陈旧内容会导致幻觉。

构建AI抽象层

这是我一直回到的模式:不是迁移你的CMS,而是在其之上添加一个AI抽象层。

[WordPress/Payload/Supabase] → [内容同步] → [AI层(pgvector/Pinecone)] → [AI消费者]

AI层:

  1. 同步内容 通过webhook或轮询从你的CMS
  2. 规范化 它成为一致的结构,无论来源如何
  3. 生成嵌入 并将它们与规范化内容一起存储
  4. 暴露AI优化的API 用于RAG、工具调用和语义搜索
// 简化的内容同步管道
interface NormalizedContent {
  id: string;
  source: 'wordpress' | 'payload' | 'supabase';
  sourceId: string;
  title: string;
  body: string; // 纯文本,去除标记
  structuredBody: object; // JSON AST(如果可用)
  metadata: {
    type: string;
    intent: string;
    topics: string[];
    entities: string[];
    createdAt: string;
    updatedAt: string;
  };
  embedding?: number[];
}

async function syncContent(source: ContentSource): Promise<void> {
  const rawContent = await source.fetchAll();
  
  for (const item of rawContent) {
    const normalized = source.normalize(item);
    const embedding = await generateEmbedding(
      `${normalized.title}\n${normalized.body}`
    );
    
    await aiLayer.upsert({
      ...normalized,
      embedding,
    });
  }
}

这种方法有一个巨大的优势:你的编辑者继续使用他们认识的CMS。没有重新培训,没有迁移,没有停机时间。AI层与你现有的堆栈并存。

无需完整迁移的向量嵌入

让我们谈谈2025年的成本和工具,因为这对于真实世界的决定很重要:

嵌入提供者 模型 每百万令牌成本 维度 注释
OpenAI text-embedding-3-small $0.02 1536 最佳成本/质量比率
OpenAI text-embedding-3-large $0.13 3072 更高的准确性
Cohere embed-v4 $0.10 1024 很好的多语言支持
Voyage AI voyage-3 $0.06 1024 对代码内容有强大支持
本地(Ollama) nomic-embed-text 免费 768 隐私优先的选项

对于典型的内容站点,有5,000篇文章,平均每篇1,500字,你看起来大约7.5M令牌。使用OpenAI的小模型,这是$0.15来嵌入你的整个内容库。即使每周重新嵌入也是微不足道的。

向量存储选项

解决方案 免费层 定价(2025) 最适合
Supabase pgvector 500MB数据库 8GB为$25/月 已在Supabase上的团队
Pinecone 5M向量 $70/月启动器 大规模生产RAG
Qdrant Cloud 1GB集群 $25/月 高级过滤需求
Weaviate Cloud 50k个对象 $25/月 多模态内容
Turbopuffer 1M向量 按查询付费 成本敏感项目

如果你已经在运行Supabase,pgvector是明显的选择。没有额外的服务,没有额外的计费,没有额外的故障点。

真实世界架构模式

让我分享两个我实际构建过的架构:

模式1:WordPress + Supabase AI层

对于有50k+个WordPress帖子的媒体公司:

  1. WordPress webhook在帖子保存/更新时触发
  2. Supabase Edge Function接收webhook
  3. 通过WP REST API获取内容,规范化和嵌入
  4. 存储在Supabase中,带pgvector
  5. Next.js前端上的AI聊天机器人查询Supabase进行语义搜索
  6. 结果作为GPT-4o的上下文传递用于答案生成

额外基础设施总成本:~Supabase pro层$25/月。

模式2:具有内置AI的Payload CMS

对于Payload v3上的SaaS文档站点:

  1. Payload钩子在每个文档保存时生成嵌入
  2. 嵌入存储在Payload使用的同一Postgres数据库中的 vector 列中
  3. 用于语义搜索的自定义Payload端点
  4. 由同一数据库支持的AI文档助手
  5. 不需要外部向量存储

额外基础设施总成本:$0超出OpenAI API调用(每月几分钱)。

这两个模式花了大约2-3周来实现,而不是完整CMS迁移的3-6个月。如果你在考虑这种架构,我们有定价层涵盖这些项目类型。

常见问题

我真的需要为AI重构我的内容,还是只是炒作? 这不是炒作,但紧急程度取决于你的用例。如果你在构建AI功能(聊天机器人、语义搜索、个性化),结构化内容是必不可少的。如果你在针对AI驱动的搜索(如Google的AI概览或ChatGPT的浏览)进行优化,结构化数据和干净的内容层次可以测量地改善你的可见性。Authoritas进行的2025年研究发现,具有架构标记的页面在AI生成的答案中出现的可能性高40%。

我应该做的最少是什么来使WordPress内容AI就绪? 三件事:(1)始终使用Gutenberg块而不是粘贴HTML,(2)向每个页面添加JSON-LD结构化数据,以及(3)通过REST API暴露自定义字段。这在几周的集中工作中将你从第0-1级提升到第2-3级。你不需要一夜之间重构整个站点。

Payload CMS能否替代Sanity用于AI驱动的内容? 对于大多数用例,是的。Payload v3具有Lexical富文本存储为结构化JSON,具有类型化字段和关系,并支持带pgvector的Postgres。Sanity提供而Payload没有本机提供的主要内容是具有内置AI功能的托管Content Lake。但是如果你愿意连接自己的嵌入管道(这需要大约一天),Payload为你提供等效的功能。

为现有CMS添加向量嵌入的成本是多少? 令人惊讶地少。对于有10,000篇文章的站点,使用OpenAI的text-embedding-3-small进行初始嵌入生成成本约$0.30。更新内容的持续成本通常低于$5/月。向量存储是更大的成本——根据你的提供者和规模预计$0-70/月。Supabase的免费层可以处理许多中小型站点。

我应该使用单独的向量数据库还是在现有数据库中存储嵌入? 如果你在Postgres上(Payload v3和Supabase都使用),在同一数据库中存储嵌入,使用pgvector。一项较少的服务要管理,一项较少的同步要破坏。专用向量数据库(如Pinecone)在你有数百万文档或需要亚毫秒查询时间时有意义。对于大多数内容站点,pgvector速度足够快——对于少于1M向量的集合,典型查询时间为5-20ms。

我如何保持AI嵌入与内容更改同步? Webhooks是你的朋友。每个现代CMS都支持它们。创建或更新内容时,触发webhook来触发重新嵌入。在嵌入旁边存储内容哈希,以便你可以跳过未更改的内容。对于WordPress,使用 save_post 操作。对于Payload,使用 afterChange 钩子。对于Supabase,使用数据库触发器或Realtime订阅。

多种语言中的内容——这种方法是否仍然有效? 是的,但仔细选择你的嵌入模型。OpenAI的text-embedding-3模型处理多语言内容效果很好。Cohere的embed-v4专门针对跨语言检索进行了优化。规范化层应该将语言代码存储为元数据,以便你的AI消费者可以相应地过滤。一个重要的注意事项:单独嵌入每个语言版本,而不是连接翻译。

迁移到无头CMS是AI就绪内容的前提吗? 不是前提,但它帮助很多。无头CMS架构自然地将内容与演示分离,这是AI就绪的基础。如果你仍在运行单体WordPress主题,内容烤入模板文件中,同时转向无头(WordPress作为后端,使用Next.jsAstro前端)会同时改善你的AI就绪和前端性能。即使在考虑AI用例之前,这通常也值得投资。如果你想探索这个,联系我们——这字面上是我们每天做的事情。