Xuất bản: Ngày 30 tháng 1 năm 2026
Khi xây dựng AI hỗ trợ cho Hiệu suất, thách thức kỹ thuật chính là giúp Gemini hoạt động trơn tru với các dấu vết hiệu suất được ghi lại trong DevTools.
Các Mô hình ngôn ngữ lớn (LLM) hoạt động trong một "cửa sổ ngữ cảnh", tức là có giới hạn nghiêm ngặt về lượng thông tin mà các mô hình này có thể xử lý cùng một lúc. Dung lượng này được đo bằng mã thông báo. Đối với các mô hình Gemini, một mã thông báo tương đương với khoảng 4 ký tự.
Dấu vết hiệu suất là các tệp JSON có kích thước lớn, thường có dung lượng vài megabyte. Việc gửi một dấu vết thô sẽ ngay lập tức làm cạn kiệt cửa sổ ngữ cảnh của mô hình và không còn chỗ cho các câu hỏi của bạn.
Để có thể cung cấp tính năng hỗ trợ của AI cho chiến dịch Tối đa hoá hiệu suất, chúng tôi phải thiết kế một hệ thống tối đa hoá lượng dữ liệu hữu ích cho mô hình ngôn ngữ lớn (LLM) với mức sử dụng mã thông báo tối thiểu. Trong blog này, bạn có thể tìm hiểu về các kỹ thuật mà chúng tôi đã sử dụng để thực hiện việc này và áp dụng chúng cho các dự án của riêng bạn.
Điều chỉnh bối cảnh ban đầu
Gỡ lỗi hiệu suất của một trang web là một việc phức tạp. Nhà phát triển có thể xem toàn bộ dấu vết để biết bối cảnh, hoặc tập trung vào Các chỉ số quan trọng chính và khoảng thời gian liên quan của dấu vết, hoặc thậm chí đi sâu vào chi tiết và tập trung vào các sự kiện riêng lẻ như lượt nhấp hoặc lượt cuộn và ngăn xếp lệnh gọi liên quan.
Để hỗ trợ quá trình gỡ lỗi, tính năng hỗ trợ AI của Công cụ cho nhà phát triển cần phải phù hợp với hành trình của nhà phát triển và chỉ hoạt động với dữ liệu liên quan để đưa ra lời khuyên cụ thể cho trọng tâm của nhà phát triển. Vì vậy, thay vì luôn gửi toàn bộ dấu vết, chúng tôi đã xây dựng tính năng hỗ trợ AI để cắt dữ liệu dựa trên tác vụ gỡ lỗi của bạn:
| Gỡ lỗi tác vụ | Dữ liệu ban đầu được gửi đến trợ lý AI |
|---|---|
| Trò chuyện về dấu vết hiệu suất | Tóm tắt dấu vết: Báo cáo dựa trên văn bản, bao gồm thông tin cấp cao từ dấu vết và phiên gỡ lỗi. Bao gồm URL của trang, các điều kiện điều tiết, chỉ số hiệu suất chính (LCP, INP, CLS), danh sách thông tin chi tiết có sẵn và bản tóm tắt CrUX (nếu có). |
| Trò chuyện về Thông tin chi tiết về hiệu suất | Thông tin tóm tắt về dấu vết và tên của Thông tin chi tiết về hiệu suất đã chọn. |
| Trò chuyện về một việc cần làm trong dấu vết | Bản tóm tắt dấu vết và cây lệnh gọi được chuyển đổi tuần tự, nơi có tác vụ đã chọn. |
| Trò chuyện về yêu cầu kết nối mạng | Thông tin tóm tắt về dấu vết, khoá yêu cầu và dấu thời gian đã chọn |
| Tạo chú thích theo dõi | Cây lệnh gọi được chuyển đổi tuần tự, nơi có tác vụ đã chọn. Cây được chuyển đổi tuần tự xác định nhiệm vụ nào được chọn. |
Hầu như lúc nào hệ thống cũng gửi bản tóm tắt dấu vết để cung cấp bối cảnh ban đầu cho Gemini, mô hình cơ bản của trợ lý AI. Đối với chú thích do AI tạo, phần này sẽ bị bỏ qua.
Cung cấp công cụ cho AI
Tính năng trợ lý AI trong Công cụ cho nhà phát triển hoạt động như một tác nhân. Điều này có nghĩa là mô hình có thể tự động truy vấn thêm dữ liệu dựa trên câu lệnh ban đầu của nhà phát triển và ngữ cảnh ban đầu được chia sẻ với mô hình. Để truy vấn thêm dữ liệu, chúng tôi đã cung cấp cho trợ lý AI một tập hợp các hàm được xác định trước mà trợ lý này có thể gọi. Một mẫu được gọi là Gọi hàm hoặc Sử dụng công cụ.
Dựa trên các hành trình gỡ lỗi đã nêu trước đó, chúng tôi đã xác định một tập hợp các chức năng chi tiết cho tác nhân. Các hàm này đi sâu vào những điểm cụ thể được coi là quan trọng dựa trên ngữ cảnh ban đầu, tương tự như cách một nhà phát triển là con người sẽ tiếp cận việc gỡ lỗi hiệu suất. Tập hợp các hàm như sau:
| Chức năng | Mô tả |
|---|---|
getInsightDetails(name) |
Trả về thông tin chi tiết về một Thông tin chi tiết cụ thể về hiệu suất (ví dụ: thông tin chi tiết về lý do LCP bị gắn cờ). |
getEventByKey(key) |
Trả về các thuộc tính chi tiết cho một sự kiện cụ thể. |
getMainThreadTrackSummary(start, end) |
Trả về bản tóm tắt hoạt động của luồng chính cho các ranh giới đã cho, bao gồm cả bản tóm tắt từ trên xuống, từ dưới lên và của bên thứ ba. |
getNetworkTrackSummary(start, end) |
Trả về thông tin tóm tắt về hoạt động mạng trong khoảng thời gian đã cho. |
getDetailedCallTree(event_key) |
Trả về toàn bộ cây lệnh gọi cho một sự kiện cụ thể trên luồng chính trong dấu vết hiệu suất |
getFunctionCode(url, line, col) |
Trả về mã nguồn cho một hàm được xác định tại một vị trí cụ thể trong tài nguyên, được chú thích bằng dữ liệu hiệu suất thời gian chạy từ dấu vết hiệu suất |
getResourceContent(url) |
Trả về nội dung của một tài nguyên văn bản mà trang sử dụng (ví dụ: HTML hoặc CSS). |
Bằng cách giới hạn nghiêm ngặt việc truy xuất dữ liệu đối với các lệnh gọi hàm này, chúng tôi đảm bảo rằng chỉ thông tin liên quan mới được đưa vào cửa sổ ngữ cảnh ở định dạng được xác định rõ, giúp tối ưu hoá việc sử dụng mã thông báo.
Ví dụ về một thao tác của tác nhân
Hãy xem một ví dụ thực tế về cách trợ lý AI sử dụng tính năng gọi hàm để truy xuất thêm thông tin. Sau câu lệnh ban đầu "Tại sao yêu cầu này lại chậm?", Tính năng hỗ trợ AI có thể gọi các hàm sau theo cách gia tăng:
getEventByKey: Tìm nạp thông tin chi tiết về thời gian (TTFB, thời gian tải xuống) của yêu cầu cụ thể mà người dùng đã chọn.getMainThreadTrackSummary: Kiểm tra xem luồng chính có bận (bị chặn) hay không khi yêu cầu lẽ ra phải bắt đầu.getNetworkTrackSummary: Phân tích xem có tài nguyên nào khác cạnh tranh băng thông cùng lúc hay không.getInsightDetails: Kiểm tra xem Trace Summary (Tóm tắt dấu vết) đã đề cập đến một thông tin chi tiết liên quan đến yêu cầu này dưới dạng nút thắt cổ chai hay chưa.
Bằng cách kết hợp kết quả của các lệnh gọi này, trợ lý AI có thể chẩn đoán và đề xuất các bước có thể thực hiện, chẳng hạn như đề xuất cải thiện mã bằng getFunctionCode hoặc tối ưu hoá việc tải tài nguyên dựa trên getResourceContent.
Tuy nhiên, việc truy xuất dữ liệu có liên quan chỉ là một nửa thách thức. Ngay cả khi các hàm cung cấp dữ liệu chi tiết, dữ liệu do các hàm đó trả về có thể có kích thước lớn. Ví dụ: getDetailedCallTree có thể trả về một cây có hàng trăm nút. Trong JSON tiêu chuẩn, sẽ có nhiều { và } chỉ để lồng!
Do đó, cần có một định dạng đủ dày đặc để tiết kiệm mã thông báo, nhưng vẫn đủ cấu trúc để LLM hiểu và tham chiếu.
Chuyển đổi dữ liệu thành chuỗi
Hãy tìm hiểu sâu hơn về cách chúng tôi tiếp cận thách thức này, tiếp tục với ví dụ về cây lệnh gọi, vì cây lệnh gọi chiếm phần lớn dữ liệu trong dấu vết hiệu suất. Để tham khảo, các ví dụ sau đây cho thấy một tác vụ duy nhất trong ngăn xếp lệnh gọi ở định dạng JSON:
{
"id": 2,
"name": "animate",
"selected": true,
"duration": 150,
"selfTime": 20,
"children": [3, 5, 6, 7, 10, 11, 12]
}
Một dấu vết hiệu suất có thể chứa hàng nghìn dấu vết như vậy, như minh hoạ trong ảnh chụp màn hình sau. Mỗi hộp nhỏ có màu được biểu thị bằng cấu trúc đối tượng này.

