FDE PulseViệc làm FDE đang mở 632Mới trong 7 ngày 57Công ty đang tuyển 95Nhận làm từ xa 24%Lương trung vị (Mỹ) $205kTuyển nhiều nhất Databricks 114
EN

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

Bách khoa

Thực hành: phân cụm 5.000 ticket hỗ trợ bằng embedding để chọn use case AI đầu tiên

Chỉ cần một file CSV chứa ticket là bạn đã có thể mở buổi họp đầu tiên bằng bản đồ những gì khách hàng của họ thực sự hỏi, thay vì để cả buổi xoay quanh cảm giác của người ngồi ghế giám đốc.

Nhóm nhân viên chăm sóc khách hàng làm việc trước màn hình máy tính trong văn phòng hiện đại
Ảnh: Vagaro / Unsplash
Chia sẻLinkedInFacebookX

Tóm tắt nhanh

  • k-means bắt bạn tự chọn số cụm trước; HDBSCAN thì không, và còn tách riêng những ticket không thuộc chủ đề nào thành nhãn -1.
  • min_cluster_size là tham số quan trọng nhất: đặt nhỏ sẽ ra hàng trăm chủ đề vụn, đặt lớn thì dễ gộp mất các vấn đề ít gặp.
  • Cụm lớn nhất chưa chắc là use case đầu tiên; nên chọn cụm vừa đông vừa lặp lại và dễ kiểm chứng.
Biểu đồ cột ngang so sánh ba cụm ticket trong kịch bản giả định: giao hàng trễ 900 ticket, đặt lại mật khẩu và khoá tài khoản 600 ticket, chính sách hoàn tiền 400 ticket. Cụm mật khẩu được tô màu cam vì là ứng viên use case đầu tiên: câu trả lời lặp lại và kết quả đo được. Cụm giao hàng tuy đông nhất nhưng cần tra hệ thống vận chuyển.
Cụm giao hàng có nhiều ticket nhất, nhưng cụm mật khẩu lặp lại, ít rủi ro và có tỷ lệ tự giải quyết để đo giá trị. Đây là kịch bản giả định trong bài, không phải dữ liệu thật. Nguồn: kịch bản minh hoạ của FDE Times.

Thử hình dung một khách có 5.000 ticket hỗ trợ, không ai trong phòng đọc hết, nhưng ai cũng tin mình biết khách hàng phàn nàn về điều gì. Giám đốc vận hành chắc chắn là chuyện giao hàng. Trưởng nhóm CSKH lại cho rằng đó là lỗi thanh toán.

Với một FDE, câu hỏi “nên làm use case AI nào trước” thường đến đúng vào lúc như vậy: dữ liệu có sẵn, còn ý kiến thì mỗi người một kiểu.

Hướng dẫn này đi qua một pipeline đủ gọn để chạy trên laptop. Với quy mô giả định 5.000 ticket, một buổi chiều là ước lượng hợp lý cho kịch bản này, không phải con số đo được. Pipeline biến đống ticket thô thành một bản đồ chủ đề có tên, có số lượng, rồi từ đó chọn ra use case đầu tiên.

Đây cũng là ý tưởng các đội lớn đang dùng. Fin.ai của Intercom phân cụm hội thoại hỗ trợ để khám phá chủ đề. Clio của Anthropic gom hội thoại theo chủ đề rồi để LLM viết tiêu đề và tóm tắt cho từng cụm. Phiên bản của bạn nhỏ hơn nhiều, nhưng logic thì giống hệt.

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

Đầu ra là một bảng: mỗi dòng là một chủ đề, kèm tên dễ đọc, số ticket và vài ví dụ tiêu biểu. Pipeline gồm năm bước: tạo embedding, giảm chiều bằng UMAP, phân cụm bằng HDBSCAN, đọc nhóm nhiễu, rồi đặt tên cụm.

Bạn cần Python, pandas, hai thư viện umap và hdbscan, quyền gọi một model embedding và một LLM mà khách cho phép dùng với dữ liệu của họ. Điều kiện cuối quan trọng nhất. Ticket thường chứa email, số điện thoại, mã đơn hàng, nên hãy hỏi về quy định dữ liệu trước khi gửi bất kỳ dòng nào ra ngoài.

Code bên dưới là bản giản lược để minh họa luồng xử lý, không phải code production. Hàm embed() là một hàm bọc bạn tự viết quanh model embedding được duyệt.

Bước 1–2: biến chữ thành vector, rồi thu nhỏ không gian

Bắt đầu bằng việc nạp ticket và tạo embedding cho từng dòng.

# Giản lược: embed() bọc model embedding mà khách cho phép
import pandas as pd

