KB-106B

Đề bài Kestra — Lắp ráp điều phối, không tự xây engine

11 min read Revision 1
laws-newkestraworkfloworchestrationde-baidraftanti-bloatassembly-firstio-contractcouncil-review

ĐỀ BÀI KESTRA — Lắp ráp điều phối quy trình, không tự xây engine

Mã tài liệu: KESTRA-DE-BAI-001 Đường dẫn: knowledge/dev/laws-new/kestra/de-bai-kestra.md Trạng thái: DRAFT — ĐỀ BÀI CHỜ HỘI ĐỒNG XEM XÉT Phạm vi: Đặt đề bài đúng cho việc dùng Kestra làm engine điều phối quy trình. Ghi rõ nguyên tắc, dự kiến cấu trúc, các điều tuyệt đối tránh, yêu cầu lắp-ráp-tối-đa / code-tối-thiểu. KHÔNG thuộc phạm vi: Đây KHÔNG phải luật ban hành, KHÔNG tự ủy quyền thay đổi production/DB thật, KHÔNG sửa knowledge/dev/laws/. Mọi enact đi lane riêng sau khi hội đồng + Owner duyệt.

Triết lý nền: Lắp ráp tối đa. Chỉ code khi không còn lựa chọn nào khác. Đã thắng với KB → PG → Directus → Nuxt. Nay áp cho điều phối quy trình.


0. Một câu tóm tắt

Kestra là kernel chạy quy trình. UI 4 Bà Mẹ là mặt nghiệp vụ. DOT là cổng nghiệp vụ + write-gate. IO contract là ranh giới module duy nhất. Agent lắp quy trình từ catalog DOT đã duyệt, không viết tự do.


1. Mục tiêu

Tạo quy trình nhanh, khai báo được, để DOT/AI/Agent thực hiện, nối với PG — mà không tự xây engine điều phối, không tạo thêm quái vật kỹ thuật, và dễ nâng cấp về sau.


2. Bối cảnh & bài học (vì sao Kestra)

Phần thất bại nhiều tháng qua chính là phần điều phối: runner, retry, log, trigger, execution state, lịch chạy, UI debug, concurrency, error handling, history, secrets, worker. Đây đúng là vùng AI dễ ảo giác và thất bại tích hợp, và là vùng đã đẻ ra "đống rác trên VPS".

Kestra đã có sẵn toàn bộ các khối đó (flow, task, input/output, trigger, retry, concurrency, execution, subflow, UI logs/topology, REST API, plugin Postgres, MCP, AI Agent, pause/resume, plugin versioning). Tự xây lại = lặp lại thảm họa.

Kết luận: dùng đồ chuẩn đã trưởng thành, giữ UI + logic nghiệp vụ của mình, chứng minh bằng một flow thật nhỏ trước khi mở rộng.


3. Quyết định điều hành

Dùng Kestra làm workflow engine.
Giữ UI 4 Bà Mẹ làm giao diện nghiệp vụ chính.
KHÔNG dùng Kestra UI làm giao diện người dùng cuối (chỉ là cockpit kỹ thuật/debug).
KHÔNG tự xây runner/orchestrator trong giai đoạn này.
KHÔNG nhét linh hồn hệ thống vào Kestra — chỉ nhờ Kestra chạy.

4. Nguyên tắc cốt lõi (xương sống — giữ được cái này thì mọi thứ khác tự đúng)

  1. Kestra mỏng, logic ngoài Kestra. Flow Kestra chỉ gồm các task kiểu "gọi DOT này, rồi gọi DOT kia". Mọi logic nghiệp vụ sống NGOÀI Kestra, sau IO contract của mình.
  2. IO contract là ranh giới module duy nhất. Các dịch vụ (Bà Mẹ UI, DOT, PG, Directus, agent) giao tiếp qua hợp đồng IO ổn định. Ưu tiên IO vì nó đã trưởng thành và tin được.
  3. Đóng gói + cô lập. Mỗi miếng là hộp đen có mấu nối chuẩn; không miếng nào thò tay vào ruột miếng khác.
  4. Kestra phải thay được. Nhờ logic nằm ngoài + IO contract, nếu mai đổi engine chỉ phải viết lại adapter; DOT và hợp đồng giữ nguyên.

5. Dự kiến cấu trúc

UI 4 Bà Mẹ (giao diện nghiệp vụ)
  ↓  (khai báo/duyệt quy trình theo ngôn ngữ nghiệp vụ)
DSL quy trình đơn giản của Incomex
  ↓  (adapter mỏng compile)
Kestra YAML (flow declarative)
  ↓
Kestra execution engine  ← KERNEL, để nguyên bản (stock)
  ↓  (mỗi task gọi ra ngoài)
DOT / PG function / API / Agent
  ↓
Logs + outputs + status  →  trả về MOUT/MOW

Vai trò 4 Bà Mẹ (giữ nguyên, chỉ định vị lại):

