Chuyển đến nội dung chính

Bài đăng

Hiển thị các bài đăng có nhãn Kiến trúc

Stream trong Dart chuyên sâu: backpressure, broadcast và những rò rỉ ở giữa

Bad state: Stream has already been listened to. là lỗi đẩy phần lớn mọi người lên Stack Overflow, và câu trả lời họ tìm thấy — “dùng .asBroadcastStream() ” — thường là cách sửa sai. Nó làm lỗi biến mất và âm thầm thay đổi ngữ nghĩa phân phối dữ liệu của bạn. Hai loại stream, và vì sao mặc định là loại nghiêm ngặt Stream đơn đăng ký chỉ được nghe đúng một lần. Nó đệm sự kiện cho tới khi có listener, và giao mọi sự kiện cho listener đó. Đọc file, thân phản hồi HTTP và các hàm async* đều sinh ra loại này. Stream broadcast có bao nhiêu listener cũng được, chỉ giao sự kiện cho những listener đang đăng ký, và không đệm gì cả. Listener tới muộn thì đơn giản là lỡ mất những gì đã qua. final single = StreamController < int >(); // đơn đăng ký final bus = StreamController < int >. broadcast (); Cụm “không đệm gì cả” là mấu chốt. Chuyển một stream đơn đăng ký sang broadcast để làm im lỗi nghĩa là các sự kiện phát ra trước khi listener thứ hai gắn vào sẽ mất — kh...

Prompt injection: cái gì thật sự phòng thủ được

Tiền đề khó chịu, nói thẳng ra: một mô hình ngôn ngữ chỉ có một kênh đầu vào. System prompt của bạn, tin nhắn người dùng, một tài liệu truy hồi được và kết quả từ công cụ đều tới dưới dạng văn bản, và sự phân biệt của mô hình giữa “chỉ dẫn tôi tuân theo” và “dữ liệu tôi xử lý” là một xu hướng được học, không phải một ranh giới được cưỡng chế. Vì thế mọi phòng thủ dựa trên việc nhờ mô hình một cách kiên quyết hơn đều chỉ là biện pháp giảm nhẹ mang tính xác suất. Nó hạ tỉ lệ; nó không bịt được lỗ hổng. Những phòng thủ trụ được là những cái giả định rằng mô hình rồi sẽ bị lái đi, và giới hạn việc lái đó đạt được gì. Hình dạng của cuộc tấn công Injection trực tiếp là khi người dùng gõ “bỏ qua các chỉ dẫn trước đó”. Đó là ca dễ, và phần lớn chỉ gây phiền — người dùng đang tấn công chính phiên của họ. Injection gián tiếp mới là vấn đề thật sự. Chỉ dẫn tới bên trong dữ liệu mà hệ thống của bạn truy hồi thay mặt người dùng: Một ticket hỗ trợ mà phần thân chứa văn bản nhắm tới agent phâ...

Chọn mô hình embedding: những câu hỏi thật sự quan trọng

Quy trình thường thấy là: mở bảng xếp hạng, sắp theo điểm trung bình, lấy mô hình đứng đầu vừa túi tiền, rồi đi tiếp. Nó cho ra lựa chọn bảo vệ được khoảng một nửa số lần, và ở nửa còn lại, nó hỏng một cách đắt đỏ — vì đổi mô hình embedding nghĩa là nhúng lại toàn bộ và dựng lại mọi chỉ mục. Đây là những câu hỏi dự báo kết quả tốt hơn thứ hạng. Nó có chạy tốt trên văn bản của bạn không? Điểm trung bình trên benchmark là một hỗn hợp có trọng số của nhiều tác vụ, mà phần lớn không phải tác vụ của bạn. Một mô hình dẫn đầu tổng thể có thể tụt hậu tệ hại ở đúng cái bạn cần — điều khoản pháp lý, mã hàng, ticket hỗ trợ khách hàng tiếng Việt, hay mã nguồn. Bài đánh giá thật sự quan trọng chỉ mất một buổi chiều: Thu 50-100 truy vấn thật từ log của bạn (hoặc tự viết, nếu chưa có lưu lượng). Với mỗi truy vấn, đánh dấu những tài liệu trong kho đáng lẽ phải được truy hồi. Việc gán nhãn này chính là phần công việc thật. Nhúng kho tài liệu bằng từng mô hình ứng viên, chạy các truy vấn, đo r...

Chia nhỏ tài liệu cho truy hồi: quyết định âm thầm chặn trần chất lượng RAG

Một hệ thống truy hồi trả lời đúng câu “thời hạn hoàn tiền của chúng ta là bao lâu?” nhưng thất bại với câu “thời hạn hoàn tiền có khác với khách doanh nghiệp không?” thường không có vấn đề về mô hình. Nó có một đoạn chứa chính sách chung và một đoạn khác, không bao giờ được truy hồi, chứa ngoại lệ. Việc chia đoạn quyết định một đơn vị truy hồi là gì . Làm sai và không lượng rerank, kỹ thuật prompt hay nâng cấp mô hình nào cứu lại được thông tin bạn đã cắt rời. Chia theo kích thước cố định là mốc so sánh, không phải chiến lược Cách mặc định ai cũng bắt đầu — cắt mỗi N ký tự với M ký tự chồng lấn — đáng được hiểu chính xác, vì đó là thứ bạn sẽ đem ra so sánh. def fixed_chunks (text, size = 1000 , overlap = 200 ): chunks, start = [], 0 while start < len (text): chunks.append(text[start:start + size]) start += size - overlap return chunks Nó làm đúng chỗ nào: đoạn đều nhau, chi phí đoán được, không giả định gì về định dạng tài liệu. Nó là...