df = pd.read_csv("tickets.csv")   # cột: id, text
X = embed(df["text"].tolist())    # mỗi ticket -> một vector

Cần kiểm tra ngay: số vector phải bằng đúng số dòng, trong kịch bản này là 5.000. Nếu thiếu, thường là do có ticket rỗng hoặc vượt giới hạn độ dài của model. Hãy lọc hoặc cắt những dòng đó trước khi đi tiếp.

Bước tiếp theo đi theo cách Fin.ai làm: trước khi phân cụm, họ dùng UMAP chiếu embedding xuống một không gian ít chiều hơn, và ghi nhận UMAP đặc biệt hiệu quả với văn bản ngắn như tin nhắn hỗ trợ.

Tài liệu BERTopic giải thích lý do: UMAP giữ được một phần cấu trúc cục bộ lẫn tổng thể của dữ liệu, nên những ticket gần nghĩa vẫn nằm cạnh nhau sau khi bị thu nhỏ.

import umap
# Điểm khởi đầu, không phải giá trị chuẩn: 5 chiều, đo khoảng cách bằng cosine
X_low = umap.UMAP(n_components=5, metric="cosine").fit_transform(X)

Đừng để UMAP chạy với mặc định 2 chiều: mức đó hợp để vẽ biểu đồ, còn khi phân cụm, ép xuống quá ít chiều dễ làm mất thông tin. Cosine là lựa chọn quen thuộc để so độ gần nghĩa giữa các embedding văn bản. Cả hai giá trị đều nên được thử lại trên dữ liệu của khách.

Bước 3: vì sao không dùng k-means?

Cookbook phân cụm của OpenAI dùng k-means đơn giản trên 1.000 dòng embedding tính sẵn, và để người đọc tự chọn số cụm phù hợp với use case.

Như vậy k là một quyết định chủ quan chứ không phải con số tính ra được. Với ticket của một khách mới, bạn chưa biết có 8 hay 80 chủ đề.

Nếu đoán k, bạn đang áp giả định của mình lên dữ liệu.

Fin.ai chọn HDBSCAN chính vì nó không cần biết trước số cụm. Tài liệu BERTopic bổ sung hai điểm mạnh khác: HDBSCAN tìm được những cụm có hình dạng khác nhau, và nó tách riêng các điểm ngoại lai thay vì nhét mọi tài liệu vào một chủ đề nào đó.

import hdbscan
df["cluster"] = hdbscan.HDBSCAN(min_cluster_size=15).fit_predict(X_low)
print(df["cluster"].value_counts().head(20))

Tham số quan trọng nhất là min_cluster_size, tức số ticket tối thiểu để tạo thành một cụm. Fin.ai coi đây là tham số then chốt khi tinh chỉnh. Họ chốt ở 15, và trên dữ liệu Intercom của chính họ con số này cho ra hơn 700 chủ đề nhỏ.

Thử hình dung với 5.000 ticket của bạn: nếu để 15 mà nhận về hàng trăm cụm vụn, chẳng ai trong phòng họp đọc nổi. Hãy chạy lại với vài giá trị lớn hơn, chẳng hạn 30 và 60, rồi so danh sách. Dừng lại khi các cụm đầu bảng trùng với những vấn đề mà nhân viên CSKH vẫn nhắc đến hằng ngày.

Bước 4: đừng xoá nhãn -1

HDBSCAN gán nhãn -1, tức nhiễu, cho mọi ticket không thuộc cụm ổn định nào. Phản xạ tự nhiên là lọc bỏ nhóm này cho bảng sạch. Đừng làm vậy.

noise = df[df["cluster"] == -1]
print(len(noise), "ticket nhiễu")
print(noise.sample(min(30, len(noise)))["text"].tolist())

Nhóm nhiễu có thể chứa các vấn đề long-tail: lỗi mới xuất hiện tuần trước, một khách doanh nghiệp đang gặp sự cố riêng, hay một yêu cầu tính năng chưa ai gom lại.

Đọc tay 30 dòng không tốn nhiều thời gian. Bù lại, bạn thấy được những chi tiết mà bảng đếm số lượng bỏ sót. Một phép kiểm tra nên chạy thêm: so kích thước nhóm -1 giữa hai lần chạy với hai mức min_cluster_size khác nhau, rồi đọc xem những ticket nào chuyển từ cụm sang nhiễu hoặc ngược lại.

Bước 5: đặt tên cụm bằng hai lớp

Một cụm chỉ có số thứ tự thì vô nghĩa với khách. Cách rẻ nhất là theo BERTopic: rút ra những từ quan trọng nhất trong mỗi cụm bằng c-TF-IDF để có ngay mô tả thô cho chủ đề.

