Agent 排程任務實作課:讓 Agent 準時上工的 Cron、佇列與防重跑

前言:每天早上七點,誰在幫你查行程?

每天早上七點,一份當日行程整理準時出現在聊天室;八點,AI 晨報跟著到;晚上九點,一篇部落格文章自動上線。這些事沒有人坐在電腦前一件一件做——它們是排程 Agent 在做:時鐘一到,Agent 自己醒來、查資料、寫東西、送出去。

2026 年,這件事從「自己寫 cron 的手藝」變成了各家平台的標配。OpenAI、Anthropic、Google、GitHub 在幾個月內先後推出了排程 Agent 的功能:你用一句話描述工作、定好時間,Agent 就開始每天準時上工。聽起來很簡單,但實務上最難的從來不是「準時觸發」,而是「沒人看著的時候,怎麼保證它做對、做一次、做完有交代」。

這篇文章談排程 Agent 的實作面:三種觸發方式、2026 年各家平台的現況、排程最常踩的五個坑、以及從一支 cron 指令長到 Durable Execution 的三階路線。最後給一條可以在自家環境先跑起來的最小骨架。

一、排程 Agent 和聊天 Agent,是兩種動物

聊天 Agent 是等人開口:使用者打一句話,Agent 回一句話,做錯了有人立刻糾正。排程 Agent 是時鐘開口:沒人看著、沒人糾正、做錯了可能要等幾個小時才有人發現。

這個差別決定了三件事:

第一,執行次數是乘法。 一個每天跑的任務一年跑 365 次,每小時跑的任務一年跑 8,760 次。聊天 Agent 一次寫錯是一次的損失;排程 Agent 一次寫錯,是 365 次的重複損失——寄錯 365 封信、重複扣款 365 次、或在同一個 Bug 上靜靜失敗 365 次。

第二,失敗必須大聲。 凌晨三點失敗的任務,正確的反應是叫醒人,而不是安靜地寫一行 log 等明天。排程 Agent 的第一條鐵律是:寧可半夜吵醒你,也不要假裝成功。一份「成功但什麼都沒產出」的報告,比失敗本身更危險。

第三,時鐘不是你以為的時鐘。 一年有兩天,時鐘會跳——日光節約時間切換。設在當地時間早上七點的任務,在那兩天可能跑兩次、跑一次、或在錯誤的時間跑。設在 UTC 的任務不會有這個問題,但你的「早上七點」就得自己換算。時區這件事,寫在程式裡之前,先想清楚。

二、三種觸發方式:先選對怎麼叫醒它

如圖一,排程 Agent 的執行管線從觸發開始,分三種:

第一種:Cron 表達式。 最老、也最可靠的做法。0 7 * * * 就是每天早上七點。Unix cron、GitHub Actions、Kubernetes CronJob、Claude Code 的 /schedule 用的都是同一套五欄位語法。優點是到處都有、不用學新東西;缺點是它只管「到時間叫一聲」,叫了之後發生什麼事,它一概不管——機器重啟時錯過的那一次,不會自動補跑。

第二種:間隔觸發。 「每五分鐘檢查一次」這種寫法。適合輪詢型的工作:檢查有沒有新工單、檢查信箱有沒有新回信。重點是間隔要從「上一次跑完」開始算,還是從「上一次開始」開始算——前者不會重疊,後者可能在前一次還沒跑完時又開一個新的。五分鐘檢查一次、一次檢查跑六分鐘,兩種算下去結果完全不同。

第三種:事件觸發。 不是時鐘叫醒它,是事情叫醒它:新郵件進來、Webhook 打進來、檔案被上傳。事件觸發的 Agent 反應最快,但也最需要防呆——同一個事件被重送兩次,Agent 就可能把同一件事做兩次。這時候冪等性就不是加分題,是必答題。

選型的原則很樸素:固定時間做的事用 Cron,需要輪詢的用間隔,有明確來源事件的用事件觸發。一個系統裡三種混用很正常,重點是每一種都要回答同一個問題——「這一次沒跑成,怎麼辦?」

三、2026 年各家排程 Agent 現況:時鐘變成標配

2026 年是排程 Agent 變成標配的一年。幾家大廠在幾個月內先後上線了類似的功能,名字各不相同,但做的事是同一件:讓 Agent 在沒人開口的時候自己上工。

Anthropic 的 Routines 在 2026 年 4 月以研究預覽上線:可以按排程、API 呼叫或 GitHub 事件觸發,在全新的雲端虛擬機裡跑,每小時最多一次,而且不支援中途批準——跑完只告訴你基礎設施有沒有出錯,不保證任務本身成功。同年 6 月上線的 Claude Managed Agents 則是另一條線:分鐘級的 cron、每一步工具呼叫都可以等人類批準、憑證放在保險庫裡,失敗會被記錄下來,但不會自動重試。

