Quay lại trang blogWorkflow lỗi im lặng: phát hiện trước khi khách hàng thấy
tự động hoá workflowkiểm thử workflowRenyrolỗi im lặngfrozen replay

Workflow lỗi im lặng: phát hiện trước khi khách hàng thấy

22 tháng 9, 2026•7 phút đọc

"Workflow báo thành công nhưng output sai là kiểu lỗi tốn kém nhất. Cách dùng evaluator, freeze và replay của Renyro để bắt lỗi trước khi khách hàng thấy."

Một workflow gửi hoá đơn chạy hằng đêm. Sáng ra bảng điều khiển vẫn xanh, không có cảnh báo nào. Đến chiều, khách quen nhắn: đơn của họ không thấy hoá đơn đâu. Bạn mở lịch sử chạy ra, mọi lượt đều báo thành công.

Đó là lỗi im lặng điển hình. Với Renyro, mọi lượt chạy workflow đều đi qua TriggerService rồi tới DAG engine — Web UI, CLI và MCP đều gọi chung một REST API, nên không có đường chạy nào lách qua engine. Nhưng engine chỉ biết một node đã chạy xong mà không ném lỗi. Nó không biết field bạn map có đúng hay không.

Điểm xanh không chứng minh output đúng

Cụ thể: execution_success: 1 không có nghĩa là output đúng. Một binding sai vẫn cho execution_success: 1 nhưng điểm đúng là 0. Nếu bạn chỉ nhìn cột thành công/thất bại, bạn đang không đo thứ khách hàng quan tâm.

Lỗi im lặng không phải là workflow crash. Nó là workflow chạy trơn tru và trả về dữ liệu sai. Engine không có cách nào tự biết so_tien bị map nhầm sang trường ghi chú, hay ma_khach bị rỗng. Muốn biết, bạn phải chấm điểm output.

Chấm điểm output, không chỉ đếm lỗi

Renyro tách hai loại chỉ số. execution_success là metric do framework cấp. failure_rate, latency_ms, p95_latency_ms cũng vậy — nhưng ba cái này chỉ dùng làm gate; gán trọng số cho chúng sẽ bị từ chối. Nghĩa là bạn không thể trộn “chạy nhanh” vào điểm chất lượng như thể chúng cùng loại.

Phần đo chất lượng thật nằm ở evaluator. Renyro đăng ký đúng năm evaluator: exact_match, field_match, numeric_tolerance, required_fields, schema_validation. Mỗi case là một input kèm expected output tuỳ chọn; evaluator trả về điểm trong khoảng [0,1]. Metric là phép tổng hợp có tên trên toàn bộ case, chấm theo trọng số tường minh — hệ thống không tự bịa điểm cho bạn.

Ví dụ với workflow hoá đơn: dùng required_fields để bắt buộc các field ma_don, so_tien, ma_khach phải có; dùng field_match cho so_tien so với expected; dùng schema_validation để chặn output sai cấu trúc. Ba evaluator này bắt đúng loại lỗi mà execution_success bỏ qua.

Một run chỉ PASS khi mọi case chạy thành công, mọi gate đều đạt, và nếu bạn không cấu hình gate nào thì mọi evaluator phải trả về đúng 1 trên mọi case.

Hai điều cần biết trước khi cấu hình:

  • Evaluation mặc định từ chối graph có node cần credential hoặc có classified effect. Muốn chạy không người, bạn phải khai báo approved_effects và approved_credentials — nghĩa là bạn chủ động nói trước workflow này được phép làm gì.
  • allowed_effects trên fixture case mới chỉ được khai báo và băm, chưa được enforce lúc evaluation. Đừng dựa vào nó như một hàng rào.

Về tính tái lập: suite là định nghĩa có phiên bản; đăng phiên bản mới vẫn giữ bản cũ. Kết quả mang suite_digest (sha256 chuẩn tắc trên nội dung suite), evaluator_versions, và phân tách theo partition; case không đặt partition mặc định là DEVELOPMENT. Vì kết quả ghim phiên bản semantics của từng evaluator, hành vi evaluator đổi về sau sẽ không âm thầm viết lại ý nghĩa của kết quả cũ.

Một lưu ý nữa: gate chỉ áp cho metric tổng hợp. Muốn đặt ngưỡng riêng theo partition, bạn phải tự đọc breakdown partitions mà kiểm. Nếu chưa chắc engine đang chạy hỗ trợ evaluator nào và tham số ra sao, gọi renyro_list_evaluators — nó trả về evaluation contract của engine đang chạy.

Ghim đúng thứ đã test trước khi build

Đây là chỗ nhiều người mất thời gian nhất: test xanh, build ra, rồi có người sửa một node trong graph và build lại mà không ai chạy lại evaluation.

