CYBERSOFT
Đăng nhập

AI agent hỗ trợ kiểm thử phát hiện gian lận fintech: bất biến, human review và oracle false-positive/negative

Thực chiến doanh nghiệpFintechAI AgentAPIThực tếNâng cao
🗓 1 tháng trước21 phút đọc·👁 1,801 lượt xem👤 384 người đọc

Cách dùng AI agent để đề xuất kịch bản gian lận và dữ liệu test, trong khi con người chốt oracle nghiệp vụ (KYC, velocity, geo/device, số dư, idempotency, đối soát) và duyệt mọi hành động rủi ro. Bài đi từ bối cảnh doanh nghiệp, mô hình dữ liệu, bất biến làm oracle, test plan, ma trận case, tới CI và góc phỏng vấn.

1. Bối cảnh doanh nghiệp: tại sao kiểm thử phát hiện gian lận là bài toán khó

Hãy hình dung một ví điện tử fintech phục vụ mười triệu người dùng, xử lý khoảng ba nghìn giao dịch mỗi giây vào giờ cao điểm, với thoả thuận mức dịch vụ yêu cầu quyết định chặn hay cho qua trong dưới hai trăm mili-giây. Hệ thống phát hiện gian lận đứng giữa người dùng và tiền: nếu nó chặn nhầm khách thật, ta mất doanh thu và uy tín; nếu nó cho lọt kẻ gian, ta mất tiền mặt và có thể vi phạm quy định chống rửa tiền. Đây là một hệ thống mà cả hai kiểu sai đều đắt, nên kiểm thử phải đo được cả hai chiều chứ không chỉ 'hệ thống chạy'.

Vì sao bài toán khó? Thứ nhất, dữ liệu thật cực kỳ nhạy cảm: số thẻ, số dư, thông tin định danh KYC, không thể bê nguyên vào môi trường test. Thứ hai, gian lận là kẻ thù thích nghi: mẫu tấn công thay đổi liên tục, nên tập test tĩnh nhanh chóng lỗi thời. Thứ ba, tín hiệu rất mất cân bằng: tỉ lệ giao dịch gian lận thực sự có thể dưới một phần nghìn, khiến độ chính xác tổng thể trở thành thước đo vô nghĩa. Đây chính là nơi một AI agent có ích: nó giỏi sinh ra nhiều biến thể kịch bản nghi vấn và dữ liệu tổng hợp để mở rộng vùng phủ, miễn là con người giữ chặt oracle.

Kiến trúc: agent đề xuất — con người chốt oracle — hệ thống thật được bảo vệ AI AGENT (tester) đề xuất kịch bản nghi vấn sinh dữ liệu test tổng hợp KHÔNG chạm hệ thống thật HUMAN REVIEW chốt oracle nghiệp vụ duyệt hành động rủi ro bác kịch bản không hợp lệ FRAUD ENGINE rule: KYC · velocity geo · device · score chạy trên STAGING Oracle nghiệp vụ (con người sở hữu) giới hạn KYC · velocity/geo/device · số dư bảo toàn · idempotency · đối soát khớp đối tác Oracle FP/FN giao dịch sạch KHÔNG bị chặn (FP) giao dịch gian lận PHẢI bị bắt (FN) Ranh giới an toàn không gọi API chuyển tiền thật không dùng PII khách thật
Kiến trúc ba lớp: agent chỉ đề xuất, con người chốt oracle và duyệt, hệ thống thật chạy trên staging.
Nguyên tắc cốt lõi: agent là công cụ KHÁM PHÁ và SINH kịch bản, không phải người phán xử. Oracle 'đúng-sai' luôn thuộc về con người và về bất biến nghiệp vụ đã ký duyệt.

2. Oracle trước tiên: định nghĩa đúng-sai trước khi cho agent chạy

Sai lầm phổ biến nhất là thả agent vào hệ thống rồi hỏi 'giao dịch có bị chặn không'. Câu hỏi đó không phải oracle, vì bản thân việc chặn hay không chưa nói lên đúng-sai. Oracle thật phải neo vào sự thật đã biết: với một giao dịch ta phải biết trước nó nên bị chặn (nhãn gian lận) hay nên cho qua (nhãn sạch), rồi so sánh quyết định của hệ thống với nhãn đó. Nếu không có nhãn, ta không thể phân biệt true positive với false positive, và mọi con số đều là ảo tưởng.

Vì vậy chúng ta liệt kê các bất biến nghiệp vụ làm oracle chính. Giới hạn KYC: tài khoản chưa xác minh không được vượt hạn mức nạp/rút hằng ngày. Velocity: quá n giao dịch trong t giây từ cùng một thiết bị phải kích hoạt cờ nghi vấn. Geo: đăng nhập ở hai quốc gia cách nhau vô lý trong thời gian ngắn là bất khả thi vật lý. Device: thiết bị lạ chưa từng thấy cho một khoản lớn cần bước xác thực thêm. Bảo toàn số dư: tổng tiền vào bằng tổng tiền ra cộng chênh lệch số dư, không được tự sinh hay bốc hơi. Idempotency: cùng một khoá giao dịch gửi lại nhiều lần chỉ tạo đúng một hiệu ứng.

