FDE PulseViệc làm FDE đang mở 316Mới đăng 7 ngày qua 10Chủ đề nổi bật: Đào tạo kỹ năng FDE tại Đông Nam Á

Tờ báo của nghề Forward Deployed Engineer

Bách khoa

Thực hành: dựng cổng duyệt qua Slack trước khi agent hoàn tiền hay gửi email

Sáu bước để mọi lệnh hoàn tiền hay email của agent đều qua tay người duyệt mà khách không phải chờ vô thời hạn: trả lời cú bấm trong 3 giây, chờ quyết định bao lâu cũng được, và tự từ chối khi không ai bấm.

Đồ hoạVòng đời một yêu cầu duyệt qua Slack
  1. 1Agent đề xuất tool callVí dụ: hoàn tiền cho một đơn hàng, kèm số tiền và lý do
  2. 2Đối chiếu chính sách duyệtChỉ thanh toán, email, thay đổi dữ liệu mới phải dừng chờ
  3. 3Gửi Slack đủ tham sốTin nhắn ghi chính xác hành động sẽ làm, kèm nút Approve/Reject
  4. 4Cổng chờ bền vữngTrạng thái được lưu lại, sống sót qua mất kết nối và deploy
  5. 5Quyết định hoặc timeoutTrả lời Slack trong 3 giây; không ai bấm thì leo thang, tự từ chối
  6. 6Ghi audit, thực thi/luiLưu mọi quyết định; bị reject thì agent nhận lý do và đổi hướng

Cổng duyệt tốt dừng đúng hành động, cho người duyệt thấy đủ tham số, có timeout và ghi lại mọi quyết định.

Đồ hoạ: FDE Times

Tóm tắt nhanh

  • Chỉ xin duyệt với hành động có hậu quả thật: thanh toán, gửi email, thay đổi dữ liệu.
  • Cổng duyệt phải bền: lưu trạng thái chờ, đặt timeout, ghi lại mọi quyết định.
  • Với Slack, trả lời cú bấm nút trong 3 giây rồi mới làm phần việc nặng.
Chia sẻLinkedInFacebookX

Slack cho ứng dụng của bạn đúng 3 giây để trả lời khi ai đó bấm nút. Trong khi đó, tài liệu Cloudflare Agents mô tả một cổng duyệt có thể chờ hàng tháng, thậm chí lâu hơn, mà không cần giữ agent chạy.

Cổng duyệt tốt phải làm được cả hai việc: xác nhận cú bấm gần như tức thì, nhưng kiên nhẫn chờ quyết định bao lâu cũng được mà không đánh mất trạng thái.

Thử hình dung agent của bạn vừa được cấp quyền hoàn tiền, gửi email cho khách hay sửa bản ghi CRM. Khi đó, khách hàng sẽ muốn biết một điều trước tiên: ai là người bấm nút cuối cùng?

Bài này hướng dẫn bạn dựng câu trả lời: một agent xin duyệt qua Slack trước khi làm việc quan trọng, có timeout, có đường lui và có nhật ký.

Bạn sẽ dựng gì, và cần chuẩn bị gì?

Kịch bản: agent hỗ trợ khách hàng đề xuất hoàn 2 triệu đồng cho một đơn hàng. Trước khi gọi tool hoàn tiền, nó đăng một tin nhắn vào kênh Slack của bộ phận tài chính, ghi rõ số đơn, số tiền, lý do, kèm hai nút Approve và Reject. Agent tạm dừng. Khi có người bấm, nó chạy tiếp hoặc huỷ.

Nếu không ai bấm, yêu cầu được nhắc lại rồi tự từ chối.

Bạn cần một Slack workspace thử nghiệm có quyền tạo app với interactivity, và một runtime agent hỗ trợ tạm dừng có trạng thái. Hai lựa chọn được nhắc tới ở đây là Cloudflare Agents (có waitForApproval() chạy trên Cloudflare Workflows) và LangChain (có middleware human-in-the-loop).

Các khối code bên dưới là phác thảo đã giản lược để minh hoạ luồng; hãy đối chiếu tài liệu của framework để lấy chữ ký hàm chính xác.

Bước 1: Không phải tool nào cũng cần người duyệt

Lỗi phổ biến nhất là bắt duyệt mọi thứ. Thử hình dung người duyệt nhận 40 tin nhắn một ngày cho các lệnh tra cứu vô hại: họ sẽ học cách bấm Approve không cần đọc, và lúc đó cổng duyệt chỉ còn là hình thức.

