發布日期:2026 年 1 月 30 日
建構效能 AI 輔助功能時,主要的工程挑戰是讓 Gemini 順利處理在開發人員工具中記錄的效能追蹤記錄。
大型語言模型 (LLM) 的運作範圍是「脈絡窗口」,也就是一次可處理的資訊量上限。這項容量是以權杖為單位。以 Gemini 模型來說,一個符記約為一組四個字元。
效能追蹤記錄是龐大的 JSON 檔案,通常包含數百萬位元組。傳送原始追蹤記錄會立即耗盡模型的脈絡窗口,導致無法提出問題。
為提供成效 AI 輔助功能,我們必須建構一個系統,盡可能提供大量實用資料給大型語言模型,同時盡量減少權杖用量。在這篇網誌中,您可以瞭解我們使用的技術,並將這些技術套用至自己的專案。
調整初始情境
偵錯網站效能是一項複雜的工作。開發人員可以查看完整追蹤記錄瞭解脈絡,也可以專注於核心網頁指標和追蹤記錄的相關時間範圍,甚至深入瞭解詳細資料,專注於個別事件 (例如點擊或捲動) 及其相關呼叫堆疊。
為輔助偵錯程序,開發人員工具的 AI 輔助功能必須符合這些開發人員歷程,且只使用相關資料,提供與開發人員專注事項相關的建議。因此,我們並非一律傳送完整追蹤記錄,而是建構 AI 輔助功能,根據您的偵錯工作分割資料:
| 偵錯工作 | 最初傳送給 AI 輔助功能的資料 |
|---|---|
| 討論效能追蹤記錄 | 追蹤記錄摘要:以文字為主的報表,內含追蹤記錄和偵錯工作階段的概略資訊。包括網頁網址、節流條件、主要成效指標 (LCP、INP、CLS)、可用洞察資訊清單,以及可用的 CrUX 摘要。 |
| 討論成效洞察 | 追蹤記錄摘要,以及所選成效洞察的名稱。 |
| 討論追蹤記錄中的工作 | 追蹤摘要,以及所選工作所在的序列化呼叫樹狀結構。 |
| 討論網路要求 | 追蹤摘要,以及所選要求金鑰和時間戳記 |
| 產生追蹤註解 | 所選工作所在的序列化呼叫樹狀結構。序列化樹狀結構會指出選取的工作。 |
系統幾乎都會傳送追蹤記錄摘要,為 AI 輔助功能的基礎模型 Gemini 提供初步脈絡資訊。如果是 AI 生成的註解,則會省略。
將工具提供給 AI
開發人員工具中的 AI 輔助功能會做為代理。也就是說,根據開發人員的初始提示和分享的初始背景資訊,Gemini 可以自主查詢更多資料。為了查詢更多資料,我們為 AI 輔助功能提供了一組可呼叫的預先定義函式。這類模式稱為「函式呼叫」或「工具使用」。
根據先前列出的偵錯歷程,我們為代理定義了一組細微函式。這些函式會根據初始環境深入探究重要細節,類似於人類開發人員進行效能偵錯的方式。函式集如下:
| 函式 | 說明 |
|---|---|
getInsightDetails(name) |
傳回特定成效洞察的詳細資訊 (例如,LCP 遭到標記的原因)。 |
getEventByKey(key) |
傳回單一特定活動的詳細屬性。 |
getMainThreadTrackSummary(start, end) |
傳回指定界限內主要執行緒活動的摘要,包括由上而下、由下而上和第三方摘要。 |
getNetworkTrackSummary(start, end) |
傳回指定時間範圍內的網路活動摘要。 |
getDetailedCallTree(event_key) |
傳回效能追蹤記錄中特定主執行緒事件的完整呼叫樹狀結構 |
getFunctionCode(url, line, col) |
傳回資源中特定位置定義的函式原始碼,並附上效能追蹤記錄中的執行階段效能資料。 |
getResourceContent(url) |
傳回網頁使用的文字資源內容 (例如 HTML 或 CSS)。 |
嚴格限制資料擷取作業只能透過這些函式呼叫進行,可確保只有相關資訊會以明確定義的格式進入脈絡視窗,進而提高權杖用量。
代理程式作業範例
讓我們看看實際範例,瞭解 AI 輔助功能如何使用函式呼叫來擷取更多資訊。在「為什麼這個要求速度緩慢?」的初始提示後,AI 輔助功能可以逐步呼叫下列函式:
getEventByKey:擷取使用者選取特定要求的詳細時間細目 (TTFB、下載時間)。getMainThreadTrackSummary:檢查要求應啟動時,主要執行緒是否忙碌 (遭到封鎖)。getNetworkTrackSummary:分析是否有其他資源同時爭用頻寬。getInsightDetails:檢查「追蹤摘要」是否已提及與此要求相關的洞察資訊,指出要求是瓶頸。
結合這些呼叫的結果後,AI 輔助功能就能提供診斷結果,並提出可執行的步驟,例如建議使用 getFunctionCode 改善程式碼,或根據 getResourceContent 最佳化資源載入作業。
不過,擷取相關資料只是其中一半的挑戰。即使函式提供精細資料,這些函式傳回的資料也可能很大。以 getDetailedCallTree 為例,getDetailedCallTree 可以傳回含有數百個節點的樹狀結構。在標準 JSON 中,這會是許多 { 和 },
只是為了巢狀結構!
因此,我們需要一種格式,既要夠密集,能有效運用權杖,又要夠結構化,讓 LLM 能夠瞭解及參照。
序列化資料
讓我們繼續以呼叫樹狀結構為例,深入瞭解我們如何解決這項挑戰,因為呼叫樹狀結構占效能追蹤資料的大宗。如需參考,以下範例顯示 JSON 格式的呼叫堆疊中單一工作:
{
"id": 2,
"name": "animate",
"selected": true,
"duration": 150,
"selfTime": 20,
"children": [3, 5, 6, 7, 10, 11, 12]
}
如下列螢幕截圖所示,一個成效追蹤記錄可能包含數千個這類事件。每個小色塊都是使用這個物件結構表示。

