抓、筛、写、发四步,只有抓和发要工具。 没人盯着的时候,问题不在「答得对不对」,在「今天有没有发」。 三条运营要求:每天都有、不重复、能追溯。
前两个案例都是用户开口才动。这一个相反:到点自己动,没人问。 触发方式一变,评价标准也变。对话类看「答得准不准」,定时类看「有没有漏、有没有重」。 教材这一节是一个现成的开源项目,讲的是部署和配置。部署那一步照原样说明。
一、项目介绍:把「每天看资讯」变成一条流水线
起因很朴素:想每天知道几条跟自己相关的事,又不想挨个网站刷。 拆开就是四步:从几个源抓今天的条目、按关键词筛几条、每条写一句人话、推到邮箱或者群里。 四步里只有第一步和第四步要工具。中间两步是模型出力。
所以这类智能体的质量主要取决于提示词,不取决于接了多少 Server。 这一点和大多数人的第一反应相反。 教材用的是开源项目,README 里就写着怎么配。这类项目值得先原样跑一遍再改,别一上来就重写。
二、部署介绍:定时从哪来
对话类由用户触发,定时类得有个东西替你按点叫醒它。 常见三种:本机定时任务(crontab、计划任务)、平台的定时触发器、云函数自带的定时。 选哪种取决于「机器开不开」。自己电脑晚上关机,本机定时就没意义。
部署这一步教材是照着项目说明做的,本沙箱没有那些平台账号,照原样写、不执行。 但有一条跟平台无关:定时任务要能手动触发一次。不然第一次配错,你得等到明天才知道。
三、抓:归一化是地基
演示 ② 那张表四行,来自四个源,形状一模一样:分类、标题、摘要。 这一步看着平淡,实际是整条流水线能不能成立的前提。形状不统一,后面每一步都得写分支。 如果三个源各给一种结构(XML、JSON、HTML),归一化就得写三份。这部分工作量往往比接 MCP 还大。
所以接 MCP 不等于省掉脏活。 协议统一了接口,没统一数据。这句话在第 9 章龙虎榜那一节说过一次,这里再说一次。 归一化的标准很简单:下游要哪几个字段,就只留哪几个。多留的字段以后会变成麻烦。
四、筛和写:模型出力的两步
演示 ③ 从 4 条筛到 2 条,靠的是关键词命中。 筛太狠的后果很具体:某天一条都没有,用户以为坏了。所以要么给兜底,要么放宽到分类级别。 写这一步最容易写成标题复读。 「某 IDE 宣布原生支持 MCP」抄一遍不叫写,写是补一句「这意味着什么」。
这两步都没有工具可依,全靠提示词里那几句约束。想让它写得不空,就得在提示词里放一个反例。 也就这么一句:不要复述标题,要说这条对读者意味着什么。
五、发:去重放在最后一层
演示 ④ 里同一条发第二次被拒了,指纹是 adbbd39643。 指纹用「标题加正文」的 md5,不用时间——同一篇换了时间还是同一篇。 为什么放在「发」这一层,而不是抓那一层? 因为上游可能重抓、可能换源、可能重跑,只有发这一层是唯一的出口。
这条思路可以用在任何有外部可见副作用的地方:发消息、发邮件、发布网页。 第 11 章那个发布网页的也是这么防重的。 一句话:去重放在最靠近副作用的那一层。
六、动手 Demo:每日资讯:抓、筛、写、发
造一个资讯 Server:一个抓工具、一个发工具,中间两步留空。 走一遍完整流程,看四步里哪两步真的调了工具。 最后重复发一条,看指纹怎么把它挡住。
nz = Server('news-mcp')
nz.add(Tool('news.list', '取今天的资讯条目:分类、标题、摘要', SCHEMA, fn))
nz.add(Tool('push.send', '把一条推到邮箱或群里,重复的拒收', SCHEMA, push))
items = call(nz, 'news.list', {})['items'] # 1 抓(工具)
pick = [x for x in items if hit(x)] # 2 筛(模型)
brief = [one_line(x) for x in pick] # 3 写(模型)
for x in pick: # 4 发(工具)
r = call(nz, 'push.send', {'title': x['title'], 'body': x['digest']})
print(r['ok'])
实跑输出:
--------------------------------------------------------------------
每日资讯:抓、筛、写、发
--------------------------------------------------------------------
① 四步流水线
--------------------------------------------------------------------
| 步 | 做什么 | 工具 | 最容易出错的地方 |
|---|---|---|---|
| 1 抓 | 从几个源取今天的条目 | news.list | 源挂了没兜底 |
| 2 筛 | 按关键词和去重挑几条 | 不调工具 | 筛太狠,一天没有一条 |
| 3 写 | 每条一句话说清 | 不调工具(模型写) | 写成标题复读 |
| 4 发 | 推到邮箱或群里 | push.send | 发重了 |
四步里只有第 1 步和第 4 步要工具。
第 2、3 步是模型出力,这也是为什么这类智能体的质量取决于提示词,不取决于工具。
② 抓:三个源,一个形状
--------------------------------------------------------------------
| 分类 | 标题 | 摘要 |
|---|---|---|
| 科技 | MCP 协议发布 2025-06-18 修订版 | 新增结构化工具输出 |
| 科技 | 某 IDE 宣布原生支持 MCP | 配置一段 JSON 即可接入 |
| 财经 | 两市成交额连续三日过万亿 | 券商板块走强 |
| 生活 | 本周气温回落至 20 度上下 | 注意添衣 |
四个源被抓成同一个形状,后面才能用同一段提示词。这一步是这类系统的地基。
如果三个源各给一种结构(XML、JSON、HTML),归一化就得写三份,这部分工作量往往比接 MCP 还大。
③ 筛:关键词加去重
--------------------------------------------------------------------
| 阶段 | 条数 |
|---|---:|
| 全部抓回 | 4 |
| 命中关键词 | 2 |
| 去重之后 | 2 |
从 4 条筛到 2 条。
筛太狠的后果是某天一条都没有,所以要么给兜底(没有就发一句「今天没大事」),要么放宽到分类级别。
④ 发:发重了怎么办
--------------------------------------------------------------------
1. MCP 协议发布 2025-06-18 修订版 -> 已发
2. 某 IDE 宣布原生支持 MCP -> 已发
再发一次第一条,看它怎么拒:
-> ok=False,这一条发过了(指纹 adbbd39643)
去重放在「发」这一层最有效:不管你上游重抓几次,发一次就是一次。
指纹用「标题加正文」的 md5,不用时间——同一篇换了时间还是同一篇。
⑤ 每日这件事,重点在「不出错」
--------------------------------------------------------------------
| 要保的 | 做法 | 不做会怎样 |
|---|---|---|
| 每天都有 | 没有内容也发一句 | 用户以为坏了 |
| 不重复 | 发的这一层做指纹 | 同一篇来三遍 |
| 能追溯 | 每条带上源链接 | 看到错的没法核 |
三条都是「运营」层面的要求,跟协议一句都不沾。
所以做完一个能跑的智能体,离「能用」还差这一步:把不出错的情况都想一遍。
①第一张表的「工具」那一列只有两行有名字,另两行写着不调工具。这一列是这一节的重点。 ②第二张表四个源四行,字段一模一样。归一化做在这一步,后面才有得谈。 ③第三张表三个数:4 条、2 条、2 条。去重这一次没挡掉东西,因为四条本来就不重复。 ④第四段两条已发,第二条重发被拒,指纹 adbbd39643。真机上这个指纹要落库,不能只存内存。 ⑤最后那张表三行,是「能用」和「不出错」之间的差距。写完代码还得把这三条各想一遍。
30 秒实验
- 把关键词从四个减到一个,看筛完还剩几条。
- 把关键词换成一个不可能命中的词,看兜底逻辑会发什么。
- 把指纹从「标题加正文」改成「只有标题」,看会不会误挡两条摘要不同的。
常见误区
- 「这类智能体靠工具多。」四步里两步是模型出力。质量看提示词,不看 Server 数。
- 「MCP 统一了接口,数据形状也就统一了。」接口统一了,数据还得自己归。
- 「去重放在抓那一层更早更保险。」上游会重抓重跑,只有发这一层是唯一出口。
自测三问(点开对答案)
1. 四步里哪两步不调工具? —— 筛和写。都是模型出力,靠提示词里那几句约束。 2. 为什么去重放在「发」这一层? —— 上游可能重抓、重跑、换源,发是唯一出口。去重放在最靠近副作用的那一层最有效。 3. 筛完一条都没有,该怎么办? —— 要有兜底,发一句「今天没大事」。不然用户以为坏了。第 10 章三个案例都是「把外面的东西搬进来或搬出去」。 下一章四个是「产出一样新东西」:网页、表格、提纲、配图。从下一章开始,写操作变成常态。
- ☐ (本篇完,下一篇 11.1 网页生成部署智能体)