Hướng dẫn của Cloudflare nói rõ: chỉ yêu cầu xác nhận với hành động có hậu quả đáng kể, như thanh toán, email và thay đổi dữ liệu.

LangChain biến nguyên tắc này thành cấu hình. Middleware HITL nhận một tham số interrupt_on, ánh xạ từng tool tới các loại quyết định được phép cho tool đó. Hãy viết chính sách thành dữ liệu trước:

# Phác thảo: bảng chính sách duyệt
# Tên các loại quyết định ở đây chỉ để minh hoạ;
# lấy danh sách chính xác từ tài liệu HITL của LangChain.
interrupt_on = {
    "issue_refund":   {"allowed_decisions": ["approve", "edit", "reject"]},
    "send_email":     {"allowed_decisions": ["approve", "reject"]},
    "update_crm":     {"allowed_decisions": ["approve", "reject"]},
    "search_orders":  False,   # chỉ đọc, không cần duyệt
}

Kiểm tra: in bảng ra và hỏi chủ quy trình phía khách hàng từng dòng một. Nếu họ không giải thích được vì sao một tool cần duyệt, có lẽ nó không cần.

Bước 2: Cổng chờ phải sống sót qua mất kết nối

Một yêu cầu duyệt có thể nằm đó qua đêm, qua cuối tuần. Nếu trạng thái chỉ nằm trong bộ nhớ, một lần deploy lại sẽ xoá sạch nó. LangChain bắt buộc bạn cấu hình checkpointer để giữ trạng thái đồ thị qua các lần interrupt. Cloudflare khuyên lưu các yêu cầu đang chờ trong state của agent để chúng không mất khi rớt kết nối.

// Phác thảo trên Cloudflare Agents
const decision = await this.waitForApproval({
  action: "issue_refund",
  args: { orderId: "DH-1042", amount: 2000000, reason: "giao sai hàng" },
});

Điểm quan trọng: theo tài liệu Cloudflare, waitForApproval() tạo một cổng bền vững chạy trên Workflows, nên việc chờ không tiêu tốn một agent đang chạy. Nếu dùng LangChain, khi lên production hãy chọn checkpointer ghi trạng thái xuống nơi lưu trữ bền vững, đừng dùng loại chỉ giữ trong bộ nhớ.

Kiểm tra: gửi một yêu cầu duyệt, khởi động lại service, rồi bấm Approve. Agent phải chạy tiếp đúng chỗ đã dừng, đúng tham số cũ.

Bước 3: Tin nhắn Slack phải cho thấy chính xác điều sẽ xảy ra

Người duyệt chỉ ra quyết định tốt bằng thông tin họ thấy. Cloudflare khuyên hiển thị chính xác hành động sẽ làm gì, kèm toàn bộ tham số. Một tin nhắn “Agent muốn hoàn tiền, duyệt không?” là chưa đủ. Hãy ghi rõ “hoàn 2.000.000đ cho đơn DH-1042, lý do: giao sai hàng, về tài khoản gốc của khách”.

Với email, nguyên tắc y hệt: đưa nguyên văn tiêu đề, người nhận và nội dung vào tin nhắn duyệt, không tóm tắt. Thứ người duyệt đọc phải trùng khít với thứ agent sẽ gửi đi.

Bước 4: Trả lời Slack trong 3 giây, làm việc nặng sau

Khi người duyệt bấm nút, Slack gửi một payload tới endpoint của bạn và đòi phản hồi trong vòng 3 giây. Nếu handler của bạn vừa ghi database, vừa đánh thức agent, vừa gọi API hoàn tiền rồi mới trả lời, rất dễ vượt ngưỡng đó.

// Phác thảo handler interactivity
async function onSlackAction(payload) {
  enqueue({ approvalId: payload.actionValue, user: payload.user,
            decision: payload.actionId, responseUrl: payload.responseUrl });
  return new Response("", { status: 200 }); // trả lời ngay
}

Phần việc thật chạy sau, trong một worker nền. Khi xong, bạn cập nhật tin nhắn gốc qua response_url. Slack cho phép gửi tới response_url tối đa 5 lần trong 30 phút kể từ khi nhận payload, đủ để báo “Đang xử lý” rồi “Đã hoàn tiền”.

