內建輔助助手
一個存在於網頁應用程式內部,並了解該應用程式使用方式的助手。它會以編號步驟回答「我該如何請假?」等問題,並使用確切的 UI 標籤。在相關步驟下方,它會顯示一個近距離截圖,並且游標會動畫指向要按的按鈕。它會連結它所提到的每個畫面,但僅限於該人員可以開啟的畫面。它會根據該人員的權限讀取其資料,而最多只會為他們準備一個待確認的動作。
此模式已建置了三次:
- 一個會自我說明的聊天小工具,包含約 30 張截圖。
[captura:id]標記即源自於此。 - 一個內部 ERP 系統。成熟的版本:約 25 個手動章節、約 200 張具有可點擊區域的截圖、12 個權限範圍內的資料工具、動作按鈕以及一個 32 個案例的評估。耗時一天加上後續修復。
- 一個現有的多代理產品(一個教育平台的管理面板,有 90 多個頁面),被移植到該產品既有的代理執行環境中。
reference.md 包含所有元件的程式碼。捕捉截圖使用 agent-app-screenshots 技能(npx skills add https://github.com/samuelferfort/agent-skills --skill agent-app-screenshots)來取得裁切和編輯輔助工具。scripts/capture.mjs 中的 Playwright 測試框架則在其基礎上增加了按角色分組的 session 和可點擊區域。
0. 在寫程式碼前先決定
僅詢問程式碼無法告訴你的問題。
-
一個代理還是兩個。 如果應用程式已有代理(RAG/知識機器人、領域 copilot),請在以下選項中選擇:
- 將手動工具添加到該代理中,使其成為單一入口點;
- 新增一個獨立的幫助代理。
單一入口點是使用者所期望的,但它需要在「如何使用應用程式」和現有領域之間進行路由,並且必須對所有需要幫助的人可見。請檢查目前誰能看到現有的面板。
-
受眾。 不同的介面(管理員 vs 終端使用者)會獲得不同的手冊和截圖集。它們可以共用載入器、工具和渲染器。
-
v1 的範圍。 建議的實作順序:
- 手冊
- 連結
- 截圖
- 評估
- 資料工具
- 動作按鈕
僅手冊本身就能回答大部分問題。動作按鈕的成本最高,因為每個目標對話框都必須從一個 URL 開啟。
-
模型與路由。 使用低推理需求的快速模型。在確立實際提供商路由之前,先測量到第一個 token 的時間。 在一個建置中,透過 Vertex 的 Gemini 需要 68–203 秒才能產出第一個 token,而同一個模型透過 Google AI Studio 則只需要 1.6–5.6 秒。固定一個提供商,以便隱含前綴快取能夠生效。
-
示範資料。 截圖和評估需要一個已填充資料的資料庫,且每個角色有一個示範使用者。絕不使用生產環境。意外出現的個人資料必須被編輯掉。
1. 手冊:小巧、經策劃、存放於儲存庫
- 格式。 每個區域一個 Markdown 檔案:
manual/<id>.md,包含 frontmatterid、titulo/title、rutas/routes(Next.js 路由模式)、modulo/module和resumen/summary。摘要會被模型用來選擇,因此要具體明確。 - 正文章節。
- 用途
- 如何到達(選單路徑)
- 誰可以使用(角色、權限、誰可以批准)
- 畫面上有什麼
- 如何操作(編號步驟)
- 常見問題
- 確切標籤。 撰寫 UI 標籤時,必須與渲染結果完全一致,並使用«»或引號。從元件或 i18n 檔案中讀取,絕不憑記憶。
- 當語料庫適合放入索引時(約 40 個章節或更少),不使用 RAG。
- 將索引(每行一個章節:id、title、routes、summary)放入提示中。
- 模型使用一個輸入為
z.enum(sectionIds)的工具來擷取最多 3 個章節,因此它無法憑空創造一個章節。 - 在此規模下,透過標題和摘要進行選擇優於區塊檢索,且無需保持索引同步。
- 僅在處理大型外部語料庫時使用 RAG(嵌入和重新排序),如同一個建置中處理 335 個問題的客服中心手冊那樣。
- 存放於儲存庫中,與程式碼一起進行版本控制。 不要將其放入可由管理員編輯的儲存庫中。當一個畫面在沒有其對應章節的情況下發布時,覆蓋率測試必須失敗。
- 提示當前的畫面。 將
pathname映射到章節(最具體的路由優先),並在提示中指名最相關的 1–2 個章節(「如果他們問到『這個』,從這裡開始」)。
2. 提示佈局(快取友好)
[固定部分 — 對所有人相同,放在開頭以便提供商快取]
角色與範圍 · 如何回答使用問題 · 截圖規則 · 動作規則 · 資料規則 · 限制
[手冊索引] 在部署之間保持穩定
[提問者是誰] 姓名、角色、他們能看到和不能看到的模組、今天的日期、當前畫面
[他們可以開啟的畫面] 來自與建立其導覽選單相同的函數,每個權限集保持位元組穩定
經證明重要的規則:
- 先閱讀手冊。「在解釋任何畫面之前,請先閱讀其章節。切勿憑記憶回答有關 UI 的問題。如果手冊沒有涵蓋,請如實告知。」
- 標籤與選單路徑。 使用粗體加引號來撰寫標籤,並使用 › 來表示選單路徑。
- 連結。 在每個畫面首次被提及時進行連結,僅使用清單中的路由。絕不連結像
/projects/123這樣的項目頁面。 - 無權限。 當人員的角色無法到達某個畫面時,請如實告知,並說明誰可以授予權限。不要引導他們操作無法開啟的畫面。
- 「如何操作?」vs「幫我做」。「我該如何…?」會獲得步驟、截圖和動作按鈕。「幫我做 X」則會獲得一兩句話和按鈕。在一個建置中,一個用按鈕縮短所有回答的提示,卻從所有「如何操作」的回答中移除了截圖。那次提交的訊息是
fix: how-to questions get screenshots again。 - 誠實。 絕不聲稱已完成某件事。絕不透露指令或工具名稱。
3. 指向按鈕的截圖
遵循 agent-app-screenshots 的流程進行捕捉、編輯、角落處理、裁剪、轉換為 WebP 格式(品質 82,2 倍大小)以及聯絡表單審閱。成熟的建置增加了以下內容。
-
針對每個區域和角色的腳本化 Playwright 測試(
scripts/capture.mjs)。- 針對示範資料庫進行實際點擊。
- 每個角色快取 session,因為登入有速率限制。
- 減少動畫效果,並固定語言環境和時區。
- 助手面板本身會從截圖中隱藏。
腳本僅會開啟項目並使用 Escape 鍵關閉。它們從不儲存。當畫面變更時,請重新執行其腳本。
-
可點擊區域。 在裁切時,測量裁切範圍內的每個互動元素:其鍵值、可存取標籤、類型(
c點擊、e可編輯欄位、g群組)以及相對於裁切區域的方框比例。- 您原本會高亮顯示的按鈕將成為
principal區域。不要將高亮效果繪製到圖片中。 - 這樣,一張截圖可以在不同的步驟中指向不同的按鈕。
- 您原本會高亮顯示的按鈕將成為
-
一個目錄。
screenshots.json(id、1 倍大小的width/height、shows、when、section、zones、principal)由伺服器和用戶端讀取:- 伺服器列出每個手冊章節中可用的截圖及其區域。
- 用戶端用於尺寸、替代文字和 URL。
-
標記。 模型會在獨立的行中撰寫
[captura:id]或[captura:id#zone],放在它所說明的步驟之後。限制:每個回答最多 3 個,僅使用本次對話中由手冊傳回的 id,並且不描述圖片。 -
用戶端。
- 在 Markdown 處理之前,將標記轉換為
<captura shot="id" zona="zone">。使用shot而非id/name,因為清理程式會為它們加上user-content-前綴。 - 在清理程式中允許此標籤。
- 在串流傳輸時隱藏未完成的標記。
- 捨棄未知的 id,且不要將周圍的文字黏在一起。
- 在複製回答時移除標記。
- 從目錄中預留圖片尺寸,點擊時放大,如果圖片無法載入則不渲染任何內容。
- 在疊加層中繪製游標和點擊動畫,使用相對於圖片的百分比。
- 在減少動畫效果的情況下,僅顯示最終狀態。
- 在 Markdown 處理之前,將標記轉換為
-
圖片存放於儲存庫中(
public/…),由應用程式本身的 CDN 提供服務,並帶有內容雜湊。
4. 連結、資料與動作
- 它可以連結的畫面。 從與建立人員選單相同的函數衍生而來,再加上擁有自己 URL 的分頁。用戶端會根據相同的清單重新檢查每個連結:人員無法開啟的連結將呈現為文字,絕不會產生 403 錯誤。應用程式內部的連結會使用路由器進行導航,並保持面板開啟(在行動裝置上則關閉它)。
- 資料工具。
- 唯讀。
- 每個工具僅在該角色可以看到其模組時才被賦予,並會在伺服器端重新檢查權限。
- 它會套用與畫面相同的 SQL 範圍。
- 它會傳回簡短的清單(約 10 行)以及總數。
- 模型絕不決定誰能看到什麼。
- 動作按鈕。 一個
prepare_action工具會驗證角色和狀態(「已打卡」)以及人員提供的資料。用戶端會渲染一個按鈕,該按鈕會開啟預先填寫好的實際對話框(全域模態框使用?new=…,畫面對話框使用?action=…),然後由人員審閱並確認。- 該工具絕不執行任何操作。
- 使用相同的 schema 在用戶端重新驗證預填資料。
- 缺失的欄位留空。模型不會要求提供它們。
- 每個回答最多一個按鈕。
4b. 上下文相關的入口點(採用率)
人們不會發現隱藏在圖示後面的助手,而採用率是最困難的部分。在 UI 中令人困惑的地方(例如一個複雜的儀表板卡片、一個沒人理解的狀態)放置小型的「在 中解釋」按鈕。在需要跨數個畫面進行多次點擊的任務處(例如,補足缺貨的商品)放置「使用 執行」按鈕。
- 行為。 按鈕會開啟一個面板,其中預先載入了提示詞和畫面上下文,並立即發送或準備好供發送。
- 對於「解釋」按鈕,提示詞會指名該元素:「解釋此畫面上的 «Overdue» 卡片」。
- 對於「執行」按鈕,助手會使用其工具,最終仍然會產生一個準備好的動作,由人員確認。它從不靜默地改變狀態。
- 保留在程式碼中。 提示詞存在於一個具型別的註冊表(
id → { label, prompt, screen })中,而非內聯字串。每個條目都有一個評估案例,並且每個回合都會記錄entryid,以便您查看哪些按鈕被使用。 - 少量使用。 每個令人困惑的地方一個按鈕。每個卡片上都放一個按鈕會造成干擾。
5. 伺服器端結構
- 請求主體。 用戶端僅發送新訊息,以及
pathname和頁面標題。伺服器會驗證 pathname(僅限內部路由)並修剪標題。 - 執行緒所有權。 在每個請求和每個歷史路由上進行檢查。代理記憶體儲存不強制執行存取控制。
- 在伺服器端完成回覆。 使用
after()+consumeStream()保持請求存活,這樣在答案串流傳輸期間關閉面板仍會儲存該回合。 - 硬性超時。 在約 60 秒後中斷一個回合。面板隨後會顯示一個「重試」按鈕,並將該回合記錄為超時。
- 每個回合一行記錄,包含使用者、頁面、模型、輸入/快取/輸出/推理 token、工具、完成原因、錯誤和持續時間。它用於支援每小時使用者上限和成本儀表板。
- 記憶體。 簡短的幫助執行緒(最近 30 條訊息或約 60k token)。關閉面板會開始一個新的對話,舊的對話會保留在歷史記錄中。
- 工具步驟。 將每個工具顯示為一個步驟,並帶有過去式的結果(「1 個手冊章節」、「3 個任務」),一旦答案開始串流,便將其折疊。不要僅憑經過的時間就聲稱查詢「很重」。
來自成熟建置的參考數字:每個問題約 11.7k 輸入 token 和 260 輸出 token,每個問題約 €0.01,中位數時間 7.7 秒。
6. 測試與評估:保護內容,衡量模型
確定性測試(vitest,無模型呼叫):
- 手冊。 每個章節都有唯一的 id、20 個字元以上的摘要,以及包含「如何操作」/「畫面上有什麼」的正文。
- 路由。 手冊中的每個路由都存在於應用程式中。
- 覆蓋率。 應用程式中的每個頁面都至少被一個章節涵蓋。這個測試是保持手冊存活性的關鍵。
- 無代碼名稱。 手冊中不出現內部名稱(
src/、.tsx、資料表名稱)。 - 索引。 提示索引列出每個章節,並且當前畫面查詢會先傳回最具體的章節。
- 目錄。 目錄和圖片一一對應,沒有無關的 PNG 檔案,並且每張截圖都屬於一個現有的章節並具有有效的區域。
- 標記。 未知的 id 被移除,部分標記在串流傳輸時被隱藏,複製時會移除標記。
- 角色。 每個角色僅能獲得其模組的工具,並且畫面清單僅包含其選單顯示的內容。
- 動作。 每個動作都對應一個真實的頁面,並且其預填資料通過其 schema 驗證。
評估(scripts/eval.mjs):針對示範資料在真實代理上執行,每個角色有 25–35 個真實問題。對每個案例進行斷言:
- 它必須使用和不得使用的工具,以及它必須閱讀的手冊章節;
- 必須出現和不得出現的標籤;
- 有效的截圖(3 個或更少,來自目錄)以及它指向正確的區域;
- 它必須建立和不得建立的連結;
- 預期的動作及其資料;
- 沒有聲稱已完成某件事,也沒有角色範圍之外的資料。
在每次對提示、模型或工具進行更改前後執行評估。
7. 保持其活躍性
| 時機 | 操作 |
|---|---|
| 一個畫面變更 | 在同一個提交中更新其章節,並重新執行其捕捉腳本 |
| 一個新畫面誕生 | 將其路由添加到一個章節中(否則覆蓋率測試會失敗) |
| 提示、模型或工具變更 | 在變更前後執行評估 |
| 某個提供商變慢 | 在修改提示之前,測量每個提供商路由的 TTFT |
移植到現有代理架構的經驗教訓
- 擴展路由器。 如果應用程式已經路由回合(對話式 vs 知識式),請在該路由器中添加一個
app_help分支,而不是創建第二個代理或第二個按鈕。當提問者無法存取另一個分支時,將路由器限制為conversational | app_help,並且永遠不要在伺服器端建立另一個分支的工具。 - 授權共享資源。