Multica Docs

Autopilots

让智能体按 cron 定时自己开工,或在 webhook 到来时被触发——也可以通过 UI / CLI 手动触发一次。

Autopilots 让 智能体 按调度自动开工——配好 cron 和时区,到点 Multica 自己派发 task,不需要你每次触发。适合定期巡检、周期性报告、凌晨跑的清理任务这类"standing order"场景。和前三种触发方式(分配 / @ 提及 / 对话 都是你主动喊一声)相比,Autopilots 的核心差别是时间驱动

配置一个 Autopilot

在工作区的 Autopilot 页新建一条 autopilot,要定下:

  • 名字 — 显示名
  • 执行智能体 — 到点派给谁
  • 优先级 — 继承给它产生的 task(语义同 issue 优先级)
  • 描述 / Prompt — 智能体每次执行拿到的工作说明
  • 执行模式 — 见下节
  • 触发器 — 至少加一条 schedule(cron + 时区)或 webhook

选择执行模式

Autopilot 有两种执行模式,建议从"先建 issue 模式"开始

  • 先建 issue 模式create_issue)—— 默认,推荐。每次触发先在工作区里建一个 issue(标题目前只支持一个占位符 {{date}},会插值成 UTC 日期 YYYY-MM-DD;其他 {{...}} 形式的占位符会在创建时被拒绝,避免拼错以后悄无声息地把原文当成 issue 标题),再按分配流程把 issue 派给智能体。所有工作都落在 issue 看板上,历史、评论、状态和手动分配的 issue 完全一致。
  • 直跑模式run_only)—— 不建 issue,直接入队一个 task。看板上看不到这一次运行——只能在 Autopilot 的运行历史里看到。

让它按时间跑

每个 Autopilot 至少要一个 schedule 触发器。Cron 是标准 5 字段格式(分 时 日 月 周),最小粒度 1 分钟(没有秒级)。时区用 IANA 格式(例如 Asia/Shanghai),决定 cron 表达式按哪个时区解读。

几个例子:

  • 0 9 * * 1-5Asia/Shanghai —— 工作日北京时间早上 9 点
  • */30 * * * *UTC —— 每 30 分钟一次
  • 0 3 * * *UTC —— 每天 UTC 凌晨 3 点

Multica 服务器每 30 秒扫一次到期的触发器——触发时刻最多延迟 30 秒,不是秒级精准。服务器重启时如果恰好错过触发点,启动时会补扫漏掉的触发(不会丢触发,但会立刻补跑)。

手动触发一次

调试 Autopilot 时不想等 cron,可以手动触发一次:

  • UI:在 Autopilot 详情页点"手动运行"
  • CLI:
multica autopilot trigger <autopilot-id>

手动触发走和 schedule 触发完全相同的执行流程,只是运行记录里 source 字段标为 manual

通过 Webhook 触发

Autopilot 也可以由入站 HTTP webhook 触发。在详情页添加一个 Webhook 触发器,Multica 会生成一个唯一的 URL:

https://<你的 Multica host>/api/webhooks/autopilots/awt_…

向这个 URL POST 任意 JSON——Multica 会记录一条 source = webhook 的 run,把请求体保存为 run 的 trigger_payload,然后按和 schedule 触发器 完全一致的方式派发给智能体。

curl -X POST "$MULTICA_WEBHOOK_URL" \
  -H "Content-Type: application/json" \
  -d '{"event":"demo.received","eventPayload":{"message":"hello"}}'

先建 issue 模式下,入站 payload 会附加在新 issue 的描述里供智能体 直接读到;直跑模式下,payload 也会随 run 一并交给 daemon。

Payload 形态

可以发自己的封装:

{ "event": "github.pull_request.opened", "eventPayload": { } }

也可以直接发任意 JSON 对象 / 数组。Multica 会规范化为内部封装:

{
  "event": "<推断>",
  "eventPayload": <你的 body>,
  "request": { "receivedAt": "<rfc3339>", "contentType": "application/json" }
}

不带 event 字段时,Multica 会按以下顺序从常见 header 和 body 字段 推断:X-GitHub-Event + body actionX-Gitlab-EventX-Event-Type、body 里的 event / type / action。都不命中时事件 名退化为 webhook.received

配置 GitHub 之类的来源时,请把 content type 设为 application/json—— 表单编码的 webhook payload 在 v1 里不接受。

事件过滤

新建的 webhook 触发器对每一条入站 POST 都会触发,这在单一用途的 URL 上没问题,但对那种会扇出很多事件类型的来源(典型就是 GitHub—— 一个仓库 webhook 就会同时下发 pushpull_requestworkflow_runcheck_suite 等等)就会很吵。webhook 触发器上的事件过滤区块用来 限制哪些事件真正派发一次 run;其它的只记录到投递历史里, status = ignoredreason = event_filtered,不会建任何 issue 或 run。

每一行是一条规则:一个事件名加可选的、逗号分隔的 action 列表。 任意一行命中即放行;区块留空则接受所有事件(即过滤前的行为)。

例子:

事件名Actions命中范围
workflow_runcompleted, failed只有 actioncompletedfailedworkflow_run 事件
workflow_run(留空)所有 workflow_run 事件,不限 action
push(留空)所有 push 事件

事件名和 action 从哪来

Multica 按下面的顺序从入站请求里推断 eventaction先命中先用

1. Body envelope。 如果 body 是一个 JSON 对象、且带字符串字段 event,就直接用它作为事件名。可选的 eventPayload 对象再 从自己的 action / state / conclusion / status 字段里提供 action 候选。

curl -X POST "$MULTICA_WEBHOOK_URL" \
  -H 'Content-Type: application/json' \
  -d '{"event":"trigger","eventPayload":{"action":"true"}}'
# 推断结果:event = trigger,action 候选 = true

2. 请求头。 没有 body envelope 时按以下头部识别:

  • X-GitHub-Event: <event> —— 结合 body 顶层的 action 字段 (如果有),拼成 github.<event>.<action>
  • X-Gitlab-Event: <event> —— 拼成 gitlab.<event>
  • X-Event-Type: <event> —— 原样使用。
# GitHub 风格:事件名来自 header,action 来自 body。
curl -X POST "$MULTICA_WEBHOOK_URL" \
  -H 'X-GitHub-Event: workflow_run' \
  -H 'Content-Type: application/json' \
  -d '{"action":"completed"}'
# 推断结果:event = github.workflow_run.completed
#        → 命中 workflow_run / completed 的过滤规则

# 通用 event-type 头部 —— 不需要任何 body 字段。
curl -X POST "$MULTICA_WEBHOOK_URL" \
  -H 'X-Event-Type: trigger.true' \
  -H 'Content-Type: application/json' \
  -d '{}'
# 推断结果:event = trigger.true → 命中 trigger / true

3. Body 兜底。 Body envelope 和上面的 header 都没有时, 从 body 顶层字符串字段里依次找:eventtypeaction

curl -X POST "$MULTICA_WEBHOOK_URL" \
  -H 'Content-Type: application/json' \
  -d '{"type":"trigger","action":"true"}'
# 推断结果:event = trigger(取自 `type`),action 候选 = true

4. 默认值。 以上都没命中时,事件名取 webhook.received, 没有 action 候选。

action 候选的完整清单。 事件名确定后,下面这些值都会被列为 可能的 action:

  • 事件名后缀,当事件形如 provider.event.<action> 时 (例如 github.workflow_run.completedcompleted)。
  • body 里 actionstateconclusionstatus 这四个字段—— 必须是 JSON 字符串。布尔({"action": true})或数字都不算 候选,所以 event=trigger, action=true 的过滤规则永远命中不了 {"trigger": true} 这种 body,因为 true 是 bool 不是字符串。

常见误区。 一条 Event name: trigger / Actions: true 的规则 不是「body 里出现 trigger: true 就放行」的意思——事件过滤匹配的是 推断出来的事件和 action,不是任意 body 字段。要命中它,请用 X-Event-Type 头发送 trigger.true,或者用上面的 body envelope。 保存时带空格的值(例如 " workflow_run ")会被原样保存,但永远命中 不了——保存前请先 trim。

快速验证

配好过滤后,可以用 curl 同时验证「命中」和「被过滤」两条路径:

# 命中 —— 请求头给出 event=workflow_run,body 给出 action=completed
curl -X POST "$MULTICA_WEBHOOK_URL" \
  -H 'X-GitHub-Event: workflow_run' \
  -H 'Content-Type: application/json' \
  -d '{"action":"completed"}'
# → 200 {"status":"accepted", ...}

# 被过滤 —— 同样的事件,但 action 不在白名单
curl -X POST "$MULTICA_WEBHOOK_URL" \
  -H 'X-GitHub-Event: workflow_run' \
  -H 'Content-Type: application/json' \
  -d '{"action":"in_progress"}'
# → 200 {"status":"ignored","reason":"event_filtered"}

URL 即 bearer secret

生成的 URL 就是凭证,谁拿到都能触发这个 Autopilot。请按 token 对待:

  • 不要贴到公开 issue 评论、截图、聊天记录里。
  • 泄漏后立即重新生成——在触发器上点"重新生成 URL",或运行 multica autopilot trigger-rotate-url <autopilot-id> <trigger-id>。 旧 URL 立即失效。
  • 对需要强来源认证的源,等 per-trigger HMAC 签名校验上线;v1 URL 仅 bearer。
  • 当前能查看 Autopilot 的工作区成员都能看到它的 webhook URL——更细的 权限可见性是后续工作。

状态码语义

正常的 no-op 路径都返回 200 OKstatus 字段,避免外部 webhook 重试 机制反复打:

  • {"status":"accepted","run_id":"…","autopilot_id":"…","trigger_id":"…"} —— 已派发一次 run。
  • {"status":"skipped","run_id":"…","reason":"agent runtime is offline at dispatch time"} —— 受派智能体的 runtime 离线,记为 skipped run。
  • {"status":"ignored","reason":"trigger_disabled"} —— 触发器已禁用。
  • {"status":"ignored","reason":"autopilot_paused"} —— Autopilot 已暂停。
  • {"status":"ignored","reason":"autopilot_archived"} —— Autopilot 已归档。

非 2xx 是真正的失败:

  • 400 —— 无效 JSON、scalar body、空 body。
  • 404 —— 未知 token({"error":"webhook not found"})。
  • 413 —— 请求体超过 256 KiB。
  • 429 —— 单 token 速率限制(默认 60 次 / 分钟)。

自托管:配置公开 URL

服务端设置 MULTICA_PUBLIC_URL(例如 https://multica.example.com)后, 触发器响应里会带绝对的 webhook_url,UI 直接显示可复制的 URL。没设 时 UI 会用客户端的 API origin 拼出 URL——desktop 和同源 web 没问题, 但自定义反向代理就不行了。Multica 故意不Host / X-Forwarded-Host header 推断公开主机,避免反代配置失误时被诱导生成 指向攻击者域名的 webhook URL。

看运行历史

每次触发都会产生一条运行记录(run),可以在 Autopilot 详情页的"历史"tab 看到:

  • 触发源(schedule / manual / webhook
  • 开始时间、完成时间
  • 状态(issue_created / running / completed / failed / skipped
  • 关联的 issue(先建 issue 模式)或 task(直跑模式)
  • 失败原因(失败或跳过时)

Autopilot 失败会怎样

Autopilot 失败不自动重试,也不发 inbox 通知。 失败后只在运行历史里留一条 failed 记录——不会像分配 / @ 提及那样由系统重新排队,也不会给任何人发通知。如果这条 Autopilot 是周期任务,下一次 cron 到点会重新触发一次(新的 run),但这一次失败的工作不会被自动补跑。

如果 Autopilot 很重要,要自己设计监控——例如让智能体在成功时给自己发个评论,通过缺失评论来发现失败。

不自动重试的理由:Autopilot 本身是周期性的,系统层再加自动重试容易和下一次调度叠加,产生重复执行。调度权完全交给 cron 最干净。

暂不可用的能力

API 类型触发器尚未接入。 触发器 schema 里保留了 api 类型但没有 入站路由会触发它;UI 会给已有的此类记录打 Deprecated 标签,也不显示 copy / rotate 操作。Per-trigger HMAC 签名校验、IP allowlist、按提供方 的事件预设是后续工作;v1 URL 仅 bearer。

下一步