Skip to content

feat(seo): lead compare/blog titles with search phrase + stat-led meta descriptions / compare 与博客标题以搜索短语开头并优化 meta 描述#598

Merged
Oseltamivir merged 2 commits into
masterfrom
seo/compare-blog-metadata
Jul 20, 2026
Merged

feat(seo): lead compare/blog titles with search phrase + stat-led meta descriptions / compare 与博客标题以搜索短语开头并优化 meta 描述#598
Oseltamivir merged 2 commits into
masterfrom
seo/compare-blog-metadata

Conversation

@Oseltamivir

@Oseltamivir Oseltamivir commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

SEO click-through improvements for pages that already rank on Google page 1 but lose the click at the SERP. Grounded in Google Search Console data:

  • /compare/deepseek-v4-b200-vs-b300 ranks position 7–9 for "b200 vs b300" with 0 clicks — the title buries the query behind a version-grouped model label and em-dash, and truncates after the long site-name suffix.
  • Blog meta descriptions come straight from subtitle and run 200–336 chars, so Google truncates them mid-sentence.

Changes

Compare <title> leads with the GPU pair (the actual query).

  • DeepSeek V4 Pro 1.6T — B200 vs B300 Inference Benchmark | InferenceX by SemiAnalysis (82c) → B200 vs B300: DeepSeek V4 Pro Inference Benchmark | InferenceX (61c).
  • New compareModelSeoName (single natural version name, e.g. GLM-5 not GLM 5/5.1) + compareSeoTitle in compare-slug.ts. Uses title.absolute to bypass the long root template; shortens Inference BenchmarkBenchmark for long pairs.

Compare meta description is stat-led, not identical boilerplate across ~360 pages.

  • Built from the interpolated head-to-head numbers at the slug's default operating point (tok/s/GPU + $/M-tok), ≤155 chars, e.g. "B200 delivers 34% more tok/s/GPU than B300 on GLM-5; B300 is 12% cheaper per token…". Falls back to compact boilerplate for sparse-data pairs. New compareMetaDescription / computeCompareStat in compare-ssr.ts.
  • ⚠️ Behavior note: compare generateMetadata now performs a benchmark fetch to build this description. It is blob-cached and shared with the page render, and uses the slug's canonical operating point (pickPairDefaults), so it stays deterministic and adds no separate data round-trip on the hot path.

Blog <title> uses the shorter | InferenceX suffix (title.absolute), leaving more room for the headline before SERP truncation.

Blog meta/OG/Twitter descriptions stop truncating.

  • New optional seoDescription frontmatter + blogDescription = seoDescription ?? smartTruncate(subtitle, 155) (word-boundary cut, no mid-word). Added explicit seoDescription to 9 posts whose subtitle exceeded ~155 chars.

/zh mirroredcompareMetaDescriptionZh (1:1 port in compare-ssr-zh.ts), zh titles, and zh seoDescription on the 9 translated posts.

Testing

  • pnpm typecheck ✅ · pnpm lint ✅ · pnpm fmt
  • pnpm test:unit: +24 new passing tests (compare-slug, compare-ssr, blog), 0 regressions. (13 pre-existing, unrelated failures on a clean tree in use-feature-gate/visit-tracking/chunk-load-recovery — a localStorage test-isolation issue, not touched here.)

Overlay support

Not applicable — this PR changes only page <title>/meta descriptions, not any inference/evaluation chart-data rendering path.

中文说明

