Không phải hệ nào cũng cần một ông sếp. Đôi khi cách nhanh và mạnh hơn là để nhiều agent chạy song song rồi tổng hợp — hoặc để chúng tranh luận để lọc ra câu trả lời tốt hơn. Bài này bàn về tô-pô ngang hàng (peer) và mẫu fan-out/fan-in.
Fan-out / fan-in (phân tán rồi gom)
Ý tưởng: chia việc thành các phần độc lập, tung cho nhiều worker chạy đồng thời, rồi một agent aggregator gom lại. Đây là nơi multi-agent thắng đậm về tốc độ.
+--> Worker A (mục 1) --+
Splitter -----+--> Worker B (mục 2) --+--> Aggregator --> kết quả
+--> Worker C (mục 3) --+
Điều kiện áp dụng: các phần thực sự độc lập (không phần này cần đầu ra phần kia). Nếu có phụ thuộc, buộc phải tuần tự hoặc chia lại.
Ví dụ cụ thể: chấm 50 hồ sơ ứng viên
Mỗi hồ sơ chấm độc lập. Thay vì một agent chạy 50 lần tuần tự (rất chậm), tung 50 lời gọi song song, mỗi cái trả điểm + lý do, rồi aggregator xếp hạng.
import asyncio
async def score_all(cvs, rubric):
tasks = [scorer.run(cv, rubric) for cv in cvs] # fan-out
results = await asyncio.gather(*tasks) # chạy song song
return aggregator.rank(results) # fan-in
Độ trễ giảm từ ~50x xuống ~1x (giới hạn bởi rate limit). Đây là lợi ích không thể có với một agent tuần tự.
Mẫu tranh luận (debate) / bỏ phiếu
Cho 3 agent độc lập cùng giải một bài, rồi:
- Bỏ phiếu đa số (self-consistency): lấy đáp án xuất hiện nhiều nhất — mạnh cho bài có đáp án rời rạc.
- Tranh luận: mỗi agent thấy đáp án của người khác và phản biện, một judge chọn lập luận tốt nhất — mạnh cho bài mở, cần lý lẽ.
Khung tư duy: chọn tô-pô
Các phần độc lập, cần TỐC ĐỘ? -> fan-out/fan-in
Bài khó, cần ĐỘ TIN CẬY cao? -> bỏ phiếu / tranh luận
Cần điều phối THÍCH ỨNG theo kết quả? -> supervisor (bài 4)
Quy mô lớn, nhiều lĩnh vực? -> phân cấp (bài 5)
Các bước triển khai fan-out/fan-in
- Xác nhận các phần độc lập thật (vẽ đồ thị phụ thuộc).
- Viết Splitter chia việc thành các đơn vị đồng nhất.
- Chạy worker song song có giới hạn đồng thời để không vượt rate limit.
- Thiết kế schema kết quả để aggregator gom được.
- Viết Aggregator: gộp/xếp hạng/khử trùng lặp/xử lý phần lỗi.
- Xử lý lỗi cục bộ: một worker fail không được kéo sập cả mẻ.
Checklist
- [ ] Đã xác nhận tính độc lập trước khi song song hóa.
- [ ] Có giới hạn đồng thời + retry theo rate limit.
- [ ] Aggregator xử lý được kết quả thiếu/lỗi.
- [ ] Với debate/vote: agent độc lập ở lượt đầu.
Sai lầm thường gặp
- Song song hóa việc có phụ thuộc. Worker B cần output của A nhưng chạy cùng lúc → B nhận dữ liệu rỗng/sai.
- Quên giới hạn đồng thời. Tung 200 request cùng lúc → dính rate limit, hàng loạt fail.
- Aggregator ngây thơ. Chỉ nối chuỗi kết quả, không khử trùng lặp hay xử lý phần lỗi.
- Debate mà agent thấy nhau ngay từ đầu. Chúng hùa theo, mất đa dạng, vô ích.
Thiết kế Aggregator không ngây thơ
Aggregator là mắt xích quyết định chất lượng đầu ra của fan-in, nhưng hay bị làm qua loa. Một aggregator tốt phải xử lý được bốn tình huống: (1) trùng lặp — hai worker trả cùng một ý, cần khử; (2) mâu thuẫn — hai worker trả kết quả trái nhau, cần chọn theo confidence hoặc đánh dấu để người xem; (3) phần lỗi — một worker fail, không được kéo sập cả mẻ; (4) xếp hạng — gộp theo tiêu chí rõ ràng.
def aggregate(results):
ok = [r for r in results if r.get("status") == "ok"]
failed = [r for r in results if r.get("status") != "ok"]
deduped = dedupe_by_key(ok, key="claim")
ranked = sorted(deduped, key=lambda r: r["confidence"], reverse=True)
return {"ranked": ranked, "skipped": len(failed)}
Cầm skipped báo cho người dùng biết có phần bị bỏ qua thay vì lặng lẽ trả kết quả thiếu — minh bạch quan trọng hơn giả vờ hoàn hảo.
Xử lý lỗi cục bộ trong mẻ song song
Khi tung 50 lời gọi song song, gần như chắc chắn vài cái sẽ fail (rate limit, timeout). Dùng cơ chế thu kết quả chịu lỗi (ví dụ gather với return_exceptions) rồi retry riêng phần fail với backoff, thay vì để một exception làm hỏng cả gather. Nguyên tắc: lỗi cục bộ phải ở lại cục bộ.
Bài 7 bước vào vận hành: quan sát (observability), chi phí và độ trễ của hệ multi-agent trong thực tế.