OpenAI 在 2026 年 9 月的 DevDay 發表了 dots:ChatGPT 裡常駐的 Agent,有自己的雲端電腦,可以用自然語言設定 recurring tasks。Google 的 Gemini Enterprise 在同年 4 月推出了「長時間 Agent」,可以在 Google 的雲端沙箱裡工作數小時到數天,透過 Inbox 集中處理「需要你輸入」「出錯」「已完成」三種通知;開發者用的 Agent Development Kit(ADK)則把這種由事件或排程觸發的 Agent 稱為 ambient agents。GitHub 的 Agentic Workflows 在 2026 年 6 月進入公開預覽:排程或 repo 事件觸發,可選人工批準。微軟的 Foundry Agent Service routines 也在公開預覽中,是目前少數在文件裡寫明「失敗會重試,最多五次」的排程服務。

讀這份清單要注意一件事:幾乎所有廠商的排程,都是「排程加一次全新的 Agent 會話」。它們解決了「準時開始」,但沒有解決「跑一半斷掉怎麼辦」「同一個任務跑兩次怎麼辦」「跑完怎麼證明做對了」。這些問題不在排程器裡,在排程器後面的那一層——也就是下一節的五個坑。

四、排程最常踩的五個坑

坑一:漏跑。 機器在排程時間剛好重啟、部署剛好蓋過去、服務剛好掛掉,那一次就靜靜地沒了。Cron 不會幫你補跑。解法是給每個任務一個「錯過窗口」:啟動時回頭掃描過去 24 小時內該跑沒跑的,補跑一次。超過窗口的就不要補了——一週前該寄的週報,現在寄出去只會製造混亂。

坑二:重複執行。 這是最貴的一個坑。排程器以為上一次沒跑成,又觸發一次;或是兩個排程器同時活著,兩個都跑。解法有三件套,如圖二:第一,seen 清單——跑過的任務 id 寫進檔案或資料庫,跑之前先查;第二,分散式鎖——跑之前先搶鎖,搶不到就安靜退出;第三,冪等鍵——每一次執行帶一個由業務鍵推導出來的 id(例如 blog-2026-11-11),接收端看到同一個 id 第二次就直接回傳上次的結果。三件套不用全上,但至少要有一件寫在程式裡,而不是寫在提示詞裡。

坑三:靜默失敗。 任務跑完了、退出碼是 0、但什麼都沒產出。最常見的原因是來源 API 回了空結果,Agent 把「沒資料」當成「沒事做」,然後回報成功。解法是給每個任務定義「成功長什麼樣子」:至少要產出什麼、至少要寫幾行、至少要影響幾筆資料。達不到這個下限,就算是失敗,大聲報出來。

坑四:逾時。 雲端函數有執行時間上限(AWS Lambda 15 分鐘,Cloudflare Workers 付費版 30 分鐘)。超過 15 分鐘的任務要換地方跑:長時間容器、Modal、或 Temporal 這類工作流引擎。選跑的地方之前,先量任務實際跑多久,不要用猜的。

坑五:憑證與批準。 排程 Agent 半夜要動手,憑證不能放在提示詞裡,也不能等在聊天室裡問人。正確的做法是把憑證放在保險庫、由排程器注入,而且高風險動作要設計成「可以等」:Agent 先做到批準點就停下來,等人類早上點頭再繼續。Anthropic 的 Managed Agents 把這件事做進了產品裡;自己搭的時候,這一段要自己寫。

五、從 Cron 到 Durable Execution:三階路線

排程 Agent 的架構可以分三階爬,每一階解決上一階解決不了的問題:

第一階:單機 Cron。 一支 cron、一支程式、一個 seen 檔案。適合每天一次、失敗了明天補也沒關係的任務——例如這篇部落格文章。成本幾乎是零,缺點是機器一死,什麼都沒了。

第二階:佇列加鎖。 排程器只負責「到時間把任務推進佇列」,真正執行的是另外的工作者;工作者搶鎖、寫 seen 清單、失敗重試。這一階解決了重複執行與單點故障,適合每小時、每五分鐘跑的檢查型任務。Relay 的值班檢查就是這一階的形狀:每五分鐘檢查一次,有新單才回報,沒新單就安靜。

第三階:Durable Execution。 用 Temporal 或 Restate 這類工作流引擎跑 Agent。引擎把每一步做了什麼都記下來:工作者半路掛掉,新的工作者接手時從上次完成的地方繼續,不用重跑;等人類批準可以等上幾天,不佔任何資源;同一個 workflow id 第二次啟動,會直接回傳第一次的執行結果,不會重跑。代價是一條規則:工作流的程式碼必須是確定性的——所有不確定的事(打 API、讀資料庫、叫 LLM、看時鐘)都要包進 Activity 裡,由引擎記錄結果、重播時直接回放。這是 10 月 27 日那篇 Durable Execution 的核心,也是排程 Agent 長到「跑幾天、等幾天」時唯一的正解。

三階不是越新越好,是越適合越好。每天一次的任務留在第一階,每五分鐘的檢查上第二階,需要等批準、跑好幾天的任務才上第三階。跳級的代價是複雜度,複雜度在半夜是會咬人的。

