前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
小程序题库
  • 每日一题每天一道,攒手感
  • 原理篇React / Vue 源码拆解
  • 知识卡片NEW碎片时间过考点
  • 历年面经按年份追踪真实考点
  • 算法题库在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
小程序题库
  • 每日一题每天一道,攒手感
  • 原理篇React / Vue 源码拆解
  • 知识卡片NEW碎片时间过考点
  • 历年面经按年份追踪真实考点
  • 算法题库在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
返回 AI 情报前线
All News · 全部资讯8294
  • 选对 Bedrock 上的 OpenAI 模型:超越每百万 token 价格
  • 用 Bedrock AgentCore 构建支持交互组件的 MCP 应用
  • 2026 Agentic AI安全警示:模型越狱与网络突破
  • 万级Agent集群架构实战:10万次编排背后的工程方法论
  • Anthropic内部标准:生产代码的AI辅助应有更高门槛
  • 陶哲轩等数学家联名批评 AI 数学推理的严重错位问题
  • 陶哲轩:AI 数学推理的严重错位
  • Hugging Face安全Txt暗藏AI伦理考题
  • Next.js Copilot安全设计审查清单
  • Agent PR中的内存缓存——代码审查的盲区
  • 一次 stamping:约束AI Agent范围的工程方法
  • AI生成的C++补丁为何在Release版崩溃
  • 本地优先Agent的四个信号决策门
  • AI Agent记忆 vs RAG:核心区别与工程选择
  • AI代码审查的签署人制度SOP
  • 用「每修复成本」替代通过率评估AI Agent
  • Agent「修复成功」但进程从未退出引发的生产事故
  • 面试AI编程能力应先评估「循环预算」
  • 部署 AI 推理网关前,先查队年龄龄而非面板利用率
  • WAF 拦截日志拷进 AI 助手?Cookie 和 token 也一起泄露了
  • 给无人值守 AI Worker 配一张 JSON 检查卡
  • Anthropic 报告:黑客用 Claude 开发导弹与无人机,中国实验室批量挖掘训练数据
  • 律师用ChatGPT生成上诉文书虚构证人证词,被罚5000美元
  • Claude Code Hookbook:20 个开箱即用的 Claude 钩子库
  • OpenAI如何扩展存储架构支撑10亿ChatGPT用户
  • 每天为同一段上下文付三次钱:Agent 记忆缺失的代价
  • OpenAI Agents API公开测试:Codex同款基础设施向开发者开放
  • Vercel开源skills:AI编程智能体技能共享生态
  • OmniRoute:聚合352个AI服务商免费额度的开源网关
  • DeepSeek V4.1 Flash发布:提供商别名路由与890B缓存的代价
  • DeepSeek Flash搅局:MoE架构将百万token成本打至新低
  • AI生成支付代码的五大安全漏洞与防护策略
  • Sakana AI推出Fugu Max/Ultra v2,主打多Agent编排降本
  • Google 发布 ToolGrad:先建 API 链再写 Query,ToolBench 通过率 99.8%
  • OpenAI GPT-Live-1 API支持实时语音交互
  • 美团Agent评测白皮书:评测体系全览
  • Apify MCP服务器让AI实时获取电商数据
  • 语音AI延迟从4.2秒压到780ms:逐字优化实录
  • Datasette紧急安全补丁:AI辅助审计发现多个微妙漏洞
  • DevOps AI Agent 审计追踪:关联每次 kubectl 与 Git 操作
  • AI 写的 PR 测试全绿:你的合并标准是什么
  • 放 AI Agent 进仓库前必须锁死的五项配置
  • LLM API 重试逻辑:官方 SDK 只做了一半
  • 生产环境微调对比:Claude vs GPT vs Llama 实操指南
  • Claude AI用11天完成费马大定理机器证明,代码1300万行
  • 面向 AI Agent 的 24 端点 URL 元数据 API
  • 免费在线工具:LLM 工具 JSONSchema 一键生成
  • LLM 智能体暴露现代软件工程的脆弱性
  • OpenAI Agents API 公测:云端智能体基础设施开放
  • GitHub Copilot支持Jira集成和Project HydraFusion
  • Redis LangCache:语义缓存可将 LLM API 成本削减 90%,响应速度快 15 倍
  • 已加载 51 / 8294
