news 2026/10/14 6:53:05

用MCP协议与多Agent协作搭建自动化内容发布流水线

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用MCP协议与多Agent协作搭建自动化内容发布流水线

上个月我终于把每天晚上雷打不动的“发文流程”给干掉了。以前一篇内容要走完“找选题—写初稿—改稿—配图—排版—发布”这条链,我手动点一遍至少 20 分钟,遇到平台抽风或者图片要重传,时间直接翻倍。后来我决定用 MCP 协议把内容平台的开放接口统一封装成标准工具,再让多 Agent 分工接力,搭出来一条自动化内容发布流水线。现在从选题到发布,我基本只负责偶尔扫一眼审核结果,剩下的事情机器替我跑完了。这篇复盘会把这一个月里踩过的坑、改过的设计、留下来的方案完整拆开,适合也在折腾内容自动化、对 MCP 和 Agent 编排感兴趣的朋友参考。

1. 为什么选 MCP 而不是把发布接口写死在脚本里

1.1 我先经历了“一堆脚本”的痛

在动手搭这套流水线之前,我已经有一版可以用的“伪自动化”脚本:一个抓素材的 Python 脚本、一个调模型接口写初稿的脚本、一个解析结果并调用内容平台开放接口发布的脚本。它们用命令行参数传数据,临时文件当中间产物。刚跑通那会确实爽,但用了一周问题就暴露得差不多了。

每次换模型要改脚本,每个平台的接口字段不一样也要改脚本,最头疼的是没人能讲清楚“现在到底跑到了哪一步”。脚本之间靠文件名约定通信,很容易出现上一个脚本没写完,下一个脚本就把空文件拿去发布了。我决定重写就是因为这个:自动化程度高不高是次要的,关键是每一步都得可控、可观测,而不是靠一串串孤立脚本碰运气。

1.2 MCP 到底解决了什么问题

MCP(Model Context Protocol)解决的是“模型怎么稳定地调用外部工具”这件事。你可以把它理解成给 AI 模型配一个“万能工具箱”:每个工具都有名字、参数说明、返回格式,模型需要某个能力时,自己去工具箱里找工具、填参数、拿结果。工具侧不需要为每个模型单独适配,模型侧也不需要为每个工具重新写一遍调用逻辑。

对我来说,最大的收获是把“发布平台能力”变成了独立组件。内容平台的开放接口、图片处理、任务通知,都被封装成一个个独立的 MCP Server。某个 Agent 想发文,只需要调用publish_article这个工具;哪天我换了一个模型,只要新模型也支持 MCP,我完全不需要改发布工具。这种解耦是脚本方案给不了的,也是我敢把流水线一直迭代下去的地基。

1.3 几个候选方案的对比

我也考虑过直接用“脚本加函数调用”,甚至纠结过要不要上一套重一点的 Agent 编排框架。最后选了 MCP + 轻量状态编排,核心原因可以看这张表。

方案工具接入效率换模型成本可观测性权限边界我的评价
写死脚本调用快高,要改代码一般,靠日志硬翻弱,脚本里什么都能干适合一次性任务,不适合流水线
自建 Agent 框架中等中等看框架实现中规中矩偏重,配置成本高
MCP Server + 任务表编排初期要花时间封装工具低,模型侧零改动工具调用有完整记录强,只暴露需要的工具最稳,后续扩展最顺

选择 MCP 并不是因为它“新”,而是因为它把工具和模型彻底解耦了。我实际用的编排层其实特别简单,核心就是一张任务状态表加一个定时调度器,没有引入复杂的 Agent 通信协议——这套组合比想象中还要皮实。

2. 流水线整体骨架:六个 Agent 怎么接力

2.1 任务流转全貌

流水线从“灵感池”开始。灵感池是一张表,里面放着我想写的选题、抓到的素材链接、历史爆文的标题,也有不少是数据 Agent 回收回来的“内容摘要”。调度器每分钟扫一次任务表,发现有可执行的任务,就唤醒对应 Agent 去干活。整个流转过程是这样的:

