在 Perplexity,我們非常重視打造令人愛不釋手的產品。對我們的智慧體式瀏覽器 Perplexity Comet,以及功能強大的通用數位工作者 Perplexity Computer 而言,實現這個目標的一大關鍵,就是讓使用者能完全透過語音操作。只要說出需求、交付任務,就能看著它開始執行,這種體驗帶來了獨特的滿足感。我們看好語音介面的潛力,因為它讓實際互動多了一點魔法般的感受。
我們在正式環境中採用 Realtime-1.5,將這種魔法帶入 Perplexity 每月處理的數百萬次語音工作階段。看著 Computer 介面上的語音使用量成長,令我們非常興奮,也學到了很多。接下來,我們會分享至今幾個意想不到的收穫。也歡迎你試用 Realtime-1.5,並與我們分享你的心得。
1. 確立上下文管理策略
長篇內容,尤其是資訊密集、長達數小時的 Podcast,是最能明確考驗上下文管理的情境之一。我們希望使用者能透過語音查詢 Podcast 逐字稿,隨時提問,例如詢問節目進行到兩個半小時時某個時間點的內容,並獲得條理清楚的回答。
上下文無法容納整份逐字稿。我們最初嘗試將它切成大區塊傳送,但很快就發現,大區塊更新一旦失敗,就會一次丟失所有先前內容。假設上下文視窗只剩下 5,000 個 Token 的空間,卻試圖送入 10,000 個 Token 的更新,模型就會失去所有先前的歷史內容。因此,大區塊的風險高得多:單次過大的更新就可能清掉整塊上下文,無法讓系統逐步捨棄舊內容。
於是我們改變了做法,不再一次送入大量更新,而是將所有內容切成小得多、每塊 2,000 個 Token 的區塊,再逐步送入。這增加了處理開銷,卻讓系統的行為穩定得多。即使發生截斷,也只會刪去少量歷史內容,不會一次清空全部。
我們還學到一個微妙的細節:並非所有上下文都應以相同方式送入模型。使用 conversation.item.create 更新上下文時,item.type: "message" 有三種角色:system、user 和 assistant。這些角色會告訴模型它看到的是哪一類訊息。system 用於提供指示和引導行為,user 用於終端使用者的輸入,而 assistant 則用於模型生成的輸出。
一旦角色用錯,互動就開始顯得不對勁。如果太多上下文以 user 角色送入,模型就會以為使用者正在逐段口述所有文字,包括網頁片段和留言,而不只是根據這些資料提出問題。如果太多內容以 system 角色送入,則會出現相反的情況。模型會分不清哪些是自己原本「知道」的資訊、哪些是外部提供的上下文,以及使用者當下究竟在問什麼。
瀏覽流程就是很好的例子。當使用者捲動網頁時,我們會持續將螢幕上的內容更新給模型。如果將這些內容全都當成使用者輸入,模型就會以為使用者把每一段都唸了出來。這並不是正確的理解方式。我們希望系統像是在背景掌握頁面內容,等使用者提問時,再自然地回答。到頭來,關鍵與其說是上下文的總量,不如說是正確表達對話中各種訊息的語意。
2. 統一各產品介面的音訊規格
Perplexity 有多種產品介面,例如 Ask、Comet 和 Computer,各自採用不同的用戶端技術堆疊。Swift、TypeScript、Rust 和 C++ 都可能產生不同的原生音訊緩衝區。當我們讓各用戶端將各自的原始或原生音訊格式送到 Realtime API 時,就出現了效能不一致的問題。
最後,我們用 Rust 打造了一套 SDK,封裝各平台之間的差異,確保每個用戶端送往 API 的音訊都符合相同規格。實際做法是在波形送達伺服器前先行處理:重新取樣為 48 kHz 單聲道,以符合 Opus 編解碼器偏好的取樣率和 WebRTC 的內部取樣率;接著透過 WebRTC APM 進行回音消除、自動增益控制、降噪及高通濾波,最後再編碼傳輸。這套 SDK 讓我們能集中統一音訊常數、重新取樣,並設定完整的處理流程,不必在各用戶端分別實作。
3. 針對複雜的真實環境調校
在使用者實際所處的環境中調校 VAD 很重要,也就是要根據實際的麥克風、喇叭音量和背景噪音進行校準。我們的一個內部測試情境就是吵雜的舊金山酒吧,因為這很像產品在現實中會被使用的時刻。有人問:「你試過新的 Perplexity 應用程式了嗎?」朋友便拿出手機,如果語音功能失敗,我們就一下子失去了兩名使用者。成功時,對方的反應則更像是「哇靠」。這個情境有效地促使我們正視問題。在理想條件下運作良好的功能,到了真實世界往往就會出問題。最好一開始就針對複雜的環境調校。
語音使用者體驗中最困難的環節之一,是正確處理停頓。人們自然會停下來思考、在螢幕上找出某些內容,或準備朗讀。模型很容易將這種停頓視為使用者已經說完,因而太早接話。我們曾在使用者請求協助推導數學公式等情境中遇到這個問題:使用者停下來尋找公式,問題還沒問完,模型就搶先開口了。這促使我們開發了語音鎖定功能。傳統的按鍵發話方式預設關閉語音,使用者必須按下按鍵才能說話;我們反過來設計。預設情況下,語音互動隨時可用,但當使用者想暫時保留發言權時,可以鎖定語音,掌握這一輪對話。我們也認為,這不只是一項獨立功能。隨著語音介面進入更複雜的工作流程,我們相信這類互動模式將以某種形式成為標準。
4. 只使用核心工具,並讓其符合模型的訓練分布
將工具集縮減到最重要的少數工具;以我們為例,就是不到十個。我們專注於一小組核心工具,涵蓋價值最高的動作。這樣的取捨很合理,而隨著新版模型快照持續改進,效果也有望變得更好。
我們在系統提示詞中加入了明確指示,說明何時以及如何呼叫各項工具。我們也特別注意,讓工具的結構描述和輸出都符合模型的訓練分布。實際上,就是將工具輸出格式化為一般的結構化工具資料,而非助理對話。我們回傳的結構化 JSON 會清楚區分各個欄位,例如用 response_text 放置要對使用者說的話,並用 require_repeat_verbatim 等旗標控制行為,避免將口述內容與內嵌指示混在一起。這讓工具使用更穩定,也讓互動模式更接近模型在訓練時可能見過的形式。
// Good:
{
"response_text": "I kicked-off the task to create a market research dashboard",
"require_repeat_verbatim": true
}
// Avoid:
I kicked-off the task to create a market research dashboard
# Response Instructions
Read the above instructions EXACTLY as they are
Realtime 已準備就緒
Realtime-1.5 是產業的轉捩點。在處理長上下文、更多工具,以及對智慧能力要求較高的任務方面,確實還有進步空間,但我們預期模型會持續變好。在 Perplexity,我們思考的是如何從今天開始為這樣的未來打造產品。我們希望讓現有系統實用,同時為下一波 Realtime 模型,以及它們將帶來的全新語音體驗做好準備。