1. 从一篇稿子到发布上线:技术博主被低估的时间黑洞
先还原一个我自己的真实场景。上个月我写了一篇关于 MySQL 8.0 安装配置的踩坑记录,本地 Typora 里改了三遍,截图裁了五张,自认为内容已经打磨到位了。结果从打开 CSDN 编辑器到真正点下"发布文章",我花了将近四十分钟。不是文章本身有问题,而是整个发布流程里塞满了琐碎动作:重新粘贴 Markdown、调整渲染错乱的代码块、给图片一张张传图床、填标签、选分类、勾选"原文链接"和"声明原创",最后再填一次摘要。这些操作没有任何技术含量,但每一秒都在消耗耐心。
我相信大多数技术博主都有类似经历。CSDN 作为国内技术人最常用的内容分发平台之一,其编辑器能力本身并不差,问题在于它与我们日常使用的本地写作工具链是完全割裂的。你写 Markdown 用 Typora、VS Code、Obsidian,管理代码工程用 Git,处理远程环境用 SSH,但到了发布这一步,一切都要退回手工操作。这种割裂感对一个习惯自动化的人来说,非常难受。
我当时的第一个念头是去找现成的发布工具。结果搜了一圈,CSDN 官方提供的同步助手只支持从特定平台导入,不支持通用 Markdown 流程;第三方方案要么年久失修,要么需要在浏览器里装插件,依然绕不开人工确认。于是我开始考虑自己动手,做一个真正"智能"的发布助手——不是简单的格式转换工具,而是一个能理解文章内容、能做出发布决策、能自动处理格式与元信息的 AI 智能体。
这篇文章会完整复盘这个项目的设计与实现过程。内容包括整体架构选择、智能体提示词设计、浏览器自动化发布管线的搭建、登录态与风控问题的处理思路,以及一套可用于验收智能体效果的数据集设计方法。如果你也在用 AI 构建自己的写作或发布工作流,或者你想了解一个真实的智能体项目如何从零落地,这篇文章应该能给你一些参考。我默认你有基本的 Python 基础,知道什么是 API、什么是浏览器开发者工具,但不需要你预先掌握任何智能体框架。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 发布流程的断点拆解:到底哪些环节值得交给智能体
动手写代码之前,我先把"发布一篇文章"这件事拆成了最小的动作序列,然后再逐个判断:这个动作适合用规则脚本解决,适合用大模型判断,还是根本应该保留人工操作。这一步非常关键,因为它决定了整个系统的复杂度和可靠性。
2.1 从本地文件到线上页面,中间隔着七道工序
拆下来大概是这样的链路:读取本地 Markdown 文件,解析标题与正文结构,处理图片资源(要么转图床、要么以 Base64 嵌入),转换或校正格式以匹配 CSDN 的 Markdown 方言,生成标签与分类建议,填写摘要,最后在编辑器中完成内容写入并点击发布。这还不算登录态维护、验证码应对、草稿保存等基建性问题。
其中"格式校正"是我最早想自动化、也是最初低估了难度的环节。CSDN 的 Markdown 渲染基于一套自定义规范,和 GitHub Flavored Markdown 有不少细微差异。举几个实际例子:CSDN 的代码块语言标识支持 python、bash 这类常见值,但个别冷门语言标识会渲染失败,直接变成纯文本;CSDN 对 Markdown 表格的列宽支持有问题,过宽的表格在移动端会被截断;还有 CSDN 编辑器对 LaTeX 公式的解析需要特定的 $$ 包裹方式,与 Typora 的自动识别行为不完全一致。这些差异如果靠人工一遍遍粘贴、预览、修正,效率极低,但完全靠规则脚本处理又不够灵活,因为不同文章的格式问题千差万别。
2.2 "规则脚本不够用,纯人工又太蠢":智能体介入的边界
这里就体现出大模型的价值了。格式校正本质上是一个"按目标平台的方言规范改写文本"的任务,它没有唯一标准答案,需要根据上下文判断,这正是大语言模型擅长的事情。但要让大模型在这条流水线上干活,不能只是简单地把文章丢给它然后说"帮我修正格式",而是必须把任务拆细,让它每一步都清楚自己在做什么、需要输出什么格式的结果。
我当时把整条流水线分成了三类动作。第一类是确定性动作,比如读取文件、上传图片、在编辑器里填入内容,这类动作用 Python 代码就能完成,不需要任何智能判断。第二类是判断性动作,比如给文章匹配标签、决定是否勾选"声明原创"、判断摘要是否吸引了读者,这类动作需要一定的语义理解能力,交给大模型来处理最合适。第三类是兜底性动作,比如验证码弹窗、账号异常提醒、编辑器出现未预期的弹窗,这类动作一旦出现,我不希望脚本自作主张去处理,而是应该立即暂停并通知我人工介入。
2.3 技术选型的横向对比:浏览器自动化并非唯一解
确定了大方向之后,我在技术方案上做了一次对比。摆在面前的主要有三条路。第一条是爬虫方案,直接模拟 HTTP 请求调用 CSDN 内部的接口完成发布,这是最"轻"的做法,速度快、不依赖浏览器环境,但缺点也明显:CSDN 的接口参数复杂且经常变动,一旦变更,整个方案的维护成本就很高,而且模拟请求的方式很容易触发平台的风控机制。第二条是官方 API 方案,如果平台提供正规的内容发布接口,那是首选,但 CSDN 的开放平台主要面向企业级应用,个人博客发布的相关接口一直没有稳定对外开放,这条路在当时走不通。第三条是浏览器自动化方案,用 Playwright 或 Selenium 驱动一个真实的浏览器,像人一样操作编辑器页面完成发布。这套方案最接近人工操作,风控特征最自然,而且不依赖内部接口,页面改版时只需要调整少量选择器。
我最终选择了 Playwright 驱动的浏览器自动化方案,理由有三点。第一,它复用了 CSDN 官方编辑器的全部能力,包括 Markdown 渲染、图片上传、标签选择、封面设置,不需要我自己维护一套格式转换逻辑。第二,浏览器的动作轨迹更接近真人操作,配合适当的延时策略,触发风控的概率远低于模拟 HTTP 请求。第三,Playwright 对登录态的管理比较友好,可以保存上下文存储状态,避免每次运行都重新扫码登录。至于这套方案的缺点,后面会单独聊,主要是环境依赖重、运行速度较慢,以及遇到复杂验证码时必须降级为人工处理。
3. 智能体的"大脑"设计:提示词、工具调用与内容决策链
如果说浏览器自动化管线是整个项目的手脚,那智能体的规划与决策能力就是它的大脑。这一章我想重点聊聊我是怎么设计大脑的,包括提示词的结构、工具调用的方式,以及"什么情况下该让模型做决定"的判断逻辑。
3.1 提示词的核心不是告诉模型"做什么",而是"按什么标准做"
一开始我踩过一个典型的新手误区:把提示词写成了操作说明书,比如"请你读取这个文件,然后修改格式,最后输出结果"。这种写法非常笼统,模型不知道你的质量标准是什么,输出的结果自然不稳定。我后来的做法是把提示词拆成三层。
第一层是角色与目标的设定,告诉模型它现在是一个"CSDN 技术博客的资深编辑",目标是把一篇本地草稿变成一篇符合平台规范、对读者友好的发布稿。第二层是流程与约束,明确要求模型按顺序执行几个子任务,每个子任务的输出都有明确的格式要求。第三层也是最重要的,是标准与样例,我会在提示词里直接列出三五条 CSDN 平台特有的格式规范,比如代码块的语言标识要使用平台支持的范围、表格宽度不得超过多少、嵌套列表的缩进规则,再附上几个"修改前 vs 修改后"的示例。
这里有个很关键的经验:大模型对"标准"的遵循程度,与标准呈现的"具体程度"成正比。如果你只说"请修正格式",它只会做最浅层的修正;如果你说"检查代码块的语言标识是否属于 YAML、JSON、Python、Bash 等平台支持的值,不支持的请自动移除或替换为 text",它的修正就会精准得多。标准的颗粒度,决定了智能体的专业度。
我用的是类似 Coze 或 Dify 这类智能体编排平台来承载这些提示词逻辑,再把中间的处理步骤包装成一个自定义工具供智能体调用。这么做的好处是,后续想调整个别环节的提示词时,不需要改代码,直接在平台上调整即可。如果你想全部用代码实现,LangChain 或者直接调用大模型 API 也可以,核心逻辑是一样的。
3.2 工具调用规划:不要让智能体"自由发挥",给它一张明确的流程图
智能体与传统脚本最大的区别在于它有能力动态决定调用哪些工具、以什么顺序调用。但这个能力是把双刃剑,如果不加约束,模型可能会在"读取文件"和"生成标签"这两个步骤之间随意跳转,导致上下文混乱,甚至出现工具参数错误。
我的处理方式是给它一张明确的决策路由表,通过提示词约束调用的先后顺序。
| 处理阶段 | 输入 | 输出 | 是否经过大模型 |
|---|---|---|---|
| 草稿读取 | Markdown 文件路径 | 原始文本 | 否,由代码完成 |
| 结构解析 | 原始文本 | 标题、正文、代码块、图片列表 | 否,由代码完成 |
| 格式校正 | 解析后的结构化内容 | 符合 CSDN 规范的 Markdown | 是 |
| 元信息生成 | 校正后的正文 | 标签、分类、摘要建议 | 是 |
| 封面与配图处理 | 图片列表 | 图床 URL 或 Base64 内容 | 部分,判断性操作过模型 |
| 发布执行 | 完整文章字典 | 发布结果(成功或失败) | 否,由 Playwright 完成 |
实际运行的时候,我要求智能体严格按照这个顺序执行,每一步完成之后必须输出一个特定格式的状态标记,我才能让它进入下一步。这么做看起来有些"死板",但换来了极高的稳定性。智能体项目的容错性本来就不如纯代码项目,宁可牺牲一点灵活性,也要保证流程的可控性。
3.3 发布决策的上移:哪些判断必须交给人工,哪些可以交给模型
另一个我比较在意的设计是"发布决策链"的分权。不是所有决策都适合让模型做,也不是所有决策都值得保留人工。我按风险等级把决策分成了三类。
第一类是低风险决策,比如"这篇讲 MySQL 的文章应该打上哪些标签""摘要应该突出哪些卖点",这类决策即使出错也不会有严重后果,全部交给模型处理。第二类是中等风险决策,比如"是否勾选声明原创",这通常需要判断文章是否包含大量引用、是否已在其他平台发布过,我会让模型给出建议,但最终是否采纳,由我预设的规则来定,规则无法判断时再询问我。第三类是高风险决策,比如"是否删除文章中的某个章节",这类涉及内容实质变动的操作,我一律不允许模型直接执行,模型只负责给出修改建议,实际的剪切、删除操作由人工确认后执行。
这样设计的目的,是在自动化程度与安全性之间找到一个平衡点。我见过一些智能体项目,把所有环节都交给模型,结果模型把文章里的一段核心结论给"优化"掉了,作者没有及时发现就发布了,造成不小的尴尬。智能体的定位应该是"能力极强的助理",而不是"全权代理",某些步骤保留一个确认点,反而能让自动化走得更远。
4. 发布管线的工程实现:用 Playwright 写出可靠的"虚拟手指"
前面讲的都是规划层面的东西,这一章进入实操环节。我会详细展示发布管线是怎么用 Python + Playwright 实现的,包括登录态管理、编辑器操作、格式容错处理,以及我在实际调试中遇到的几个棘手问题。
4.1 为什么选用 Playwright 而不是 Selenium
Selenium 是自动化测试领域的元老,资料多、生态成熟,但我在这个项目里选择了 Playwright,主要是看中它三个特性。第一,Playwright 的 expect 轮询机制比 Selenium 的隐式等待好用得多,它能自动等待元素进入可操作状态,减少了很多因为网络延迟导致的随机失败。第二,Playwright 支持自动记录浏览器上下文,登录一次之后可以把 storage_state 保存下来,下次直接复用,不用反复扫码。第三,Playwright 的浏览器启动速度和对 Chromium 的深度优化,让整个脚本的运行时间缩短了将近三分之一。
如果你对 Playwright 不熟悉,可以把它理解成一个"可以编程控制的浏览器"。你写代码指挥它打开某个页面、点击某个按钮、输入某段文字、等待某个元素出现,它就像你的虚拟手指一样,代替你在浏览器里完成所有操作。与真实人工操作相比,它速度快,但也需要你在稳定性上投入更多关注。
4.2 核心代码:发布执行器的骨架
下面这段代码是我发布执行器的简化版本,去掉了一些与业务逻辑耦合较深的部分,保留了核心骨架。
python复制import time
import logging
from typing import Dict, List
from playwright.sync_api import sync_playwright
logger = logging.getLogger("csdn_publisher")
class CSDNPublisher:
"""CSDN 文章发布执行器,负责将结构化文章数据写入编辑器并点击发布。"""
EDITOR_URL = "https://editor.csdn.net/"
def __init__(self, headless: bool = False, storage_state: str = None):
self.headless = headless
self.storage_state = storage_state
self.playwright = None
self.browser = None
self.context = None
self.page = None
def __enter__(self):
self.playwright = sync_playwright().start()
self.browser = self.playwright.chromium.launch(headless=self.headless)
if self.storage_state:
self.context = self.browser.new_context(storage_state=self.storage_state)
else:
self.context = self.browser.new_context()
self.page = self.context.new_page()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.browser.close()
self.playwright.stop()
def login_by_scan_qrcode(self, timeout_seconds: int = 60) -> None:
"""人工扫码登录,登录成功后保存登录态供后续复用。"""
self.page.goto(self.EDITOR_URL)
logger.info("请在浏览器中完成扫码登录……")
self.page.wait_for_url("**/editor/**", timeout=timeout_seconds * 1000)
self.context.storage_state(path="storage_state.json")
logger.info("登录态已保存至 storage_state.json")
def fill_article_meta(
self,
title: str,
tags: List[str],
category: str,
summary: str,
is_original: bool = True,
) -> None:
"""填充文章标题、标签、分类、摘要与原创声明。"""
# 标题输入框的 selector 在不同版本的编辑器中可能变化,需要定期维护
title_input = self.page.locator('input[placeholder*="文章标题"]')
title_input.fill(title)
if tags:
tag_input = self.page.locator("input[placeholder*='标签']")
for tag in tags:
tag_input.fill(tag)
tag_input.press("Enter")
time.sleep(0.3)
if category:
self.page.select_option("#categoryId", label=category)
if summary:
summary_input = self.page.locator("textarea[placeholder*='摘要']")
summary_input.fill(summary)
if is_original:
original_checkbox = self.page.locator("text=声明原创")
original_checkbox.check()
def fill_article_body(self, markdown_content: str) -> None:
"""写入 Markdown 正文内容。
注意:CSDN 编辑器有两种模式,富文本与 Markdown。
为了确保 Markdown 渲染正确,必须先切换到 Markdown 模式。
"""
md_tab = self.page.locator("text=Markdown")
if md_tab.count() > 0:
md_tab.first.click()
time.sleep(1)
editor = self.page.locator("div[contenteditable='true']")
editor.click()
editor.fill(markdown_content)
logger.info("正文内容已写入编辑器")
def click_publish(self) -> None:
"""点击发布按钮,并处理可能的确认弹窗。"""
publish_btn = self.page.locator("button:has-text('发布文章')")
publish_btn.click()
time.sleep(2)
confirm_btn = self.page.locator("button:has-text('确认发布')")
if confirm_btn.count() > 0:
confirm_btn.click()
# 发布完成后会跳转到文章详情页,这里等待跳转完成
self.page.wait_for_url("**/article/details/**", timeout=30000)
logger.info("文章发布成功")
def publish_article(self, article: Dict[str, any]) -> bool:
"""执行一次完整的发布流程。"""
try:
self.fill_article_meta(
title=article["title"],
tags=article.get("tags", []),
category=article.get("category", ""),
summary=article.get("summary", ""),
is_original=article.get("is_original", True),
)
self.fill_article_body(article["content"])
# 先保存草稿再发布,降低意外风险
self.page.locator("button:has-text('保存草稿')").click()
time.sleep(1)
self.click_publish()
return True
except Exception as e:
logger.error(f"发布失败: {e}", exc_info=True)
self.page.screenshot(path="fail_screenshot.png")
return False
这段代码看起来不长,但里面有几个细节我想单独说明。第一个是登录态复用,我用 storage_state 参数在每次启动时加载之前保存的登录信息,这样就省去了每次运行都扫码的过程。第二个是选择器的维护,CSDN 编辑器界面偶尔改版,选择器会失效,所以我养成了一个习惯:每次运行失败时先截图,检查是不是选择器变了,而不是急着去看代码逻辑。第三个是发布前的"保存草稿"步骤,这个操作看似多余,但可以极大降低风险——即使发布动作失败,文章也已经在草稿箱里了,不会白写。
4.3 编辑器适配:那些 Markdown 解析器不一致导致的坑
CSDN 的 Markdown 渲染与 Typora 的渲染逻辑存在不少差异,这在我初期的测试中造成了很大的困扰。有几个问题非常典型,如果你的文章恰好踩中了这些语法,发布后的排版会非常难看。
第一个问题是表格的宽度问题。CSDN 的表格在内容超出一定宽度后不会自动换行,而是直接把整个表格撑破,导致页面横向滚动。我的解决方式是在智能体的格式校正步骤中加入一条规则:所有单元格内容超过 20 个字符时,强制加入换行符号或缩短措辞,从源头避免超宽表的出现。
第二个问题是段落之间空行的处理。Typora 里两个段落之间加不加空行,渲染效果没有什么区别,但 CSDN 解析器对空行的敏感度很高。如果段落之间没有空行,CSDN 会把两个段落合并成一段,视觉上非常憋屈。这个问题没法用通用规则解决,因为不同文章的段落结构不同,我曾经试过用正则表达式对每一个换行符做处理,结果误伤了代码块内的换行,后来改为让大模型在格式校正阶段统一处理,效果好很多。
第三个问题是代码块的复制问题。CSDN 编辑器的内容可编辑区域是一个富文本区域,直接把代码以纯文本形式粘贴进去,会遇到缩进丢失、引号被转义成中文全角符号等问题。我的方案是绕过整个富文本粘贴流程,直接在编辑器区域用 Playwright 的 fill 方法写入带 Markdown 标记的原文。fill 方法会模拟真实键盘输入,不会触发浏览器对粘贴内容的自动格式化,这保证了 Markdown 原文不经过任何中间转换,直接提交给编辑器的解析器。
表格语法的处理也有讲究。CSDN 对表格的分隔行要求比较严格,必须使用至少三个连续的短横线 ---,否则表格无法正确渲染。很多 Markdown 工具会自动生成 ---,但有些会生成 - 或 --,这些在 CSDN 上都会导致表格失效。我在智能体的提示词里明确加了一条规则:"检查所有表格分隔行,统一为三个或以上短横线",并且用几个正反例让模型理解这个规范。
4.4 图片资源的处理策略:图床优先、Base64 兜底
图片是技术博客的刚需,尤其是环境配置、报错截图这类内容。在发布管线里,图片处理可以直接决定文章的可用性。我测试了三种方案。第一种是直接把本地图片文件传入 CSDN 编辑器,让编辑器自己上传,这需要模拟文件选择操作,Playwright 的 set_input_files 方法可以做到,但多个图片时顺序不稳定。第二种是先把图片上传到图床,拿到 URL 后在 Markdown 中用  的形式引用,这种方式可控性最强,也是我最终采用的方案。第三种是把图片转成 Base64 编码直接内嵌在 Markdown 里,这种方式对编辑器最友好,但会导致文件体积膨胀,不适合图片多的文章。
在选择图床时,我优先考虑了国内访问速度快、支持 HTTPS、有稳定 API 的服务。七牛云、又拍云这类对象存储服务都是不错的选择,腾讯云 COS 也可以。如果你不想搭建独立图床,CSDN 编辑器也支持直接粘贴剪贴板中的图片并上传,这个时候需要先用 Python 把图片文件复制到系统剪贴板,再在编辑器区域执行粘贴动作,这个方案也可以,但稳定性稍差,偶尔会出现上传超时。
我给智能体的图片处理工具设定了这样的逻辑:优先检查图片 URL 是否可以从公网访问,可以就直接引用;不可以就调用上传接口传到图床,返回 URL 后替换 Markdown 中的本地路径;如果上传失败,则走 Base64 兜底方案。这个策略覆盖了我在实践中遇到的绝大多数情况。
5. 登录态、验证码与风控:自动化发布最容易被拦下的三座山
前端操作写得再流畅,遇到验证码和风控策略一样会翻车。这一章我把自己的排查思路和翻车经验完整写出来,希望能帮你少走弯路。
5.1 登录态维护的正确姿势:从扫码到复用
浏览器自动化的第一个大坑就是登录。CSDN 的登录支持账号密码和扫码两种方式,账号密码登录在自动化环境中很容易触发安全验证,而且密码框往往存在加密逻辑,直接 fill 写入不一定生效。我最终选择了扫码登录配合存储态复用。
具体流程是:第一次运行时,Playwright 打开一个有头浏览器窗口,用户用手机扫码完成登录,登录成功后执行 context.storage_state(path="storage_state.json"),把登录凭据保存到本地。之后每次运行,都通过 storage_state 参数加载这个文件,跳过登录步骤,直接进入编辑器页面。这个文件包含了 Cookie 和 LocalStorage 数据,相当于把"登录状态"整个打包存了下来。
但是这里有个细节需要注意,CSDN 的登录态有效期不是永久的。我实际测试下来,短则三五天,长则一两周,Cookie 中的部分字段就会失效,表现是访问编辑器页面时被重定向到登录页。我的解决方式是在进入编辑器页面后,检查 URL 中是否包含 passport 字样,如果包含,就自动切换为扫码登录模式,等用户操作完成后再继续。这里一定要写一个明确的检测逻辑,不要指望 Playwright 的 wait_for_url 自动处理成功跳转,因为有可能等到的不是编辑器页面,而是另一个登录中间页。
5.2 不只是验证码:滑块与行为检测的应对思路
即便登录态正常,CSDN 在某些敏感操作下还是会弹出验证码或滑块验证,比如频繁发布、异地登录、浏览器指纹异常等。我给自己定了一条底线:遇到滑块验证时,绝不自动模拟拖拽。原因有两点,一是自动化的滑块轨迹很难模拟出人类的曲线和停顿,失败率很高,反复试错反而更容易触发更严格的风控;二是这类"绕过验证"的行为本身存在合规风险,我希望把整个项目保持在一个干净的自动化范围内。
我的做法是设置一个哨兵逻辑,在每步关键操作之前检查页面上是否出现了滑块容器。如果出现了,立即停止自动流程,截图保存现场,弹出一个提示框让操作者手动完成滑块验证,验证完成后再继续执行后续步骤。你可以把这个设计理解成"自动管廊"——正常情况全部自动,异常情况自动降级为人工接管,接管完成后再恢复自动。
有一个经验想分享:发布频率对风控触发的影响比操作细节更大。我做了一个压测,连续发布 5 篇文章,间隔 1 分钟,第三天账号出现了异常登录提醒;而把间隔拉长到 10 分钟以上,同时每篇之间加入随机的阅读行为(比如打开一篇文章停留 30 秒再关闭),一个月内没有触发任何风控。发布不是流水线上的机械动作,是有人味的内容分享行为,行为的随机性本身就是一种保护。
5.3 一次真实翻车:等了 20 分钟后才发现编辑器没切到 Markdown 模式
这个问题的排查过程比较典型,我详细说说。一次测试中,发布的文章排版完全错乱,代码块全部变成了纯文本,表格也没有渲染,看起来像是在富文本模式下粘贴了 Markdown 原文。我的第一反应是格式校正环节出了问题,于是回看智能体的输出,发现内容一切正常。接着我手动打开编辑器复现,才发现问题出在模式切换上——CSDN 编辑器默认打开的是富文本模式,我的脚本假设它一定停留在上次关闭时的状态,但实际情况是每次打开都会回到默认模式。
修复方案是在写入正文前,先检查编辑器工具栏中 Markdown 选项卡的选中状态,如果未被选中,就先点击切换。这个细节看起来微小,但直接影响文章排版质量。我把这个检查逻辑写成了一个独立的工具函数,放在整个发布流程的最前面,任何一次发布都必须先完成模式确认。
这段排查经历告诉我,自动化工具的稳定性问题,一半来自代码缺陷,一半来自对页面状态的无知。别急着觉得"这步肯定没问题",很多时候问题恰恰出在你没怀疑过的地方。每当你对一个页面元素的预期状态不确定时,最稳妥的做法是在代码里显式地先检查再操作,而不是依赖默认状态。
5.4 网络波动与超时:用"重试 + 幂等"来对抗不确定性
浏览器自动化依赖网络,而网络永远不可能 100% 稳定。我在实践中总结了一套组合拳来对抗这种不确定性。
第一是"超时 + 重试"机制。任何一步网络请求型的操作,都要设置超时时间,超时后不立即报错,而是重试两到三次,每次重试间隔 3 到 5 秒。这样做的前提是操作必须是幂等的,即重复执行不会产生副作用。比如填充标题这个操作,重复执行没有问题,但点击发布按钮这种操作,重复执行就可能导致重复发文,所以这类操作不能简单地重试,而是要为它设计一个"确认点"。
第二是"保存草稿兜底"。在点击发布之前,强制先保存一次草稿。这样即使发布步骤失败,文章也不会丢失,可以在人工介入时从草稿箱恢复。这个习惯帮助我避免了至少三次"文章白写"的悲剧。
第三是"失败现场留证"。每次异常退出前,把当前页面截图保存到本地,同时打印出关键元素的状态信息。这一套现场取证机制,让我在排查问题时省了大量时间,不用重新模拟一遍现场才能看到错误样子,直接看截图就能定位问题。
6. 数据集设计与验收标准:怎么证明你的智能体不是"玩具"
项目做到"能跑"只是第一步,真正让它配得上"专业"二字的,是一套经得起推敲的测试数据和验收标准。这一点很多人会忽略,但它恰恰决定了你的智能体在真实场景中靠不靠谱。
6.1 测试数据集的构成思路
在聊 AI 智能体测试时,大家很容易陷入一个误区:以为测试数据就是要尽可能多、尽可能全。但根据我的经验,对于 CSDN 文章发布助手这类垂类工具,更重要的是覆盖"典型的、真实的、易出错的"场景。我的数据集按照四个方面来组织。
第一是格式覆盖维度。我从自己过去两年写过的百余篇技术文章中,精选了 20 篇作为测试样本,人为地往里面注入了常见的格式问题:表格宽度超限、代码块语言标识缺失、嵌套列表缩进错乱、LaTeX 公式包裹方式错误、图片本地路径未替换等。每个样本只会注入一类问题,这样可以精确地验证智能体对某一类问题的处理能力。
第二是内容类型维度。不同技术方向的博客,发布要求不太一样。环境搭建类文章通常包含大量代码块和截图,踩坑记录类文章通常带有明显的时间线和结论,原理科普类文章则可能有大量公式。我要求测试集至少覆盖这三种类型,因为它们的格式侧重点完全不同。
第三是边界场景维度。比如超长文章(超过两万字)的处理,极短文章(只有一段话)的处理,包含非常见语言标识代码块的处理,包含大量 Base64 图片的处理。这些边界场景不一定日常出现,但它们在验证系统的鲁棒性方面非常有效。
第四是异常注入维度。模拟网络中断、模拟编辑器元素缺失、模拟登录态过期、模拟验证码弹出。这类测试的价值在于验证兜底逻辑是否真的可靠,因为真实环境中不可能永远一帆风顺。
6.2 从"能发布"到"发布得好":三层验收标准
测试数据集有了,那怎么评判智能体到底做得好不好?我建立了一套三层验收标准。
第一层是功能完整性验收。文章是否成功发布,页面是否可访问,标题、摘要、标签是否都已填写,这是最基础的验收门槛。这一层不通过,说明发布管线本身有问题,需要回炉重造。
第二层是格式正确性验收。人工抽查发布后的页面,检查代码块是否高亮、表格是否正常渲染、图片是否都能显示、段落是否被异常合并。我在测试中发现,格式问题比功能问题隐蔽得多,功能问题一眼就能看出失败,格式问题往往要在文章被阅读时才发现,所以这一层需要逐项人工核对,不能只看自动化报告。
第三层是内容质量验收。这一层关注的是"AI 参与的部分"是否做得好。我让另一个大模型作为独立的评审,从读者视角对发布后的文章打分,重点关注摘要是否吸引人、标签是否准确、标题是否有传播力、表述是否保留了作者个人的语气和风格。这个方法不能完全替代人判断,但它能提供相对客观的参考,帮我快速发现自己提示词里没有定义清楚的地方。
我把这个三层验收标准做成了一张简单的评分表,每次跑完一轮测试,就按表格逐项打分。分数低于 80 的测试样本,我会反过来分析是提示词的问题、工具调用的问题,还是管线本身的问题,然后针对性调整。
6.3 批量发布压测:10 分钟间隔验证出来的安全阈值
最后一个章节聊聊压测。前面提到发布频率与风控的关系,我在这里给出一个更具体的实验过程。测试环境是一个普通的新注册账号,测试方式是连续发布 30 篇预先准备好的短文,每一篇之间的间隔时间从 5 分钟到 15 分钟随机分布。整个测试持续了两天,每个小时我只发布一到两篇。
结果很能说明问题。在前三天,一切正常,没有任何异常提示。到第四天,我加大频率、把间隔缩短到 3 分钟,第五天账号收到了异地登录提醒,同时编辑器页面出现了一次滑块验证。这个实验让我明确了一个安全阈值:对于普通技术博客账号,发布间隔最好不要小于 5 分钟,批量发布时单日总量不要超过 10 篇。如果你的账号权重更高,阈值可能会放宽一些,但我建议还是保守为好。
还有一个小细节,发布动作完成之后,让 Playwright 在文章详情页停留一段时间再关闭浏览器,模拟真实的阅读行为。这个"停留"操作虽然不会直接影响风控判定,但会让整个会话的行为轨迹更自然。你在设计自己的自动化流程时,可以考虑加入类似的"拟人化停靠",而不是让每一步操作都像机器一样紧凑。
7. 智能体的未来演进:从单点自动化到内容工作流的中枢
项目跑通之后,我一直在思考一个问题:文章发布助手不应该只是一个"自动填充表单"的工具,它有潜力成为整个内容生产工作流的中枢。
我在实际使用中最大的体会是,发布只是一个结果节点,真正花时间的是发布之前的准备过程。比如写完初稿后,需要对文章进行结构复盘,看逻辑是否通顺;需要提炼几个像样的观点,作为社交媒体的分享文案;需要把长文拆成几个短段落,供后续在公众号、掘金、博客园等多个平台分发。这些事情如果全部手工完成,每篇至少还要再花一个小时。
所以我正在规划的第二版智能体,不再只是"发布助手",而是"内容中台"。它会把一篇文章作为输入,自动完成多平台格式适配、自动生成平台专属的摘要与标签、自动拆解适合在社交渠道传播的精华片段,甚至自动生成一个简版的幻灯片大纲,供我做技术分享时使用。在这个设计里,CSDN 发布助手只是其中一个"插件",其他平台的发布器可以按同样的接口规范接入。这个扩展方向,也是我为什么在一开始就坚持把智能体与发布执行器分层的原因。
另一个我在研究的方向是"主动数据回流"。目前这个智能体只在发布时间点介入,发布后的数据表现它完全不知道。我可以给智能体增加一个新的工具,用来查询文章的阅读量、收藏量、评论数,并把数据汇总成一份周报,指导我后续的内容选题。这样智能体就从一个"一次性执行工具"变成了"持续学习的内容合伙人"。
关于 multi-agent 的分工,我也做了一些预研。与其把所有任务塞给一个大模型,不如划分成三个角色:一个负责内容理解与优化,一个负责平台规则适配,一个负责调用执行器完成发布。三个角色通过一个共享的任务队列通信,各自只关注自己领域内的决策。这个架构的好处是职责边界清晰,你可以针对每个角色单独调优提示词,甚至在某个角色效果不佳时单独替换模型,而不影响其他环节。不过老实说,对于个人博主这种量级的需求,单智能体模型配合良好的提示词设计已经够用了,multi-agent 更适合内容团队或自动化程度更高的场景。
8. 写在最后:自动化不是为了省时间,是为了把时间花在更值得的地方
文章写到这里,整个项目的核心思路和技术方案已经拆解得差不多了。我想用自己真实的体会作为收尾。
这个项目从构思到稳定运行,前后花了大概两周的业余时间,其中写代码只占了三成,剩下的时间几乎都花在调试、测试和微调提示词上。过程中有过很多次"怎么又失败了"的沮丧,也有过因为一个细节排查一下午而想放弃的念头。但当我真正可以做到"本地写完稿子,丢给智能体,十分钟后检查发布结果"的时候,那种效率和掌控感,让我觉得所有投入都是值得的。
如果你也想做类似的智能体,我的建议是不要一上来就追求大而全。先用最简版本跑通单篇文章的发布流程,确认每一步都没有问题,再逐步加入智能优化、批量处理、多平台支持这些进阶能力。发布这个动作本身并不复杂,复杂的是围绕发布的那些细节决策。每添加一个自动化的环节,都要问自己一个问题:这个环节的错误会造成什么后果?如果答案是"后果很严重",那就给这个环节留一个人工确认点。
最后分享一个小技巧。如果你不打算从零写代码,也可以直接用现有的智能体平台,比如 Coze 或 Dify,把发布执行器封装成一个 HTTP 服务,然后在智能体平台里配置一个"Tool"指向它。这样你就能在完全不碰浏览器自动化代码的前提下,体验一下"AI 智能体自动发文章"的完整链路。等你有兴趣深入了,再回到代码层面去优化执行器的稳定性。这个路径比从头写代码友好得多。
内容创作的价值从来没有变过,但高效的发布方式,确实可以让创作者把精力和时间重新还给内容本身。愿你的文章,都能被顺利、体面地送到读者的眼前。