Định dạng này phù hợp để làm việc theo cách lập trình trong Công cụ cho nhà phát triển, nhưng lại lãng phí đối với LLM vì những lý do sau:
- Khoá dư thừa: Các chuỗi như
"duration","selfTime"và"children"được lặp lại cho mọi nút trong cây lệnh gọi. Vì vậy, một cây có 500 nút được gửi đến một mô hình sẽ tiêu thụ mã thông báo cho mỗi khoá trong số đó 500 lần. - Danh sách chi tiết: Việc liệt kê từng mã nhận dạng con riêng lẻ thông qua
childrensẽ tiêu tốn một lượng lớn mã thông báo, đặc biệt là đối với những tác vụ kích hoạt nhiều sự kiện hạ lưu.
Việc triển khai một định dạng tiết kiệm mã thông báo cho tất cả dữ liệu được dùng với tính năng hỗ trợ của AI cho Hiệu suất là một quy trình từng bước.
Lần lặp lại đầu tiên
Khi bắt đầu phát triển tính năng hỗ trợ dựa trên AI cho Hiệu suất, chúng tôi đã tối ưu hoá tốc độ vận chuyển. Phương pháp tối ưu hoá mã thông báo của chúng tôi rất cơ bản, chúng tôi đã loại bỏ dấu ngoặc nhọn và dấu phẩy khỏi JSON gốc, dẫn đến một định dạng như sau:
allUrls = [...]
Node: 1 - update
Selected: false
Duration: 200
Self Time: 50
Children:
2 - animate
Node: 2 - animate
Selected: true
Duration: 150
Self Time: 20
URL: 0
Children:
3 - calculatePosition
5 - applyStyles
6 - applyStyles
7 - calculateLayout
10 - applyStyles
11 - applyStyles
12 - applyStyles
Node: 3 - calculatePosition
Selected: false
Duration: 15
Self Time: 2
URL: 0
Children:
4 - getBoundingClientRect
...
Nhưng phiên bản đầu tiên này chỉ cải thiện một chút so với JSON thô. Thao tác này vẫn liệt kê rõ ràng các nút con theo mã nhận dạng và tên, đồng thời thêm các khoá lặp lại mang tính mô tả (Node:, Selected:, Duration:, …) vào trước mỗi dòng.
Tối ưu hoá danh sách nút con
Để tối ưu hoá hơn nữa, chúng tôi đã xoá tên cho các nút con (calculatePosition, applyStyles, … trong ví dụ trước). Vì tính năng hỗ trợ bằng AI có quyền truy cập vào tất cả các nút thông qua lệnh gọi hàm và thông tin này đã có trong tiêu đề nút (Node: 3 - calculatePosition), nên không cần lặp lại thông tin này. Điều này cho phép chúng ta thu gọn Children thành một danh sách số nguyên đơn giản:
Node: 2 - animate
Selected: true
Duration: 150
Self Time: 20
URL: 0
Children: 3, 5, 6, 7, 10, 11, 12
..
Mặc dù đây là một bước cải tiến đáng kể so với trước đây, nhưng vẫn còn nhiều cơ hội để tối ưu hoá hơn nữa. Khi xem ví dụ trước, bạn có thể nhận thấy Children gần như tuần tự, chỉ thiếu 4, 8 và 9.
Lý do là trong lần thử đầu tiên, chúng tôi đã sử dụng thuật toán Tìm kiếm theo chiều sâu (DFS) để chuyển đổi tuần tự dữ liệu cây từ dấu vết Hiệu suất. Điều này dẫn đến việc các mã nhận dạng không tuần tự cho các nút anh em, khiến chúng tôi phải liệt kê từng mã nhận dạng riêng lẻ.
Chúng tôi nhận thấy rằng nếu lập chỉ mục lại cây bằng cách sử dụng Tìm kiếm theo chiều rộng (BFS), chúng ta sẽ nhận được các mã nhận dạng tuần tự. Nhờ đó, chúng ta có thể thực hiện một bước tối ưu hoá khác. Thay vì liệt kê từng mã nhận dạng riêng lẻ, giờ đây, chúng ta có thể biểu thị hàng trăm phần tử con bằng một dải mã nhận dạng duy nhất, chẳng hạn như 3-9 cho ví dụ ban đầu.
Ký hiệu nút cuối cùng, với danh sách Children được tối ưu hoá sẽ có dạng như sau:
allUrls = [...]
Node: 2 - animate
Selected: true
Duration: 150
Self Time: 20
URL: 0
Children: 3-9
Giảm số lượng khoá
Sau khi tối ưu hoá danh sách nút, chúng tôi chuyển sang các khoá dự phòng. Chúng tôi bắt đầu bằng cách xoá tất cả các khoá khỏi định dạng trước đó, dẫn đến kết quả như sau:
allUrls = [...]
2;animate;150;20;0;3-10
Mặc dù hiệu quả về mã thông báo, nhưng chúng tôi vẫn cần đưa ra chỉ dẫn cho Gemini về cách hiểu dữ liệu này. Do đó, lần đầu tiên gửi cây lệnh gọi đến Gemini, chúng tôi đã đưa ra lời nhắc sau:
...
Each call frame is presented in the following format:
'id;name;duration;selfTime;urlIndex;childRange;[S]'
Key definitions:
* id: A unique numerical identifier for the call frame.
* name: A concise string describing the call frame (e.g., 'Evaluate Script', 'render', 'fetchData').
* duration: The total execution time of the call frame, including its children.
* selfTime: The time spent directly within the call frame, excluding its children's execution.
* urlIndex: Index referencing the "All URLs" list. Empty if no specific script URL is associated.
* childRange: Specifies the direct children of this node using their IDs. If empty ('' or 'S' at the end), the node has no children. If a single number (e.g., '4'), the node has one child with that ID. If in the format 'firstId-lastId' (e.g., '4-5'), it indicates a consecutive range of child IDs from 'firstId' to 'lastId', inclusive.
* S: **Optional marker.** The letter 'S' appears at the end of the line **only** for the single call frame selected by the user.
....
Mặc dù nội dung mô tả định dạng này sẽ tốn một khoản chi phí mã thông báo, nhưng đây là chi phí cố định và chỉ phải trả một lần cho toàn bộ cuộc trò chuyện. Chi phí này thấp hơn so với khoản tiết kiệm mà bạn có được thông qua các hoạt động tối ưu hoá trước đó.
Kết luận
Tối ưu hoá việc sử dụng mã thông báo là một yếu tố quan trọng cần cân nhắc khi xây dựng bằng AI. Bằng cách chuyển từ JSON thô sang định dạng tuỳ chỉnh chuyên biệt, lập chỉ mục lại các cây bằng Breadth-First Search và sử dụng lệnh gọi công cụ để tìm nạp dữ liệu theo yêu cầu, chúng tôi đã giảm đáng kể số lượng mã thông báo mà AI hỗ trợ trong Chrome DevTools sử dụng.
Những hoạt động tối ưu hoá này là điều kiện tiên quyết để bật tính năng hỗ trợ AI cho dấu vết hiệu suất. Do cửa sổ ngữ cảnh hạn chế, nếu không, mô hình này sẽ không thể xử lý khối lượng dữ liệu khổng lồ. Nhưng định dạng được tối ưu hoá cho phép một tác nhân hiệu suất có thể duy trì nhật ký trò chuyện dài hơn và cung cấp câu trả lời chính xác hơn, nhận biết được ngữ cảnh mà không bị nhiễu làm phiền.
Chúng tôi hy vọng những kỹ thuật này sẽ truyền cảm hứng cho bạn xem xét kỹ hơn cấu trúc dữ liệu của riêng mình khi thiết kế cho AI. Để bắt đầu sử dụng AI trong các ứng dụng web, hãy khám phá Tìm hiểu về AI trên web.dev.