Quy trình làm việc với agent: tool, flow, đối tượng và chỗ người phải đứng
Tổng hợp buổi trao đổi về cách tối ưu công cụ test / debug qua CLI từ hiện trạng MiniGate để tái dùng cho dự án sau, và cách tổ chức quy trình để chủ dự án bớt phải quản lý trực tiếp.
00Câu hỏi gốc
Đang có nhiều CLI cho agent (mgctl, cpctl, wsctl, deskctl, minigate, rt) và nhiều flow test khai báo. Muốn tối ưu lại để tái dùng cho dự án sau. Chuỗi câu hỏi đã đi qua:
- Cách đưa tool cho AI qua CLI và định nghĩa flow có phải hướng đi hợp lý so với cộng đồng không?
- Ngoài tool, các đối tượng khác (env, role, flow, step, fixture, report…) tổ chức thế nào?
- Các đối tượng đó định nghĩa ở đâu, có cần structure không?
- Có nên sửa tool ngay trong lúc test / debug sản phẩm không?
- Người nên tham gia ở bước nào?
- Quy trình của cả dự án nên chia pha ra sao: nháp trước rồi lặp hồi quy đến khi khách ổn?
01Hiện trạng tóm tắt
Có 6 CLI và 4 “engine flow” khác nhau cùng một ý tưởng (flow JSON gồm steps, params, expect, shot, dùng testenv) nhưng mỗi cái tự viết schema và runner riêng. Code dùng chung bị chép tay giữa repo: find-common.js giống hệt ở 3 repo, schema.js giống ở desktop và mobile, util.js mỗi repo một bản, testenv.js và mgctl.js lệch dần.
| deskctl flow | minigate flow | wsctl e2e | mgctl flow | |
|---|---|---|---|---|
| Đối tượng | Electron (CDP) | Android / iOS (adb, xcrun) | Web (Chrome) | API + gọi CLI khác |
| Bước phổ biến | expect 704 · shot 331 · wait 299 · use 180 · type 146 | run 583 (chuỗi lệnh con) · expect 534 | eval 473 (JS thô) · shot 367 · click 362 · sleep 325 | args 75 · as 66 · expect 63 · save 16 |
| Chỉ phần tử | #chat.row.c3 hoặc css=… | chat-thread.pinned-bar (có *) | CSS trong eval / click | không (API) |
| Bước tái dùng | use + steps/*.json | không | use + steps/*.json | flow con + cli |
| Vai trò | testEnv | roles A/B + {{testEnv.user}} | role (1 lần) | as + needs theo testenv |
| Điều kiện / lặp | không | không | không | when |
| Chờ | timeoutMs 281 · sleep 27 | timeoutMs 459 | sleep 325 · wait 228 | không cần |
- wsctl e2e đã rò rỉ ra JS. 473 bước
evalvà 325sleepnghĩa là DSL không đủ diễn đạt, người viết nhảy xuống code và chờ cứng. - minigate flow thực chất là shell script dạng JSON. Tốt cho agent (mỗi bước chạy tay được) nhưng không có lớp step nên 281 lần
input tapdính thẳng vào testid. - mgctl flow là lớp khác hẳn. Không chạm UI, chỉ điều phối vai trò, params, flow con, gọi CLI khác, lưu kết quả. Đây là lớp đáng giữ nhất.
Phần làm tốt và nên giữ: config + .local.json, testenv theo vai trò, mgctl api đăng nhập như app, bộ data + .runs báo cáo, doctor, quy ước testid module.screen.element dùng chung desktop và mobile.
02Hướng đi có hợp lý so với cộng đồng?
Có. “CLI cho AI + flow khai báo + testenv theo vai trò + bằng chứng mỗi lần chạy” là dòng chính 2025–2026.
- CLI thay vì tool-call / MCP. Anthropic (bài “code execution with MCP”) và OpenAI Codex đều chỉ ra: agent gọi lệnh rồi tự đọc output tiết kiệm token và dễ ghép hơn nạp hàng chục tool definition. Vercel ra
agent-browserdạng CLI vì lý do này. - Test như dữ liệu. Maestro (mobile) dùng YAML flow: tapOn, assertVisible, runFlow con. Cấu trúc flow JSON của MiniGate gần như song song.
- Progressive disclosure cho agent. help ngắn,
_doctrong config, skill, doctor: đã có. - Evidence và reproducibility. Bộ data +
.runskhớp xu hướng.
Chỗ cộng đồng làm khác
- Tránh DSL tự chế khi đã có sẵn. Maestro, Playwright, WebdriverIO có format ổn định, agent biết sẵn cú pháp. DSL riêng nên mỏng: giữ lớp nghiệp vụ, uỷ lớp tương tác cho engine chuẩn. Bốn runner tự viết là đi hơi xa.
- Một lệnh một việc, in JSON.
--json, stdout là kết quả, stderr là log, exit code có nghĩa, không hỏi khi không có TTY. mgctl đã theo, rt (menu + xác nhận) thì chưa. - Test chính tool. Ai test runner? Mobile có 2 file test, còn lại không.
- Flaky là bài toán chính. Auto-wait, retry, trace là nơi Maestro / Playwright đầu tư nhiều nhất và DSL riêng phải tự gánh.
03Flow riêng so với Maestro và Playwright
| Nhu cầu | Hiện tại (3 runner UI) | Maestro (mobile, YAML) | Playwright (web / Electron) | Nhận xét |
|---|---|---|---|---|
| Tìm phần tử | testid, mỗi runner một cú pháp | id, text, regex, index | getByTestId / Role / Text | engine ngoài có 4–5 cách, ta chỉ có testid |
| Auto-wait | không, dùng timeoutMs / sleep | có | có | chỗ ta tốn công nhất, họ cho sẵn |
| Assert | expect + text / contains / visible / gone / attr / count | assertVisible, assertNotVisible, assertTrue | 20+ matcher | đủ dùng nhưng tự bảo trì |
| Flow con | use + steps | runFlow + env | hàm / fixture | ngang nhau |
| Vai trò, testenv | mạnh | chỉ env | storageState theo tài khoản | ta mạnh hơn, đây là lớp nghiệp vụ |
| Điều kiện, lặp | không | runFlow when, repeat | JS đầy đủ | thiếu, nên có 4 flow na ná (chat-hai-nguoi-0/1/2/3) |
| Ảnh, trace, report | shot + .runs + Telegram | screenshot, record, JUnit / HTML | trace viewer, HTML report, video | trace viewer là thứ ta không có |
| Hai máy / hai vai | mobile có roles A/B | không | nhiều context trong 1 test | Playwright hơn, Maestro kém ta |
| Agent biết sẵn cú pháp? | không, đọc help mỗi phiên | có | có | chi phí ẩn của DSL riêng |
| Electron | CDP tự viết (page.js 363 dòng) | không | hỗ trợ chính thức | thay hẳn được lớp CDP |
| Android / iOS | adb / xcrun tự viết | hỗ trợ chính thức | không | thay được lớp input / expect |
| Giấy phép | của ta | Apache-2.0 | Apache-2.0 | dùng thương mại được |
Giữ gì, uỷ gì
Giữ (lớp nghiệp vụ, không ai có): testenv theo vai trò, as / needs, tài khoản bảo vệ, tiền tố [E2E] và dọn dữ liệu; mgctl flow làm bộ điều phối; lệnh đơn cho agent (api, shot, inspect, doctor); quy ước testid.
Uỷ cho engine ngoài (lớp tương tác): web và Electron → Playwright, bước UI dịch 1:1 hoặc bước playwright: <spec> để mgctl flow gọi rồi thu report. Mobile → Maestro cho input / expect, giữ app, shot, net, biometric, notif, log của minigate; hai máy thì mgctl flow điều phối 2 Maestro.
Cái giá: thêm 2 dependency lớn (Playwright kéo browser, Maestro cần Java), agent học 2 cú pháp, dịch lại flow hiện có (desktop ~40, mobile 28, ws vài chục). Đổi lại bỏ được 3 runner tự viết, có auto-wait và trace viewer, dự án sau chỉ mang theo lớp nghiệp vụ mỏng.
Phương án giữa: giữ DSL nhưng chuẩn hoá một schema step chung cho 3 runner UI, thêm auto-wait vào expect để bỏ sleep, đưa when / repeat xuống. Rẻ hơn nhưng vẫn tự nuôi engine.
Khuyến nghị: chọn uỷ, bắt đầu từ wsctl e2e vì rò rỉ nặng nhất và Playwright thay được trọn vẹn.
04Các đối tượng ngoài tool
Tool là động từ, các đối tượng là danh từ, và danh từ quyết định tái sử dụng được hay không. Xếp theo hai trục: thuộc về ai (mọi dự án, một dự án, một repo, một máy, một lần chạy) và đổi nhanh hay chậm.
| Đối tượng | Nghĩa | Thuộc về | Đổi | Nằm ở | Commit |
|---|---|---|---|---|---|
| Quy ước | testid, exit code, stdout/stderr, tên vai trò, [E2E] | mọi dự án | rất chậm | kit, 1 trang | có |
| Config lệnh | tham số không bí mật của tool | repo | chậm | config/<lệnh>.json + _doc | có |
| Override máy | ghi đè riêng máy | máy | tuỳ | *.local.json | không |
| Môi trường | server + workspace + URL + vai trò dùng biến nào | dự án | chậm | common config/test-env.json | có |
| Bí mật | mật khẩu, token theo môi trường | dự án | hiếm | secrets/test-env/<env>.env | repo private |
| Vai trò | admin, chat1, works-staff2, mobile… | dự án | chậm | một bảng duy nhất, cùng file env | có |
| Hồ sơ | Chrome profile, emulator, máy thật | máy | trung bình | config/profiles + local | một phần |
| Fixture | dữ liệu mẫu bất biến | repo | chậm | fixtures/ cạnh flow | có |
| Bước | thao tác UI đặt tên: chat.send, auth.login | repo | trung bình | steps/<module>.json | có |
| Flow | kịch bản nghiệp vụ | repo (UI) / common (liên repo) | nhanh | flows/<module>/<tên>.json | có |
| Bộ data | tham số một đợt chạy | tác vụ | mỗi đợt | work/<ngày>/<tác vụ>/ | không |
| Bằng chứng | ảnh, log, report.json | lần chạy | mỗi lần | <bộ data>/.runs/<id>/ | không |
| Tri thức agent | skill, help, README thư mục | repo | chậm | .claude/skills, help/ | có |
| Hợp đồng API | OpenAPI | repo backend | trung bình | openapi/ | có |
Nguyên tắc đặt chỗ: càng chung và càng chậm đổi thì càng lên cao (kit → common → repo); càng riêng và càng nhanh đổi thì càng ra ngoài repo (local → work → .runs).
Quan hệ giữa các đối tượng
flow ──needs──▶ role ──resolve qua──▶ env ──đọc──▶ secret │ │ ├──use──▶ step ──dùng──▶ testid (quy ước) ├──with──▶ fixture ├──chạy trên──▶ profile (máy) └──nhận──▶ dataset ──sinh ra──▶ run / evidence
- Flow không chứa URL, tài khoản, đường dẫn máy. Chỉ tên vai trò và tên fixture.
- Step là nơi duy nhất biết testid. UI đổi testid thì sửa một chỗ.
- Dataset tách khỏi flow để một flow chạy nhiều đợt, mỗi đợt có .runs riêng.
Quyết định tổ chức cụ thể
- Một bảng vai trò cho toàn hệ thống; mobile cần 2 máy thì ánh xạ A → chat1, B → chat2.
- Đặt tên flow theo
module/hành-vi, không theo thiết bị. 4 flow chat-hai-nguoi-0..3 nên là 1 flow mgctl gọi 4 bước trên 2 vai. - Tag (smoke, that, quyen, hoi-quy) thay cho hậu tố tên file.
- Fixture bất biến; thứ flow tạo ra mang
[E2E] {run.id}và có bước dọn. - Mỗi run ra một
report.jsoncùng schema dù bước chạy bằng engine nào. - Mỗi thư mục flows / steps / fixtures có README ngắn; skill chỉ trỏ tới, không chép.
05Định nghĩa ở đâu, có schema không
Schema tập trung trong kit, nội dung phân tán theo chủ sở hữu; có structure cho mọi thứ máy đọc, không cần cho thứ chỉ người và agent đọc.
| Tầng | Chứa gì | Ở đâu | Ai sửa |
|---|---|---|---|
| Kit | schema từng đối tượng, quy ước, runner lõi, template thư mục | repo riêng (mg-cli-kit), có version | hiếm, có review |
| Dự án | instance env, role, profile, flow liên repo, contract, secrets | repo common của dự án | người vận hành |
| Repo | step, flow UI, fixture, config lệnh, tri thức agent | <repo>/cli/ | dev của app |
| Ngoài repo | local override, dataset, run | *.local.json, work/, .runs/ | máy, lần chạy |
Hiện đã có tầng 2, 3, 4. Thiếu tầng 1: schema đang nằm trong repo app nên mỗi repo một bản. Đưa schema lên kit là thay đổi cấu trúc duy nhất cần làm. Mỗi file instance khai "$schema", runner validate lúc load.
| Đối tượng | Structure | Mức chặt | Lý do |
|---|---|---|---|
| Env, Role | JSON Schema | chặt | 6 CLI cùng đọc |
| Report | JSON Schema, có version | chặt | điểm nối giữa mọi engine và consumer |
| Flow, Step | JSON Schema | chặt ở khung, mở ở bước (oneOf theo driver, x-*) | bước UI khác theo driver |
| Config lệnh | schema cạnh config | vừa | như rt đang làm |
| Profile | JSON Schema | vừa | trộn local nên cần biết field hợp lệ |
| Fixture | chỉ manifest | lỏng | nội dung tuỳ nghiệp vụ; manifest ghi giấy phép ảnh / font |
| Dataset | sinh từ params của flow | tự động | không viết tay |
| Tri thức agent | không | tự do | ép schema chỉ gây phiền |
| Quy ước | văn bản | tự do | nhưng mỗi quy ước có một check trong doctor |
- Schema phải có version và lệnh migrate, nếu không kit đóng băng.
- Mở rộng có kiểm soát:
additionalProperties:falseở khung, cho phépx-<repo>-*hoặcwithtự do trong bước. - Kit là repo riêng, không phải thư mục trong common, vì common chứa secrets và dữ liệu MiniGate.
mg-cli-kit/ # tầng kit schemas/ env · role · flow · step · report · profile · config conventions.md template/ # mgctl init-cli sinh từ đây common_minigate/ # tầng dự án config/test-env.json config/profiles/ flows/<module>/ secrets/test-env/ <repo>/cli/ # tầng repo steps/<module>.json flows/<module>/*.json fixtures/ + fixtures.json config/<lệnh>.json work/<ngày>/<tác vụ>/ # ngoài repo env.json params.json .runs/<id>/report.json
06Sửa tool trong lúc test / debug sản phẩm
Nên, nhưng phân tầng. Nhu cầu thật về tool chỉ lộ ra khi đang test; cấm sửa là tự bịt nguồn phát hiện. Hai rủi ro cần chặn: nhiễm kết quả (pass vì sản phẩm đúng hay vì tool vừa đổi cách đo; agent có xu hướng nới expect, tăng timeout, thêm sleep) và trôi thiết kế (mỗi lần kẹt thêm một ngoại lệ).
| Tầng | Sửa trong lúc test? | Điều kiện |
|---|---|---|
| Step, flow, fixture | Có, tự do | commit riêng, tách khỏi commit sửa sản phẩm |
| Lệnh / flag mới CLI repo | Có, tối thiểu | ghi gap, có help, chạy lại flow từ sạch |
| Runner lõi, schema, quy ước | Không sửa tại chỗ | ghi gap kèm ví dụ, gom sửa theo đợt, nâng version kit |
Ba kỷ luật
- Tách commit và tách run. Sửa tool xong chạy lại từ sạch; report có field
toolVersion. - Sửa tool chỉ được chặt hơn hoặc rộng hơn, không lỏng hơn. Nới ngưỡng, bỏ assert, thêm sleep là che lỗi; ghi gap thay vì sửa. Đưa vào skill / CLAUDE.md cho agent.
- Mỗi lệnh sửa có một check đi kèm trong doctor hoặc test của CLI.
Nhịp: trong sprint sửa tầng 1 thoải mái, tầng 2 khi kẹt, tầng 3 chỉ ghi gap. Cuối đợt đọc gaps, gom cái lặp từ 2 repo trở lên lên kit. Test / debug là nơi phát hiện nhu cầu tool, không phải nơi thiết kế tool.
07Người đứng ở đâu
Người ở chỗ đặt ý định và chỗ phán xét; agent ở chỗ thực thi và thu thập. Bốn cổng người bắt buộc đứng, mọi chỗ khác agent tự chạy:
Người viết goal cho flow và chọn tag nào là nghiệm thu. Không có cổng này agent test cái dễ đo thay vì cái quan trọng.
Người đọc report tóm tắt và quyết fail thuộc loại nào: lỗi sản phẩm, lỗi tool, flaky. Chỉ người được kết luận.
Người cấp env / secret, duyệt flow chạy trên workspace có người thật, bảo vệ tài khoản.
Người review theo đợt mọi thay đổi schema, runner lõi, quy ước.
| Bước | Người | Agent |
|---|---|---|
| Quy ước, schema kit | quyết định, review | đề xuất kèm ví dụ từ gaps |
| Env, role, secrets | định nghĩa, cấp | chỉ dùng |
| Mục tiêu flow | viết 1–2 câu goal | dịch thành steps, expect, fixture |
| Viết step, flow, fixture | xem lần đầu khi là tiêu chí nghiệm thu | viết, chạy thử, sửa |
| Chạy, thu bằng chứng | không | chạy, sinh report |
| Phân loại fail | phán xét | đề xuất kèm bằng chứng |
| Sửa tool tầng repo | không | sửa tối thiểu, ghi gap |
| Gom gaps lên kit | đọc, chọn, bỏ | gom, nhóm, nháp |
| Flow dữ liệu thật | duyệt trước, xem ảnh sau | chạy, dọn [E2E] |
| Deploy, release | confirm | chuẩn bị, chạy sau confirm |
- Người xem ở mức tóm tắt. Phải mở từng ảnh hoặc đọc log thô là tool chưa đạt. Mục tiêu: vài phút cho một đợt vài chục flow.
- Sự tham gia giảm dần theo độ chín. Flow mới xem kỹ lần đầu xanh, sau chỉ xem khi fail. Tool mới review lúc lên kit, sau tin doctor.
08Quy trình một tính năng (hoặc một request)
| # | Bước | Người | Agent | Đầu ra | Tool |
|---|---|---|---|---|---|
| 1 | Ý định | goal, vai trò, cái không được xảy ra, tiêu chí | hỏi lại chỗ mơ hồ, nháp danh sách flow nghiệm thu | request có goal + danh sách flow | rt req new |
| 2 | Hợp đồng | duyệt spec | OpenAPI, sinh type, mock | spec + type | mgctl contract |
| 3 | Flow trước code | xem flow lần đầu | viết flow từ goal, phải đỏ đúng chỗ | flow + fixture + dataset | flow init, testenv |
| 4 | Làm | không, trừ khi block | code theo spec, chạy test + flow, commit tách tool / sản phẩm | commit Refs, push | process-requests |
| 5 | Nghiệm thu | đọc report, phán xét fail, duyệt dữ liệu thật | chạy flow theo tag, sinh report, đề xuất phân loại | report.json + ảnh, Telegram | mgctl flow run |
| 6 | Phát hành | confirm | deploy, smoke sau deploy, release, thông báo | tag, versions.json, report smoke | mgctl deploy / release |
| 7 | Gom (theo đợt) | đọc gaps, chọn | gom gaps, nháp kit, dọn flow trùng | kit version mới, bộ hồi quy | gaps, doctor |
- Bước 3 trước bước 4 là thay đổi lớn nhất: tiêu chí nghiệm thu được máy hoá trước khi code.
- Bước 2 cho phép song song backend và client trên spec + mock.
- Người chỉ xuất hiện ở 1, 3 (lần đầu), 5, 6, 7.
- Ba điểm ép: không có goal và danh sách flow thì không vào bước 4; report là đầu vào duy nhất cho deploy; flow đã dùng nghiệm thu tự thành hồi quy.
- Sửa lỗi nhỏ: 1 (mô tả + cách tái hiện) → 3 (một flow tái hiện, phải đỏ) → 4 → 5.
09Quy trình cả dự án
“Nháp → hồi quy → fix → lặp đến khi khách ổn” về cơ bản đúng, với agent càng đúng vì code nháp rẻ. Ba chỗ hay vỡ: “khách thấy ổn” không phải điều kiện dừng; nháp bị khoá thành kiến trúc (rủi ro lớn nhất khi dùng agent); hồi quy phình không kiểm soát. Giữ vòng lặp nhưng đặt vào 5 pha có điều kiện ra.
Nền (1–2 tuần)
kit, testenv, quy ước, testid, pipeline report. Làm thật vì mọi pha sau dựa vào.
Ra khi doctor xanh, một flow mẫu chạy được trên cả 3 nền tảng.
Nháp rộng
Khách thấy toàn bộ hình hài càng sớm càng tốt. Nháp có chủ đích: mock data, UI trước, backend giả (như mock/* của mobile). Nháp không gọi API thật, không migration, không flow hoi-quy.
Ra khi khách chốt phạm vi và luồng chính; đầu ra là danh sách flow nghiệm thu theo module.
Làm thật theo module
Mỗi module đi qua contract → flow đỏ → code → report xanh. Nháp của module được thay, không fix dần.
Ra khi flow nghiệm thu của module xanh và khách duyệt module đó.
Ổn định (2–3 vòng, mỗi vòng ~1 tuần)
Hồi quy → fix → hồi quy trên dữ liệu gần thật. Chỉ sửa, không thêm; yêu cầu mới vào backlog vòng sau.
Ra khi bộ hồi quy xanh N lần liên tiếp và khách ký nghiệm thu theo danh sách flow. Quá 3 vòng thì nguyên nhân thường ở pha 1 chốt chưa đủ.
Vận hành
Release, smoke sau deploy, gaps theo đợt. Hồi quy có ngân sách: smoke vài phút mỗi deploy, hoi-quy đầy đủ cuối ngày hoặc trước release, flow flaky gắn cach-ly và vào gaps.
liên tục
- Khách tham gia hai lần với hai câu hỏi khác nhau: pha 1 “đây có phải thứ anh muốn không”, pha 3 “đây có chạy đúng như đã chốt không”. Trộn hai câu là lý do vòng lặp không kết thúc.
- Pha 0 và 1 khoảng 20% thời gian, pha 2 là phần lớn.
10Việc còn mở để nghĩ tiếp
Mục tiêu đã nêu: tối ưu quy trình làm việc nhóm để chủ dự án không phải tham gia quản lý nhiều. Những quyết định còn chờ:
- DSL flow dừng ở lớp nào? Uỷ hẳn lớp tương tác cho Playwright / Maestro, hay phương án giữa (chuẩn hoá schema step, thêm auto-wait)? Bắt đầu từ wsctl e2e.
- Flow JSON thuần hay cho phép JS? Đề xuất JSON thuần, logic ở driver.
- Kit là repo riêng? Đề xuất có, common là consumer đầu tiên.
- Schema report.json là gì, đủ để người đọc vài phút một đợt? Đây là điểm nối cho mọi thứ còn lại, nên làm sớm.
- Ai giữ cổng nào khi nhóm lớn hơn một người: cổng ý định và phán xét có thể giao cho PO / QA, cổng dữ liệu thật và kit nên giữ ở người vận hành.
- Thứ tự chuyển: deskctl → minigate → wsctl e2e (theo độ gần với mgctl flow), hay wsctl e2e trước (theo mức rò rỉ)?
- Kiểm kê bước 1: bảng lệnh nào thuộc lõi hay dự án, trùng ở đâu, để quyết phạm vi kit trước khi thiết kế API.
Điều rút ra lớn nhất: phần mang sang dự án mới là quy ước, schema của env / role / flow / step / report, cấu trúc thư mục và bốn cổng người đứng. Phần ở lại là nội dung của các file đó. Ranh giới tái sử dụng đi theo loại đối tượng, không theo repo.