Multica Autopilots 实战指南:理解调度化 Agent 自动化的执行模型与 multica CLI 完整操作
【免费下载链接】multicaMake humans and AI agents work as one team — open-source and self-hostable.项目地址: https://gitcode.com/GitHub_Trending/mu/multica
Autopilot(自动飞行器)是 Multica 中“让规则代替人来派发 Agent 工作”的核心自动化机制:它以定时(schedule)、Webhook 或手动三种方式被触发,把任务可靠地投递给指定 Agent 或 Squad 的 Leader,并全程记录运行状态。本文以仓库内为编码 Agent 编写的内置技能文档 SKILL.md 为主线,结合其 源码地图 与后端 Go 实现,系统讲清 Autopilot 的对象模型、逐条 CLI 命令、Webhook 的持久化准入链路、凭证安全与排障方法,让你既能在multicaCLI 上直接上手,也能理解其底层为何能保证“不重复、不丢任务”。
这份文档是什么:给 Agent 的安全操作契约
SKILL.md并不只是普通用户手册。它存放在 server/internal/service/builtin_skills/multica-autopilots/,与multica-creating-agents、multica-squads、multica-working-on-issues等一同构成 Multica 面向 AI 操作代理的内置技能库。文档头部的 frontmatter 定义了这个技能何时启用:
--- name: multica-autopilots description: "Use when creating, updating, inspecting, triggering, or debugging a Multica autopilot (scheduled, webhook, or manual)." user-invocable: false allowed-tools: Bash(multica *) ---user-invocable: false:该技能只能由“看懂上下文后自主决定使用”的 Agent 调用,而不是用户显式点名启用;allowed-tools: Bash(multica *):Agent 只能通过multicaCLI 的子命令来触碰 Autopilot 资源;description划定了能力边界:创建、更新、检查、触发、调试Autopilot。
因此,文档内容整体带有一层强烈的“安全护栏”风格:哪些命令会产生真实副作用、哪些字段会被脱敏、什么时候必须追加--show-secrets、什么时候绝不允许为了测试而触发真实任务——这些约束既是给人看的,也是写给 Agent 看的执行纪律。
核心概念:Autopilot 不是 Agent,而是“派单规则”
文档在最前面就划清了一个关键边界:
An autopilot is not an agent. It is a rule that dispatches work to an agent, or to a squad's leader agent.
Autopilot 自身不执行任务,它是一段可持久化、可审计的“规则”,决定何时、把什么样的工作、交给谁。对象模型可概括为:
- Autopilot(规则):拥有标题、描述、执行模式、被指派的 Agent(或 Squad)、可选 Project、订阅者等属性;
- Trigger(触发器):一个 Autopilot 可挂多个触发器,类型为
schedule(cron 表达式 + IANA 时区)或webhook; - Run(运行实例):每次触发落一条
autopilot_run记录,跟踪从触发到 Agent 任务/Issue 完成的全生命周期; - Delivery(Webhook 投递):Webhook 入口先把请求持久化为一条
webhook_delivery,再驱动 Run。
完整执行链
按文档的表述,链条为:
触发器触发(schedule | webhook | manual) │ ▼ autopilot_run 记录创建 │ ▼ execution_mode 决定产出(create_issue | run_only) │ ▼ assignee 就绪检查(AgentReadiness) │ ▼ Issue / Agent 任务执行 │ ▼ 运行状态同步Webhook 路径在链首多了一层“持久准入”(durable admission):HTTP 入口先把请求体落库为排队中的webhook_delivery,同步地创建或复用幂等的autopilot_run,随即返回200(响应体含status=accepted|skipped与run_id);此后由“持有数据库租约的 worker”异步接管已接受的 Run,负责可恢复的 Issue/任务派发。
这条链路在后端源码 server/internal/service/autopilot.go 中对应三个不同入口,各有明确分工(见 源码地图):
DispatchAutopilot:调度/API/Webhook 通用入口(无人肉触发者,归因到rule_owner);DispatchAutopilotManual:成员点“立即运行”时的入口,会把运行归因(attribution)到actorUserID,即操作者同时成为 originator(授权主体)与 accountable human(责任人),两个执行模式都成立;AdmitAutopilotWebhookDelivery→DispatchAutopilotForWebhookDelivery:Webhook 专用的“先准入、后派发”两段式入口,后文专节展开。
两种执行模式
| 模式 | 行为 | 可见性 / 备注 |
|---|---|---|
create_issue | 先创建一条 Multica Issue,再驱动其执行 | Run 以 Issue 状态的形式可见;issue-title-template可用;默认run_only外的首选 |
run_only | 直接创建 Agent 任务,不建 Issue | 无 Issue 态可见,持久的“汇报位置”只能依赖任务上下文或指令自行约定 |
关于 Run 的初始状态,源码 server/internal/service/autopilot.go 显示:Webhook 准入时,create_issue模式初始状态为issue_created,run_only模式初始状态为running,之后随 Issue/任务执行而同步。
被指派者:Agent 或 Squad Leader
create时用--agent指定执行者。若被指派者其实是一个 Squad,则由resolveAutopilotLeader(autopilot.go)在派发时解析出该 Squad 的Leader Agent作为实际接收者——这与多 Agent 协作模型中“由 Leader 编排成员”的设计一致。
派发前的就绪门禁是AgentReadiness(server/internal/service/agent_ready.go):已归档(archived)或运行时未就绪(runtime-unready)的 Agent 会在入队前被拦截,对应运行会被记录为“跳过”(skipped),而不是带着错误硬跑。
快速开始:先读后写
文档给出的安全起手式是三连“只读命令”:
multica autopilot list --output json multica autopilot get <autopilot-id> --output json multica autopilot runs <autopilot-id> --output json其中get用于查看单个规则的状态、模式、被指派者与触发器;runs用于查看历史执行。文档特别强调一条红线:
Do not run
trigger,delete,trigger-delete, ortrigger-rotate-urlto test. Those are real side effects.
即不要拿trigger(真实触发一次运行)、delete、trigger-delete、trigger-rotate-url这类命令做“试试看”——它们会造成真实的持久化副作用或真实启动 Agent 工作。这一点与文档末尾“Side effects”一节严格对应。
CLI 子命令全参考
multica autopilot家族由 server/cmd/multica/cmd_autopilot.go 注册,共 12 个子命令:list、get、create、update、delete、trigger、runs、trigger-add、trigger-list、trigger-update、trigger-delete、trigger-rotate-url。下表汇总其用途与关键参数(以仓库内实际 flag 定义为准):
| 子命令 | 用途 | 关键参数 |
|---|---|---|
list | 列出工作区全部 Autopilot | --status active\|paused、--output table\|json、--full-id |
get <id> | 查看单个规则(默认脱敏 Webhook 凭证) | --output(默认 json)、--show-secrets |
create | 新建规则 | --title、--description、--agent、--mode create_issue\|run_only*、--project、--issue-title-template、--subscriber(可重复) |
update <id> | 更新规则 | --title/--description/--agent/--project/--status/--mode/--issue-title-template/--subscriber/--clear-subscribers |
delete <id> | 删除规则(含其触发器与协作者授权) | — |
trigger <id> | 手动立即运行一次 | --output |
runs <id> | 查看执行历史 | --limit(默认 20)、--offset、--output |
trigger-add <id> | 添加 schedule 或 webhook 触发器 | --kind schedule\|webhook、--cron、--timezone、--label |
trigger-list <id> | 列出触发器 id(供 update/delete/rotate 定位) | --output、--full-id |
trigger-update <id> <trigger-id> | 修改触发器 | --enabled、--cron、--timezone、--label |
trigger-delete <id> <trigger-id> | 删除触发器 | — |
trigger-rotate-url <id> <trigger-id> | 轮换 Webhook URL | --yes/-y |
带*为创建时必填。源码层面做了三类校验,可直接看成 CLI 契约:
--title、--agent、--mode缺失直接报错,--mode仅接受create_issue或run_only(cmd_autopilot.go);--kind默认schedule;schedule 必填--cron;webhook 时若同时传--cron/--timezone会被拒绝(cmd_autopilot.go);--subscriber解析后必须是成员(member),且自动去重(cmd_autopilot.go)——订阅者接收该 Autopilot 创建 Issue 的通知。
Agent/项目/成员参数支持用 UUID,也支持按名称做大小写不敏感的子串解析;当名称命中多个结果时命令会列出候选并报“ambiguous”,避免误派(见 resolveAgent)。
创建示例与标题模板约束
multica autopilot create \ --title "晨会前同步依赖风险" \ --description "扫描本工作区所有进行中 Issue,汇总阻塞项并写入任务回复" \ --agent triage-bot \ --mode create_issue \ --issue-title-template "每日同步 {{date}}" \ --output jsonissue-title-template是文档特别点名的“陷阱区”:只支持{{date}}一个占位符(UTC 日期,YYYY-MM-DD),不要自创{{trigger_id}}、{{branch}}之类的变量。后端对此有双重防线:
- 创建/更新时
ValidateIssueTitleTemplate会把模板中出现的每个{{...}}token 与支持集比对,遇到未知 token 直接报错(autopilot.go); - 触发渲染时
interpolateTemplate只替换白名单里的date,且容忍{{ date }}(花括号内带空格)写法,确保“校验通过即渲染一致”(autopilot.go); - 模板留空是合法值,此时回退使用 Autopilot 自身的
Title作为 Issue 标题。
查看与更新
runs的表格输出列包含ID / SOURCE / STATUS / ISSUE / TRIGGERED_AT / COMPLETED_AT,list表格则给出ID / TITLE / STATUS / MODE / ASSIGNEE / NEXT_RUN / LAST_RUN。其中NEXT_RUN用相对时间(如in 2h、3d ago)渲染,无后续调度时显示—,让你一眼区分“有定时计划的 Autopilot”与“根本没挂触发器”的规则(cmd_autopilot.go)。
暂停/恢复用update改状态即可:
multica autopilot update <autopilot-id> --status paused --output json multica autopilot update <autopilot-id> --status active --output json若想调整执行模式或被指派 Agent,同样走update --mode/--agent;update仅在至少给出一个字段时才有意义,全部字段未改会直接报“no fields to update”。前端同款能力对应 packages/views/autopilots/components/autopilot-dialog.tsx(创建/编辑弹窗)与 autopilot-detail-page.tsx(详情页)。
触发器管理:schedule 与 webhook
定时触发器
multica autopilot trigger-add <autopilot-id> \ --kind schedule \ --cron "0 9 * * *" \ --timezone Asia/Shanghai \ --output json--timezone使用 IANA 时区名(默认 UTC)。为帮助编辑器/用户在保存前得到权威的“下一次运行时间”,服务端暴露了只读计算端点:
GET /api/autopilots/cron-preview?expr=0 9 * * *&tz=Asia/Shanghai它返回{"next_runs": [...]},即接下来 3 次发生的 RFC3339(UTC)时间;表达式非法返回 400 +code=invalid_cron,时区不可识别返回 400 +code=invalid_timezone,两种错误分开编码是为了让界面能指出“错在哪个输入框”。该端点纯计算、不触碰任何 Autopilot 资源,只要求工作区成员身份(实现见 server/internal/handler/autopilot_cron_preview.go,前端调度编辑器见 packages/views/autopilots/components/schedule-editor/)。
Webhook 触发器
multica autopilot trigger-add <autopilot-id> --kind webhook --label "ci" --output json创建成功时 CLI 会在表格之外直接打印可用的 Webhook URL(printWebhookURL)。URL 形如:
https://<你的服务地址>/api/webhooks/autopilots/<token>持有该 URL 即等同于可以触发这条 Autopilot,因此它属于需要保护的敏感凭证(下文“凭证安全”详述)。
Webhook 为什么“可靠”:持久准入 + 幂等 + 租约 worker
Webhook 触发的核心难点在于:上游(如 GitHub)可能重试、进程可能在任意时刻崩溃。Multica 的答案是先落库、再执行、用数据库做唯一性约束。整条链路落在两个 handler 与一个 service 上:
- HTTP 入口持久化:server/internal/handler/autopilot_webhook.go 把公网投递存为排队中的
webhook_delivery记录,同时同步调用AdmitAutopilotWebhookDelivery:若已存在同一次 delivery 对应的 Run 则直接复用,否则新建 Run(create_issue初始issue_created,run_only初始running),并在 Run 上写入webhook_delivery_id,最后唤醒 worker。响应保持“快”:200+status=accepted|skipped+run_id。 - 幂等键:上游用同一
X-GitHub-Delivery/Idempotency-Key重试时,会命中已存在的 delivery/Run,从而复用原投递,绝不再创建第二个 Issue 或任务。 - 租约 worker 恢复:server/internal/handler/webhook_delivery_worker.go 以带过期时间的数据库租约认领排队中的 delivery,按触发器做派发限速,并基于
autopilot_run.webhook_delivery_id续跑已被准入的 Run。由于 delivery/run 上有部分唯一索引,即使 worker 崩溃后重新认领,也会复用原 Run 而不是产生重复任务。
代码层面对“并发/崩溃竞态”做了兜底:recoverConcurrentWebhookAdmission捕获唯一索引冲突(PG 错误码23505),冲突即表示另一副本已创建该 Run,直接查回复用(autopilot.go);ensureWebhookCreateIssueTask则修复“Issue 事务已提交、但任务入队尚未提交”的崩溃窗口(autopilot.go)。
Webhook 端点与令牌的实现细节
从 autopilot_webhook.go 还可以读到几个有意思的实现事实:
- 请求体上限 256 KiB(
maxWebhookBodyBytes):足够容纳正常规模的上游事件,同时防止攻击者用超大 JSON 撑爆 Agent 上下文; - 令牌格式为
awt_+ URL-safe base64(32 随机字节),共 47 字符:刻意不用 UUID,因为 UUID 熵低(122 bit vs 256 bit)且视觉上与内部 ID 混淆; - 投递状态取值包括
queued(worker 尚未完成派发)、dispatched、rejected、ignored、failed(携带 worker 错误);此外还存在“跳过”这一响应态——当shouldSkipDispatch判定应跳过(例如运行时离线)时,入口照常返回skipped,但 delivery 记录本身仍会标记为已交递给 Autopilot 机制(autopilot_webhook.go); - 路由层中,公网 webhook 入口是免鉴权的
/api/webhooks/autopilots/{token},与之相对的受管 REST API/api/autopilots*均在 server/cmd/server/router.go 中挂在工作区鉴权组下。
前端侧有两个值得了解的辅助函数(packages/core/autopilots/webhook.ts):buildAutopilotWebhookUrl按“服务端权威 URL → API 基地址拼接 → 当前 origin 兜底”的顺序组装完整 URL;maskAutopilotWebhookUrl只遮蔽 URL 末段 token(固定宽度••••••••••••,不泄露长度信息),因为唯一带密钥属性的只有最后一段。
凭证安全:脱敏、查看与轮换
Webhook token 就是触发密钥,因此autopilot get的默认行为是脱敏:JSON 输出中webhook_token、webhook_path、webhook_url三字段会被置空,同时补充has_webhook_token(是否存在 token)与非敏感的webhook_token_hint(token 末 4 位,用于人工比对确认“就是这条”)。该脱敏逻辑见 redactAutopilotWebhookCredentials,末四位提示取自 webhookTokenHint。
只有当你确实需要拿回“活体”凭证时,才显式追加:
multica autopilot get <autopilot-id> --show-secrets --output json约束有二:--show-secrets只对 JSON 输出生效(表格模式下直接报错),且命令会向 stderr 打印一条警告“会暴露活体 webhook 凭证,勿进入日志与共享记录”(cmd_autopilot.go)。
轮换 URL 的命令也遵循同一纪律——它会使旧 URL立即失效:
multica autopilot trigger-rotate-url <autopilot-id> <trigger-id> --yes --output json不带--yes时会弹出y/N交互确认(与 UI 端 AlertDialog 确认的风格一致,见 cmd_autopilot.go)。文档最后给出的铁律是:不要把 webhook token 或签名材料粘贴进评论、日志、文档或 PR——trigger-rotate-url应当只在你确认需要更换凭证时使用。
权限模型:谁可以看、谁可以写
Autopilot 层有一套独立的“查看/写入”双层鉴权(对应 server/internal/handler/autopilot.go):
- 读(list/get/runs/deliveries):任何工作区成员都可读;但
GetAutopilot对无写权限的调用者脱敏webhook_token/webhook_path/webhook_url——因为“看得到 token 就等于能触发”; - 写/执行(编辑、删除、触发、回放投递、管理触发器与 Webhook 密钥):要求是规则的创建者、工作区owner/admin,或被显式授予的协作者(collaborator)。判定函数为
autopilotWriteByOwnership(创建者/owner/admin,见 autopilot.go#L583-L588)与memberCanWriteAutopilot(叠加协作者查询,autopilot.go#L597-L606),并在 HTTP 层由requireAutopilotWrite统一强制; - 创建:任何成员都可创建(创建者即该规则的 owner,成为天然写权限持有者)。
显式写授权存于autopilot_collaborator表(迁移 128,仅成员可授,无外键,随删除事务一并清理)。协作管理走两个接口:POST /api/autopilots/{id}/collaborators(body 为{user_id})与DELETE /api/autopilots/{id}/collaborators/{userId}。它们由更窄的requireAutopilotAccessManagement门禁:只有创建者或 owner/admin 能授/收权——被授权的协作者只拥有写/执行权,不能再转授或收回别人,从而避免权限升级。GetAutopilot响应中会内嵌collaborators数组并盖上两个按调用者计算的布尔位:can_write(是否可编辑/运行/触发)与更窄的can_manage_access(是否可进入“管理访问”入口)。web/desktop 端对应 autopilot-access-manager.tsx。
需要注意的是,以上是 Autopilot 资源层授权;派发时刻还有一道独立的 Agent 调用权限门禁与之做 AND:shouldSkipDispatch会在入队前检查被指派 Agent 的就绪/可调用状态,而autopilotAdmitInvoke遵循“手动触发按当前点击者鉴权、定时/Webhook/API 自动化按规则创建者作为主体鉴权”,两者都 fail-closed 且不存在 admin 绕过(autopilot.go)。
调试:回答“为什么没跑”
文档给出了一个面向“why didn't it run”的结构化排障步骤,非常值得照单执行:
multica autopilot get <id> --output json—— 先确认规则本身的状态(active/paused)、执行模式、被指派者与触发器是否都在预期内;multica autopilot runs <id> --output json—— 查 Run 的状态与失败原因(failure reason);- 若指派给 Squad,则
multica squad get <squad-id> --output json检查 Squad——执行会落到它的 Leader 上; - 检查目标 Agent/运行时:
multica agent get <agent-id> --output json与multica runtime list --output json—— 对应AgentReadiness门禁;Agent 被归档或运行时离线时,运行会被跳过而非执行; - Webhook 场景看投递状态:
queued表示 worker 尚未完成派发(继续等待或检查 worker);failed则带着 worker 的错误信息。同一X-GitHub-Delivery/Idempotency-Key的重试会复用原投递,不会叠加新任务; create_issue模式下若 Run 记录关联了 Issue,再去检查这条 Issue 的实际进展。
这套排查顺序在源码中都能找到支撑点:步骤 2/5 的“Run 由 delivery 驱动且不重复”,由AdmitAutopilotWebhookDelivery的同步幂等准入与DispatchAutopilotForWebhookDelivery的恢复逻辑保证(server/internal/service/autopilot.go);步骤 3 的 Leader 解析在resolveAutopilotLeader;步骤 4 对应AgentReadiness与运行时列表接口。
副作用清单与安全注意事项
文档明确列举了会“改动持久状态或启动工作”的操作,把它们当作“需慎用”集合:
create/update/delete- 触发器的新增/更新/删除/轮换(trigger add/update/delete/rotate)
trigger(手动触发一次运行)- 向
/api/webhooks/autopilots/{token}发起 Webhook 调用
对应纪律重申:调试时优先使用只读命令;trigger仅在用户明确要求“立即手动运行”时使用;trigger-rotate-url仅在确实需要轮换 Webhook URL 时使用——且旧 URL 会立即失效。
延伸阅读:源码地图
想进一步深挖,文档自带一份“源码地图” references/autopilots-source-map.md,它把上述每个行为都对应到了具体文件:
- CLI 注册与参数定义:server/cmd/multica/cmd_autopilot.go,配套测试见 server/cmd/multica/cmd_autopilot_test.go;
- 派发/准入/幂等/Leader 解析/跳过判定等核心业务逻辑:server/internal/service/autopilot.go;
- 受鉴权 REST 路由与免鉴权 Webhook 入口的挂载:server/cmd/server/router.go;
- Webhook 持久化与 200 准入语义:server/internal/handler/autopilot_webhook.go;
- 数据库租约 worker 与派发限速:server/internal/handler/webhook_delivery_worker.go;
- cron 预览(纯计算端点):server/internal/handler/autopilot_cron_preview.go;
- 前端 Webhook URL 组装/脱敏工具:packages/core/autopilots/webhook.ts;
- 前端“管理访问”对话框等界面:packages/views/autopilots/components/。
小结
Multica Autopilot 的可靠性建立在三个设计选择上:把 Autopilot 定义为“派单规则”而非执行体,把 Webhook 做成“先持久准入、后租约派发”的两段式幂等流水线,以及把凭证与权限做成“默认脱敏、按需放开、写读分离”。对应到日常使用,你只需要记住:创建时选对create_issue/run_only、标题模板只用{{date}}、Webhook 凭证走--show-secrets要三思、排查“没跑”时按 get → runs → squad → agent/runtime → delivery 的顺序逐步缩小范围。至此,无论你是想用multica autopilot create起一个每日巡检的定时自动化,还是想接入 CI 事件让机器人自动建 Issue 并跟进,都有了可执行、可验证的完整路径。
【免费下载链接】multicaMake humans and AI agents work as one team — open-source and self-hostable.项目地址: https://gitcode.com/GitHub_Trending/mu/multica
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考