MOW  = giao diện quy trình / việc / workflow catalog.
MOT  = thư viện DOT / tool / agent skill (task config).
MOIT = input form / dữ liệu đầu vào / contract (kết nối DB).
MOUT = output / báo cáo / tài liệu / kết quả.

Các Bà Mẹ đã phản chiếu đúng mô hình Kestra: MOW↔Flow, MOT↔Task, MOIT↔PostgreSQL plugin task. Đây là phần giữ lại, không phải phần bỏ.


6. Kỷ luật IO (hợp đồng giữa các dịch vụ)

Mỗi DOT khai một hợp đồng nhỏ, ổn định:

DOT = {
  input_schema,
  output_schema,
  error_schema,
  side_effects   (được ghi vào đâu — mặc định: none / chỉ qua write-gate),
  idempotent?    (chạy lại có an toàn không)
}
  • Kestra nối DOT theo output → input của hợp đồng.
  • Bà Mẹ UI lắp quy trình cũng theo đúng hợp đồng đó.
  • Cùng một runner cho kho tạm và kho chính — nếu staging gọi một kiểu, production gọi kiểu khác thì "pass staging" không dự đoán được "chạy production".
  • Chỉ những trường có checker enforce mới có giá trị; trường không ai kiểm = tài liệu để mục.

7. Hai vùng PG (chống mở lại cửa rác)

Phân biệt rõ — đây là điểm dễ hiểu sai nhất:

DB nội bộ của CHÍNH Kestra
  (định nghĩa flow, lịch sử chạy, log, hàng đợi)
  → Kestra sở hữu 100%. KHÔNG DOT. Buông tay. Dùng đồ có sẵn.

DB NGHIỆP VỤ của mình
  (hồ sơ lao động, bảng chính)
  → Kestra ĐỌC qua role nghèo quyền (SELECT vùng cho phép).
  → Kestra GHI chỉ qua DOT (function/endpoint có kiểm soát).
  → CẤM YAML do agent sinh chạy UPDATE/DELETE/DDL thẳng bảng chính.

Phân quyền tối thiểu (đủ, không cần governance phức tạp của Kestra ở giai đoạn đầu):

Role kestra_runner : SELECT bảng được phép + EXECUTE dot_*; KHÔNG INSERT/UPDATE/DELETE/DDL bảng chính.
Role dot_owner     : sở hữu function/procedure; KHÔNG đưa credential này cho Agent/Kestra.
Kestra secrets     : chỉ giữ credential kestra_runner; KHÔNG giữ superuser PG; KHÔNG giữ Directus admin token.

8. Ranh giới điều phối AI (phải biết để khỏi ảo tưởng)

Kestra điều phối tốt quy trình nội bộ không-human-in-the-loop (AI Agent task, MCP, pause/resume, poll/LoopUntil, retry, execution có trạng thái, subflow).

Nhưng: Kestra chỉ điều phối được thứ nó GỌI được bằng chương trình (API, CLI, MCP, webhook, script).

GỌI ĐƯỢC (Kestra điều phối thẳng):
  - agent qua API (vd hermes), Claude Code / codex / gemini CLI chạy headless
  - AI Agent task của Kestra gọi LLM qua API (Anthropic/OpenAI/Google…)
  - agent nói MCP (qua Kestra MCP Server)

KHÔNG GỌI ĐƯỢC (phải bắc cầu):
  - AI chat chỉ-có-giao-diện chạy bằng gói quota (claude.ai, chatgpt web)
  → dùng Pause + agent/người trung gian lấy kết quả → resume qua API.

Phân vai đúng: Kestra = nhạc trưởng tất định (trigger → bước → bước, có retry/log/state). Mỗi agent "suy nghĩ" = một task có IO contract rõ (vào gì → ra cấu trúc gì). KHÔNG biến Kestra thành "bộ não đa-agent".


9. Collection tạm / sandbox (thiết kế TỐI GIẢN — không dựng lại quái vật)

Cô lập đến từ ba cơ chế tầm thường, KHÔNG cần stamp/matrix/loài:

1. PG schema riêng    : incomex_sandbox   (tách khỏi schema production)
2. Kestra namespace   : incomex.sandbox   (flow thử sống ở đây)
3. PG role nghèo quyền : kestra_runner    (đã nêu ở §7)
  • Seed bằng fixture nhỏ trong incomex_sandbox.
  • Vòng Lego: thử trong namespace sandbox → sai thì xoá → đúng thì promote (một DOT có kiểm soát copy kết quả sang production QUA write-gate §7).
  • TTL/cleanup = một flow Kestra chạy theo lịch, fail-safe: chỉ xoá draft/expired; nghi ngờ thì không xoá.

10. Lắp ráp tối đa / Code tối thiểu (bản đồ)

RÁP (không code):
  Kestra stock; plugin PostgreSQL; trigger/retry/pause/subflow/loop;
  tích hợp LLM + MCP server sẵn; UI debug Kestra; Directus; Nuxt.

