Các khái niệm chính của Private Aggregation API
Tài liệu này dành cho ai?
Private Aggregation API (API tổng hợp riêng tư) cho phép thu thập dữ liệu tổng hợp từ các worklet có quyền truy cập vào dữ liệu trên nhiều trang web. Các khái niệm được chia sẻ tại đây rất quan trọng đối với nhà phát triển xây dựng các chức năng báo cáo trong Shared Storage và Protected Audience API.
- Nếu bạn là một nhà phát triển đang xây dựng một hệ thống báo cáo để đo lường trên nhiều trang web.
- Nếu bạn là một nhà tiếp thị, nhà khoa học dữ liệu hoặc người sử dụng báo cáo tóm tắt khác, thì việc hiểu rõ các cơ chế này sẽ giúp bạn đưa ra quyết định thiết kế để truy xuất báo cáo tóm tắt được tối ưu hoá.
Từ khóa
Trước khi đọc tài liệu này, bạn nên làm quen với các thuật ngữ và khái niệm chính. Mỗi thuật ngữ này sẽ được mô tả chi tiết tại đây.
- Khoá tổng hợp (còn gọi là bộ chứa) là một tập hợp điểm dữ liệu được xác định trước. Ví dụ: bạn có thể muốn thu thập một nhóm dữ liệu vị trí mà trình duyệt báo cáo tên quốc gia. Một khoá tổng hợp có thể chứa nhiều phương diện (ví dụ: quốc gia và mã của tiện ích nội dung).
- Giá trị tổng hợp là một điểm dữ liệu riêng lẻ được thu thập vào một khoá tổng hợp. Nếu bạn muốn đo lường số người dùng ở Pháp đã xem nội dung của bạn, thì
France
là một phương diện trong khoá tổng hợp vàviewCount
của1
là giá trị tổng hợp. - Báo cáo tổng hợp được tạo và mã hoá trong trình duyệt. Đối với API Tổng hợp riêng tư, tập hợp này chứa dữ liệu về một sự kiện.
- Dịch vụ tổng hợp xử lý dữ liệu từ các báo cáo tổng hợp để tạo báo cáo tóm tắt.
- Báo cáo tóm tắt là kết quả cuối cùng của Dịch vụ tổng hợp, đồng thời chứa dữ liệu người dùng tổng hợp bị nhiễu và dữ liệu chuyển đổi chi tiết.
- Worklet là một phần cơ sở hạ tầng cho phép bạn chạy các hàm JavaScript cụ thể và trả về thông tin cho bên yêu cầu. Trong một worklet, bạn có thể thực thi JavaScript nhưng không thể tương tác hoặc giao tiếp với trang bên ngoài.
Quy trình làm việc của tính năng Tổng hợp riêng tư
Khi bạn gọi API Tổng hợp riêng tư bằng một khoá tổng hợp và một giá trị tổng hợp, trình duyệt sẽ tạo một báo cáo tổng hợp. Các báo cáo sẽ được gửi đến máy chủ của bạn để phân lô báo cáo. Sau đó, Dịch vụ tổng hợp sẽ xử lý các báo cáo theo lô và tạo một báo cáo tóm tắt.
- Khi bạn gọi API Tổng hợp riêng tư, ứng dụng (trình duyệt) sẽ tạo và gửi báo cáo tổng hợp đến máy chủ của bạn để thu thập.
- Máy chủ của bạn thu thập báo cáo từ các ứng dụng và phân nhóm các báo cáo đó để gửi đến Dịch vụ tổng hợp.
- Sau khi thu thập đủ báo cáo, bạn sẽ phân lô và gửi các báo cáo đó đến Dịch vụ tổng hợp (chạy trong môi trường thực thi đáng tin cậy) để tạo báo cáo tóm tắt.
Quy trình công việc được mô tả trong phần này tương tự như Attribution Reporting API. Tuy nhiên, Báo cáo phân bổ liên kết dữ liệu thu thập được từ một sự kiện hiển thị và một sự kiện chuyển đổi, xảy ra vào các thời điểm khác nhau. Tính năng Tổng hợp riêng tư đo lường một sự kiện trên nhiều trang web.
Khoá tổng hợp
Khoá tổng hợp ("khoá" viết tắt) đại diện cho bộ chứa nơi các giá trị tổng hợp sẽ được tích luỹ. Bạn có thể mã hoá một hoặc nhiều phương diện vào khoá. Phương diện đại diện cho một số khía cạnh mà bạn muốn biết thêm thông tin chi tiết, chẳng hạn như nhóm tuổi của người dùng hoặc số lượt hiển thị của một chiến dịch quảng cáo.
Ví dụ: bạn có thể có một tiện ích được nhúng trên nhiều trang web và muốn phân tích quốc gia của những người dùng đã xem tiện ích của bạn. Bạn muốn trả lời các câu hỏi như "Có bao nhiêu người dùng đã xem tiện ích của tôi đến từ Quốc gia X?" Để báo cáo về câu hỏi này, bạn có thể thiết lập khoá tổng hợp mã hoá hai phương diện: mã tiện ích và mã quốc gia.
Khoá được cung cấp cho API Tổng hợp riêng tư là một BigInt, bao gồm nhiều phương diện. Trong ví dụ này, các phương diện là mã tiện ích và mã quốc gia. Giả sử mã tiện ích có thể dài tối đa 4 chữ số, chẳng hạn như 1234
, và mỗi quốc gia được liên kết với một số theo thứ tự bảng chữ cái, chẳng hạn như Afghanistan là 1
, Pháp là 61
và Zimbabwe là 195
.
Do đó, khoá tổng hợp sẽ có độ dài 7 chữ số, trong đó 4 ký tự đầu tiên được dành cho WidgetID
và 3 ký tự cuối cùng được dành cho CountryID
.
Giả sử khoá này đại diện cho số lượng người dùng ở Pháp (mã quốc gia 061
) đã xem mã tiện ích 3276
. Khoá tổng hợp là 3276061
.
Khoá tổng hợp | |
Mã tiện ích | Mã quốc gia |
3276 | 061 |
Bạn cũng có thể tạo khoá tổng hợp bằng cơ chế băm, chẳng hạn như SHA-256. Ví dụ: chuỗi {"WidgetId":3276,"CountryID":67}
có thể được băm rồi chuyển đổi thành giá trị BigInt
của 42943797454801331377966796057547478208888578253058197330928948081739249096287n
.
Nếu giá trị băm có nhiều hơn 128 bit, bạn có thể cắt bớt giá trị đó để đảm bảo giá trị đó không vượt quá giá trị bộ chứa tối đa được phép là 2^128−1
.
Trong một công cụ lưu trữ dùng chung, bạn có thể truy cập vào các mô-đun crypto
và TextEncoder
để tạo hàm băm. Để tìm hiểu thêm về cách tạo hàm băm, hãy xem bài viết về SubtleCrypto.digest()
trên MDN.
Ví dụ sau đây mô tả cách bạn có thể tạo khoá bộ chứa từ một giá trị được băm:
async function convertToBucket(data) {
// Encode as UTF-8 Uint8Array
const encodedData = new TextEncoder().encode(data);
// Generate SHA-256 hash
const hashBuffer = await crypto.subtle.digest('SHA-256', encodedData);
// Truncate the hash
const truncatedHash = Array.from(new Uint8Array(hashBuffer, 0, 16));
// Convert the byte sequence to a decimal
return truncatedHash.reduce((acc, curr) => acc * 256n + BigInt(curr), 0n);
}
const data = {
WidgetId: 3276,
CountryID: 67
};
const dataString = JSON.stringify(data);
const bucket = await convertToBucket(dataString);
console.log(bucket); // 126200478277438733997751102134640640264n
Giá trị tổng hợp
Các giá trị tổng hợp được cộng lại theo khoá trên nhiều người dùng để tạo thông tin chi tiết tổng hợp dưới dạng giá trị tóm tắt trong báo cáo tóm tắt.
Bây giờ, hãy quay lại câu hỏi mẫu được đưa ra trước đó: "Có bao nhiêu người dùng đã xem tiện ích của tôi đến từ Pháp?" Câu trả lời cho câu hỏi này sẽ có dạng như "Khoảng 4881 người dùng đã xem Tiện ích có mã 3276 của tôi là người Pháp". Giá trị tổng hợp là 1 cho mỗi người dùng và "4881 người dùng" là giá trị tổng hợp, tức là tổng của tất cả giá trị tổng hợp cho khoá tổng hợp đó.
Khoá tổng hợp | Giá trị tổng hợp | |
Mã tiện ích | Mã quốc gia | Số lượt xem |
3276 | 061 | 1 |
Trong ví dụ này, chúng ta tăng giá trị thêm 1 cho mỗi người dùng nhìn thấy tiện ích. Trong thực tế, giá trị tổng hợp có thể được điều chỉnh theo tỷ lệ để cải thiện tỷ lệ tín hiệu nhiễu.
Ngân sách đóng góp
Mỗi lệnh gọi đến API Tổng hợp riêng tư được gọi là một đóng góp. Để bảo vệ quyền riêng tư của người dùng, số lượng đóng góp mà một cá nhân có thể thực hiện sẽ bị giới hạn.
Khi bạn cộng tất cả các giá trị tổng hợp trên tất cả khoá tổng hợp, tổng giá trị phải nhỏ hơn ngân sách đóng góp. Ngân sách được phân theo nguồn gốc của mỗi worklet, theo ngày và riêng biệt cho Protected Audience API và worklet Bộ nhớ dùng chung. Một khoảng thời gian cố định khoảng 24 giờ qua được dùng cho ngày đó. Nếu một báo cáo tổng hợp mới khiến bạn vượt quá ngân sách, thì báo cáo đó sẽ không được tạo.
Ngân sách đóng góp được biểu thị bằng tham số L1 và được đặt thành 216 (65.536) mỗi 10 phút mỗi ngày với điểm dự phòng là 220 (1.048.576). Hãy xem nội dung giải thích để tìm hiểu thêm về các thông số này.
Giá trị của ngân sách đóng góp là tuỳ ý, nhưng độ nhiễu được điều chỉnh theo tỷ lệ. Bạn có thể sử dụng ngân sách này để tối đa hoá tỷ lệ tín hiệu trên tạp âm trên các giá trị tóm tắt (sẽ được thảo luận thêm trong phần Tạp âm và điều chỉnh theo tỷ lệ).
Để tìm hiểu thêm về ngân sách đóng góp, hãy xem nội dung giải thích. Ngoài ra, hãy tham khảo phần Ngân sách đóng góp để biết thêm hướng dẫn.
Giới hạn đóng góp cho mỗi báo cáo
Tuỳ thuộc vào phương thức gọi, hạn mức đóng góp có thể khác nhau. Hiện tại, các báo cáo được tạo cho phương thức gọi API Bộ nhớ dùng chung bị giới hạn ở mức 20 lượt đóng góp trên mỗi báo cáo. Mặt khác, các phương thức gọi Protected Audience API bị giới hạn ở mức 100 nội dung đóng góp cho mỗi báo cáo. Các giới hạn này được chọn để cân bằng số lượng nội dung đóng góp có thể được nhúng với kích thước của tải trọng.
Đối với Bộ nhớ dùng chung, các nội dung đóng góp được thực hiện trong một thao tác run()
hoặc selectURL()
sẽ được phân thành một báo cáo. Đối với Protected Audience, các khoản đóng góp do một nguồn gốc thực hiện trong một phiên đấu giá sẽ được gộp lại với nhau.
Nội dung đóng góp có khoảng đệm
Các nội dung đóng góp được sửa đổi thêm bằng tính năng khoảng đệm. Việc thêm vào tải trọng giúp bảo vệ thông tin về số lượng đóng góp thực tế được nhúng trong báo cáo tổng hợp. Khoảng đệm tăng tải trọng bằng các đóng góp null
(tức là có giá trị 0) để đạt được một độ dài cố định.
Các báo cáo tổng hợp
Sau khi người dùng gọi API Tổng hợp riêng tư, trình duyệt sẽ tạo các báo cáo tổng hợp để Dịch vụ tổng hợp xử lý sau này nhằm tạo báo cáo tóm tắt. Báo cáo tổng hợp có định dạng JSON và chứa danh sách đóng góp được mã hoá, mỗi đóng góp là một cặp {aggregation key, aggregatable value}
.
Báo cáo tổng hợp được gửi với độ trễ ngẫu nhiên tối đa một giờ.
Các khoản đóng góp được mã hoá và không thể đọc được bên ngoài Dịch vụ tổng hợp. Dịch vụ tổng hợp sẽ giải mã các báo cáo và tạo một báo cáo tóm tắt. Khoá mã hoá cho trình duyệt và khoá giải mã cho Dịch vụ tổng hợp do điều phối viên phát hành, đóng vai trò là dịch vụ quản lý khoá. Trình điều phối lưu giữ danh sách các hàm băm nhị phân của hình ảnh dịch vụ để xác minh rằng phương thức gọi được phép nhận khoá giải mã.
Ví dụ về báo cáo tổng hợp có bật chế độ gỡ lỗi:
"aggregation_service_payloads": [
{
"debug_cleartext_payload": "omRkYXRhgaJldmFsdWVEAAAAgGZidWNrZXRQAAAAAAAAAAAAAAAAAAAE0mlvcGVyYXRpb25paGlzdG9ncmFt",
"key_id": "2cc72b6a-b92f-4b78-b929-e3048294f4d6",
"payload": "a9Mk3XxvnfX70FsKrzcLNZPy+00kWYnoXF23ZpNXPz/Htv1KCzl/exzplqVlM/wvXdKUXCCtiGrDEL7BQ6MCbQp1NxbWzdXfdsZHGkZaLS2eF+vXw2UmLFH+BUg/zYMu13CxHtlNSFcZQQTwnCHb"
}
],
"debug_key": "777",
"shared_info": "{\"api\":\"shared-storage\",\"debug_mode\":\"enabled\",\"report_id\":\"5bc74ea5-7656-43da-9d76-5ea3ebb5fca5\",\"reporting_origin\":\"https://localhost:4437\",\"scheduled_report_time\":\"1664907229\",\"version\":\"0.1\"}"
Bạn có thể kiểm tra các báo cáo tổng hợp trên trang chrome://private-aggregation-internals
:
Đối với mục đích thử nghiệm, bạn có thể sử dụng nút "Gửi báo cáo đã chọn" để gửi báo cáo đến máy chủ ngay lập tức.
Thu thập và báo cáo tổng hợp theo lô
Trình duyệt sẽ gửi các báo cáo tổng hợp đến nguồn gốc của worklet chứa lệnh gọi đến API Tổng hợp riêng tư, sử dụng đường dẫn đã biết:
- Đối với Bộ nhớ dùng chung:
/.well-known/private-aggregation/report-shared-storage
- Đối với Protected Audience:
/.well-known/private-aggregation/report-protected-audience
Tại các điểm cuối này, bạn sẽ cần vận hành một máy chủ — đóng vai trò là trình thu thập — nhận các báo cáo tổng hợp được gửi từ ứng dụng.
Sau đó, máy chủ sẽ phân lô báo cáo và gửi lô báo cáo đó đến Dịch vụ tổng hợp. Tạo lô dựa trên thông tin có trong tải trọng chưa mã hoá của báo cáo tổng hợp, chẳng hạn như trường shared_info
. Tốt nhất là mỗi lô nên chứa ít nhất 100 báo cáo.
Bạn có thể quyết định tạo lô hằng ngày hoặc hằng tuần. Chiến lược này rất linh hoạt và bạn có thể thay đổi chiến lược phân lô cho các sự kiện cụ thể mà bạn dự kiến sẽ có nhiều lượt hiển thị hơn, chẳng hạn như những ngày trong năm mà bạn dự kiến sẽ có nhiều lượt hiển thị hơn. Các lô phải bao gồm báo cáo từ cùng một phiên bản API, nguồn gốc báo cáo và thời gian lên lịch báo cáo.
Lọc mã nhận dạng
API và Dịch vụ tổng hợp riêng tư cho phép sử dụng mã nhận dạng lọc để xử lý các phép đo ở cấp độ chi tiết hơn, chẳng hạn như theo chiến dịch quảng cáo thay vì xử lý kết quả trong các truy vấn lớn hơn.
Để bắt đầu sử dụng tính năng này ngay hôm nay, sau đây là một số bước cơ bản để áp dụng cho cách triển khai hiện tại của bạn.
Các bước trong Bộ nhớ dùng chung
Nếu bạn đang sử dụng Shared Storage API trong luồng của mình:
Xác định vị trí bạn sẽ khai báo và chạy mô-đun Bộ nhớ dùng chung mới. Trong ví dụ sau, chúng tôi đã đặt tên tệp mô-đun là
filtering-worklet.js
, được đăng ký trongfiltering-example
.(async function runFilteringIdsExample () { await window.sharedStorage.worklet.addModule('filtering-worklet.js'); await window.sharedStorage.run('filtering-example', { keepAlive: true, privateAggregationConfig: { contextId: 'example-id', filteringIdMaxBytes: 8 // optional } }}); })();
Xin lưu ý rằng bạn có thể định cấu hình
filteringIdMaxBytes
cho mỗi báo cáo và nếu không đặt, giá trị mặc định sẽ là 1. Giá trị mặc định này là để ngăn chặn việc tăng kích thước tải trọng không cần thiết, từ đó làm tăng chi phí lưu trữ và xử lý. Hãy đọc thêm trong phần giải thích về khoản đóng góp linh hoạt.Trong tệp mà bạn đã sử dụng ở trên, trong trường hợp này là
filtering-worklet.js
, khi truyền một nội dung đóng góp đếnprivateAggregation.contributeToHistogram(...)
trong công việc Bộ nhớ dùng chung, bạn có thể chỉ định một mã nhận dạng lọc.// Within filtering-worklet.js class FilterOperation { async run() { let contributions = [{ bucket: 1234n, value: 56, filteringId: 3n // defaults to 0n if not assigned, type bigint }]; for (const c of contributions) { privateAggregation.contributeToHistogram(c); } … } }); register('filtering-example', FilterOperation);
Báo cáo tổng hợp sẽ được gửi đến nơi bạn đã xác định điểm cuối
/.well-known/private-aggregation/report-shared-storage
. Tiếp tục xem hướng dẫn lọc mã nhận dạng để tìm hiểu về những thay đổi cần thiết trong tham số công việc của Dịch vụ tổng hợp.
Sau khi quá trình tạo lô hoàn tất và được gửi đến Dịch vụ tổng hợp đã triển khai, kết quả đã lọc sẽ được phản ánh trong báo cáo tóm tắt cuối cùng.
Các bước sử dụng Protected Audience
Nếu bạn đang sử dụng Protected Audience API trong quy trình:
Trong quá trình triển khai Protected Audience hiện tại, bạn có thể thiết lập những nội dung sau để kết nối với tính năng Tổng hợp riêng tư. Không giống như Bộ nhớ dùng chung, bạn chưa thể định cấu hình kích thước tối đa của mã nhận dạng lọc. Theo mặc định, kích thước tối đa của mã nhận dạng bộ lọc là 1 byte và sẽ được đặt thành
0n
. Xin lưu ý rằng các giá trị này sẽ được đặt trong các hàm báo cáo Protected Audience (ví dụ:reportResult()
hoặcgenerateBid()
).const contribution = { ... filteringId: 0n }; privateAggregation.contributeToHistogram(contribution);
Báo cáo tổng hợp sẽ được gửi đến nơi bạn đã xác định điểm cuối
/.well-known/private-aggregation/report-protected-audience
. Sau khi quá trình tạo lô hoàn tất và được gửi đến Dịch vụ tổng hợp đã triển khai, kết quả đã lọc sẽ được phản ánh trong báo cáo tóm tắt cuối cùng. Bạn có thể xem các nội dung giải thích sau đây về API Báo cáo phân bổ và API Tổng hợp riêng tư, cũng như đề xuất ban đầu.
Tiếp tục đọc hướng dẫn lọc mã nhận dạng trong Dịch vụ tổng hợp hoặc chuyển đến các phần API Báo cáo phân bổ để đọc thông tin chi tiết hơn.
Dịch vụ tổng hợp
Dịch vụ tổng hợp nhận các báo cáo tổng hợp đã mã hoá từ trình thu thập và tạo báo cáo tóm tắt. Để biết thêm chiến lược về cách tạo lô báo cáo tổng hợp trong trình thu thập dữ liệu, hãy xem hướng dẫn tạo lô của chúng tôi.
Dịch vụ này chạy trong một môi trường thực thi đáng tin cậy (TEE), giúp đảm bảo tính toàn vẹn của dữ liệu, tính bảo mật của dữ liệu và tính toàn vẹn của mã. Nếu bạn muốn tìm hiểu kỹ hơn về cách sử dụng trình điều phối cùng với TEE, hãy đọc thêm về vai trò và mục đích của trình điều phối.
Báo cáo tóm tắt
Báo cáo tóm tắt cho phép bạn xem dữ liệu đã thu thập cùng với tiếng ồn. Bạn có thể yêu cầu báo cáo tóm tắt cho một tập hợp khoá nhất định.
Báo cáo tóm tắt chứa một tập hợp các cặp khoá-giá trị theo kiểu từ điển JSON. Mỗi cặp chứa:
bucket
: khoá tổng hợp dưới dạng chuỗi số nhị phân. Nếu khoá tổng hợp được sử dụng là "123", thì bộ chứa sẽ là "1111011".value
: giá trị tóm tắt cho một mục tiêu đo lường nhất định, được tổng hợp từ tất cả các báo cáo tổng hợp hiện có có thêm nhiễu.
Ví dụ:
[
{"bucket":` `"111001001",` `"value":` `"2558500"},
{"bucket":` `"111101001",` `"value":` `"3256211"},
{"bucket":` `"111101001",` `"value":` `"6536542"},
]
Độ nhiễu và tỷ lệ
Để bảo vệ quyền riêng tư của người dùng, Dịch vụ tổng hợp sẽ thêm nhiễu một lần vào mỗi giá trị tóm tắt mỗi khi có yêu cầu báo cáo tóm tắt. Các giá trị nhiễu được lấy ngẫu nhiên từ một phân phối xác suất Laplace. Mặc dù không kiểm soát trực tiếp cách tạp âm được thêm vào, nhưng bạn có thể tác động đến mức độ ảnh hưởng của tạp âm đối với dữ liệu đo lường.
Phân phối nhiễu giống nhau bất kể tổng của tất cả các giá trị tổng hợp. Do đó, giá trị tổng hợp càng cao thì tác động của nhiễu càng ít.
Ví dụ: giả sử phân phối nhiễu có độ lệch chuẩn là 100 và tập trung tại 0. Nếu giá trị báo cáo tổng hợp được thu thập (hoặc "giá trị tổng hợp") chỉ là 200, thì độ lệch chuẩn của nhiễu sẽ là 50% giá trị tổng hợp. Tuy nhiên, nếu giá trị tổng hợp là 20.000,thì độ lệch chuẩn của nhiễu sẽ chỉ là 0, 5% của giá trị tổng hợp. Do đó, giá trị tổng hợp là 20.000 sẽ có tỷ lệ tín hiệu trên tạp âm cao hơn nhiều.
Do đó, việc nhân giá trị tổng hợp của bạn với hệ số tỷ lệ có thể giúp giảm độ nhiễu. Hệ số tỷ lệ biểu thị mức độ bạn muốn điều chỉnh theo tỷ lệ một giá trị tổng hợp nhất định.
Việc tăng tỷ lệ các giá trị bằng cách chọn hệ số tỷ lệ lớn hơn sẽ làm giảm độ nhiễu tương đối. Tuy nhiên, điều này cũng khiến tổng số đóng góp trên tất cả các bộ chứa đạt đến giới hạn ngân sách đóng góp nhanh hơn. Việc giảm tỷ lệ các giá trị bằng cách chọn hằng số hệ số tỷ lệ nhỏ hơn sẽ làm tăng độ nhiễu tương đối, nhưng giảm nguy cơ đạt đến giới hạn ngân sách.
Để tính hệ số điều chỉnh thích hợp, hãy chia ngân sách đóng góp cho tổng giá trị tổng hợp tối đa trên tất cả các khoá.
Hãy xem tài liệu về Ngân sách đóng góp để tìm hiểu thêm.
Tương tác và chia sẻ ý kiến phản hồi
API Tổng hợp riêng tư đang được thảo luận và có thể thay đổi trong tương lai. Nếu bạn dùng thử API này và có ý kiến phản hồi, chúng tôi rất mong được nghe.
- GitHub: Đọc nội dung giải thích, đặt câu hỏi và tham gia thảo luận.
- Hỗ trợ nhà phát triển: Đặt câu hỏi và tham gia thảo luận trên kho lưu trữ Hỗ trợ nhà phát triển Hộp cát về quyền riêng tư.
- Hãy tham gia nhóm Shared Storage API (API Bộ nhớ dùng chung) và nhóm Protected Audience API (Protected Audience API) để xem các thông báo mới nhất liên quan đến tính năng Tổng hợp riêng tư.