缓存时间:
2026/04/20 08:26
# 为博客转新闻邮件工具添加新内容类型 - 智能代理工程模式
来源:https://simonwillison.net/guides/agentic-engineering-patterns/adding-a-new-content-type/
这里有一个看似简短的提示示例,它一次性完成了相当多的工作。
首先,背景介绍。我大概每周会通过免费Substack新闻邮件(https://simonw.substack.com/)发送一期内容,这些内容是从我的博客复制粘贴过来的。实际上,我只是把Substack当成一种轻量级方式,让人们可以通过邮件订阅我的博客。
我通过我的博客转新闻邮件工具(https://tools.simonwillison.net/blog-to-newsletter)生成新闻邮件——这是一款HTML和JavaScript应用,可以从这个Datasette实例(https://datasette.simonwillison.net/)获取我的最新内容,并将其格式化为富文本HTML,然后我可以复制到剪贴板,粘贴到Substack编辑器中。这里有一个详细的说明,介绍它是如何工作的(https://simonwillison.net/2023/Apr/4/substack-observable/)。
最近,我为博客添加了一种新内容类型(https://simonwillison.net/2026/Feb/20/beats/),用于收录我在别处发布的内容,我称之为"beats"。这些内容包括我开源项目的发布、我构建的新工具、我参观过的博物馆(来自niche-museums.com(https://www.niche-museums.com/))以及其他外部内容。
我希望在生成的新闻邮件中包含这些内容。以下是我针对托管我`blog-to-newsletter`工具的simonw/tools仓库(https://github.com/simonw/tools)运行的提示,使用的是基于网页的Claude Code(https://code.claude.com/docs/en/claude-code-on-the-web)。
这个提示让我得到了我需要的精确解决方案(https://github.com/simonw/tools/pull/268)。下面我们来拆解这个提示。
> `将 simonw/simonwillisonblog 从 GitHub 克隆到 /tmp 以供参考`
我经常使用这个模式。编码代理可以从GitHub克隆代码,而解释问题的最佳方式通常是让它们查看相关代码。让它们克隆到`/tmp`可以确保它们不会意外将那份参考代码包含在之后的提交中。
simonw/simonwillisonblog仓库(https://github.com/simonw/simonwillisonblog)包含我基于Django的simonwillison.net(https://simonwillison.net/)博客的源代码。其中包括我新"beats"功能的逻辑和数据库模式。
> `更新 blog-to-newsletter.html,使其包含带有描述的 beats——类似于博客上Atom完整订阅源的工作方式`
只需引用`blog-to-newsletter.html`,我就能告诉Claude它应该修改`simonw/tools`仓库中200多个HTML应用里的哪一个。
Beats会从多个来源自动导入。很多时候它们并不太有趣——比如我某个小开源项目的点版本bug修复。
我的博客提供了一种方式,让我可以为任何beat添加额外描述,描述不仅能增加额外评论,也标记这个beat比那些我没有注释的更值得关注。
我已经用这个标准来决定哪些beat出现在我网站的Atom订阅源(https://simonwillison.net/about/#atom)中。让Claude模仿这个逻辑,省得我额外描述细节。
> `使用 python -m http.server 运行它,并用 uvx rodney --help 测试——比较新闻邮件中显示的内容与 https://simonwillison.net 主页上的内容`
编码代理如果有某种验证机制来测试自己的工作,效果总是最好的。
在这个例子中,我希望Claude Code主动检查它对工具所做的更改是否正确获取并显示了最新数据。
我提醒它使用`python -m http.server`作为静态服务器,因为我之前遇到过应用在从磁盘文件而非本地服务器提供时获取数据的失败问题。在这个特定案例中可能没必要,但我的提示肌肉记忆已经内置了`python -m http.server`!
我在智能代理手动测试章节(https://simonwillison.net/guides/agentic-engineering-patterns/agentic-manual-testing/#using-browser-automation-for-web-uis)中描述了`uvx rodney --help`这个技巧。Rodney是一款浏览器自动化软件,可以用`uvx`安装,其`--help`输出设计用来教会代理使用该工具所需的一切知识。
我认为让Claude比较新闻邮件中的结果与博客主页的内容,就足以让它自信地验证新更改是否正常工作,因为我最近正好发布了符合新要求的内容。
你可以在这里查看完整的会话(https://claude.ai/code/session_01BibYBuvJi2qNUyCYGaY3Ss),或者如果那个链接失效,我还有一份替代会话记录(https://gisthost.github.io/?e906e938100ab42f4d6a932505219324/page-001.html#msg-2026-04-18T00-13-57-081Z),显示了所有独立的工具调用。
最终的PR(https://github.com/simonw/tools/pull/268)做出了完全正确的更改。它在获取博客内容的SQL查询中添加了一个额外的UNION子句,过滤掉了草稿beat以及`note`列为空的beat:
```sql
...
union all
select
id,
'beat' as type,
title,
created,
slug,
'No HTML' as html,
json_object(
'created', date(created),
'beat_type', beat_type,
'title', title,
'url', url,
'commentary', commentary,
'note', note
) as json,
url as external_url
from blog_beat
where coalesce(note, '') != '' and is_draft = 0
union all
...
```
它还推导出了beat类型到正式名称的映射关系,推测来源于它探索参考代码库时读到的Django ORM定义(https://github.com/simonw/simonwillisonblog/blob/2e9d7ebe64da799b3927e61b4f85d98f7e9bc9aa/blog/models.py#L545-L551):
```javascript
const beatTypeDisplay = {
release: 'Release',
til: 'TIL',
til_update: 'TIL updated',
research: 'Research',
tool: 'Tool',
museum: 'Museum'
};
```
让代理使用另一个代码库作为参考,是一种强大的捷径,可以在提示中只需极少额外信息就能传达复杂概念。