8.0
热点
AI SCORE
编程提效2026-09-11 23:08

给无人值守 AI Worker 配一张 JSON 检查卡

dev.to · AI#AI Worker#工程化#可靠性
Editor brief · 编辑速览

在正式跑通宵批量任务前,用一个 JSON 配置文件(gates.json)设置 8 项硬性约束:心跳可写、最大运行时间、最大调用次数、空输出策略、输出目录白名单、STOP 文件等,避免跑飞或空转浪费。

文章思维导图
Knowledge map
拖拽缩放
Full translation

完整中文译文

我几乎让一个 Worker 通宵跑了一整夜。CLI 还在和远程模型通信。我的笔记本盖子今晚已经合上一半了。到底是什么能证明这个 Worker 应该继续跑下去?

一个绿色的样本不算真正的关卡。我想要一个能够Fail Closed的文件。这个文件必须在我不在的时候也能正常工作。

周五的约束

这个 Worker 只做一个很窄的任务:把一个文件夹里的纯文本笔记分类,然后把JSON行写入磁盘。

今晚我不会守着这个循环跑。我不想为重试风暴买单。如果远程链路挂了,本地存根(stub)就接管。

时间上限四十五分钟。Token 上限只用免费模型。三次探测失败后就放弃,然后停止。

想要一个可以复制使用的工件而不是感觉?用一个 JSON 关卡文件加一个小的运行器。这两个文件就是整个启动仪式。

这个检查清单不是什么

这不是又一个模型横评。这不是 Agent 框架评测。这是一个 CLI 的"发货还是睡觉"检查清单。

如果一个关卡无法打印证据,就Fail。如果必需的关卡缺失,就不启动。运行器刻意保持Fail Closed状态。

我绝不跳过的八个关卡

我把这八行写在卡片上。然后复制到 gates.json。运行器是唯一可以说"走"的权威。

确认心跳路径可写。

把墙上时钟上限锁定为正整数。

把远程调用次数上限锁定为硬限制。

把空模型输出视为硬中止。

把输出文件限制在一个简短的允许列表内。

在下一个Tick时Honor STOP文件。

要求本地 stub 命令在 PATH 中。

磁盘剩余空间低于下限时不启动。

八个关卡对于一个任务来说足够了。第九个关卡就会变成宣言。下午六点我不需要宣言。

第一步——写关卡文件

把这个 JSON 保存到 Worker CLI 旁边。改任何值之前读两遍。注意这里根本没有"无限重试"这个 key。

{
  "task": "classify-notes",
  "heartbeat_path": "./run/heartbeat.txt",
  "max_wall_seconds": 3600,
  "max_remote_calls": 40,
  "empty_output_policy": "fail_closed",
  "output_allowlist": ["./out/", "./run/"],
  "stop_file": "./run/STOP",
  "stub_command": ["python", "stub_classify.py"],
  "min_free_mb": 512,
  "probe_attempts": 3,
  "probe_timeout_seconds": 8
}

缺失的那个 key 就是整个设计理念。通宵运行的 Worker 不应该自己发明新的重试策略。上限要写在文件里,而不是靠我记忆。

第二步——运行 Go/No-Go 检查器

运行器从不调用远程模型。它只回答启动还是等待。退出码 0 表示开始。码 2 表示等待。

#!/usr/bin/env python3
"""Unattended go/no-go gates. Copy, pin, and refuse defaults."""
from __future__ import annotations

import json
import shutil
import sys
import time
from pathlib import Path

def fail(msg: str) -> None:
    print(f"NO-GO: {msg}", file=sys.stderr)
    sys.exit(2)

