Lewati ke konten

Pipeline dan otomatisasi

Pipeline adalah separuh deterministik dari Control Center. Agen memutuskan apa yang harus dikatakan; pipeline memutuskan apa yang terjadi selanjutnya, dengan cara yang sama setiap kali. Pipeline adalah DAG berisi langkah-langkah yang dapat mengirim agen, menjalankan skrip, bercabang berdasarkan suatu kondisi, menyebar ke seluruh koleksi, dan meneruskan data antarlangkah — dipersistenkan, dapat dilanjutkan, dan dihitung biayanya.

Pipeline ada karena tiga alasan: untuk merangkai proses berulang yang terdiri dari banyak langkah, untuk mengoordinasikan beberapa agen secara berurutan atau paralel, dan untuk bereaksi terhadap sesuatu yang terjadi tanpa ada manusia yang hadir.

Sebuah templat (PipelineDefinition) adalah DAG deklaratif: langkah-langkah, input yang dideklarasikan, dan konfigurasi. Ia tersimpan di basis data milik ruang kerja itu sendiri. Sebuah eksekusi (PipelineRun) adalah satu kali menjalankan templat tersebut, yang membawa wadah state yang dapat diubah, total biaya dan token, serta status per langkah.

Satu templat memiliki banyak eksekusi. Eksekusi bertahan melewati proses mulai ulang: mesin memuat ulang eksekusi yang sedang berjalan dan melanjutkannya dari tempat ia berhenti.

Dua jaring pengaman sengaja menggagalkan seluruh eksekusi alih-alih melanjutkan sebuah langkah, karena melanjutkan akan lebih buruk daripada berhenti:

  • Langkah yang sudah suspended lebih lama daripada suspendedStepTimeout (bawaannya 24 jam) sedang menunggu tiket yang tidak akan pernah mencapai status terminal, sehingga langkah itu digagalkan untuk membebaskan eksekusi. Pengecualiannya adalah extras['awaitApproval'] == true — itulah gerbang persetujuan sebagian orchestration.awaitApproval. human.gate tidak dikecualikan.
  • Langkah yang dideklarasikan extras: {'idempotent': false} dan sedang berjalan ketika server berhenti mungkin sudah menerapkan efek sampingnya (squash merge, pembersihan worktree). Menjalankannya ulang dapat menerapkannya dua kali, jadi mesin menggagalkannya dan menyerahkan percobaan ulang kepada Anda.

Sebuah langkah memiliki dua bagian yang ortogonal, dan memisahkan keduanya itulah yang membuat mesin tetap kecil.

Jenis (StepKind) memberi tahu mesin kapan sebuah langkah menjadi dapat dijalankan relatif terhadap langkah-langkah di hulunya — hanya itu yang perlu diketahui penjadwal. Ada enam jenis: trigger, listen, join, router, forEach, dan terminal. Badan (bodyKey) adalah pekerjaan yang dilakukan langkah setelah dijadwalkan. Badan adalah closure yang didaftarkan berdasarkan kunci saat server dimulai, sehingga banyak langkah dari jenis berbeda dapat berbagi satu badan: misalnya beberapa langkah listen yang semuanya menjalankan conversation.promptAgent.

Karena semantik penjadwalan berada di enam jenis, katalog perilaku dapat bertambah tanpa perlu menyentuh penjadwal. Lihat Jenis langkah pipeline untuk katalog lengkap jenis, badan, dan konfigurasinya.

Ada dua konsekuensi dari desain registri yang perlu Anda ketahui sebelum menulis templat dengan tangan:

  • Langkah yang bodyKey-nya tidak terdaftar menghasilkan peringatan saat templat dimuat lalu tidak pernah dijalankan. Tidak ada yang gagal secara mencolok saat runtime. Langkah trigger dan terminal dikecualikan dari peringatan itu: pemicu tetap menjalankan badan pipeline.trigger yang tidak melakukan apa-apa agar pemantau di hilir terpicu; langkah terminal tidak punya badan dan merupakan sentin penjadwal.
  • Kunci di peta extras sebuah node harus cocok persis dan jika tidak akan diabaikan secara diam-diam. Hanya extras['spaceId'] yang menempatkan pekerjaan langkah agen ke dalam percakapan yang sudah ada. extras['mode'] (chat / plan / review) dibaca pada node yang membuka ruang tersebut (messaging.createSpace, bawaannya review) — langkah agen yang datang belakangan tidak dapat mengubah izin percakapan yang telah diikutinya. Penulisan lain apa pun untuk salah satu kunci itu akan diabaikan.

Langkah-langkah tidak memanggil satu sama lain. Mereka membaca dari dan menulis ke wadah state bersama pada eksekusi: sebuah langkah membaca input-nya berdasarkan kunci, menjalankan tugasnya, lalu menulis kembali output-nya di bawah outputKey miliknya sendiri. StateReducer menggabungkan tulisan yang bersamaan ketika cabang paralel menargetkan kunci yang sama, dan TemplateRenderer menggantikan placeholder {{...}} di prompt dan skrip dari state yang sama itu.

Inilah sebabnya pipeline dapat diperiksa setelah kejadian — wadah state adalah keseluruhan kisah eksekusi tersebut.