這種格式很適合在開發人員工具中以程式輔助方式使用,但對 LLM 來說卻很浪費資源,原因如下:
- 多餘的鍵:呼叫樹狀結構中每個節點都會重複使用
"duration"、"selfTime"和"children"等字串。因此,如果將含有 500 個節點的樹狀結構傳送至模型,系統會針對每個鍵耗用 500 次權杖。 - 詳細清單:透過
children個別列出每個子項 ID 會耗用大量權杖,特別是會觸發許多下游事件的工作。
為所有用於 AI 輔助成效的資料導入權杖效率格式,是逐步完成的過程。
第一次疊代
我們開始為「成效」頁面開發 AI 輔助功能時,首先著重於運送速度。我們採用的權杖最佳化方法很基本,就是從大括號和半形逗號中移除原始 JSON,產生如下格式:
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
...
但這個第一版只比原始 JSON 略有改善。但仍會明確列出具有 ID 和名稱的節點子項,並在每行前面加上重複的描述性鍵 (Node:、Selected:、Duration: 等)。
最佳化子節點清單
為進一步最佳化,我們移除了節點子項的名稱 (上例中的 calculatePosition、applyStyles 等)。由於 AI 輔助功能可透過函式呼叫存取所有節點,且這項資訊已位於節點標題 (Node: 3 - calculatePosition) 中,因此不需要重複提供這項資訊。這讓我們得以將 Children 摺疊成簡單的整數清單:
Node: 2 - animate
Selected: true
Duration: 150
Self Time: 20
URL: 0
Children: 3, 5, 6, 7, 10, 11, 12
..
雖然這項做法比以往有顯著進步,但仍有進一步的優化空間。查看上一個範例時,您可能會發現 Children 幾乎是連續的,只缺少 4、8 和 9。
原因是在第一次嘗試時,我們使用深度優先搜尋 (DFS) 演算法,從效能追蹤記錄序列化樹狀資料。這導致同層級節點的 ID 不是連續的,因此我們必須個別列出每個 ID。
我們發現,如果使用廣度優先搜尋 (BFS) 重新建立樹狀結構的索引,我們就能取得連續 ID,進而進行其他最佳化。現在我們不必列出個別 ID,而是可以透過單一精簡範圍 (例如原始範例中的 3-9),代表數百名兒童。
最終節點標記 (含最佳化 Children 清單) 如下所示:
allUrls = [...]
Node: 2 - animate
Selected: true
Duration: 150
Self Time: 20
URL: 0
Children: 3-9
減少索引鍵數量
節點清單最佳化完成後,我們接著處理多餘的金鑰。我們首先從先前的格式中移除所有鍵,結果如下:
allUrls = [...]
2;animate;150;20;0;3-10
雖然這種做法的權杖用量較少,但我們仍需指示 Gemini 如何解讀這項資料。因此,我們第一次將呼叫樹狀結構傳送給 Gemini 時,加入了以下提示:
...
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.
....
雖然這種格式說明會產生權杖費用,但這是靜態費用,整場對話只需支付一次。但先前最佳化作業帶來的節省金額,已超過這筆費用。
結論
使用 AI 建構內容時,請務必盡量減少權杖用量。我們將原始 JSON 轉換為專用的自訂格式,使用廣度優先搜尋重新建立樹狀結構索引,並使用工具呼叫依需求擷取資料,大幅減少 Chrome 開發人員工具中的 AI 輔助功能消耗的符記數量。
啟用成效追蹤的 AI 輔助功能前,必須先完成這些最佳化作業。否則,由於脈絡視窗有限,模型無法處理大量資料。但最佳化格式可讓效能代理程式維持較長的對話記錄,並提供更準確、符合情境的答案,不會因雜訊而不知所措。
希望這些技巧能啟發您,在設計 AI 應用程式時,重新審視自己的資料結構。如要在網頁應用程式中開始使用 AI,請參閱 web.dev 上的「瞭解 AI」。