Kiểm tra: thêm log thời gian ở đầu và cuối handler. Con số phải dưới 3 giây với cả lần chạy đầu tiên, khi mọi thứ còn lạnh.

Bước 5: Không ai bấm thì sao?

Đây là chỗ nhiều bản demo bỏ qua. Người duyệt nghỉ phép, kênh Slack bị tắt thông báo, và yêu cầu nằm chờ mãi trong khi khách vẫn chờ tiền. Cloudflare khuyên dùng schedule() để leo thang hoặc tự từ chối sau một khoảng hợp lý.

// Phác thảo: nhắc sau 4 giờ, tự từ chối sau 24 giờ
await this.schedule(4 * 3600, "escalateApproval", { approvalId });
await this.schedule(24 * 3600, "autoRejectApproval", { approvalId });

Các con số 4 và 24 giờ ở đây chỉ là ví dụ. Mức thật phải chốt với khách: hoàn tiền nhỏ có thể chờ một ngày, nhưng email xin lỗi khách VIP có lẽ cần leo thang sau một giờ. Khi đã có quyết định, nhớ huỷ các lịch hẹn còn lại để không tự từ chối một yêu cầu đã được duyệt.

Bước 6: Reject phải có đường lui, và mọi quyết định phải được ghi lại

Trong LangChain, quyết định reject từ chối tool call và gửi phản hồi về cho agent thay vì thực thi. Nghĩa là agent nhận được lý do, chẳng hạn “khách đã được đổi hàng, không hoàn tiền”, và có thể soạn câu trả lời khác cho khách. Cloudflare cũng khuyên luôn chuẩn bị phương án dự phòng khi bị từ chối.

Một agent bị reject rồi im lặng là lỗi sản phẩm, không phải tính năng an toàn.

Cuối cùng là audit trail. Cloudflare gợi ý dùng this.sql để ghi lại mọi quyết định duyệt phục vụ tuân thủ.

// Phác thảo bảng audit
this.sql`INSERT INTO approvals
  (approval_id, action, args_json, decided_by, decision, decided_at)
  VALUES (${id}, ${action}, ${JSON.stringify(args)}, ${user}, ${decision}, ${now})`;

Kiểm tra: chạy ba kịch bản: approve, reject, và để timeout. Cả ba phải để lại đúng một dòng trong bảng, kèm người quyết định (hoặc “hệ thống” nếu tự từ chối).

Những lỗi hay gặp

Lỗi thứ nhất là để handler Slack làm mọi việc đồng bộ, dễ vượt giới hạn 3 giây mà Slack đặt ra cho việc phản hồi payload. Lỗi thứ hai là tin nhắn duyệt tóm tắt thay vì liệt kê tham số. Lỗi thứ ba là lưu yêu cầu chờ trong bộ nhớ, rồi mất sạch sau lần deploy đầu tiên.

Lỗi thứ tư khó thấy hơn: không kiểm tra người bấm có đúng là người được quyền duyệt hay không. Hãy so user trong payload với danh sách người duyệt của tool đó trước khi chấp nhận quyết định.

Ở site khách hàng, kỹ năng này trông như thế nào?

Bạn thường sẽ không tự cài được app. Theo Slack, chủ sở hữu và quản trị viên workspace kiểm soát agent app nào được thêm vào. Vì thế cuộc họp đầu tiên nên có cả người quản trị Slack lẫn chủ quy trình nghiệp vụ, và bảng chính sách ở Bước 1 chính là tài liệu để hai bên cùng ký.

Nếu bạn đang nhắm vai trò FDE, hãy đưa phần này vào CV một cách cụ thể: “thiết kế cổng duyệt cho tool thanh toán và email, có timeout tự từ chối và audit trail”. Khi đọc job description, những cụm như “human-in-the-loop”, “approval workflow” hay “compliance” là dấu hiệu công ty cần đúng kỹ năng này.

Agent đáng tin không phải là agent không bao giờ sai, mà là agent biết dừng đúng chỗ và để lại dấu vết cho từng lần dừng.

4 nguồn
Đọc tiếp trên lộ trình · Chặng 5: Triển khaiKhi nào agent phải dừng: bốn lớp chặn và lúc bàn giao cho con ngườiMột agent không có điều kiện dừng có thể chạy vô hạn và đốt chi phí mà không ném ra lỗi nào, và người phải giải thích với khách hàng là FDE.