Baris PipelineTrigger mendeklarasikan kapan sebuah templat mulai secara otomatis. Ada empat jenis:

Pemicu Memulai eksekusi ketika
Peristiwa domain Peristiwa domain yang cocok terpicu — misalnya PullRequestPublished atau TicketAssigned
Jadwal Ekspresi cron/interval jatuh tempo
Manual Anda menjalankannya dengan tangan; keberadaan pemicu manual yang aktif itulah yang menempatkan templat di pemilih eksekusi
Webhook Sistem eksternal melakukan POST ke /webhooks/<token> pada port HTTP server

Pemicu peristiwa adalah himpunan tertutup: 16 jenis peristiwa dikenali dispatcher dan ditawarkan di pemilih (EventPayloadMapper.knownEventTypes), dan tidak ada hal lain yang dapat memulai pipeline. Empat belas dari 16 jenis itu memiliki pemetaan payload. Dua — TicketCreated dan TicketStatusChanged — tidak memilikinya, sehingga pemicu pada salah satunya akan tetap aktif dan tidak pernah terpicu. Lihat Peristiwa domain untuk daftar lengkapnya.

Token webhook sekaligus berfungsi sebagai secret HMAC: header X-Hub-Signature-256: sha256=<hex> dari permintaan diverifikasi terhadap HMAC-SHA256(body, token), pengiriman yang tidak terverifikasi ditolak dan tidak dapat diputar ulang, dan duplikat dibuang oleh dedupe_key. Karena baris pemicu unik pada (ruang kerja, jenis peristiwa, templat), sebuah templat hanya dapat memiliki paling banyak satu pemicu webhook dan satu pemicu per jenis peristiwa domain.

Templat bawaan menyemai pemicu manual dan peristiwanya dalam keadaan aktif; pemicu terjadwal disemai dalam keadaan nonaktif, jadi sapuan berulang selalu harus Anda aktifkan sendiri.

Tiga belas templat pipeline disemai ke setiap ruang kerja dan delapan di antaranya dikirim dalam keadaan nonaktif: external_pr_welcome, cross_review, ticket_to_pr, pr_triage, pre_merge_gate, release_notes, dep_audit, dan pr_digest.

Bagian inilah yang mengejutkan orang. Pemicunya disemai dalam keadaan aktif, tetapi PipelineEngine.start menolak templat yang nonaktif dan mengembalikan null — sehingga pemicu terpicu tanpa hasil dan tidak ada eksekusi yang muncul. Templatlah yang menjadi sakelarnya, bukan pemicunya. Aktifkan templat di Pengaturan → Ruang kerja → Template pipeline sebelum mengharapkan kedelapan templat itu melakukan sesuatu.

Penyemaian ulang sebuah ruang kerja mempertahankan apa pun yang Anda pilih: setelan isEnabled Anda dan baris pemicu yang sudah ada tetap bertahan. Ada tepat satu pengecualian yang disengaja — jadwal harian pr_merged_cleanup dipaksa aktif bahkan jika sebelumnya nonaktif, karena jadwal itu adalah pengumpulan sampah. Tanpa itu, baris worktree, partisi graf kode, dan salinan copy-on-write akan menumpuk, dan hal itu memang tidak pernah dimaksudkan sebagai pilihan pengguna.

Satu lagi asimetri yang perlu diketahui: bawaan pr_review sengaja tidak lagi memposting ke GitHub. Memublikasikan tinjauan adalah langkah terpisah yang digrebangi pengguna, dan pipeline yang memposting langsung akan memublikasikan ganda. cross_review, ticket_to_pr, dan pr_triage masih memposting komentar langsung ke GitHub saat dijalankan.

PipelineEngine membuat catatan eksekusi, menjadwalkan node awal yang cocok dengan jenis peristiwa eksekusi tersebut, lalu menelusuri DAG: saat langkah selesai, ia mengevaluasi pemicu di hilir, menyelesaikan cabang router, menunggu pada join, menerapkan kebijakan percobaan ulang dan perilaku lanjut meski gagal, serta mempersistenkan state, biaya, dan error pada setiap langkah. Sisi yang keluar dari titik awal lain diabaikan untuk eksekusi itu, sehingga pemicu jadwal tidak menunggu kondisi yang hanya berlaku untuk pemicu manual.

Apa yang tidak diketahuinya adalah segala hal tentang tiket, pengiriman, GitHub, atau perpesanan. Satu-satunya titik ekstensinya adalah PipelineBodyRegistry. Badan yang terdaftar di sana itulah yang memegang AgentDispatchPort, MessagingPort, dan port sejenisnya — itulah sebabnya menambahkan kemampuan ke pipeline tidak pernah berarti mengubah penjadwal.

Langkah yang gagal mencatat pesan error-nya dan berpindah ke failed. Jika langkah mendeklarasikan continueOnFail, langkah-langkah di hilir berlanjut dan error-nya disimpan di state eksekusi; jika tidak, eksekusi gagal. Langkah dengan StepRetryPolicy akan dicoba ulang terlebih dahulu, dengan backoff linear atau eksponensial, sebelum salah satu dari dua aturan itu diterapkan.