new → ready → writing → written → reviewing → approved → publishing → published → analyzing → done

文章在“审核通过”之前的路径是:选题 Agent 从灵感池里挑候选并生成标题;写作 Agent 根据选题卡片写完整初稿;编辑 Agent 做文字校对、压缩、摘要和关键词抽取;审核 Agent 做事实和合规审查。审核通过后交给发布 Agent,它会先把内容格式化成平台要求的 HTML、配图,再调用发布工具提交。发布 24 小时后,数据 Agent 拉取表现数据,把核心结论写回灵感池,整个闭环才算完成。

2.2 每个 Agent 的职责边界

我设计了六个角色,但每个角色的输出都必须是结构化 JSON,不允许丢一堆自然语言让下游自己猜。以写作 Agent 为例,它的输出长这样:

{ "title": "正文标题", "content_md": "正文 Markdown 全文", "abstract": "一句话摘要", "keywords": ["关键词一", "关键词二"] }

下游的编辑、审核、发布都能直接解析这些字段,不需要再做信息抽取。我对每个 Agent 的职责边界卡得很死:

  • 选题 Agent:只负责从灵感池中筛选和生成标题候选,不写正文。
  • 写作 Agent:按选题卡片生成初稿,不负责发布格式。
  • 编辑 Agent:负责校对、压缩、抽取摘要和关键词,不改事实性数据。
  • 审核 Agent:负责合规和事实核查,只输出通过或打回意见。
  • 发布 Agent:格式化内容、上传配图、调用 MCP 发布工具,不写内容。
  • 数据 Agent:拉取数据、生成复盘摘要,写回灵感池。

Agent 之间的协作不是“自由对话”,而是“谁干完活,就把结果写进任务表;下一个按状态机拿走继续”。这种模式最大的好处是定位问题容易——任务卡在哪个状态,就是对应环节的问题,不用去猜是哪句话把 Agent 带偏了。

2.3 为什么不让 Agent 互相自由交流

我特意把协作方式设计成“同一时刻只有一个 Agent 干活”,原因是多 Agent 自由对话场景里,几个 Agent 同时改同一份文档很容易互相覆盖,而且上下文一拉长,token 消耗涨得飞快。接力棒模式虽然朴素,但稳定、便宜、好排查。后面如果想并行,我会上真正的并行子任务拆分,而不是让 Agent 挤在一个任务里各说各话。

3. 把内容平台封装成 MCP Server 的核心实操

3.1 工具清单

发布环节的核心,是把内容平台的开放接口封装成 MCP Server。我暴露的工具不多,一共六个,刻意保持“够用就好”。

工具名作用关键参数权限说明
get_category_list获取栏目和分类列表无只读
create_draft在平台创建草稿title, content_md, cover_url, tags写入草稿,不发布
submit_article提交发布或定时发布article_id, publish_time, client_request_id写操作,有幂等保护
update_article更新已有文章article_id, title, content_md写操作
fetch_metrics拉取文章数据article_id, date_range只读
send_notify发送一条消息给维护者message特殊

删除文章、修改账号设置这类接口一律没有放进 MCP Server。这样哪怕模型被诱导,或者某个 Agent 突然幻觉发作,它能造成的破坏也有限。工具白名单本身就是一条权限边界,这是我强烈建议照做的设计。

3.2 鉴权:模型永远看不到密钥

平台侧的 access_token 只存在 MCP Server 的环境变量里,由工具内部自行读取,模型能看到的只有业务参数:文章 ID、标题、正文。另外 token 每两小时过期,我在 MCP Server 里实现了自动刷新机制——只要发现错误码是TOKEN_EXPIRED,就静默刷新一次并重发请求,对这个 Agent 来说完全透明。

这个设计看起来很简单,但很关键。如果让模型直接拿着长凭证去调平台,一旦它在某个非发布场景里不小心把凭证打印出来,问题就大了。把鉴权下沉到工具层,整个流水线才敢让模型放开手脚干活。

3.3 统一返回结构与错误码

所有工具统一返回下面这种结构:

{ "success": true, "data": { "article_id": "123456" }, "error": null }

错误时success为 false,error里带上固定错误码和人类可读的 message。错误码我定成了四个:RATE_LIMITED、TOKEN_EXPIRED、CONTENT_REJECTED、VALIDATION_ERROR。错误信息必须写得像人话,比如CONTENT_REJECTED后面一定会跟原因,因为审核 Agent 需要根据这个原因去修改正文。

代码层面,一个发布工具大体长这样:

@server.tool() def submit_article(article_id: str, publish_time: str = "", client_request_id: str = "") -> dict: token = get_valid_token() resp = platform_api.post("/articles/submit", token=token, json={ "article_id": article_id, "publish_time": publish_time, "client_request_id": client_request_id }) if resp.status_code == 429: return { "success": False, "data": None, "error": { "code": "RATE_LIMITED", "message": "请求太频繁,建议 60 秒后重试" } } # 其余状态码同理处理

这套返回结构让模型很容易判断下一步该干什么:看见RATE_LIMITED就等一会儿,看见CONTENT_REJECTED就把正文调整后再来一次。

3.4 幂等参数为什么是保命设计

发布不是查询,写操作一旦重试就可能产生脏数据。我在submit_article里强加了client_request_id,流水线每次发布都生成一个 UUID 并透传过去,平台侧(或 MCP Server 内部缓存)对同一个 request_id 只处理一次。这个细节刚开始我没有做,后来付出了一篇重复文章的代价,具体事故见第 5 章。现在幂等逻辑已经成为流水线的默认前置条件,没有幂等保护的写工具,我不会放给 Agent 用。

4. 多 Agent 协作的调度与状态机细节

4.1 一张任务表搞定协作

Agent 之间没有握手协议,真正的“协作中心”是 SQLite 里的一张 task 表。主要字段有:task_id、topic、current_agent、status、input_json、output_json、attempt_count、error_msg、created_at、updated_at、deadline。

调度器每分钟查一次,规则只有一条:如果任务的当前状态有对应的待执行 Agent,并且 updated_at 超过 30 秒没有再更新,就把它标记为 running 并唤醒对应的 Agent。这其实是一个简单租约机制:状态更新操作会把旧的 updated_at 带进 WHERE 条件里,防止两个调度进程同时处理同一个任务。

4.2 状态机与乐观锁

状态迁移不允许乱跳。我在代码里维护了一张迁移表,写作状态只能走向“已写完”,已写完只能走向“审核中”,审核中只能走向“通过”或“打回”。更新任务时必须带上预期状态作为条件:

UPDATE task SET status = 'written', output_json = ?, current_agent = 'editor', updated_at = ? WHERE task_id = ? AND status = 'writing'

如果影响行数是 0,说明状态已经被别的进程改了,这一次写入直接视为失败并回滚。这是非常朴素的乐观锁,但足够可靠。状态迁移表用 Python 字典维护也很直观:

TRANSITIONS = { "writing": {"written"}, "written": {"reviewing"}, "reviewing": {"approved", "rejected"}, "approved": {"publishing"}, "publishing": {"published", "retrying"}, "retrying": {"publishing", "dead"}, }

代码里再写一个can_transit(from_state, to_state)的判断函数,所有 Agent 更新状态前都必须过这一关。这套机制保证任何 Agent 都没办法跳过审核直接发布。

4.3 上下文传递和 token 预算

每个 Agent 的输入不是“整篇文章全部塞进去”,而是读取上一环 output_json 里的必要字段。写作 Agent 拿的是选题卡片和素材摘要;编辑 Agent 拿的是正文和当前标题、摘要。每个 Agent 的上下文都有明确边界,不会无限膨胀。

我还给输出做了长度预算:比如审核 Agent 的 review_comment 最多 500 字,避免一个 Agent 把冗余内容灌给下一个。这一步纯粹是成本控制,实测对 token 消耗影响非常大,而且强制压长度反而倒逼 Agent 输出更核心的信息。

4.4 失败重试与人工兜底

