Đối soát (reconciliation) là trái tim của agent tài chính: khớp mỗi dòng sao kê ngân hàng với một dòng trong sổ nội bộ. Bài này xây tầng khớp tất định trước, rồi bổ sung khớp mờ.
Chiến lược phân tầng (matching cascade)
Đừng để LLM khớp tất cả. Hãy dùng nhiều tầng, từ chắc chắn nhất đến mờ nhất:
Tầng 1: Khớp chính xác -> cùng số tiền + cùng ngày + cùng mã tham chiếu
Tầng 2: Khớp gần đúng -> cùng số tiền + ngày lệch <= 3 + tên đối tác giống
Tầng 3: Khớp mờ (LLM) -> số tiền khớp nhưng mô tả khó hiểu, cần suy luận
Còn lại: Ngoại lệ -> đẩy cho con người xem
Mỗi tầng chỉ xử lý phần chưa khớp từ tầng trước. Càng lên tầng cao càng ít dòng, nên LLM (đắt) chỉ chạy trên phần nhỏ.
Tầng 1 & 2: code tất định
def reconcile_deterministic(bank, ledger, date_window=3):
matches, used = [], set()
for b in bank:
for l in ledger:
if l["id"] in used:
continue
same_amt = b["amount"] == l["amount"]
days = abs(date_diff(b["date"], l["date"]))
if same_amt and days == 0:
matches.append((b, l, 1.0)); used.add(l["id"]); break
if same_amt and days <= date_window \
and fuzzy_name(b["counterparty"], l["counterparty"]) >= 0.85:
matches.append((b, l, 0.9)); used.add(l["id"]); break
unmatched_bank = [b for b in bank if b not in [m[0] for m in matches]]
return matches, unmatched_bank
Lưu ý dùng used để một dòng sổ không bị khớp hai lần.
Khớp mờ tên đối tác
Tên đối tác thường viết tắt, thiếu dấu, gõ hoa. fuzzy_name nên chuẩn hóa trước khi so:
def normalize(name):
name = name.lower().strip()
name = remove_accents(name) # bỏ dấu tiếng Việt
for w in ["cong ty","ct","tnhh","cp","co.","ltd"]:
name = name.replace(w, "")
return " ".join(name.split())
Ví dụ: "CT TNHH VP MIEN NAM" và "Văn phòng phẩm Miền Nam" sau chuẩn hóa còn "vp mien nam" vs "van phong pham mien nam" — độ tương đồng đủ cao để tầng 2 khớp.
Ví dụ chạy thực tế
Với 500 dòng sao kê: Tầng 1 khớp 410 dòng (82%), Tầng 2 khớp thêm 60 (12%), còn 30 dòng chuyển sang Bài 4 để LLM và con người xử lý. Tỷ lệ tự động đạt 94% mà không dùng token LLM nào — tiết kiệm chi phí lớn.
Khung tư duy: một-một, một-nhiều
- 1–1: một chuyển khoản = một hóa đơn (dễ nhất).
- 1–nhiều: một lần chi gộp trả nhiều hóa đơn (cần khớp tổng).
- nhiều–1: khách trả góp nhiều lần cho một hợp đồng.
Các bước
- Chuẩn hóa cả hai danh sách về schema chung.
- Chạy Tầng 1 (khớp tuyệt đối), đánh dấu đã dùng.
- Chạy Tầng 2 (ngày lệch + fuzzy tên) trên phần còn lại.
- Ghi độ tin cậy cho mỗi cặp khớp.
- Xuất danh sách chưa khớp cho tầng LLM.
- Viết unit test với ca một-nhiều để chắc không khớp sai.
Sai lầm thường gặp
- Khớp một dòng sổ cho nhiều dòng bank: gây khớp trùng, lệch sổ. Luôn đánh dấu
used. - So tên thô không chuẩn hóa: hoa/thường, có dấu/không dấu làm trượt khớp. Chuẩn hóa trước.
- Đặt cửa sổ ngày quá rộng: khớp nhầm hai giao dịch cùng số tiền khác bản chất. Giữ 2–3 ngày.
- Bỏ qua độ tin cậy: không phân biệt được khớp chắc và khớp đoán. Luôn gắn score.