typescript
// oracle.ts — bất biến nghiệp vụ là NGUỒN CHÂN LÝ; agent không được sửa file này.
export interface Txn {
  id: string; accountId: string; amount: number; currency: "VND";
  deviceId: string; ip: string; countryFromIp: string; ts: number;
  kycVerified: boolean; idempotencyKey: string;
}
export interface Decision { blocked: boolean; reasons: string[]; }

// Ngưỡng do RỦI RO/COMPLIANCE ký duyệt — người sở hữu, không phải agent.
export const LIMITS = {
  unverifiedDailyTopUp: 20_000_000,   // 20tr VND
  velocityCount: 10, velocityWindowSec: 60,
  impossibleTravelKmPerHour: 900,      // nhanh hơn máy bay => bất khả thi
  stepUpAmount: 50_000_000,
};

// Oracle KYC: tài khoản chưa xác minh KHÔNG được vượt hạn mức ngày.
export function oracleKyc(txn: Txn, todayTopUp: number): boolean {
  if (txn.kycVerified) return true;                 // không áp trần này
  return todayTopUp + txn.amount <= LIMITS.unverifiedDailyTopUp;
}

// Oracle bảo toàn số dư: không tiền nào tự sinh/bốc hơi.
export function oracleBalanceConserved(before: number, after: number, delta: number): boolean {
  return Math.abs((before + delta) - after) < 1e-6;
}
💡 Đặt file oracle vào thư mục chỉ-đọc với agent (allowlist ghi không bao gồm nó). Agent có thể ĐỌC để hiểu tiêu chí, nhưng mọi thay đổi ngưỡng phải qua pull request và người duyệt của Rủi ro/Compliance.

3. Mô hình dữ liệu và luồng quyết định của engine gian lận

Để kiểm thử tử tế, ta cần hiểu rõ mô hình dữ liệu và luồng ra quyết định. Mỗi giao dịch tham chiếu tới tài khoản, thiết bị, phiên đăng nhập và một khoá idempotency. Engine gian lận tính một điểm rủi ro từ nhiều tín hiệu: lịch sử velocity, khoảng cách địa lý so với lần trước, độ quen của thiết bị, và một số quy tắc cứng như trần KYC. Điểm rủi ro so với ngưỡng cho ra quyết định chặn, cho qua, hay yêu cầu xác thực thêm. Điểm mấu chốt: quyết định phải xác định và có thể tái lập với cùng đầu vào, nếu không ta không thể viết test ổn định.

typescript
// engine.ts — luồng quyết định (đơn giản hoá). QUYẾT ĐỊNH PHẢI XÁC ĐỊNH.
import { Txn, Decision, LIMITS } from "./oracle";

export function velocityFlag(recent: Txn[], now: number): boolean {
  const inWindow = recent.filter(t => now - t.ts <= LIMITS.velocityWindowSec * 1000);
  return inWindow.length >= LIMITS.velocityCount;
}

// khoảng cách/thời gian => tốc độ di chuyển ngụ ý; nhanh bất khả thi => cờ.
export function impossibleTravel(prevKm: number, dtHours: number): boolean {
  if (dtHours <= 0) return prevKm > 0;
  return (prevKm / dtHours) > LIMITS.impossibleTravelKmPerHour;
}

export function decide(txn: Txn, ctx: {
  recent: Txn[]; prevKm: number; dtHours: number; deviceKnown: boolean;
}): Decision {
  const reasons: string[] = [];
  if (velocityFlag(ctx.recent, txn.ts)) reasons.push("VELOCITY");
  if (impossibleTravel(ctx.prevKm, ctx.dtHours)) reasons.push("GEO");
  if (!ctx.deviceKnown && txn.amount >= LIMITS.stepUpAmount) reasons.push("DEVICE_STEPUP");
  // quy tắc cứng: trần KYC là chặn tuyệt đối
  // (todayTopUp lấy từ ledger; ở đây minh hoạ)
  return { blocked: reasons.length > 0, reasons };
}
  • Mỗi giao dịch có khoá idempotency duy nhất để chống double-charge khi retry.
  • Điểm rủi ro là hàm xác định của tín hiệu — không dùng thời gian ngẫu nhiên bên trong.
  • Ngưỡng và quy tắc nằm trong cấu hình có phiên bản, thay đổi phải qua duyệt.
⚠️ Nếu engine dùng thời gian hệ thống hoặc số ngẫu nhiên bên trong hàm quyết định, test sẽ flaky và agent sẽ 'chữa' bằng cách nới lỏng assertion. Hãy tiêm đồng hồ và seed từ bên ngoài để mọi quyết định tái lập được.
🔒

Nội dung bị khoá

Bạn đang xem bản xem trước. Đăng nhập để mở khoá toàn bộ bài viết và tất cả tài liệu Tester/QA.

Đã xem 24% nội dung
  • Đọc trọn vẹn mọi bài viết
  • Lưu bài & ghi chú cá nhân
  • Theo dõi tiến độ đã đọc
  • Luyện phỏng vấn, ISTQB, Mock
Đăng nhập để đọc tiếp

Chưa có mã? Lấy mã qua Fanpage / Zalo CyberSoft.

0

💬 Bình luận (0)

Bạn có thể đọc mọi bình luận. Đăng nhập để bình luận

Chưa có bình luận nào. Hãy là người đầu tiên!