每个 Agent 最多重试 3 次。重试时我不会让同一个 Agent 用完全相同的 prompt 再跑一遍,而是把上一次的错误信息追加进去,或者调高输出的随机性,避免同一个错误反复出现。3 次还失败,任务进入dead状态,同时调用send_notify工具给我发一条消息提醒。

我另外加了一个兜底规则:任何任务 24 小时没推进,也会触发提醒。毕竟“全自动”不等于没人管,尤其是审核边界不太清晰的新内容类型,关键时刻还是要人工介入看一眼。

5. 实测中踩过的坑:从内容翻车到重复发文

5.1 审核意见没有传回写作 Agent

第一次跑通全流程后,我发现一个问题:审核 Agent 经常打回文章,但写作 Agent 完全不知道原因。排查后定位到原因,是 output_json 里只存了approved: false,没存意见。

修复非常简单:强行要求审核 Agent 输出review_comment字段,并且在状态变为rejected时,把这个意见注入到下一次写作 Agent 的输入里,包括标题修订建议、分类修改建议、正文修改建议,最多三条。改完之后,文章基本一次打回就能改到点子上,二次打回的概率大幅下降。

5.2 超时重试导致同一篇文章发布两次(完整排查链路)

这个坑最疼。有天早上打开平台后台,发现同一篇内容出现了两个版本,发布时间只差 40 秒。我第一反应是平台接口 bug,翻日志才发现根本不是。完整排查链路是这样的:

  1. 先查任务表的 publishing 状态,发现这个任务 attempt_count = 2。
  2. 打开 MCP Server 的调用日志,看到第一次submit_article请求已经发出,响应是 success。
  3. 但客户端这边因为网络超时没有收到响应,于是触发了重试逻辑。
  4. 重试请求带着同一个 article_id 到平台,平台又创建了一次发布,于是变成两篇文章。
  5. 根因是没有任何东西告诉平台“这两个请求其实是同一个操作”。

修复就是前面说的client_request_id。发布工具要求流水线每次为任务生成唯一 ID 并透传,平台侧记录已处理过的 request_id,相同 ID 直接返回第一次的结果,不再重复创建。这是写操作接口必须有的设计,我第一版偷懒没做,结果踩了大坑。从那以后,幂等键成了我所有 MCP 写工具的基础设施。

5.3 图片外链第二天全部失效

还有一次排版没问题,但发布完第二天,用户打开文章发现图片全裂了。排查发现是配图 Agent 把图片传到了平台的临时上传接口,拿到的 URL 有效期只有 24 小时。

修复方案是加一个自建的文件交付服务,生成的图片先传到自建静态资源服务,拿到永久地址后再放进正文。如果自建成本嫌高,退一步也至少要传到平台的素材库,引用素材的永久 ID,坚决不能用临时直链。这个问题藏得很深,因为发布当天预览完全是正常的,只有等第二天才暴露。

5.4 模型幻觉给内容安错分类和标签

模型写出来的文章,内容明明是新闻快讯,分类却被归进“教程”,标签也是随手生成的。这件事看起来不起眼,但非常影响文章检索和后续数据统计。

我的修复方案是在审核 Agent 里加了一个“白名单校验”:先把平台的分类和标签候选拉出来,模型只能从候选里选,选错就返回VALIDATION_ERROR,并且把候选列表一起放进错误信息里。人的工作从“逐篇改标签”变成“维护白名单模板”,可靠得多。

5.5 集中发布触发限流

第一次批量上线 10 篇时,调度器在同一分钟全部提交,平台很干脆地返回了 429。当时我以为“只要工具层报了错,系统自己会处理”,结果系统确实在处理,但处理方式是所有任务一起重试,场面更乱了。

修复分两步:第一步,调度策略改成每分钟最多提交 2 篇,天然错峰;第二步,对RATE_LIMITED错误做指数退避,第一次等 30 秒,第二次 90 秒,第三次 300 秒。自从加了这两个策略,限流再也没有成为阻塞项。

5.6 踩坑清单汇总