Renyro xử lý bằng freeze. Freeze biến một candidate đã đạt kiểm thử thành freeze manifest, ghim graph_digest, suite_digest, fixture_manifest_digest, runtime_identity_digest và fingerprint_closure. fingerprint_closure là digest tổng hợp trên ba thành phần: graph + fixture manifest + runtime closure — đổi bất kỳ thành phần nào cũng làm closure đổi.

renyro_build_app mặc định bắt buộc gắn freeze. Server tự resolve freeze rồi xác minh với stored state. Build kiểu thường phải khai báo rõ allow_unverified. Build freeze-bound xác minh 9 điều kiện, trong đó có: candidate ở trạng thái FROZEN/PROMOTED, freeze hoàn chỉnh và đúng candidate/workflow, evaluation result passed == true, và graph workflow hiện hành phải khớp frozen fingerprint. Điều kiện cuối chính là cái chặn trường hợp sửa graph sau khi test.

Mọi build đều sinh build receipt ghim freeze_id, candidate_id, evaluation_id, artifact digest và manifest digest — kể cả build allow_unverified (khi đó verified: false và không có khối manifest). Nên kể cả khi bạn đi đường vòng, vẫn còn dấu vết để kiểm tra lại.

Phía dữ liệu test: fixture set là corpus bất biến, content-addressed, nhận 1..500 case, bytes_base64 giải mã tối đa 32 MiB, retention chỉ nhận IMMUTABLE. fixture_manifest_digest là sha256 của manifest chuẩn tắc sắp theo case_id, và ổn định khi tái tuần tự hoá — đổi thứ tự case, đổi fixture_set_id/version, hay ghi rõ partition mặc định đều không làm đổi nó.

Quan trọng hơn: pinning corpus được enforce lúc chạy. Runner resolve corpus đã pin và đòi case của suite khớp chính xác về tập case-id, partition, input, expected và allowed_effects. Sai lệch trả lỗi 409. Bạn không thể “test một đằng, chạy một nẻo”.

Ghi lại để lần sau tái hiện được

Engine dùng chung nhận ba chế độ LIVE, PLAN và REPLAY; bỏ trống thì chuẩn hoá thành LIVE. Với doanh nghiệp nhỏ, PLAN là cách rẻ nhất để xem workflow sẽ làm gì trước khi cho nó làm thật.

Khi workflow có effect vật chất do Renyro sở hữu, thứ tự bắt buộc là ghi dòng PROPOSED bền vững trước khi gọi executor thật; nếu ghi thất bại thì executor không được gọi. Effect Ledger chỉ mang ngữ nghĩa — nó không lưu API key, credential, token bot, auth header, URL bí mật, body request/response hay nội dung tin nhắn.

Mọi execution LIVE mới tự động ghi lại kết quả dependency được hỗ trợ ngay sau khi executor trả về, và việc capture không bao giờ gọi provider. Đó là nguyên liệu cho Frozen Replay qua POST /api/v1/replay-capsules/{id}/replay. REPLAY không nạp credential production, và thiếu bằng chứng thì fail closed.

Hai giới hạn cần biết: Frozen Replay chỉ phủ danh sách built-in trong internal/nodes/replay_contract.go, và node mcpClient chưa nằm trong danh sách. Nếu workflow của bạn phụ thuộc node đó, đừng giả định replay tái hiện được đầy đủ.

Về phía an toàn node: mỗi node tự khai báo phân loại (PureData, EffectAction, ReadsHostIO, TalksToExternalService, DelegatesExecution), và quyền chạy không cần người được suy ra từ khai báo này. Cơ chế là fail closed: node không khai báo gì hoặc không phải built-in đã đăng ký thì không bao giờ được phép; khai báo mâu thuẫn cũng bị từ chối.

Việc cần làm tiếp theo

  1. Chọn một workflow đang chạy không người và có ảnh hưởng tới khách hàng. Viết vài case với expected output rõ ràng.
  2. Gắn evaluator phù hợp — thường là required_fields cộng field_match, thêm schema_validation nếu output có cấu trúc.
  3. Chạy evaluation, đọc breakdown theo partition, đừng chỉ nhìn execution_success.
  4. Freeze candidate đã đạt, rồi build có gắn freeze. Mở build receipt và kiểm verified.
  5. Chạy workflow ở chế độ PLAN một lần trước khi bật LIVE, và giữ bằng chứng replay cho lần chạy thật.

Nói thẳng về bối cảnh: bản hiện tại là release candidate 1.0.0-rc.2 chưa ký, không nằm trên kênh latest. Chưa có installer, chưa có marketplace, chưa có control plane hosted; chưa có audit độc lập và chưa có multi-user/RBAC/SSO. Triển khai mặc định là một tệp thực thi Go kèm SQLite — không cần Docker, không cần Node.js runtime, không cần database ngoài — và server mặc định chỉ bind loopback. Nếu bạn cần một hệ thống nhỏ, tự host, kiểm soát được dữ liệu chạy qua nó, thì đây là điểm khởi đầu hợp lý. Nếu bạn cần RBAC và audit độc lập ngay hôm nay, hãy chờ.