Lớp thứ hai là LLM. Cookbook của OpenAI gửi một mẫu ngẫu nhiên gồm 5 review mỗi cụm cho gpt-4 để đặt tên. Trang này đã được đánh dấu lưu trữ nên tên model có thể đã cũ, nhưng cách làm vẫn đúng. Fin.ai cũng có một bước LLM riêng để biến chủ đề cuối cùng thành nhãn dễ đọc.

# Giản lược: llm() bọc LLM được duyệt, trả về một chuỗi
names = {}
for c, g in df[df["cluster"] >= 0].groupby("cluster"):
    sample = g["text"].sample(min(5, len(g))).tolist()
    names[c] = llm("Đặt tên ngắn cho nhóm ticket này:\n" + "\n".join(sample))

df["topic"] = df["cluster"].map(names).fillna("Nhiễu (-1)")
print(df["topic"].value_counts().head(20))

Kiểm tra sau bước này: mọi cụm khác -1 đều phải có tên trong cột topic. Sau đó đặt tên LLM cạnh từ khoá c-TF-IDF. Nếu hai thứ lệch nhau, ví dụ LLM viết “vấn đề thanh toán” trong khi từ khoá toàn là “mật khẩu” và “đăng nhập”, thì cụm đó chưa thuần hoặc mẫu 5 ticket chưa đại diện. Hãy lấy mẫu lại.

Cụm đông nhất có phải use case đầu tiên?

Không hẳn. Đây là chỗ công việc của FDE bắt đầu, vì thuật toán chỉ đếm được số lượng. Thử hình dung ba cụm đứng đầu bảng (số liệu dưới đây là giả định):

Cụm (giả định) Số ticket Câu trả lời có lặp lại? Đo được kết quả?
Đặt lại mật khẩu, khoá tài khoản 600 Gần như luôn giống nhau Có: tỷ lệ tự giải quyết
Khiếu nại giao hàng trễ 900 Không, cần tra hệ thống vận chuyển Khó, phụ thuộc đối tác
Hỏi chính sách hoàn tiền 400 Có, theo tài liệu chính sách Có: so với câu trả lời chuẩn

Cụm giao hàng đông nhất nhưng câu trả lời phụ thuộc vào dữ liệu nằm ngoài tầm với của bạn ở tuần đầu. Cụm mật khẩu ít hơn nhưng lặp lại, rủi ro thấp và có một con số rõ ràng để chứng minh giá trị.

Đó là ứng viên tốt cho use case đầu tiên, còn cụm giao hàng là lý do để xin thêm quyền truy cập ở giai đoạn sau.

Những lỗi hay gặp

Lỗi đầu tiên là chạy một lần rồi tin luôn kết quả. Phân cụm nhạy với min_cluster_size, nên hãy luôn trình bày kết quả của ít nhất hai mức để khách thấy chủ đề nào vẫn đứng vững khi thay đổi tham số.

Lỗi thứ hai là coi tên do LLM đặt như kết luận đã chốt. Tên chỉ là giả thuyết, hãy đưa cho trưởng nhóm CSKH đọc cùng vài ticket mẫu trước khi đưa lên slide. Lỗi thứ ba là bỏ qua dữ liệu nhạy cảm. Hãy che thông tin cá nhân trước khi embedding, kể cả khi model chạy trong hạ tầng của khách.

Kỹ năng này xuất hiện ở đâu trong công việc thật?

Ở tuần đầu của một engagement, bảng chủ đề này giúp buổi customer discovery đi từ “chúng tôi nghĩ” sang “dữ liệu của chính anh chị cho thấy”. Nó cũng trở thành baseline: khi agent đã chạy, bạn phân cụm lại ticket mới để xem cụm mật khẩu có co lại thật không.

Nếu bạn đang muốn chuyển sang FDE, hãy làm bài này trên một dataset công khai và viết lại thành một đoạn trong CV theo dạng: số ticket, phương pháp, các cụm tìm được, use case đề xuất và lý do chọn.

Phần gom và đếm, thuật toán làm thay bạn. Phần khách trả tiền cho bạn là chọn ra chủ đề nào nên làm trước, và giải thích vì sao.

Bài này có hữu ích không?

Dùng cùng trợ lý AIHỏi Claude ↗Hỏi ChatGPT ↗
5 nguồn
Đọc tiếp trên lộ trình · Chặng 3: AI ứng dụngTài liệu của khách đổi hằng tuần: cập nhật và xoá trong chỉ mục RAG mà không dựng lại từ đầuThêm tài liệu mới vào chỉ mục thì dễ. Phần khó là làm cho chỉ mục quên đúng những tài liệu khách đã gỡ khỏi nguồn mà không xoá nhầm phần còn lại.