def main() -> None:
    cfg = json.loads(Path("gates.json").read_text())
    Path("./run").mkdir(exist_ok=True)

    hb = Path(cfg["heartbeat_path"])
    try:
        hb.write_text(str(time.time()))
    except OSError as exc:
        fail(f"heartbeat not writable: {exc}")

    if int(cfg["max_wall_seconds"]) <= 0:
        fail("max_wall_seconds must be positive")
    if int(cfg["max_remote_calls"]) <= 0:
        fail("max_remote_calls must be positive")
    if cfg.get("empty_output_policy") != "fail_closed":
        fail("empty output must fail closed")

    stop = Path(cfg["stop_file"])
    if stop.exists():
        fail(f"stop file present: {stop}")

    for folder in cfg["output_allowlist"]:
        Path(folder).mkdir(parents=True, exist_ok=True)

    stub = cfg["stub_command"]
    if stub[0] not in {"python", "python3"} and shutil.which(stub[0]) is None:
        fail(f"stub command missing: {stub[0]}")

    free_mb = shutil.disk_usage(".").free // (1024 * 1024)
    if free_mb < int(cfg["min_free_mb"]):
        fail(f"disk free {free_mb}MB below floor")

    print("GO")
    sys.exit(0)

if __name__ == "__main__":
    main()

这个脚本是华丽的自动化吗?差得远。它能阻止一个糟糕的无人值守之夜吗?能的。离开键盘的时候我想要无聊的软件。

每次无人值守启动前这样运行它:

python go_nogo.py && python worker.py
echo $?

如果看到 NO-GO,就不启动。也不要说"就这一次"。这句话是小磁盘填满的途径。

第三步——保持一个失败fixture

我想要一个必须Fail Closed的fixture。一句博客句子不是证据。零字节的批处理文件才是证据。

mkdir -p fixtures out run
: > fixtures/empty_batch.jsonl
python - <<'PY'
from pathlib import Path
p = Path("fixtures/empty_batch.jsonl")
if p.stat().st_size == 0:
    raise SystemExit("FAIL-CLOSED: empty batch")
print("unexpected non-empty fixture")
PY

实际的 Worker 应该匹配那个 fixture。没有记录。不要用零行假扮成功。实时调用后零行是一起事故。

在同一规则内在写入器中接入:

def write_batch(path: str, rows: list[dict]) -> None:
    if not rows:
        raise RuntimeError("empty output: fail closed")
    target = Path(path)
    if not str(target).startswith(("out/", "run/", "./out/", "./run/")):
        raise RuntimeError("path outside allowlist")
    target.write_text("\n".join(json.dumps(r) for r in rows) + "\n")

你会发货一个庆祝沉默的分类器吗?我不会在今晚发那个分类器。沉默是整夜笔记消失的方式。

第四步——探测远程链路,然后退出

关卡文件把 probe_attempts 设为 3。那个数字就是放弃标准。在错误变得更好听之前不要提高它。

import subprocess
import sys

def fail(msg: str) -> None:
    print(f"NO-GO: {msg}", file=sys.stderr)
    sys.exit(2)

def probe_remote(cmd: list[str], attempts: int, timeout: int) -> None:
    last = None
    for _ in range(attempts):
        try:
            subprocess.run(cmd, check=True, timeout=timeout)
            return
        except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as exc:
            last = exc
    fail(f"remote probe failed {attempts} times: {last}")

只有在 go_nogo.py 退出码为 0 之后才调用探测。不要在一个半配置好的目录树里探测。顺序是检查清单的一部分,不是脚注。

一个免费的远程机器有助于这一步。我不想让我的笔记本当通宵宿主机。

披露:本文是 MonkeyCode 产品推广的一部分。

MonkeyCode 是一个开源项目,提供免费模型访问。它也为小规模运行提供免费服务器选项。我把这一对当作这些关卡的试验台,而且它是重放同一个 Go/No-Go 文件的一个地方。