针对已经排在 Google 搜索结果第一页、却在结果页流失点击的页面做 SEO 点击率优化,依据 Google Search Console 数据:/compare/deepseek-v4-b200-vs-b300 在 “b200 vs b300” 查询上排在第 7–9 位却零点击(标题把查询词埋在版本分组标签和破折号后面,且被过长的站点名后缀截断);博客的 meta 描述直接取自 subtitle,长达 200–336 字符,被 Google 从句中截断。

  • compare 的 <title> 改为以 GPU 对(真实搜索词)开头:例如上面的例子改为 B200 vs B300: DeepSeek V4 Pro Inference Benchmark | InferenceX(61 字符)。新增 compareModelSeoName(单一自然版本名,如 GLM-5 而非 GLM 5/5.1)与 compareSeoTitle,用 title.absolute 绕过冗长的根模板,长组合时把 Inference Benchmark 缩短为 Benchmark
  • compare 的 meta 描述改为数据驱动:在该 slug 默认工作点下由插值出的正面对比数据(tok/s/GPU 与 $/M tok)生成,控制在 155 字符内,数据稀疏时回退到精简样板文案。⚠️ 说明:compare 的 generateMetadata 现在会拉取一次基准测试数据来生成描述,该请求经 blob 缓存并与页面渲染共享,且使用 slug 的规范工作点(pickPairDefaults),保持确定性、不在热路径上新增数据往返。
  • 博客 <title> 改用更短的 | InferenceX 后缀,为标题留出更多空间。
  • 博客 meta/OG/Twitter 描述不再被截断:新增可选 seoDescription frontmatter,配合 blogDescription = seoDescription ?? smartTruncate(subtitle, 155)(按词边界截断),并为 9 篇超长副标题文章补充 seoDescription
  • /zh 侧同步compareMetaDescriptionZhcompare-ssr-zh.ts 中 1:1 移植)、中文标题,以及 9 篇译文的中文 seoDescription

测试:pnpm typecheckpnpm lintpnpm fmt 均通过;pnpm test:unit 新增 24 个通过用例,无回归(13 个既有失败与本 PR 无关)。叠加层(overlay)支持:不适用,本 PR 仅修改页面标题与 meta 描述,不涉及推理/评估图表数据渲染路径。


Note

Low Risk
Metadata and content frontmatter only; compare metadata adds a cached benchmark read in generateMetadata but reuses existing SSR fetch paths with no auth or data-model changes.

Overview
Improves SERP click-through by retuning blog and compare metadata: shorter titles, bounded descriptions, and compare pages that lead with the GPU query and real benchmark deltas.

Compare pages (/compare and /zh/compare) now build <title> as {GPU pair}: {model seoName} Inference Benchmark | InferenceX via compareSeoTitle and title.absolute, so queries like “b200 vs b300” aren’t buried behind long model labels or the root “by SemiAnalysis” template. Each model gains a seoName (e.g. GLM-5, Kimi K2.6) in compare-slug.ts. Meta descriptions are generated by compareMetaDescription / compareMetaDescriptionZh: tok/s/GPU and $/token percentages from interpolated data at the slug’s default operating point (≤155 chars), with a shorter boilerplate fallback when data is thin. generateMetadata on compare now awaits cached benchmark data (getCachedBenchmarks + pickPairDefaults)—same cache as the page body.

Blog posts (EN + ZH) use title.absolute with | InferenceX, and blogDescription (seoDescription frontmatter or smartTruncate(subtitle, 155)) for meta, OG, Twitter, and JSON-LD. Nine EN + nine ZH posts add explicit seoDescription where subtitles were too long.

Unit tests cover truncation, blog descriptions, SEO title helpers, and compare meta description logic.

Reviewed by Cursor Bugbot for commit d3116d7. Bugbot is set up for automated code reviews on this repo. Configure here.

…immed meta descriptions

Improve SERP click-through on page-1 compare pages and blog posts.

- Compare <title> now leads with the GPU pair (the actual query, e.g.
  "B200 vs B300: GLM-5 Inference Benchmark | InferenceX") via title.absolute,
  bypassing the long root template; falls back "Inference Benchmark"→"Benchmark"
  for long pairs. New compareModelSeoName (single natural version name) +
  compareSeoTitle in compare-slug.ts.
- Compare meta description is now stat-led from interpolated head-to-head
  numbers at the default interactivity target (tok/s/GPU + $/M-tok), ≤155 chars,
  gracefully falling back to compact boilerplate for sparse-data pairs.
  New compareMetaDescription / computeCompareStat in compare-ssr.ts.
- Blog <title> uses the shorter "| InferenceX" suffix (title.absolute).
- Blog meta/OG/Twitter descriptions use blogDescription = seoDescription ??
  smartTruncate(subtitle, 155) (word-boundary cut, no mid-sentence truncation).
  Added optional seoDescription frontmatter to 9 long-subtitle posts.
- Mirrored every change on /zh (compareMetaDescriptionZh, zh titles, zh
  seoDescription on the 9 translated posts).