问题根因修复方案
审核意见弄丢输出只存布尔结果强制结构化 review_comment 字段
重复发文两篇超时重试无幂等保护全局 client_request_id
第二天图片失效临时 URL 有有效期自建文件服务出永久链接
分类标签乱模型自由发挥平台候选白名单校验
集中发布限流没有节流策略每分钟 2 篇 + 指数退避

6. 跑了一个月之后的变化和下一步尝试

6.1 现在流水线跑成什么样了

运行了大约四周,自动处理了 30 多篇内容,成功发布 22 篇,打回重写 8 次,人工介入 3 次。整体数据大致是这样的:

指标数据
单篇平均生产耗时约 8 分钟
单篇平均重试次数0.7 次
人工介入率10% 左右
发布后阅读表现与人工编辑基本持平

对个人内容更新频率来说,这套投入已经值回票价。最大的收益反而不是省时间,而是每个环节都变成可测试、可重放、可定位的模块。文章发布后出了任何问题,我能快速锁定是哪个环节的问题,而不是对着一个黑盒脚本发呆。

6.2 半自动起步的真实建议

如果只想复制这套思路,我的建议是从半自动开始。第一个版本别追求“一键全文发”,让每个 Agent 干完活先停在那里,你检查一遍再放行;连续两周没出质量问题后,再把发布 Agent 的自动开关打开。另外,审核 Agent 和幂等机制必须排在自动化发布之前做好,这两件事没到位,全自动就等于定时炸弹。

最后分享一个我一直觉得很重要的小细节:我把整个流水线的运行记录统一写进一张审计表,谁在什么时间调用了什么工具、返回了什么错误,全部留痕。这张表几乎不占地方,但排错时有奇效,尤其是某个模型输出不稳定的时候,你能一眼看出它是不是连续调错了几次参数。这套流水线之后如果要加新平台、新内容类型,我也会继续沿着“工具封装 + 状态机 + 白名单 + 审计”这套底座来扩展,不会轻易换成更重的框架。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 6:53:00

从暴力枚举到数字DP:彻底讲清“包含数字9的数”

我们去翻任何一个入门编程练习集,大概率会看到这样一道题,标题就叫"包含数字9的数",前面往往还挂着一个序号"1."。它通常跟在"打印Hello World"后面,题目描述很简单:从1到100&#xff0…

作者头像 李华
网站建设 2026/10/14 6:52:55

AI生成内容标注自查清单:12项可落地的合规实践

1. 项目概述:一份能真正落地的AI内容标注自查工具最近在帮某高校实验室做一批生成式AI应用的合规预审,对方拿出一份欧盟刚更新的AI Act实施细则——不是那种泛泛而谈的政策摘要,而是直接指向“高风险系统”中“生成内容透明度义务”的实操条款…

作者头像 李华
网站建设 2026/10/14 6:52:46

GitHub自动化浪潮:从AI Agent到CI/CD的技术逻辑

3月10日的GitHub热门榜单,翻来覆去绕不开一个词:自动化。从AI Agent编排到CI/CD流水线,从工作流调度到测试脚本生成,一眼扫过去,几乎每个能进榜单前排的项目都在琢磨同一件事——怎么把重复劳动交给机器。这股浪潮不是…

作者头像 李华
网站建设 2026/10/14 6:52:36

AI 数字人舱怎么选更耐用?三个硬件维度帮你避坑

本文针对AI数字人舱选购中常见的“用不久、故障多、运维贵”痛点,从硬件防护、功耗设计、结构可维护性三个维度拆解耐用性判断标准,结合蓝速科技(larxu)的工业级产品参数,给出不同场景的选型参考。一、为什么很多数字人…

作者头像 李华
网站建设 2026/10/14 6:52:16

物流系统前后台实时闭环设计:货-人-单状态强一致实践

简介:本资源是一套完整的物流管理系统前后端实战项目,面向Java/Web开发初学者与中级工程师,聚焦企业级物流业务场景下的系统设计与实现。资源涵盖客户下单查询的前台交互界面与订单处理、仓储调度、异常管理等后台核心功能,深度融…

作者头像 李华