CODE chỉ ở RÌA (khi không còn lựa chọn):
  1. Vài DOT function làm write-gate cho DB nghiệp vụ.
  2. Adapter mỏng "DSL Bà Mẹ → Kestra YAML" (dùng template + catalog DOT đã duyệt).
  3. UI 4 Bà Mẹ.

Agent lắp quy trình từ catalog DOT + template, KHÔNG viết YAML tự do.

11. CÁC ĐIỀU TUYỆT ĐỐI TRÁNH (anti-quái vật)

✗ Viết plugin Kestra riêng (Java) — động vào engine, vùng AI ảo giác, đau khi nâng cấp.
✗ Tự xây runner/orchestrator/scheduler/execution-state/retry/log/UI.
✗ Tự xây tầng governance mới (stamp/matrix/loài/Luật WF làm engine).
✗ Sinh YAML tự do không template.
✗ Cho Kestra UPDATE/DELETE/DDL thẳng bảng nghiệp vụ chính.
✗ Đưa superuser PG / Directus admin token cho Kestra/Agent.
✗ Dùng Kestra UI làm sản phẩm nghiệp vụ cho người dùng cuối.
✗ Nhét logic nghiệp vụ sâu vào Kestra YAML.
✗ Viết thêm luật/đề án lớn TRƯỚC khi một flow thật chạy.
✗ Đẻ việc mới khi việc cũ chưa xong.

12. Tiêu chí "đang biến thành quái vật" (dấu hiệu phải dừng)

  • Bắt đầu thiết kế cái "thông minh hơn" ở vùng đã ghi ✗ ở §11.
  • Số tài liệu/luật tăng nhanh hơn số flow thật chạy được.
  • Một việc nhỏ vẫn mất nhiều ngày sau khi đã theo đề bài này.
  • Phải tra "bảng quy đổi tự chế" để làm một việc đáng ra tầm thường.
  • Phụ thuộc vào một hệ chỉ agent của mình hiểu, không tài liệu, không đường lùi.

→ Gặp bất kỳ dấu hiệu nào: DỪNG, không viết thêm, quay lại chứng minh một flow thật.


13. Pilot 7 ngày (tiêu chí nghiệm thu — không làm đại dự án)

1. Cài Kestra trên VPS (stock, sau nginx, ghim heap JVM, dùng schema PG riêng cho backend Kestra).
2. Tạo 1 namespace: incomex.sandbox.
3. Tạo role kestra_runner nghèo quyền.
4. Tạo 3 DOT mẫu: dot_echo_input, dot_validate_contract, dot_write_sandbox_result.
5. Tạo 1 flow Kestra gồm 2–3 task gọi DOT.
6. Bà Mẹ UI chưa cần nối full — chỉ cần một màn hình hoặc 1 file mô tả workflow.
7. Chạy workflow thật.
8. Xem logs/status/output từ Kestra.
9. CHỨNG MINH: PG nghiệp vụ không bị ghi ngoài DOT.

Nếu pilot không chạy trong vài ngày → DỪNG, mổ chỗ kẹt, KHÔNG viết thêm luật.

14. Quan hệ với laws-new và luật cũ

  • Đề bài này thuộc track DRAFT laws-new/. Không tự ủy quyền gì, không enact.
  • thay thế tham vọng tự-xây engine điều phối (hướng M-002 Luật Workflow + matrix/stamp dùng làm engine) bằng "dùng Kestra + lắp ráp". Phần tự-xây engine dừng, không phát triển tiếp.
  • Không sửa knowledge/dev/laws/. Luật enacted thắng cho tới khi có bản mới được enact theo lane riêng.
  • Tài liệu kho-tam cũ (KHO-TAM-LEGO-ROOT-001) được thay bằng thiết kế sandbox tối giản §9.

15. Câu hỏi cho hội đồng

  1. Adapter "DSL Bà Mẹ → Kestra YAML": tự định nghĩa DSL tối thiểu, hay để Bà Mẹ sinh thẳng YAML từ template? (Ưu tiên cái nào ít code hơn mà vẫn kiểm soát được.)
  2. Catalog DOT v0: tập DOT lõi nào đủ cho pilot và cho 1 quy trình nghiệp vụ thật đầu tiên?
  3. Cơ chế "promote từ sandbox sang production qua write-gate": tối thiểu cần gì để an toàn mà không thành nghi lễ?
  4. Việc nghiệp vụ nhỏ nào nên chọn làm flow thật đầu tiên?
  5. Ranh giới AI gọi-được/không-gọi-được (§8): những bước nào trong quy trình thật buộc phải bắc cầu human/agent?

Đề bài Kestra v0.1 | DRAFT chờ hội đồng | "Lắp ráp tối đa, code tối thiểu; Kestra chỉ để chạy, linh hồn nằm ở UI + DOT + IO contract; chứng minh một flow thật trước khi mở rộng."