- Unit tests for compareModelSeoName, compareSeoTitle, compareMetaDescription
  (+ zh structural port), smartTruncate, and blogDescription.

中文:让 /compare 与博客页面的 SEO 元数据以搜索短语开头,提升谷歌搜索结果页点击率。
compare 的 <title> 改为以 GPU 对(真实搜索词,如“B200 vs B300: GLM-5 Inference
Benchmark | InferenceX”)开头,使用 title.absolute 绕过冗长的根模板,长组合时将
“Inference Benchmark”缩短为“Benchmark”;新增 compareModelSeoName(单一自然版本名)
与 compareSeoTitle。compare 的 meta 描述改为在默认交互性目标下由插值出的正面对比
数据(tok/s/GPU 与 $/M-tok)驱动,控制在 155 字符内,数据稀疏时优雅回退到精简样板
文案;新增 compareMetaDescription / computeCompareStat。博客 <title> 改用更短的
“| InferenceX”后缀。博客 meta/OG/Twitter 描述改用 blogDescription =
seoDescription ?? smartTruncate(subtitle, 155)(按词边界截断,避免句中截断),并为
9 篇长副标题文章新增 seoDescription frontmatter。所有改动均在 /zh 侧同步(新增
compareMetaDescriptionZh、中文标题、9 篇译文的中文 seoDescription)。补充相应单元测试。
@Oseltamivir
Oseltamivir requested a review from adibarra as a code owner July 20, 2026 08:55
@vercel

vercel Bot commented Jul 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
inferencemax-app Ready Ready Preview, Comment Jul 20, 2026 9:05am

Request Review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 601e448. Configure here.

Comment thread packages/app/src/app/blog/[slug]/page.tsx
Cursor Bugbot flagged that the BlogPosting JSON-LD description still used
the full meta.subtitle while the HTML meta, OpenGraph, and Twitter
descriptions were routed through blogDescription — so posts with a
seoDescription or long subtitle exposed different text in structured data
than in the SERP/social tags. Route the JSON-LD description through the
same blogDescription helper (EN + zh) so they stay in sync. Logic is
already covered by the blogDescription unit tests.

中文:修复博客 JSON-LD 描述与 meta/OG 描述不一致的问题。Cursor Bugbot 指出
BlogPosting JSON-LD 的 description 仍使用完整的 meta.subtitle,而 HTML meta、
OpenGraph 和 Twitter 描述已改用 blogDescription,导致设置了 seoDescription 或
副标题较长的文章在结构化数据与搜索/社交标签中显示不同文本。这里让 JSON-LD 的
description 同样走 blogDescription(EN 与 zh 两侧),保持一致;相关逻辑已由
blogDescription 单元测试覆盖。
@Oseltamivir
Oseltamivir merged commit 93ae9b4 into master Jul 20, 2026
26 checks passed
@Oseltamivir
Oseltamivir deleted the seo/compare-blog-metadata branch July 20, 2026 09:23
Oseltamivir added a commit that referenced this pull request Jul 20, 2026
#598 (compare/blog metadata CTR) merged to master first. Reconciled the
overlapping blog-post changes:
- Kept #599's builder-based JSON-LD (buildBlogPostingJsonLd + BreadcrumbList)
  and the <h1>, alongside #598's title.absolute + blogDescription metadata.
- Routed buildBlogPostingJsonLd's description through blogDescription so
  #598's structured-data/SERP consistency fix survives the refactor.
- Unioned the imports and the blog.test.ts helper imports; updated the
  BlogPosting description assertion to track blogDescription.

中文:#598(compare/博客元数据点击率优化)先行合入 master,这里协调两者对博客文章
的重叠改动:保留 #599 基于构建器的 JSON-LD(buildBlogPostingJsonLd 与
BreadcrumbList)及 <h1>,同时保留 #598 的 title.absolute 与 blogDescription 元数据;
让 buildBlogPostingJsonLd 的 description 走 blogDescription,以在重构后延续 #598 的
结构化数据与搜索摘要一致性修复;合并相关 import 与 blog.test.ts 的辅助函数导入,并将
BlogPosting 的 description 断言改为跟随 blogDescription。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant