Multica Docs

오토파일럿

에이전트가 cron 스케줄, 인바운드 webhook으로 작업을 시작하거나, UI나 CLI로 한 번 수동 트리거하게 합니다.

오토파일럿은 에이전트스케줄에 따라 자동으로 작업을 시작하게 합니다 — cron 표현식과 타임존을 설정하면, 여러분이 아무것도 트리거하지 않아도 Multica가 알아서 task를 디스패치합니다. 정기 점검, 반복 보고서, 야간 정리 작업 등 "상시 지시(standing order)" 형태의 작업에 잘 맞습니다. 나머지 세 가지 트리거 경로(할당, @-멘션, 채팅 — 모두 여러분이 직접 시작하는 방식)와 비교했을 때, 오토파일럿의 핵심 차이는 시간 기반이라는 점입니다.

오토파일럿 구성하기

워크스페이스의 오토파일럿 페이지에서 새 오토파일럿을 만듭니다. 다음 항목을 설정합니다.

  • 이름(Name) — 표시 이름
  • 에이전트(Agent) — 실행을 디스패치할 대상
  • 우선순위(Priority) — 생성되는 task에 상속됩니다(이슈 우선순위와 동일한 의미)
  • 설명 / 프롬프트(Description / prompt) — 매 실행마다 에이전트가 받는 작업 설명
  • 실행 모드(Execution mode) — 아래 참고
  • 트리거(Triggers)schedule(cron + 타임존) 또는 webhook 중 최소 하나

실행 모드 선택하기

오토파일럿에는 두 가지 실행 모드가 있습니다. "이슈 생성" 모드부터 시작하세요.

  • 이슈 생성 모드(Create issue mode)(create_issue) — 기본값이며 권장됩니다. 각 트리거는 먼저 워크스페이스에 이슈를 생성한 다음(제목에는 현재 단일 플레이스홀더 {{date}} 하나만 지원되며, 이는 YYYY-MM-DD 형식의 UTC 날짜로 보간됩니다. 그 외의 {{...}} 토큰은 생성 시점에 거부되므로, 오타가 이슈 제목에 리터럴 문자열로 조용히 들어가는 일을 막습니다), 일반 할당 흐름을 통해 그 이슈를 에이전트에게 할당합니다. 모든 작업은 수동으로 할당한 이슈와 동일한 히스토리, 댓글, 상태를 가진 채 이슈 보드에 올라갑니다.
  • 실행 전용 모드(Run-only mode)(run_only) — 이슈 생성을 건너뛰고 task를 곧바로 대기열에 넣습니다. 이 실행은 보드에 표시되지 않으며 — 오토파일럿의 실행 히스토리에서만 확인할 수 있습니다.

스케줄에 따라 실행하기

모든 오토파일럿에는 최소 하나의 schedule 트리거가 필요합니다. Cron은 표준 5필드 형식(분 시 일 월 요일)을 사용하며, 최소 단위는 1분입니다(초 단위는 없음). 타임존은 IANA 형식(예: Asia/Shanghai)이며, cron 표현식이 어느 타임존으로 해석될지를 결정합니다.

몇 가지 예시입니다.

  • 0 9 * * 1-5, Asia/Shanghai — 평일 베이징 시간 오전 9시
  • */30 * * * *, UTC — 30분마다
  • 0 3 * * *, UTC — 매일 UTC 오전 3시

Multica 서버는 30초마다 만료된 트리거를 스캔합니다 — 실제 발화 시각은 최대 30초까지 지연될 수 있으며, 초 단위로 정확하지는 않습니다. 발화 시각 즈음에 서버가 재시작되면, 시작 시 놓친 트리거를 따라잡습니다(아무것도 유실되지 않지만 즉시 발화됩니다).

수동으로 한 번 트리거하기

오토파일럿을 디버깅하는 동안 cron을 기다리지 않으려면 수동으로 트리거하세요.

  • UI: 오토파일럿 상세 페이지에서 "Run now" 클릭
  • CLI:
multica autopilot trigger <autopilot-id>

수동 트리거는 schedule 트리거와 완전히 동일한 실행 흐름을 거치며 — 실행 레코드의 source 필드만 manual로 표시됩니다.

Webhook으로 트리거하기

오토파일럿은 인바운드 HTTP webhook으로도 발화할 수 있습니다. 오토파일럿 상세 페이지에서 Webhook 트리거를 추가하면, Multica가 다음과 같은 형태의 고유 URL을 생성합니다.

https://<your-multica-host>/api/webhooks/autopilots/awt_…

그 URL로 아무 JSON이나 POST하세요 — Multica는 source = webhook로 실행을 기록하고, 본문을 실행의 trigger_payload로 저장한 다음, schedule 트리거와 정확히 동일한 방식으로 에이전트를 디스패치합니다.

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

이슈 생성 모드에서는 인바운드 payload가 새 이슈의 설명에 덧붙여져 에이전트가 인라인으로 읽을 수 있습니다. 실행 전용 모드에서는 payload가 데몬이 에이전트에게 넘기는 실행 컨텍스트의 일부가 됩니다.

Payload 형태

직접 만든 봉투(envelope)를 보낼 수 있습니다.

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

…또는 임의의 JSON 객체/배열을 보낼 수도 있습니다. Multica는 이를 내부 봉투로 정규화합니다.

{
  "event": "<inferred>",
  "eventPayload": <your body>,
  "request": { "receivedAt": "<rfc3339>", "contentType": "application/json" }
}

event 필드를 제공하지 않으면, Multica는 일반적인 헤더와 본문 필드로부터 이를 추론합니다(X-GitHub-Event + 본문 action, X-Gitlab-Event, X-Event-Type, 본문의 event/type/action). 어느 것도 일치하지 않으면 이벤트는 webhook.received가 됩니다.

GitHub 같은 소스를 구성할 때는 content type을 application/json으로 설정하세요 — 폼 인코딩된 webhook payload는 허용되지 않습니다.

이벤트 필터

새 webhook 트리거는 인바운드 POST마다 발화합니다. 단일 용도 URL에는 괜찮지만, 여러 이벤트 타입을 팬아웃하는 소스(GitHub가 대표적입니다 — 단일 저장소 webhook 하나가 push, pull_request, workflow_run, check_suite 등을 전달할 수 있습니다)에는 시끄럽습니다. webhook 트리거의 이벤트 필터(Event filters) 섹션을 사용하면 실제로 실행을 디스패치할 이벤트를 제한할 수 있으며, 그 외의 모든 것은 status = ignored, reason = event_filtered로 전달 히스토리에 기록되고 실행이나 이슈는 생성되지 않습니다.

각 행은 하나의 규칙입니다. **이벤트 이름(event name)**과 선택적으로 쉼표로 구분한 action 목록으로 구성됩니다. Multica는 어느 한 행이라도 일치하면 webhook을 허용합니다. 섹션을 비워 두면 모든 것을 수락합니다(필터링 이전 동작).

예시:

이벤트 이름Actions일치 대상
workflow_runcompleted, failedaction: completed 또는 action: failedworkflow_run 이벤트만
workflow_run(비어 있음)action에 상관없이 모든 workflow_run 이벤트
push(비어 있음)모든 push 이벤트

이벤트 이름과 action의 출처

Multica는 다음 순서로 인바운드 요청에서 event 이름과 action을 도출하며 — 첫 번째로 일치하는 것이 우선합니다.

1. 본문 봉투(Body envelope). 본문이 문자열 event 필드를 가진 JSON 객체이면, 그 값이 곧 이벤트 이름입니다. 선택적인 eventPayload 객체는 자신의 action / state / conclusion / status 필드에서 action 후보를 제공합니다.

curl -X POST "$MULTICA_WEBHOOK_URL" \
  -H 'Content-Type: application/json' \
  -d '{"event":"trigger","eventPayload":{"action":"true"}}'
# inferred: event = trigger, action candidate = true

2. 헤더(Headers). 본문 봉투가 없을 때, Multica는 다음의 잘 알려진 제공자 헤더를 읽습니다.

  • X-GitHub-Event: <event> — (있는 경우) 최상위 본문 action 필드와 결합해 github.<event>.<action>을 형성합니다.
  • X-Gitlab-Event: <event>gitlab.<event>가 됩니다.
  • X-Event-Type: <event> — 그대로 통과합니다.
# GitHub-style: header gives the event name, body gives the action.
curl -X POST "$MULTICA_WEBHOOK_URL" \
  -H 'X-GitHub-Event: workflow_run' \
  -H 'Content-Type: application/json' \
  -d '{"action":"completed"}'
# inferred: event = github.workflow_run.completed
#        → matches a filter row of workflow_run / completed

# Generic event-type header — no body fields needed.
curl -X POST "$MULTICA_WEBHOOK_URL" \
  -H 'X-Event-Type: trigger.true' \
  -H 'Content-Type: application/json' \
  -d '{}'
# inferred: event = trigger.true → matches trigger / true

3. 본문 폴백(Body fallback). 본문 봉투도 알려진 헤더도 없으면, Multica는 다음 순서로 최상위 본문 문자열 필드로 폴백합니다: eventtypeaction.

curl -X POST "$MULTICA_WEBHOOK_URL" \
  -H 'Content-Type: application/json' \
  -d '{"type":"trigger","action":"true"}'
# inferred: event = trigger (from `type`), action candidate = true

4. 기본값(Default). 위 어느 것도 일치하지 않으면, 이벤트는 webhook.received이고 action 후보는 없습니다.

action 후보, 전체 목록. 이벤트가 결정되면, Multica는 아래의 모든 값을 가능한 action 일치 대상으로 고려합니다.

  • 이벤트가 provider.event.<action> 형태일 때의 이벤트 이름 접미사(예: github.workflow_run.completedcompleted).
  • 본문 필드 action, state, conclusion, statusJSON 문자열일 때만 해당됩니다. 불리언({"action": true})이나 숫자는 자격이 없으므로, event=trigger, action=true를 기대하는 필터는 {"trigger": true} 본문과 절대 일치하지 않습니다. true는 문자열이 아니라 bool이기 때문입니다.

흔한 함정. Event name: trigger / Actions: true 같은 필터 행은 "본문에 trigger: true가 있으면 발화하라"는 뜻이 아닙니다 — 이벤트 필터는 임의의 본문 필드가 아니라 추론된 이벤트와 action에 일치시킵니다. 이를 적중시키려면 X-Event-Typetrigger.true를 보내거나(또는 위에 표시된 본문 봉투를 사용하세요). 저장된 필터 행에서 주변 공백(" workflow_run ")은 그대로 저장되어 절대 일치하지 않으므로 — 저장하기 전에 trim하세요.

빠른 테스트

필터를 구성한 후에는 curl로 두 분기를 모두 확인할 수 있습니다.

# Allowed — header drives event=workflow_run, body drives 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", ...}

# Filtered — same event, action not in allowlist
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 자체가 자격 증명입니다. 그것을 가진 사람은 누구나 오토파일럿을 발화할 수 있습니다. 토큰처럼 다루세요.

  • 공개된 이슈 스레드, 스크린샷, 채팅 히스토리에 붙여넣지 마세요.
  • 유출되면 교체하세요 — 트리거 행에서 "Rotate URL"을 클릭하거나 multica autopilot trigger-rotate-url <autopilot-id> <trigger-id>를 실행하세요. 기존 URL은 즉시 작동을 멈춥니다.
  • 강력한 소스 인증이 필요한 소스의 경우, 트리거별 HMAC 서명 검증을 기다리세요. 이 v1 URL은 bearer 방식만 지원합니다.
  • 현재로서는 오토파일럿을 볼 수 있는 워크스페이스 멤버라면 그 webhook URL을 읽을 수 있습니다 — 역할별로 더 엄격한 secret 가시성은 후속 작업입니다.

상태 코드 의미

Multica는 정상적인 no-op 결과에 대해 status 필드와 함께 200 OK를 반환하므로, 제공자의 webhook 재시도 메커니즘이 URL을 계속 두드리지 않습니다.

  • {"status":"accepted","run_id":"…","autopilot_id":"…","trigger_id":"…"} — 실행이 디스패치되었습니다.
  • {"status":"skipped","run_id":"…","reason":"agent runtime is offline at dispatch time"} — 수신 대상의 런타임이 오프라인이며, skipped 실행으로 기록됩니다.
  • {"status":"ignored","reason":"trigger_disabled"} — 트리거가 비활성화되어 있습니다.
  • {"status":"ignored","reason":"autopilot_paused"} — 오토파일럿이 일시 정지되어 있습니다.
  • {"status":"ignored","reason":"autopilot_archived"} — 오토파일럿이 보관되어 있습니다.

2xx가 아닌 응답은 실제 실패를 다룹니다.

  • 400 — 유효하지 않은 JSON, 스칼라 본문, 빈 본문.
  • 404 — 알 수 없는 토큰({"error":"webhook not found"}).
  • 413 — payload가 256 KiB를 초과했습니다.
  • 429 — 토큰별 속도 제한 초과(기본값 60 req/min).

자체 호스팅: 공개 URL 구성하기

서버에 MULTICA_PUBLIC_URL이 설정되어 있으면(예: https://multica.example.com), 트리거 응답에 절대 경로 webhook_url이 포함되고 UI에는 복사 가능한 URL이 바로 표시됩니다. 설정하지 않으면 UI는 클라이언트의 API origin으로부터 URL을 구성합니다 — 데스크톱과 동일 출처 웹에는 괜찮지만, 커스텀 자체 호스팅 리버스 프록시에는 적합하지 않습니다. Multica는 잘못 구성된 리버스 프록시가 공격자가 제어하는 호스트를 가리키는 webhook URL을 서버가 발행하도록 속이지 못하게, Host / X-Forwarded-Host 헤더로부터 공개 호스트를 도출하지 않도록 의도적으로 설계되었습니다.

실행 히스토리 보기

모든 트리거는 **실행 레코드(run record)**를 생성하며, 오토파일럿 상세 페이지의 "History" 탭에서 볼 수 있습니다.

  • 트리거 소스(schedule / manual / webhook)
  • 시작 시각, 완료 시각
  • 상태(issue_created / running / completed / failed / skipped)
  • 연결된 이슈(이슈 생성 모드) 또는 task(실행 전용 모드)
  • 실패 사유(실패하거나 건너뛴 경우)

오토파일럿이 실패하면 어떻게 되나요

오토파일럿 실패는 자동으로 재시도되지 않으며 인박스 알림도 보내지 않습니다. 실패는 실행 히스토리에 failed 항목을 남길 뿐 — 할당이나 @-멘션처럼 시스템 수준에서 다시 대기열에 넣는 일도 없고, 누구에게도 알림이 가지 않습니다. 오토파일럿이 주기적이라면 다음 cron 발화가 새 실행을 트리거하지만, 실패한 작업이 자동으로 다시 실행되지는 않습니다.

오토파일럿이 중요하다면 직접 모니터링을 설계하세요 — 예를 들어 에이전트가 성공 시 댓글을 남기게 하고, 댓글이 누락된 것을 알아채 실패를 잡아내는 식입니다.

자동 재시도가 없는 이유: 오토파일럿은 이미 주기적이므로, 시스템 수준의 재시도를 추가하면 다음 예약된 실행 위에 겹쳐 중복 실행을 만들어 냅니다. 스케줄링을 전적으로 cron에 맡기면 깔끔하게 유지됩니다.

아직 제공되지 않는 기능

API 종류의 트리거는 아직 연결되어 있지 않습니다. 트리거 스키마는 api 종류를 예약해 두었지만, 그것을 발화하는 인그레스 라우트가 없습니다. UI는 기존 행에 Deprecated 배지를 표시하며 복사/교체 기능을 제공하지 않습니다. 트리거별 HMAC 서명 검증, IP 허용 목록, 제공자별 이벤트 프리셋은 후속 작업으로 추적되고 있으며, v1 URL은 bearer 방식만 지원합니다.

다음