# 如何修复AI忽略SEO元数据

> AI代理在构建Next.js和Astro页面时，缺少标题标签、Open Graph元数据或规范URL，导致页面在搜索引擎和社交爬虫中不可见。

**Type:** Failure  
**Tools:** Cursor, Claude Code, Codex, Windsurf  
**Stack:** Next.js, Astro, TypeScript  
**Updated:** 2026-06-08

---

代理生成的页面组件没有导出`Metadata`，没有`title`标签，也没有Open Graph标签，导致页面在搜索引擎中排名不佳，在社交媒体上分享效果也不好。

## 症状

Next.js App Router页面在构建时没有导出任何元数据，导致标签页标题空白，`head`中除了Next.js默认内容外为空。

```tsx
// app/blog/[slug]/page.tsx — WRONG
export default async function BlogPost({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug);
  return (
    <article>
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.html }} />
    </article>
  );
  // No <title>, no meta description, no og:image, no canonical URL
}
```

Google看到：`Untitled document`。Twitter/LinkedIn显示空白卡片。

## 原因

代理专注于可见的UI——数据获取和渲染——而将`head`元数据视为事后考虑。SEO在本地开发中不可见，也不会产生运行时错误，因此代理从未收到缺失的反馈。

## 如何发现

- App Router页面文件中没有`export const metadata`或`export async function generateMetadata`。
- Astro布局中没有`Astro.props`或由前置元数据驱动的`title`/`description`。
- 新页面的Lighthouse SEO评分低于90。
- 执行`curl -s http://localhost:3000/blog/my-post | grep "<title>"`返回空或通用网站标题。
- `head`中缺少`og:image`（社交预览显示空白卡片）。

## 如何修复

为动态页面导出`generateMetadata`并添加所有必需的标签。

```tsx
// app/blog/[slug]/page.tsx — CORRECT
import type { Metadata } from "next";

export async function generateMetadata({
  params,
}: {
  params: { slug: string };
}): Promise<Metadata> {
  const post = await getPost(params.slug);
  return {
    title: post.title,
    description: post.excerpt,
    alternates: { canonical: `https://example.com/blog/${params.slug}` },
    openGraph: {
      title: post.title,
      description: post.excerpt,
      url: `https://example.com/blog/${params.slug}`,
      images: [{ url: post.ogImage, width: 1200, height: 630, alt: post.title }],
      type: "article",
      publishedTime: post.publishedAt,
    },
    twitter: {
      card: "summary_large_image",
      title: post.title,
      description: post.excerpt,
      images: [post.ogImage],
    },
  };
}

export default async function BlogPost({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug);
  return (
    <article>
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.html }} />
    </article>
  );
}
```

对于Astro，通过布局传递元数据：

```astro
---
// src/pages/blog/[slug].astro
const { post } = Astro.props;
---
<Layout
  title={post.title}
  description={post.excerpt}
  ogImage={post.ogImage}
  canonical={`https://example.com/blog/${post.slug}`}
>
  <article>
    <h1>{post.title}</h1>
    <post.Content />
  </article>
</Layout>
```

```txt
[ ] Every page exports generateMetadata (Next.js) or passes title+description to layout (Astro)
[ ] title is unique per page — not just the site name
[ ] description is 120-160 characters and describes the page content
[ ] og:title, og:description, og:image (1200x630), og:url are all set
[ ] twitter:card is "summary_large_image" for pages with images
[ ] canonical URL is set to the preferred URL (no trailing slash ambiguity)
[ ] robots meta is not accidentally set to "noindex"
[ ] Verify with: curl -s <url> | grep -E "<title>|og:title|description"
```

## 修复提示

```txt title="Fix Prompt"
This page component is missing all SEO metadata. Add an async generateMetadata
export (Next.js App Router) that returns title, description, canonical URL, full
openGraph object (title, description, url, images with 1200x630 dimensions, type),
and twitter card metadata. Derive values from the page's data fetch — do not use
placeholder strings. Also confirm that no parent layout accidentally sets
robots: noindex.
```

## 测试

```bash
# Check that <title> and og:title appear in the server-rendered HTML
curl -s http://localhost:3000/blog/my-post \
  | grep -E '<title>|og:title|og:description|og:image|canonical' \
  | grep -v "node_modules" \
  && echo "Metadata present" || echo "FAIL: metadata missing"
```