Alejandro Rioja.
AI Agents Growth

如何用AI代理自动化你的新闻通讯

Alejandro Rioja
Alejandro Rioja
2 分钟阅读
TL;DR

Claude代理读取我的内容队列,选择本周最强的切入角度,用我的语气起草新闻通讯,按参与度层级分段列表,并通过Kit API安排发送——全程无需我打开编辑器。我查看渲染预览并点击批准。困难的创意工作是我的;机械执行是代理的。

免费新闻通讯

每周三。28,400+ 读者。纯干货。

目录

2026年6月更新。

TL;DR: Claude代理读取我的内容队列,选择本周最强的切入角度,用我的语气起草新闻通讯,按参与度层级分段列表,并通过Kit API安排发送——全程无需我打开编辑器。我查看渲染预览并点击批准。困难的创意工作是我的;机械执行是代理的。

[运营者阅读] 一个持续发送的新闻通讯胜过那个”更好”但靠灵感驱动才发的。制约因素是执行开销,不是创意。我有想法;我没有每周格式化、安排和分段它们的带宽。代理消除了这个差距。

大多数新闻通讯工作流中真正的瓶颈

大多数新闻通讯自动化建议关注错误的事情:欢迎序列、自动化、标签逻辑。这些都很好,但解决不了每周的创建问题。

真正的阻碍是:你知道想说什么,但坐下来格式化、写主题行变体、选择合适的受众段、在正确时间安排发送,每周需要2-3小时的上下文切换。乘以52周,你已经花了整整一周只是在发送新闻通讯。

代理处理”我知道本周的角度是什么”之后的每个步骤。

我使用的技术栈

如果你不在Kit上,相同的模式适用于任何有REST API可以创建和安排广播的平台。

第一步:内容队列

代理需要一个关于”我们在写什么”的真实来源。我的是一个Airtable表,包含以下列:

每周,我花10分钟在队列中添加2-3个主题。这是我的创意输入。其余的是代理的工作。

第二步:起草代理

typescript
// workers/newsletter-agent/index.ts
import Anthropic from "@anthropic-ai/sdk";
import Airtable from "airtable";

const client = new Anthropic();

const VOICE_SYSTEM = `You are writing a weekly newsletter for Alejandro Rioja's subscribers.
His audience: founders and operators interested in AI agents, SEO, and growing a one-person business.
Voice: direct, first-person, practitioner. No hype, no "exciting times," no excessive bullet lists.
Structure every newsletter as:
1. One-sentence hook (the problem or observation)
2. The core insight (3–5 paragraphs, no headers, conversational)
3. One concrete action the reader can take this week
4. A short sign-off (2 sentences max)
Subject line: specific, outcome-oriented, under 50 chars. No clickbait.
Return JSON: { "subject": "...", "preheader": "...", "body": "..." }`;

async function getNextTopic(): Promise<{ id: string; topic: string; notes: string; tier: string }> {
  const base = new Airtable({ apiKey: process.env.AIRTABLE_API_KEY }).base(process.env.AIRTABLE_BASE_ID!);
  const records = await base("Newsletter Queue")
    .select({ filterByFormula: "{Status} = 'Queue'", sort: [{ field: "Created", direction: "asc" }], maxRecords: 1 })
    .firstPage();
  if (!records.length) throw new Error("Queue is empty. Add topics.");
  const r = records[0];
  return { id: r.id, topic: r.get("Topic") as string, notes: (r.get("Notes") as string) ?? "", tier: (r.get("Tier") as string) ?? "all" };
}

async function draftNewsletter(topic: string, notes: string): Promise<{ subject: string; preheader: string; body: string }> {
  const msg = await client.messages.create({
    model: "claude-sonnet-4-6",
    max_tokens: 2048,
    system: VOICE_SYSTEM,
    messages: [{ role: "user", content: `Write this week's newsletter on: "${topic}". Additional notes: ${notes || "none"}` }],
  });
  const text = (msg.content[0] as any).text.replace(/```json\n?/, "").replace(/```/, "").trim();
  return JSON.parse(text);
}

async function scheduleWithKit(draft: { subject: string; preheader: string; body: string }, tier: string): Promise<string> {
  const segmentId = tier === "engaged" ? process.env.KIT_ENGAGED_SEGMENT_ID : null;
  const sendAt = new Date();
  sendAt.setDate(sendAt.getDate() + ((4 - sendAt.getDay() + 7) % 7)); // next Thursday
  sendAt.setHours(9, 0, 0, 0); // 9am CT

  const payload: any = {
    broadcast: {
      subject: draft.subject,
      content: draft.body,
      description: draft.preheader,
      send_at: sendAt.toISOString(),
      email_layout_template: "minimal",
    },
  };
  if (segmentId) payload.broadcast.segment_id = segmentId;

  const res = await fetch("https://api.kit.com/v4/broadcasts", {
    method: "POST",
    headers: { "Content-Type": "application/json", "X-Kit-Api-Key": process.env.KIT_API_KEY! },
    body: JSON.stringify(payload),
  });
  const data = await res.json();
  return data.broadcast?.id ?? "";
}

export default {
  async scheduled(_event: ScheduledEvent, env: Env) {
    // Inject env vars
    Object.assign(process.env, env);
    const { id, topic, notes, tier } = await getNextTopic();
    const draft = await draftNewsletter(topic, notes);
    const broadcastId = await scheduleWithKit(draft, tier);

    // Mark as Approved in Airtable (not Sent — human reviews the Kit preview before confirm)
    const base = new Airtable({ apiKey: env.AIRTABLE_API_KEY }).base(env.AIRTABLE_BASE_ID);
    await base("Newsletter Queue").update(id, { Status: "Approved", KitBroadcastId: broadcastId });

    console.log(`Scheduled broadcast ${broadcastId} for topic: ${topic}`);
  },
};

第三步:审批步骤

代理在Kit的草稿状态下创建广播,并将Airtable记录标记为”Approved”。Kit向我发送带有预览链接的通知。我点击它,阅读,如果看起来没问题,我确认发送。如果我想要更改,直接在Kit中编辑。

这是防止代理在外发邮件上完全自主的关卡。我信任草稿大约90%的时间。审查中发现的10%——语气稍微不对、我想验证的统计数据、想添加的链接——值得那3分钟的审查。

代理处理的我再也不想做的事

我仍然拥有的

想法。队列中的主题是我的。角度是我的。代理是清晰简报的出色执行者;它不是战略层。如果我把一个糟糕的主题放入队列,我会得到一封关于糟糕主题的写得很好的新闻通讯。

还有:第一次审查关卡。每一次发送在发出之前都会经过我的眼睛。这不会改变。

运营者的底线

如果你每周花超过一个小时在新闻通讯机制上——格式化、安排、分段——你应该自动化它。Kit API很干净,Worker cron触发器稳如磐石,Claude草稿质量足够高,让我可以批准约90%的第一稿而不作修改。在Airtable中建立队列,连接Worker,然后回归创造想法而不是执行发送。

继续阅读

将AI实战手册发送到您的邮箱

每周三。28,400+ 读者。纯干货。

↵ 查看全部结果 esc esc 关闭