跳到正文
Simon Willison 博客· Simon Willison·· 2026-04-18AI 评分69

为博客通讯工具新增内容类型抓取功能

Adding a new content type to my blog-to-newsletter tool

AI 导读

Simon Willison 为其 blog-to-newsletter 工具新增 "beats" 内容类型抓取功能,可将开源项目发布、工具构建等外部内容自动导入 Substack 新闻通讯。开发者通过 Claude Code 单条提示词完成开发:克隆参考代码库、模仿 Atom Feed 逻辑筛选带描述的优质内容,并使用 rodney 工具验证结果与博客首页一致性。此次更新针对 simonw/tools 仓库中 200 余个 HTML 应用,最终生成 PR #268。

正文 · 原文

Here's an example of a deceptively short prompt that got a quite a lot of work done in a single shot.

First, some background. I send out a free Substack newsletter around once a week containing content copied-and-pasted from my blog. I'm effectively using Substack as a lightweight way to allow people to subscribe to my blog via email.

I generate the newsletter with my blog-to-newsletter tool - an HTML and JavaScript app that fetches my latest content from this Datasette instance and formats it as rich text HTML, which I can then copy to my clipboard and paste into the Substack editor. Here's a detailed explanation of how that works.

I recently added a new type of content to my blog to capture content that I post elsewhere, which I called "beats". These include things like releases of my open source projects, new tools that I've built, museums that I've visited (from niche-museums.com) and other external content.

I wanted to include these in the generated newsletter. Here's the prompt I ran against the simonw/tools repository that hosts my blog-to-newsletter tool, using Claude Code on the web.

This got me the

exact solution

I needed. Let's break down the prompt.

Clone simonw/simonwillisonblog from github to /tmp for reference

I use this pattern a lot. Coding agents can clone code from GitHub, and the best way to explain a problem is often to have them look at relevant code. By telling them to clone to /tmp I ensure they don't accidentally end up including that reference code in their own commit later on.

The simonw/simonwillisonblog repository contains the source code for my Django-powered simonwillison.net blog. This includes the logic and database schema for my new "beats" feature.

Update blog-to-newsletter.html to include beats that have descriptions - similar to how the Atom everything feed on the blog works

Referencing blog-to-newsletter.html is all I need here to tell Claude which of the 200+ HTML apps in that simonw/tools repo it should be modifying.

Beats are automatically imported from multiple sources. Often they aren't very interesting - a dot-release bug fix for one of my smaller open source projects, for example.

My blog includes a way for me to add additional descriptions to any beat, which provides extra commentary but also marks that beat as being more interesting than those that I haven't annotated in some way.

I already use this as a distinction to decide which beats end up in my site's Atom feed. Telling Claude to imitate that saves me from having to describe the logic in any extra detail.

Run it with python -m http.server and use `uvx rodney --help` to test it - compare what shows up in the newsletter with what's on the homepage of https://simonwillison.net

Coding agents always work best if they have some kind of validation mechanism they can use to test their own work.

In this case I wanted Claude Code to actively check that the changes it made to my tool would correctly fetch and display the latest data.

I reminded it to use python -m http.server as a static server because I've had issues in the past with applications that fetch data and break when served as a file from disk instead of a localhost server. In this particular case that may not have been necessary, but my prompting muscle memory has python -m http.server baked in at this point!

I described the uvx rodney --help trick in the agentic manual testing chapter. Rodney is browser automation software that can be installed using uvx, and that has --help output designed to teach an agent everything it needs to know in order to use the tool.

I figured that telling Claude to compare the results in the newsletter to the content of my blog's homepage would be enough for it to confidently verify that the new changes were working correctly, since I had recently posted content that matched the new requirements.

You can see the full session here, or if that doesn't work I have an alternative transcript showing all of the individual tool calls.

The resulting PR made exactly the right change. It added an additional UNION clause to the SQL query that fetched the blog's content, filtering out draft beats and beats that have nothing in their note column:

...
unionall
select
id,
'beat'astype,
title,
created,
slug,
'No HTML'ashtml,
json_object(
'created',date(created),
'beat_type',beat_type,
'title',title,
'url',url,
'commentary',commentary,
'note',note
)asjson,
urlasexternal_url
fromblog_beat
wherecoalesce(note,'')!=''andis_draft=0
unionall
...

And it figured out a mapping of beat types to their formal names, presumably derived from the

Django ORM definition

that it read while it was exploring the reference codebase:

const beatTypeDisplay = {
  release: 'Release',
  til: 'TIL',
  til_update: 'TIL updated',
  research: 'Research',
  tool: 'Tool',
  museum: 'Museum'
};

Telling agents to use another codebase as reference is a powerful shortcut for communicating complex concepts with minimal additional information needed in the prompt.

GIF optimization tool using WebAssembly and Gifsicle

Prompts I use

This is a chapter from the guide Agentic Engineering Patterns.

Chapters in this guide

  1. Principles
    1. What is agentic engineering?
    2. Writing code is cheap now
    3. Hoard things you know how to do
    4. AI should help us produce better code
    5. Anti-patterns: things to avoid
  2. Working with coding agents
    1. How coding agents work
    2. Using Git with coding agents
    3. Subagents
  3. Testing and QA
    1. Red/green TDD
    2. First run the tests
    3. Agentic manual testing
  4. Understanding code
    1. Linear walkthroughs
    2. Interactive explanations
  5. Annotated prompts
    1. GIF optimization tool using WebAssembly and Gifsicle
    2. Adding a new content type to my blog-to-newsletter tool
  6. Appendix
    1. Prompts I use

prompt-engineering

github

Created: 18th April 2026
Last modified: 18th April 2026
4 changes

来源:Simon Willison 博客 · simonwillison.net