如果三次探测都失败了,就运行 stub_classify.py。Stub 使用本地启发式规则,没有别的。它绝不会假装是远程模型。

# stub_classify.py — local, boring, honest
import json
import sys
from pathlib import Path

for line in Path(sys.argv[1]).read_text().splitlines():
    note = line.strip()
    if not note:
        continue
    label = "todo" if "todo" in note.lower() else "other"
    print(json.dumps({"text": note, "label": label, "source": "stub"}))

无聊的输出是特性,不是 bug。通宵 Worker 应该保持易于解释。如果我无法解释一个 Tick,我就不应该启动。

第五步——Tick 时带上 kill switch

刻意让无人值守循环保持短。每次 Tick 必须刷新心跳文件。每次 Tick 必须在任何调用之前 Honor STOP。

import time
from pathlib import Path

def run_ticks(max_calls: int, wall: int, stop: Path, hb: Path) -> None:
    start = time.time()
    calls = 0
    while calls < max_calls:
        if stop.exists():
            raise SystemExit("STOP file: abort")
        if time.time() - start > wall:
            raise SystemExit("wall clock: abort")
        hb.write_text(str(time.time()))
        # one remote or stub call lives here
        calls += 1
        time.sleep(1)

为什么用 STOP 文件而不是 Ctrl-C?因为我不会坐在那台键盘前。SSH 会话会断开,记忆不是方案。

从任何其他 Shell 中止可以用这个:

touch ./run/STOP

下一个 Tick 应该无需争论地死去。如果循环忽略 STOP,那是 bug。提 issue。不要把 bug 调教成重试。

时间、成本与回滚

在四十五分钟内把文件复制进去。第一个晚上不花任何付费 Token。要么一小时后停、要么四十次远程调用后停。

回滚就是 gates.json 里的 stub 命令。如果远程链路没了,就运行那个 stub。如果 stub 本身就没了,就不启动。

当以下任一情况发生时放弃整个 Worker:

三次远程探测连续失败

心跳文件在磁盘上变旧

输出出现在允许列表之外

可用磁盘降到 512MB 以下

最后那个关卡在小机器上坑过我。日志在卷满之前感觉是免费的。检查清单的存在就是为了让那个失败变得响亮。

谁不应该复制这个

如果你跑多租户 mesh,跳过这个。如果你需要正式的 SRE 覆盖,跳过这个。当任务不是一次性的时候跳过往。

这个检查清单是为独立 builder 准备的。它适配一个单任务 CLI 和一个短暂的夜晚。它不能替代真正的平台团队。

不要把 Worker 指向 secrets。不要把它指向生产写操作。不要让它在 out/ 和 run/ 之外创建文件。

我仍在持有的限制

我今天有空输出的关卡。我没有可信垃圾的关卡。这两种失败不是同一个 bug。

远程模型可能返回自信的胡说八道。JSON 可能解析零错误。标签可能完全错误。

那是我想要的下一个 Fail-Closed 检查。我不想要更大的 Agent 框架。我想要在文件里再加一个无聊的关卡。

如果你在免费远程机器上试这个,从空 fixture 开始。然后在一个 Tick 还在运行时 touch STOP。然后在临时文件夹里强制触发磁盘下限。

Worker 在每一个上都停了吗?如果它继续跑了,那检查清单骗了你。

在你的机器上,哪个无人值守关卡最先失败:空输出、心跳变旧,还是循环忽略的 STOP 文件?

要采取进一步行动,你可以考虑屏蔽这个人或举报滥用。

Original source

本文由 AI 翻译整理自 dev.to · AI,原文版权归原作者所有。

阅读英文原文
上一篇
WAF 拦截日志拷进 AI 助手?Cookie 和 token 也一起泄露了
下一篇
Anthropic 报告:黑客用 Claude 开发导弹与无人机,中国实验室批量挖掘训练数据