🔌 第10章 基于MCPServer的个人效率类智能体应用
📄 一句话概括

四个动作:读、洗、传、记。只有第三个要调工具。 去重靠内容哈希,断点靠记 id。这两件事跟 MCP 一点关系都没有,但决定这个智能体能不能天天用。 最容易忽略的是失败的写法:一条失败不能停整批。

📝 本章和前面几章的关系

第 9 章的七个案例都是「用户问一句、答一句」。这一章三个都是「没人问,自己跑」。 触发方式一变,要考虑的东西全变:没人盯着的时候,出错要自己兜。 教材这一节是把本地 markdown 笔记批量传到 flomo。这一节讲流程,界面操作照原样说明。

一、思路解析:先把四个动作分开

用户的原话大概就是一句「帮我把笔记传上去」。听起来是一个动作,做的时候是四个。 读:把本地文件读成一条一条。洗:去空行、去重、截长。传:逐条调工具。记:把传成功的 id 记下来。 四个动作里只有「传」需要 MCP。其余三步是任何同步任务都要写的代码。

所以判断一个任务要不要 MCP,问一句就够了:有没有一个外部服务要访问?有,就是它; 没有,纯写代码。 这一节把四个动作摆开,就是想让人看见这个比例:一个工具,三十分代码。

二、获取 Flomo API:一个地址,一个 POST

教材的做法是登录 flomo 网页版,进「扩展中心与小工具」,找到 API 那一栏, 拿到一条专属的 webhook 地址。 往这个地址 POST 一条 JSON,正文放在 content 字段里,笔记就进去了。 不用 OAuth,不用审核,一个地址够用。 这个地址等于密码:拿到它的人可以往你的笔记库里写任何东西。所以它只能待在环境变量里, 不能写进代码,更不能进 Git。

这一步是纯界面操作,本沙箱没有那么一个账号,所以照原样写步骤、不执行。 后面的演示用一个同形状的 note.add 替它。 替的原则还是老一条:替的是「往哪写」,不是「怎么写」。报文形状、失败处理、去重逻辑一模一样。

三、搭建工作流:先洗后传

洗这一步看着琐碎,实际决定了后面会不会一团糟。 演示里原始文件 10 行,去掉标题和空行剩 4 条,去重之后 3 条。一条重复的就是在这一步挡掉的。 四分之一的重复率在这个规模上不算高。 真把几年的笔记导进来,重复会更多——同一个想法在不同时间记过两遍,很常见。

洗的另外两件事:截断过长的(有些笔记站单条有字数上限),去掉不该传的(草稿、待办、 带密钥的临时记录)。 顺序不能反:先洗后传。反了就是把垃圾先传上去、再回头删。删比不传贵得多。

四、去重靠哈希,断点靠 id

去重用的是单条内容的 md5 前八位。存八个字符比存整条原文省地方,比对也快。 前八位够不够?三万多条里撞一次的量级,个人笔记够用。要更稳就取十六位,代价只是存储翻一倍。 断点有三种存法(演示 ④ 那张表):存内存变量重启就丢、存本地 json 换机器就失效、 存服务端比对最稳但要一次额外调用。

选哪种取决于「你会不会换设备」。只在一台电脑上跑,本地 json 就够; 手机和电脑都要传,就得用服务端的 id 比对。 这里有个取舍值得记住:多花一次调用,能省掉一整套断点逻辑。能这么换的时候,就该换。

五、换个源头就是另一个智能体

演示 ⑤ 那个框是这一章三个案例的共同骨架:定时触发、读源、洗、逐条调工具、记断点、汇总报告。 把「本地文件」换成「微信收藏」、换成「浏览器书签」、换成「邮件附件」,骨架一个字不用改。 变的只是第 1 步的读取方式和第 3 步的工具。

所以这三个案例不是三个智能体,是同一个智能体的三次换装。看骨架比看案例有用。

六、动手 Demo:上传笔记:读、洗、传、记四步

造一个 flomo 形状的 Server,只有一个 note.add 工具。 拿一份 10 行的 markdown 当输入,把四个动作依次跑一遍。 重点看两个数:洗掉了多少、去重挡掉几条。

fl = Server('flomo-mcp')
fl.add(Tool('note.add', '往笔记站加一条,返回它的 id', SCHEMA, fn))

lines = [x.strip() for x in raw.split('\n')]
body = [x for x in lines if x and not x.startswith('# ')]
keep, seen = [], set()
for x in body:
    h = md5(x)[:8]                 # 内容哈希,不是原文
    if h not in seen:
        seen.add(h); keep.append(x)

for i, x in enumerate(keep, 1):
    r = call(fl, 'note.add', {'content': x})
    print(i, r['id'])              # 记下 id,下次接着走

实跑输出:

