把 LLM 接到生产链路里,先解决上下文污染
把 LLM 接到生产链路里,先解决上下文污染
用最少的工程动作,把自动化脚本从“能跑”收敛到“可信”
很多团队都有一类脚本:它们不是核心业务代码,但又承担了发布、巡检、同步、清理这类真实职责。问题通常不在于“能不能跑起来”,而在于一旦脚本进入日常使用,大家会迅速发现另一个更尖锐的问题:它到底可不可信。
“能跑”和“可信”之间,差的往往不是复杂架构,而是几个很朴素的工程动作。下面结合我最近整理自动化脚本的一些经验,谈一个更实用的收敛路径。
一、先区分一次性脚本和长期脚本
很多混乱的起点,是把两类脚本当成同一种东西维护。
一次性脚本的目标是尽快完成任务,代码丑一点通常不是问题;长期脚本则不同,它会被 cron、CI、运维任务或者同事反复调用,这时候脚本本身就已经是一个小型产品了。
可以用一个简单标准判断:
# 如果一个脚本会被重复执行,就不要只优化“第一次跑通”
crontab -l
只要脚本会重复运行,就至少应该具备下面四个特征:
- 输入明确
- 输出结构化
- 失败可定位
- 结果可验证
很多线上事故,本质上都不是脚本“不会执行”,而是执行失败时没人知道失败在哪里,或者执行成功时也没人能确认结果是不是真的符合预期。
二、把输入收紧,不要让 shell 替你猜
脚本最常见的问题之一,是参数约定松散。标题、正文、路径、环境变量都能传,但没有主次,没有边界,最后 shell 转义、换行和空格把事情搞得不可预测。
长期脚本应尽量遵循两个原则:
第一,短参数走命令行; 第二,长内容走文件或 stdin。
比如发布文章这类操作,标题适合用参数传,正文更适合走 stdin:
node cnblogs-post.js "自动化脚本的可信收敛" < article.md
这样做有三个直接收益:
- 避免超长命令行参数带来的截断和转义问题
- 保留 Markdown 原始结构,不被 shell 额外处理
- 更容易在本地复现与调试
看起来只是调用方式的变化,但它直接减少了一类最难排查的问题:脚本逻辑没错,真正坏在输入层。
三、输出必须结构化,别让人读日志猜结果
很多脚本的“成功标准”藏在日志里。例如打印了一串 URL,或者打印一句 publish done,然后让调用方自己分析。这种方式在人工盯着看时勉强可用,但一旦进入自动化流程,就会立刻暴露脆弱性。
更稳的做法是让脚本在结束时输出结构化结果,例如 JSON:
{
"success": true,
"postId": "12345678",
"url": "https://i.cnblogs.com/posts/edit-done;postId=12345678;isPublished=true",
"title": "自动化脚本的可信收敛"
}
这类输出有两个价值。
一是方便后续系统消费。无论是另一个脚本、一个 cron 任务,还是一个 AI agent,都不需要重新猜测日志含义。
二是降低误判概率。比如“保存成功”和“发布成功”并不是同一个状态,如果只看页面跳转,很容易把草稿态当成发布态。把 isPublished=true、postId 这类关键信号单独提取出来,才能真正把结果说清楚。
四、对外部系统的自动化,优先保存状态而不是反复登录
凡是涉及浏览器自动化、后台发布、第三方平台操作,都应该尽量减少重复登录。不是因为登录步骤麻烦,而是因为登录本身往往就是最不稳定的环节。
以 Web 自动化为例,重复登录常见的问题有:
- 触发验证码或风控
- 临时页面改版导致选择器失效
- 需要额外的人机确认
- 登录成功但 session 没被正确复用
更稳妥的做法是:
- 使用持久化浏览器 profile
- 把 cookies 作为补充兜底
- 启动前清理锁文件,避免上次异常退出留下坏状态
示意代码如下:
const ctx = await chromium.launchPersistentContext(userDataDir, {
headless: true,
args: ['--no-sandbox']
});
这里真正重要的不是 API 名字,而是工程思路:
不要每次都从“全新登录”开始,而要尽量从“复用已验证状态”开始。这样脚本的成功率通常会有明显提升。
五、失败路径要写给未来的自己看
很多人写脚本时,只认真写 happy path,异常分支只是简单 catch 一下打印错误。问题在于,几周之后回头看,这种错误信息对排查几乎没有帮助。
更有效的做法是把失败分类:
- 输入错误
- 权限错误
- 页面状态不符
- 网络超时
- 外部系统风控
例如下面这种返回方式,就比单纯抛一个异常更有工程价值:
return {
success: false,
error: 'CAPTCHA or login required',
url: currentUrl,
postId: null
};
这样的结果至少能回答三个问题:
- 失败是不是预期内的一类故障
- 故障发生在哪个阶段
- 现在应不应该自动重试
如果错误原因是验证码或权限问题,重试往往没有意义;如果是偶发超时,才值得做有限次数的重试。没有分类,就谈不上后续策略。
六、最后一步不是“执行完成”,而是“验证完成”
这是最容易被忽略的一点。
很多自动化流程在点击“发布”或者调用接口返回 200 之后,就直接结束。但真实世界里,“请求发出”不等于“结果达成”。
更可靠的习惯是,在动作之后再做一层结果确认。例如:
# 关键不是发请求,而是验证状态信号
# success=true
# isPublished=true
# postId 非空
只有当这些信号同时满足,才应该把这次执行标记为成功。
这类验证看似啰嗦,实际上是在降低后续总成本。因为一旦缺少验证,后面要花更多时间人工补查,甚至在错误结果上继续叠加操作。
结语
自动化脚本并不一定需要复杂设计,但它需要最基本的工程约束。我的经验是,只要把下面几件事做好,脚本的可信度就会明显提升:
- 输入收紧
- 输出结构化
- 登录状态复用
- 失败分类
- 结果验证
这些动作听上去都不大,却正好覆盖了脚本从“偶尔可用”到“持续可信”最容易失守的地方。
对长期运行的脚本来说,真正重要的从来不是它第一次有没有跑通,而是一个月后你还敢不敢继续把事情交给它。

浙公网安备 33010602011771号