增长案例库 Maxed 归档 SEO增长AI搜索优化AI自动化

我如何搭建 SEO/AEO 博客引擎

how i built an SEO/AEO blog engine

中文译文 · 17k 字

一句话摘要

搭建 SEO 与 AEO 博客引擎

我如何构建了一个 SEO/AEO 博客引擎 August 24, 2026 · 15 min read · View source ↗ SEO AI Slack Automation 这个夏天,我作为 @browserbase 的增长工程实习生,端到端地提出并执行了一个工程项目:blogEO,一个每周审计、重写并生成面向 SEO/AEO 优化的博客、然后衡量其中任何一项是否起效的引擎。 tl;dr browserbase 的博客没有 SEO/AEO 策略或工具 → 我们 53% 的搜索流量来自 5 篇帖子,约 74 篇帖子的 SEO 字段(title + description)是空的,而且 77 篇帖子中只有 17 篇曾经被 AI 引擎引用过。 我构建了 blogEO,一个每周运行的引擎,它 1) 审计现有博客帖子并提出修复建议,2) 生成新博客来填补真实的搜索需求,3) 追踪 AEO 指标。 重要的是,这个 agent(bb)没有写工具。它只负责评分、起草,并把按钮发到 Slack 里。由服务端的 handler 在人类点击批准之后才执行写入。 每个被批准的审计编辑都会在改动前拍一张快照,连同同一日期的博客整体数据一起。这张快照会在第 28 天和第 56 天被重新读取。 截至 2026 年 8 月 21 日,blogEO 已经生成了 18 篇博客帖子和 50 多个编辑。 一个没有测量的博客 开发者读博客。它们是事实来源,而 LLM 会从中汲取内容,来建立关于如何写代码的信心。如果我们不出现,那我们就没能捕捉到那种在你睡觉时被动复利增长的流量。 我们的博客是手动的、无人维护的、无人测量的。我对它很了解——毕竟从 2025 年 10 月起,我自己就写了几十篇 browserbase 的博客。较老的帖子引用了过时的产品细节,表现不佳的帖子无人问津,因为没有任何东西在监控排名,而所有这些分析数据散落在 5 个没人登录的地方。 当我在夏初拉出这些数据时,我意识到这不是靠简单清洗就能解决的问题。我们博客上的少数帖子扛起了绝大部分数字,而其余的都是一潭死水。 有排名但没有点击 更令人沮丧的是,我们确实有排名。我们的信息型博客排在 6-13 位,却没有任何点击。有一篇帖子有 329,454 次展示,平均排名 9.4,却只产生了 857 次点击。那是 0.26% 的点击率…… Google 把我们的内容展示给了 30 万多人,而他们基本没有一个进来。 顺便说一下,排名指的是一个链接在 Google 搜索结果页中的位置。第 1 位意味着它是第一条链接,在最上面。 对 LLM 不可见 我还想知道,答案引擎到底有没有引用过我们。AEO 策略会随之而来(我在这里解释过 AEO)。在我们 6 月之前发布的所有 77 篇博客帖子里,只有 17 篇曾经被引用过。就连这个数字也是偏斜的,其中 3 篇承担了大部分的工作。 为什么没有解决方案? 这些问题的每一个都可以被手动发现。不过那个发现过程相当艰难——它意味着有人得把 5 个不互通的系统(我们的 CMS Sanity、Google Search Console、SEMrush、PostHog,以及上周的数字)桥接起来,然后每周重做一次这个连接,好让数据不过期。 我构建了什么:blogEO 我把所有这些拼块,以及更多,都连接了起来。blogEO 是一个审计器、生成器和 AEO 追踪器。这三者都通过我们的内部 agent bb,以每周的节奏自动运行,汇入一个 Slack 频道。这三项功能也都可以通过 Slack 按需调用。 博客审计(修复已有的)→ 它基于机会和衰减给每一篇已发布的帖子打分,为机会最高的帖子起草外科手术式的修复,并把每个编辑作为 Approve/Edit/Skip 卡片发到一个 Slack 线程里。 博客生成器(创造缺失的)→ 它遍历实时搜索数据,找出我们还没有用博客帖子匹配的需求,写出一份代码片段都能追溯到我们文档的草稿,并用 4 道自动化检查来把关。 AEO 追踪 → 看 ChatGPT、Claude 和 Google 的 AI Overviews 是否引用了我们,并判断一篇帖子为什么没有被引用。 一个核心设计决策 bb 能看、能想、能建议,但我没有给它任何写工具。它不能改动网站。能改动的代码,是在人类点击一个按钮(approve)之后,才能在服务器上运行的那部分。 这样一来,一次糟糕的运行最坏也只是在 Slack 里提了一个糟糕的建议。 这个设计也让代码正确地保持了可塑性。agent 的判断住在一个 skill 文件里(改起来便宜),而写入路径是有测试的代码,它会重新验证 agent 声称的一切。 挑选要修什么(审计) 审计是一次运行:它给每篇已发布的帖子打分,为机会最高的那些起草修复,然后把编辑发到 Slack。 然后这次运行就结束了。 按钮点击之后由一个独立的 handler 处理,并且独立于 agent 的会话。 审计步骤: 5 次批量数据拉取,来自 Sanity、GSC、SEMrush、PostHog 和上周的运行(通过 bb 用工具完成)。 对每篇帖子做卫生扫描,检查断链、失效图片/嵌入、定位漂移、缺失的 SEO 字段和错别字(通过 bb 且在内存中完成)。 给每篇帖子的机会打分并排序,然后只对排在前约 15 篇的已标记帖子验证事实性声明(通过 bb 完成)。 起草外科手术式编辑,并把两种安全的编辑类型标记为自动发布(bb 提议,服务器验证)。 把每条建议持久化到 KV,然后把一次运行快照持久化到 Postgres(通过服务器完成)。 发布审计摘要 + 每篇被标记帖子/编辑的线程卡片(通过服务器完成)。 按机会打分 审计按余量(headroom)排序,而不是按年龄。我这么做,是因为我想基于这个问题的答案来行动:"如果我把这篇博客帖子改好,我们到底能多拿到多少访客?" 为此,每篇帖子都对照过去 28 天做 3 种打分。这 3 个估计里最大的那个"胜出",从而决定要写哪种修复。 recover → 相比上一个窗口丢失的点击。因此,是要修的回归。 ctr → 我们博客帖子没拿到的首页点击,对比它那个排名本应挣到的预期 CTR。因此,是 title/description 的修复。 rank → 一篇第二页的帖子如果落到第一页能多获得的点击数。因此,是一次内容推进。 CTR 杠杆需要一个"多少点击 × 什么排名本该挣到多少"的模型。那是一条有机点击率曲线。 export function expectedCtr(position: number): number { if (position <= 0) return 0; if (position <= 1) return 0.28; if (position <= 2) return 0.15; if (position <= 3) return 0.10; if (position <= 5) return 0.06; if (position <= 7) return 0.04; if (position <= 10) return 0.025; return 0.01; } 如果我把这篇帖子过一遍 CTR 杠杆,就能得到一个"机会"数字,把它推到审计队列的顶端。 measurement value impressions 329,454 clicks 857 actual CTR 0.260% expectedCtr(9.4) 2.500% gap x impressions 0.02240 x 329,454 estimated recoverable clicks ≈ 7,380 大约 7k 点击的余量。"机会"的价值 > 因为"这篇帖子旧了"而刷新。 这里的局限是,2.5% 的基准用的是平均排名,而 GSC 会把一个页面出现的每一个查询都混合进来。它高估了真实余量——所以它是一个偏软的绝对信号,但对于这个审计队列的排序很有价值。 编辑队列排名有两条护栏: 如果展示量一开始就低,那么低 CTR 也不会被打成高机会。任何编辑都无法修复"根本没人在搜这个"的事实。这类帖子会被标记为低可见度并从排名中剔除。 真实的点击流失 > 任何估计。一篇真正流失了流量的帖子,无论它在机会排名里的什么位置,都会浮出来。这种流失的判定,需要同时存在一次实质性的绝对点击下降 + 一次真实的成比例下降。 配给检查,而非 token 最大化(tokenmaxxing) 便宜的检查跑在所有已发布帖子上(年龄、空 SEO 字段、断链、流量下降,以及对旧定位做一次关键词搜索)。 更贵的检查(把帖子和我们文档做对比)会烧很多 token,所以它只跑在排前 15 的已标记帖子上 + 额外 3 篇作为缓冲,这样低展示量的帖子就不会永远得不到验证。 这些标记也不是来自记忆。必须加载真实页面;只有直接的矛盾才会被标记,并且引用出处。 编辑被刻意做得很小 一条建议只能改 SEO title/description,或者替换一个确切的短语/链接。如果一个修复没法做成一刀干净的替换,那么卡片就只显示 Edit 和 Skip。这种情况必须由人把编辑写进去。 对于面向公众的行文,我认为这正是该划线的位置。 另外,有已知替代链接的断链 + 填补空的 SEO 字段,是两类会被自动应用的编辑,所以它们完全绕过人类审批。 衡量它是否起效 一旦一次真实编辑上线,就会拍一张 28 天前的 GSC 快照。还会捕获同一日期区间内整个博客表现如何的数据,因为少了这块,一个 20% 的改善对我毫无意义——毕竟 Google 可能把一切都抬高了。 写新帖子(生成器) 审计其实是我项目的 v0。它为我真正的目标铺好了地基,那个目标是提升 SEO/AEO 流量。单靠审计救不了一个 72/77 篇帖子都没流量的博客——流量根本不在那里。 最丰厚的流量来源是"擦肩而过"(near-misses):Google 已经把我们的内容展示了 150 次以上,但我们却停在 5-20 位,且没有一篇专门的帖子。我在 GSC 上的一次运行找到了 120 个这样的擦肩而过,包括 "what is a captcha solver?"——19.7k 次展示、排名 10.6,而我们甚至没有一篇专门讲这个查询的博客。 一次生成器请求产出一份可直接发布的草稿,未列出(unlisted),并通过 Slack 等待人类审批。和审计一样,bb 永远不会发布博客。 生成器步骤: 从 GSC 数据里挑一个主题,或接收一个给定的主题(通过 seo-strategy skill)。 写草稿,所有代码片段都精确取自我们的文档(通过 browserbase-writing skill)。 对照写作清单检查草稿(通过 bb)。 在保存任何东西之前,用 4 道自动化检查给草稿把关(通过代码)。 创建未列出的草稿(通过代码)。 发布带 Approve 和 Discard 按钮的 Slack 卡片(通过代码)。 点击 Approve 会把它以未列出状态发布并开始测量(通过服务器)。 3 个 skill 我为这个流程拆出了 3 个 skill,好让它可维护,因为一条规则的 2 份副本会变成 2 条不同的规则,事情就乱套了。如果流水线文件开始塑造"什么是好写作",那就意味着它放错了文件。 自动化闸门 在到达 Sanity 之前,一份草稿必须通过 4 道自动化检查。这样一来,一份失败的草稿就不会变成需要清理的垃圾。 strategy → 确保没有禁用词,且帖子映射到一个真实的内容集群 + 人物画像。 structure → 确保帖子以一个带标签的 TL;DR 部分开头 + 其他几处小挑剔。 code provenance → 确保每个代码片段都有它来源的 docs URL。不许有编造的代码。 cannibalization → 确保没有任何目标查询已经被我们自己的某篇帖子覆盖。写第二篇帖子会分流我们自己的排名,所以禁止 bb 重试/改写。相反,拥有这个查询的那篇帖子会被识别出来,作为一次编辑交给审计。 input.body.forEach((block, i) => { if (block.type !== 'code') return; if (!block.docsUrl) { failures.push(`code block ${i} (${block.language}) has no docsUrl (source of truth)`); } else if (!/^https?:\/\//i.test(block.docsUrl)) { failures.push(`code block ${i} docsUrl is not an http(s) URL: "${block.docsUrl}"`); } // advisory only, won't block the draft if (!block.version) advisories.push(`code block ${i} has no version pin`); }); 对行文和结构重度设闸,意味着把品味硬编码,而那是极其不可维护的代码。那篇写作到底好不好,是清单的职责,由 bb 在设闸之前完成。 bb 有 2 次失败后重写的机会,然后停下,不产出任何草稿。 衡量一篇从零开始的帖子 编辑有"之前"(before)。一篇新帖子没有,所以它改用一条增长曲线来测量,结构同样是 28 天和 56 天的读数,并包含同一日期的博客整体数字。在批准时,帖子被发布、登记、然后记录。这样一来,记录失败不会阻断登记 → 登记失败不会阻断发布。 LLM 是否引用我们 GSC 告诉我 Google 是否送来了人,但关于 ChatGPT、Claude 或 AI Overviews 的推荐/引用,我的数据仍然是 0。我做的第一件事,是把"AI 推荐"拆成 3 段: 引擎是否爬取了页面? 它是否在某个答案里引用了我们? 有没有任何人点击进来? 我通过一个我为 SEMrush AI 可见性导出写的解析器发布了 cite,可惜那个导出只能手动导出、没有 API。它把每一行映射回一个帖子 slug。 click 已经能用了,因为 Google 把 AI Overviews 折叠进了正常的搜索表现,而其他引擎则按 referrer 域名分类。 有趣的是,在 Google 里胜出的帖子,和被 AI 引用的帖子,并不是同一批。我们最强的搜索表现者只被 AI 引用了两次。 我最终推迟了爬取这一层,因为它需要重新划定范围,才能访问 SSO 后面的 Vercel 日志流(log drains)。我把下一步写进了一份交给团队的交接文档里。 这一切如何流动 拼起来看,bb 负责读和推理,一个服务器负责写,而它们俩在 Slack 里与一个人会合。 测量住在一个跨 5 张表的 schema 里: blog_audit_runs → 每篇帖子的打分快照 blog_audit_edits → 改了什么、改动前快照、+28d/+56d 读数与冷却期 blog_generated_posts → 质量闸门报告与检查点 blog_aeo_signals → AI 引用计数 blog_aeo_domain → 域名范围的 AI 可见性 每周三条消息 我坚信,新工具应该融入现有的工作流,而不是再给人一个网站或登录。碎片化在这里恰恰就是出问题时的样子。BlogEO 的整个界面就是一个带定时运行的 Slack 频道: 新博客草稿 → 太平洋时间周一上午 9 点 → 本周生成的帖子,带 Approve 卡片 + 一段关于写了什么、为什么写的说明。 博客审计 → 太平洋时间周二上午 9 点 → 一个排好序的现有帖子线程、卫生发现,以及每条建议编辑的 Approve/Edit/Skip 卡片。 表现与调优 → 太平洋时间周五上午 9 点 → 一份报告,包括增长曲线、编辑测量状态、每篇帖子的 AEO 诊断,以及建议的测量编辑。 一张卡片从一条建议开始,以一个指向已上线帖子的链接结束。中间的状态是最需要打磨才能做对的。只要有人点击,建议就会锁定,这样两个人就不能同时发布。如果有人在 Sanity 上有一份该帖子的未保存草稿,发布也会拒绝执行,因为那份草稿会来自一个更旧的版本,一旦有人保存它,就会撤销这次编辑。 在每周节奏之外,同样的拼块依然可以调用: "@bb what's the SEO performance of this blog: ___" "@bb draft a blog explaining how to browse the web securely." "@bb draft a blog" "@bb give me a title for this blog: ____" "@bb run the blog audit" 我们的内部 agent 现在也有了一套全新的工具包。 顺便说,一个被请求的主题并不是一个预先批准的主题——它依然会被分类、对照护栏检查、并跑过蚕食检查。如果某人的想法失败了,bb 会停下来问后续问题。 迄今的结果 已经生成并发布了 18 篇帖子,约 74 篇帖子回填了 SEO 标题/描述,自 7 月以来编辑了 53 篇以上的旧帖子,并且 BlogEO 已经以自己的节奏运行了 6 周。 最有成就感的部分,是看到团队在我交接并完成实习之后,继续使用这个工具,并生成/批准了更多帖子。 我在 browserbase 的博客举措(写作和这个项目)让搜索展示增长了 5.8 倍,首页查询增长了 9.8 倍,我们的博客平均排名从 10.9 → 6.6。 几个局限包括:机会估计偏软、爬取被推迟、事实核查做了配给、以及 AI 引用数据目前仍是手动 CSV 导出。 我学到了什么 总的来说,从端到端执行我的第一个工程项目,我学到了非常多。这只是我这个夏天做的众多事情之一——我很快会回顾和总结其余的部分。 一些我稍后会在另一篇帖子里展开的快速教训: 我学会了如何用大量的规划来划定和重新划定一个工程项目的范围。 对测量一丝不苟很重要。对照组很重要! UX 就是产品,所以它必须存在于工作已经在发生的地方。没人想要另一个登录。 还要特别感谢 @JaySahnan,他是一位了不起的导师,戳我思维的漏洞,带我去喝旧金山最好的 chai,还审阅了我所有的 PR…… 前往下一个!🔨 > harsehaj 💌 Tags: # X # SEO # AI # Slack # Automation # Claude # Thread # Guide # Chatgpt Related articles 10 SEO backlink Claude automations for 61k AI mentions in 3 months Link building is trial and error. SEO AI Claude Automation

原文参考:https://maxed.wiki/posts/how-i-built-an-seo-aeo-blog-engine/ (Maxed.wiki,本页为站内中文整理)