--------------------------------------------------------------------
自动上传笔记:一条流水线
--------------------------------------------------------------------


  ① 四步,缺一不可
  --------------------------------------------------------------------

| 步 | 做什么 | 错了会怎样 |
|---|---|---|
| 1 读 | 把本地笔记文件读成一条一条 | 读错分隔,整篇变一条 |
| 2 洗 | 去掉空行、去重、截断过长的 | 重复上传,笔记站里全是复本 |
| 3 传 | 逐条调 note.add | 一条失败就全停,前面白传 |
| 4 记 | 把传成功的 id 记下来 | 下次重跑又传一遍 |

  四步里只有第 3 步是协议的事。另外三步是任何同步任务都要做的事,跟用不用 MCP 无关。
  所以这一节的重点不在工具,在【第 2 步和第 4 步】:去重靠什么、断点记在哪。

  ② 读和洗:先算一遍
  --------------------------------------------------------------------

| 阶段 | 条数 | 说明 |
|---|---:|---|
| 原始行 | 10 | 含标题行和空行 |
| 去掉标题与空行 | 4 | 这两类不该进笔记站 |
| 去重之后 | 3 | 重复那条被 md5 挡掉了 |

  去重用的是内容的 md5 前八位,不是整条原文。存 hash 比存原文省地方,也比逐条比对快。

  ③ 传:逐条,失败不停
  --------------------------------------------------------------------
逐条传,每条都记下 id:
  1. MCP 的工具描述会进模型上下文。  -> m1
  2. stdio 就是两根管道,一行一条 JSON。  -> m2
  3. 笔记站的 API 要一个 Key,别写进代码。  -> m3
  三条都成了。真机上要假设第二条可能失败:失败了记下这一条,接着传第三条,最后统一重试。
  「一条失败就全停」的写法在演示里看不出问题,在真机上会卡住整批。

  ④ 记:断点存在哪
  --------------------------------------------------------------------

| 存法 | 重启后还认得吗 | 适不适合 |
|---|---|---|
| 存内存变量 | 不认得,全丢 | 一次性任务 |
| 存本地一个 json | 认得 | 单人小批量 |
| 存服务端(笔记站的 id 比对) | 认得,而且跨机器 | 多人或多设备 |

  三种里第二种最常用。它的问题是换台机器就失效,所以要重传一次。
  第三种最稳:传之前先问「最新的一条是什么」,按时间比对。多花一次调用,省掉一整套断点逻辑。

  ⑤ 这类智能体的通用骨架
  --------------------------------------------------------------------
  定时触发 -> 读源 -> 洗 -> 逐条调工具 -> 记断点 -> 汇总报告
                ↑                          |
                └──── 下次从这里接着走 ─────┘
  这条流水线换个源头就是另一个智能体:把「本地文件」换成「微信收藏」是同一个形状,
  换成「浏览器书签」也是。
  第 10 章三个案例都是这条流水线,只是第 3 步的工具不一样。

①第一张表四行,只有第三行要工具。另三行是普通代码,占的篇幅反而更多。 ②第二张表三个数:10 行、4 条、3 条。中间少掉的 6 行是标题和空行,最后少的 1 条是重复。 ③第三段三条 id 连号 m1、m2、m3。真机上要假设中间一条会失败——失败记下来,接着传下一条。 ④第四张表三种存法。选哪种取决于换不换设备,不取决于哪种更高级。 ⑤最后那个框是骨架。下一节记账也是这条线,只是第 1 步从「读文件」变成「读一句话」。

30 秒实验

  1. 把演示里那份 raw.md 再贴三遍,重跑,看最后传上去几条。
  2. 把 md5 取前八位改成取前四位,看有没有撞上。
  3. 人为让第二条报错,看「一条失败就停」和「记下来接着传」两种写法各传了几条。

常见误区

  1. 「上传笔记就是一个工具调用。」一个工具,三十分代码。洗和记断点才花时间。
  2. 「去重应该存原文比对。」存哈希,八位就够。存原文又占地方又得逐条比。
  3. 「断点存内存变量就行。」重启就丢,下次全量重传。日常要用的东西不能存内存。
自测三问(点开对答案) 1. 这条流水线四步里,哪一步真的用到 MCP? —— 第三步传。读、洗、记都是普通代码,跟用不用 MCP 无关。 2. 为什么去重存 md5 前八位,不存原文? —— 省地方、比得快。三万多条撞一次的量级,个人笔记够用;要更稳取十六位。 3. 断点存本地 json 有什么问题? —— 换机器就失效,会重传一遍。多设备就得用服务端的 id 比对,多花一次调用换掉整套逻辑。

上传是「把已有的东西搬出去」。下一节反过来:从一句话里把结构化字段抠出来,再写进去。 难点从「传」挪到了「抠」。

相关笔记