Stream trong Dart chuyên sâu: backpressure, broadcast và những rò rỉ ở giữa

Bad state: Stream has already been listened to. là lỗi đẩy phần lớn mọi người lên Stack Overflow, và câu trả lời họ tìm thấy — “dùng .asBroadcastStream() ” — thường là cách sửa sai. Nó làm lỗi biến mất và âm thầm thay đổi ngữ nghĩa phân phối dữ liệu của bạn. Hai loại stream, và vì sao mặc định là loại nghiêm ngặt Stream đơn đăng ký chỉ được nghe đúng một lần. Nó đệm sự kiện cho tới khi có listener, và giao mọi sự kiện cho listener đó. Đọc file, thân phản hồi HTTP và các hàm async* đều sinh ra loại này. Stream broadcast có bao nhiêu listener cũng được, chỉ giao sự kiện cho những listener đang đăng ký, và không đệm gì cả. Listener tới muộn thì đơn giản là lỡ mất những gì đã qua. final single = StreamController < int >(); // đơn đăng ký final bus = StreamController < int >. broadcast (); Cụm “không đệm gì cả” là mấu chốt. Chuyển một stream đơn đăng ký sang broadcast để làm im lỗi nghĩa là các sự kiện phát ra trước khi listener thứ hai gắn vào sẽ mất — kh...

Prompt injection: cái gì thật sự phòng thủ được

Tiền đề khó chịu, nói thẳng ra: một mô hình ngôn ngữ chỉ có một kênh đầu vào. System prompt của bạn, tin nhắn người dùng, một tài liệu truy hồi được và kết quả từ công cụ đều tới dưới dạng văn bản, và sự phân biệt của mô hình giữa “chỉ dẫn tôi tuân theo” và “dữ liệu tôi xử lý” là một xu hướng được học, không phải một ranh giới được cưỡng chế. Vì thế mọi phòng thủ dựa trên việc nhờ mô hình một cách kiên quyết hơn đều chỉ là biện pháp giảm nhẹ mang tính xác suất. Nó hạ tỉ lệ; nó không bịt được lỗ hổng. Những phòng thủ trụ được là những cái giả định rằng mô hình rồi sẽ bị lái đi, và giới hạn việc lái đó đạt được gì. Hình dạng của cuộc tấn công Injection trực tiếp là khi người dùng gõ “bỏ qua các chỉ dẫn trước đó”. Đó là ca dễ, và phần lớn chỉ gây phiền — người dùng đang tấn công chính phiên của họ. Injection gián tiếp mới là vấn đề thật sự. Chỉ dẫn tới bên trong dữ liệu mà hệ thống của bạn truy hồi thay mặt người dùng: Một ticket hỗ trợ mà phần thân chứa văn bản nhắm tới agent phâ...

Chọn mô hình embedding: những câu hỏi thật sự quan trọng

Quy trình thường thấy là: mở bảng xếp hạng, sắp theo điểm trung bình, lấy mô hình đứng đầu vừa túi tiền, rồi đi tiếp. Nó cho ra lựa chọn bảo vệ được khoảng một nửa số lần, và ở nửa còn lại, nó hỏng một cách đắt đỏ — vì đổi mô hình embedding nghĩa là nhúng lại toàn bộ và dựng lại mọi chỉ mục. Đây là những câu hỏi dự báo kết quả tốt hơn thứ hạng. Nó có chạy tốt trên văn bản của bạn không? Điểm trung bình trên benchmark là một hỗn hợp có trọng số của nhiều tác vụ, mà phần lớn không phải tác vụ của bạn. Một mô hình dẫn đầu tổng thể có thể tụt hậu tệ hại ở đúng cái bạn cần — điều khoản pháp lý, mã hàng, ticket hỗ trợ khách hàng tiếng Việt, hay mã nguồn. Bài đánh giá thật sự quan trọng chỉ mất một buổi chiều: Thu 50-100 truy vấn thật từ log của bạn (hoặc tự viết, nếu chưa có lưu lượng). Với mỗi truy vấn, đánh dấu những tài liệu trong kho đáng lẽ phải được truy hồi. Việc gán nhãn này chính là phần công việc thật. Nhúng kho tài liệu bằng từng mô hình ứng viên, chạy các truy vấn, đo r...

Chia nhỏ tài liệu cho truy hồi: quyết định âm thầm chặn trần chất lượng RAG

Một hệ thống truy hồi trả lời đúng câu “thời hạn hoàn tiền của chúng ta là bao lâu?” nhưng thất bại với câu “thời hạn hoàn tiền có khác với khách doanh nghiệp không?” thường không có vấn đề về mô hình. Nó có một đoạn chứa chính sách chung và một đoạn khác, không bao giờ được truy hồi, chứa ngoại lệ. Việc chia đoạn quyết định một đơn vị truy hồi là gì . Làm sai và không lượng rerank, kỹ thuật prompt hay nâng cấp mô hình nào cứu lại được thông tin bạn đã cắt rời. Chia theo kích thước cố định là mốc so sánh, không phải chiến lược Cách mặc định ai cũng bắt đầu — cắt mỗi N ký tự với M ký tự chồng lấn — đáng được hiểu chính xác, vì đó là thứ bạn sẽ đem ra so sánh. def fixed_chunks (text, size = 1000 , overlap = 200 ): chunks, start = [], 0 while start < len (text): chunks.append(text[start:start + size]) start += size - overlap return chunks Nó làm đúng chỗ nào: đoạn đều nhau, chi phí đoán được, không giả định gì về định dạng tài liệu. Nó là...