feat(seo): lead compare/blog titles with search phrase + stat-led meta descriptions / compare 与博客标题以搜索短语开头并优化 meta 描述#598
Merged
Conversation
…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)。补充相应单元测试。
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ 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.
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
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。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

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-b300ranks 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.subtitleand 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).compareModelSeoName(single natural version name, e.g.GLM-5notGLM 5/5.1) +compareSeoTitleincompare-slug.ts. Usestitle.absoluteto bypass the long root template; shortensInference Benchmark→Benchmarkfor long pairs.Compare meta description is stat-led, not identical boilerplate across ~360 pages.
compareMetaDescription/computeCompareStatincompare-ssr.ts.generateMetadatanow 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| InferenceXsuffix (title.absolute), leaving more room for the headline before SERP truncation.Blog meta/OG/Twitter descriptions stop truncating.
seoDescriptionfrontmatter +blogDescription = seoDescription ?? smartTruncate(subtitle, 155)(word-boundary cut, no mid-word). Added explicitseoDescriptionto 9 posts whose subtitle exceeded ~155 chars./zhmirrored —compareMetaDescriptionZh(1:1 port incompare-ssr-zh.ts), zh titles, and zhseoDescriptionon 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 inuse-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 从句中截断。<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。generateMetadata现在会拉取一次基准测试数据来生成描述,该请求经 blob 缓存并与页面渲染共享,且使用 slug 的规范工作点(pickPairDefaults),保持确定性、不在热路径上新增数据往返。<title>改用更短的| InferenceX后缀,为标题留出更多空间。seoDescriptionfrontmatter,配合blogDescription = seoDescription ?? smartTruncate(subtitle, 155)(按词边界截断),并为 9 篇超长副标题文章补充seoDescription。/zh侧同步:compareMetaDescriptionZh(compare-ssr-zh.ts中 1:1 移植)、中文标题,以及 9 篇译文的中文seoDescription。测试:
pnpm typecheck、pnpm lint、pnpm 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 (
/compareand/zh/compare) now build<title>as{GPU pair}: {model seoName} Inference Benchmark | InferenceXviacompareSeoTitleandtitle.absolute, so queries like “b200 vs b300” aren’t buried behind long model labels or the root “by SemiAnalysis” template. Each model gains aseoName(e.g.GLM-5,Kimi K2.6) incompare-slug.ts. Meta descriptions are generated bycompareMetaDescription/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.generateMetadataon compare now awaits cached benchmark data (getCachedBenchmarks+pickPairDefaults)—same cache as the page body.Blog posts (EN + ZH) use
title.absolutewith| InferenceX, andblogDescription(seoDescriptionfrontmatter orsmartTruncate(subtitle, 155)) for meta, OG, Twitter, and JSON-LD. Nine EN + nine ZH posts add explicitseoDescriptionwhere 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.