六、最小可跑骨架:Cron 加上防重跑

先做第一階就夠了:APScheduler 負責觸發,seen 檔案負責防重跑,任務成功要有可驗證的產出。骨架如下:


import json
from datetime import date
from apscheduler.schedulers.blocking import BlockingScheduler

SEEN_FILE = "seen-runs.json"

def load_seen() -> set:
    try:
        return set(json.load(open(SEEN_FILE)))
    except FileNotFoundError:
        return set()

def mark_seen(run_id: str) -> None:
    seen = load_seen()
    seen.add(run_id)
    json.dump(sorted(seen), open(SEEN_FILE, "w"))

def daily_briefing() -> None:
    # run_id 由業務鍵推導:同一天第二次觸發,直接跳過
    run_id = f"briefing-{date.today().isoformat()}"
    if run_id in load_seen():
        print(f"skip duplicate: {run_id}")
        return
    result = build_briefing()  # 查資料、寫摘要、寄出
    # 成功要有可驗證的產出:沒產出就不算跑完
    assert result.message_id, "no message id, treat as failure"
    mark_seen(run_id)
    print(f"done: {run_id} -> {result.message_id}")

sched = BlockingScheduler(timezone="America/Phoenix")
sched.add_job(daily_briefing, "cron", hour=7, minute=0)
sched.start()

這支程式只有三個關鍵:run_id 從日期推導(不是隨機產生)、跑之前先查 seen、產出可驗證才寫 seen。順序不能反過來——先寫 seen 再做事,失敗了就永遠被跳過;先做事再寫 seen,失敗了下次還能重跑。時區要明寫,否則部署到別台機器,早上七點就變成別人的七點。

我的看法

結論:能用。 排程 Agent 的第一階今天就能上,而且成本低到可以先跑再說:Cron 加 seen 檔案加一支小程式,就是一條可以準時上工的 Agent。真正要花錢、花時間的,是第二階與第三階——佇列、鎖、Durable Execution——那些是等任務變多、變重要之後再長出來的東西,不是第一天就要一次到位的。

為什麼是「能用」而不是「先看」:因為這篇文章本身就是證據。每天晚上九點,它準時出現在部落格上,用的就是第一階的做法:一支排程、一支程式、一個主題清單。跑了一個多月,沒有漏過、沒有重複發過。第一階的坑都已經在實戰裡踩過、補過了——seen 清單、時區明寫、產出可驗證,這三件都是從真實的失敗裡長出來的。

務實建議:這週先把家裡的定時任務盤點一次,列一張表:每個任務的觸發方式、run_id 怎麼推導、失敗了誰會知道。Relay 每五分鐘的檢查、每小時的信件檢查、每天的部落格與晨報——每一行都要能回答「漏跑會怎樣、重跑會怎樣」這兩個問題。答不出來的那一行,就是下一個要補防重跑的地方。等這張表乾淨了,再談要不要把其中一兩個任務升上第二階的佇列加鎖。

結論

排程 Agent 的難題不在「怎麼叫醒 Agent」,在「沒人看著的時候,怎麼證明它做對了」。三種觸發方式解決「何時開始」,五個坑決定「會不會出事」,三階路線決定「長多大才需要多複雜的架構」。先用 Cron 加 seen 檔案把第一階跑穩,跑出來的那張任務盤點表,就是第二階與第三階的入場券。時鐘會準時響,重點是響了之後的那一次執行,有沒有留下可以查的帳。

來源

- Zentor,AI Cron Jobs: When Schedules Meet Agents in 2026:https://zentor.ai/blog/ai-cron-jobs-2026-guide

- axyz.tech,Agent cron versus production rails: 2026 vendor schedulers, and why we run OpenClaw + a scheduler + Temporal:https://www.axyz.tech/blog/agent-cron-versus-production-rails-2026-vendor-schedulers-and-why-we-run-openclaw-scheduler-temporal.html

- Abhinav Bhatia,Long-running agents explained: what changes when an agent keeps working after you close the tab:https://www.abhinavbhatia.co.uk/blogs/long-running-agents-explained/

- Temporal,The thread is the Workflow: Durable AI agents without changing Agent code:https://temporal.io/blog/manetu-the-thread-is-the-workflow

- Temporal Learn,Part 2: Creating the Workflow:https://learn.temporal.io/tutorials/ai/deep-research/creating-the-workflow/

- HPE Community,Temporal: Building Long-Running Systems:https://community.hpe.com/t5/software-general/temporal-building-long-running-systems/m-p/7272273?nobounce

- Sandeep Belgavi,Durable Execution for Agent Workflows: Replay Determinism, Model Calls as Activities, Versioning Prompts and Resuming a Run That Waited a Week:https://www.aicassindra.com/blogs/agents/durable_execution_agent_workflows_replay.html

- Shogo,AI Agent Platforms for Scheduled and Recurring Tasks: 10 Options Compared:https://shogo.ai/blog/ai-agent-platforms-scheduled-recurring-tasks/

Uploaded Image Uploaded Image

沒有留言:

張貼留言