把 LLM 接到生产链路里,先解决上下文污染

把 LLM 接到生产链路里,先解决上下文污染

用最少的工程动作,把自动化脚本从“能跑”收敛到“可信”

很多团队都有一类脚本:它们不是核心业务代码,但又承担了发布、巡检、同步、清理这类真实职责。问题通常不在于“能不能跑起来”,而在于一旦脚本进入日常使用,大家会迅速发现另一个更尖锐的问题:它到底可不可信。

“能跑”和“可信”之间,差的往往不是复杂架构,而是几个很朴素的工程动作。下面结合我最近整理自动化脚本的一些经验,谈一个更实用的收敛路径。

一、先区分一次性脚本和长期脚本

很多混乱的起点,是把两类脚本当成同一种东西维护。

一次性脚本的目标是尽快完成任务,代码丑一点通常不是问题;长期脚本则不同,它会被 cron、CI、运维任务或者同事反复调用,这时候脚本本身就已经是一个小型产品了。

可以用一个简单标准判断:

# 如果一个脚本会被重复执行,就不要只优化“第一次跑通”
crontab -l

只要脚本会重复运行,就至少应该具备下面四个特征:

  1. 输入明确
  2. 输出结构化
  3. 失败可定位
  4. 结果可验证

很多线上事故,本质上都不是脚本“不会执行”,而是执行失败时没人知道失败在哪里,或者执行成功时也没人能确认结果是不是真的符合预期。

二、把输入收紧,不要让 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=truepostId 这类关键信号单独提取出来,才能真正把结果说清楚。

四、对外部系统的自动化,优先保存状态而不是反复登录

凡是涉及浏览器自动化、后台发布、第三方平台操作,都应该尽量减少重复登录。不是因为登录步骤麻烦,而是因为登录本身往往就是最不稳定的环节。

以 Web 自动化为例,重复登录常见的问题有:

  • 触发验证码或风控
  • 临时页面改版导致选择器失效
  • 需要额外的人机确认
  • 登录成功但 session 没被正确复用

更稳妥的做法是:

  1. 使用持久化浏览器 profile
  2. 把 cookies 作为补充兜底
  3. 启动前清理锁文件,避免上次异常退出留下坏状态

示意代码如下:

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 非空

只有当这些信号同时满足,才应该把这次执行标记为成功。

这类验证看似啰嗦,实际上是在降低后续总成本。因为一旦缺少验证,后面要花更多时间人工补查,甚至在错误结果上继续叠加操作。

结语

自动化脚本并不一定需要复杂设计,但它需要最基本的工程约束。我的经验是,只要把下面几件事做好,脚本的可信度就会明显提升:

  • 输入收紧
  • 输出结构化
  • 登录状态复用
  • 失败分类
  • 结果验证

这些动作听上去都不大,却正好覆盖了脚本从“偶尔可用”到“持续可信”最容易失守的地方。

对长期运行的脚本来说,真正重要的从来不是它第一次有没有跑通,而是一个月后你还敢不敢继续把事情交给它。

posted @ 2026-09-01 20:54  fitch_liu  阅读(11)  评论(0)    收藏  举报