n8n + Claude:搭一个每周内容审计 Agent,把悄悄掉队的文章抓出来
目录
每个周一上午 8:14,Slack 的 #content-audit 频道都会准时收到一条消息。里面是过去 90 天里 4 到 9 篇"悄悄掉队"的文章——有曝光但没点击,或者过去一个月莫名其妙下滑了 3 个排名的页面。上周抓出来的是一篇 1,800 字的对比指南,在第二页卡了 11 周,每个月 220 次曝光、0 次点击。我重写了开头,加了一张真正的对比表,18 天后这篇文爬到了第 7 名。这一次修复的回报,就抵得上这个 Agent 一年的运行成本。
抓出它的 Agent 是一套自托管的 n8n(一个公平代码协议的工作流自动化工具)流程:抓 GSC(Google Search Console,谷歌站长工具)和 GA4(Google Analytics 4,谷歌分析 4)的数据,用 Claude Sonnet 4.5 给页面打分,最后把结果写到 Slack 和 Google Sheet 里。整个流程在周一早晨 3 到 4 分钟跑完,API 成本不到 0.2 美元,已经安静地帮我抓出了好几个手动月度复盘根本不会注意到的问题。
下面是完整搭建过程。
你实际在搭什么
整个工作流有 8 个节点,每周跑一次,最终产物是 Slack 一条消息 + 表格里每个被标记页面的一行。结构如下:
| # | 节点 | 职责 |
|---|---|---|
| 1 | Schedule Trigger | 每周一 08:00 触发 |
| 2 | HTTP Request | 拉取 GSC 最近 28 天的页面级数据 |
| 3 | HTTP Request | 拉取同一时间窗口的 GA4(互动指标) |
| 4 | Code | 合并两份数据,计算衍生列(CTR、排名趋势、发布天数) |
| 5 | Filter | 只保留"值得关注"的行(曝光>50、排名>8,或趋势下滑) |
| 6 | Loop Over Items + Anthropic Chat Model | Claude 给每行打分,返回结构化结论 |
| 7 | IF | 按 needsAction 分流——true 进 Slack,false 进"观察名单"表 |
| 8 | Slack + Google Sheets | 最终输出 |
为什么这算"Agent"而不是仪表盘?答案在第 6 步。Claude 是唯一能"读懂一行数据并判断为什么表现差"的环节,而不仅仅是"它确实表现差"。Code 节点能告诉你 CTR 是 0.9%,但它无法判断 meta description(搜索引擎结果页里那段页面摘要文字)和搜索意图不匹配,也无法判断 H2 写得像个目录大纲而不是用户真正会搜的问题。这是只有人脑(或者 LLM,Large Language Model,大语言模型)才能干的工作,恰好是它最擅长的。
准备工作
开工前你需要接好五件事:
- n8n(n8n Cloud 的 $24/月 Starter 套餐就够用,或者自托管 Docker 镜像 90 秒就能起)
- 一个 Anthropic API Key(开通 Claude Sonnet 4.5 访问权限),在 n8n 里配成凭证
- 已验证站点的 GSC API 访问权限(只读搜索分析数据)
- 一个 GA4 属性(Analytics Data API 已开启,并在 n8n 凭证里配好服务账号 JSON Key)
- 一个 Slack Incoming Webhook(或 Slack OAuth 凭证),指向你想接收报告的频道
如果不用 n8n Cloud,同一套搭在 Make、Zapier,甚至一个 cron 触发的 Python 脚本里都行。我最终选了 n8n,是因为它的 Code 节点做数据塑形是原生支持的,LangChain(一个开源的 LLM 应用开发框架)集成点两下鼠标就有,每次执行的成本是固定的。
Step 1 — 定时触发 + 拉 GSC
触发器是一个 Schedule Trigger 节点,配置为每周一上午 8:00 触发。第一个动作是一个 HTTP Request 节点,调用 GSC 的 Search Analytics API:
GET https://www.googleapis.com/webmasters/v3/sites/{siteUrl}/searchAnalytics查询参数:
startDate、endDate设为最近 28 天dimensions[]=pagerowLimit= 5,000(单个站点完全够用)dataState=final
返回结果里每个 URL 一行,包含 clicks、impressions、ctr、position。把这份数据存进工作流状态的 gscPages 字段,继续下一步。rowLimit 设为 5,000 是因为超过这个数几乎都是 tag/分类归档页,审计根本不该去追它们。
Step 2 — 拉 GA4 并合并
第二个 HTTP Request 命中 Analytics Data API,相同时间窗口,参数是 dimensions: ["pagePath", "pageTitle"],指标用 screenPageViews, engagementRate, averageSessionDuration, conversions。端点是:
POST https://analyticsdata.googleapis.com/v1beta/properties/{propertyId}:runReport两份数据不能直接对得上——GSC 标准化 URL 时会保留末尾斜杠,GA4 有时候不会。一个 Code 节点做一遍规范化:去掉末尾斜杠、host 转小写、用清洗后的路径 join。30 行 JavaScript(JS)。这部分看起来无聊,但恰恰是救你数据质量的那块。
合并后的行大致长这样:
{
url, title, clicks, impressions, ctr, position,
pageViews, engagementRate, avgEngagementTime, conversions,
gsc28d: {...}, ga4Previous28d: null
}Step 3 — 计算真正有用的 delta
一个 Code 节点,一遍跑完。对每条合并行我算:
positionDelta——本期排名 vs 上一个 28 天(负数=在涨,正数=在掉)ctrVsExpected——实际 CTR(Click-Through Rate,点击率)vs 按位置加权的预期值(#3 的位置理论上该拿 ~10% CTR,#9 该拿 ~3%)trafficShare——该页点击数占站点总点击的比例daysSinceLastUpdate——能拉就拉(CMS,Content Management System,内容管理系统的 API 或者页面上的lastmod)
这些 delta 是真正起决定性作用的字段。排名 11、曝光 1,200 的页面值得关注;排名 11、曝光 90 的页面不值得,后面 LLM 怎么说都不重要。Code 节点的工作就是确保只有前者能走到 Claude 面前。
Step 4 — 激进地过滤
Filter 节点是整个工作流里控制成本最关键的一刀。我只保留满足以下任一条件的行:
impressions >= 100且position > 8positionDelta > 2(过去 28 天排名下滑 ≥ 2)ctrVsExpected < 0.5(CTR 不到该位置应有值的一半)engagementRate < 0.4且pageViews > 50(人进了页面但直接跳走)
这把一份 1,000-5,000 行的数据集砍到 30-80 行。只有这些行会被送进 Claude。按 $3/百万 input token、每行约 400 token 算,50 行的审计成本大概 $0.06。"全送进去"的版本会贵 50 倍,Claude 标出来的页面集合却几乎一样——只是更慢、更吵。
Step 5 — 用 Claude 打分
这是 LangChain Agent / Anthropic Chat Model 节点,遍历过滤后的行逐条处理。System prompt 是我重写了 5 遍才定下来的那一版:
You are a senior SEO editor reviewing a weekly content audit.
For each post, you will be given:
- 28-day GSC metrics (clicks, impressions, CTR, position, prior position)
- 28-day GA4 metrics (page views, engagement rate, average engagement time, conversions)
- Computed deltas (position trend, CTR vs expected, traffic share)
- The page title and URL
Your job: decide whether this post is underperforming in a way a human editor
should look at, and if so, explain why in one sentence a non-SEO can act on.
Output a JSON object with exactly these keys:
{
"url": string,
"verdict": "OK" | "WATCH" | "FIX",
"reason": string, // 1 sentence, max 30 words, cite the specific metric
"suggestedAction": "rewrite_intro" | "add_table" | "update_facts" |
"tighten_meta" | "consolidate" | "delete" | "none"
}
Rules:
- "OK" if the metrics are healthy for the position, even if absolute traffic is low
- "WATCH" if the trend is slipping but the page still has potential
- "FIX" only if there is a specific, concrete issue a human could address
- Be conservative. False positives waste editor time. If unsure, return "WATCH"
- Never invent data not in the input. Cite the metric in the reason.每行 max_tokens 上限设为 400。输出过一遍 Code 节点做 JSON 校验,把格式错的行单独塞进一个 "agent errors" 表里。大概每 200 行有 1 行会格式错——几乎都是 reason 字段超过 30 词限制被截断。重试路径是一条追加 "Previous response was truncated. Be more concise." 的再请求。
Step 6 — 分流 + 通知
一个简单的 IF 节点按 verdict == "FIX" || verdict == "WATCH" 拆。FIX 分支发一条 Slack 消息,每篇文章一行摘要,按潜在影响排序(positionDelta * impressions 降序)。WATCH 分支追加到审计 Google Sheet 的 Watchlist 工作表——不打扰人,但行被保留下来,下周审计可以对比。
Slack 消息本身用 Block Kit(一种富文本 JSON 格式)渲染,每个被标记的页面是一条可点击链接,附 verdict emoji 和具体指标。示例:
:rotating_light: *Content Audit — 2025-02-24*
本周有 6 篇文章需要关注
• :writing_hand: *FIX* — `/blog/best-llm-tools-2025/`
#12 位置 2,400 次曝光,CTR 0.8% 远低于预期的 3.2%。Meta description
与用户搜索词不匹配。建议动作:`tighten_meta`
• :chart_with_downwards_trend: *WATCH* — `/blog/zapier-vs-n8n/`
28 天内排名从 6 掉到 9。互动率跌到 31%。建议动作:`update_facts`跑 6 周后我改了什么
三个文档里不会写的生产经验。
第一版打分太凶。 我最初的 prompt 写的是"任何可以改进的页面都标出来"。结果每周一出来 30+ 条 FIX,大部分是假阳性——文章其实没问题,只是某个段落弱一些。修法是在 prompt 里给"FIX"加更严的判定标准,并硬性限制每次最多 10 条 FIX(cap 以下的全部降级为 WATCH)。现在那条 Slack 消息真的会被人看完。
"距离上次更新"这个指标在季末那周差点毁掉整个审计。 客户的博客有 80 篇文章 18 个月没动。审计把几乎全部都标成了"陈旧",把真正表现差的都淹没了。修法是加一个 pages_within_quota 过滤:只有文章在窗口内曝光 ≥ 100 时才把"陈旧"作为判定依据。一篇没流量的陈旧文章不是审计该解决的问题——那是内容日历该解决的问题。
WATCH 分支把 Sonnet 换成 Haiku,省了 70% 成本,质量没掉。 道理很简单:WATCH 行本来就不会被人工处理,"够用"的标准就低。同一个循环里,FIX 走 Sonnet 4.5,WATCH 走 Haiku 4.5。每周总成本从 $0.22 降到 $0.08。
它不是什么
这个 Agent 不是内容策略工具,也不是写手。它不产选题、不重写、不发布。它唯一的职责是:每周挑出 5-10 篇"人应该看一下"的文章,每篇附一句话原因。剩下的事——决定改什么、怎么改、什么时候发——全是人的活。
这个边界反而是它能用的原因。Agent 失败的方式是有趣的、可恢复的:某行返回了格式错的 JSON、prompt 跑偏开始标正常页、GSC 因为站点被重新验证返回了 0 行。这些失败都不是灾难性的,审计 Google Sheet 本身就是一份"Agent 历史上都说过什么"的记录。一个 tab 攒半年的周报,本身就是一份内容策略文档。
如果你要搭,前两周的输出会偏吵是正常的。调 prompt、收窄过滤、接受"suggested action"几乎不该是 delete——信噪比一个月内就会稳住。Agent 抓出的第一篇文章,往往就是你会再错过一季度的那一篇。