簡介
LAPLACE Eidolon 是主播人格的語音分身——訪客透過瀏覽器走進「拉普拉斯花店」,與一台會說話、會記事的 AI 即時對話
技術上是一台基於 Pipecat 的級聯式語音機器人,串聯 STT、LLM 與 TTS,並疊加可選的多層 RAG 與每位觀眾的長期記憶,讓分身能夠「記得」過往直播片段、人設設定,以及與每位訪客之間的私人交流
線上展示
laplace.live 上的 AI 聊天頁面即由本專案驅動,開啟即可與分身對話:
管線結構
transport.input → STT → user_aggregator
→ Mem0(每位觀眾的長期記憶,可選)
→ StreamArchive(過往直播 RAG,可選)
→ PersonaFacts(人設事實 RAG,可選)
→ LLM → TTS → transport.output → assistant_aggregator技術堆疊
- STT:Soniox(
stt-rt-v5) - LLM:OpenRouter(預設
~google/gemini-flash-latest) - TTS:Fish Audio(
s2.1-pro) - 傳輸:SmallWebRTC(Pipecat 自架)
- 向量庫:LanceDB(本機)
- 長期記憶:自行架設的 Mem0 + Qdrant(可選)
僅支援自行架設的 SmallWebRTC,依賴自有反向代理終止 TLS,不接入 Daily 或 Pipecat Cloud
四層人設記憶
機器人透過四層持久化儲存為 LLM 注入上下文,每一層都可獨立啟停,僅啟用 persona.md 的最小設定也能穩定執行:
- persona.md:恆常生效的身分、語氣與硬性規則,啟動時直接載入 system prompt
- Persona facts:手工整理的「她說過的話」原子事實,依語意相關度檢索(LanceDB 索引由
laplace-ingest-facts建立) - Stream archive:經過 LLM 二次精煉的往期直播片段,依語意相關度檢索(LanceDB 索引由
laplace-ingest-streams建立),並附帶一層整場概覽的場次目錄 - Mem0:從對話中自動擷取的每位觀眾情景記憶,依 Bilibili UID 隔離;以 Qdrant 承載向量,作為函式庫嵌入機器人行程執行
混合檢索與重排
Persona facts 與 Stream archive 兩層 RAG 共用一條混合檢索管線,每輪對話即時執行:
- 建構查詢:綜合最近幾輪使用者發言,讓短促語音(如「嗯,那呢?」)也能藉上下文檢索
- 統一向量化:以
EMBED_MODEL(預設 Gemini,3072 維)嵌入一次查詢 - 日期預過濾(僅 persona facts):查詢提及日期(
2026年5月9日/5月9号)時,先依date欄收窄候選集 - 混合搜尋:向量檢索疊加 jieba 斷詞的中文 BM25/FTS,拉取
PERSONA_FACTS_CANDIDATES(預設 30)或STREAM_ARCHIVE_CANDIDATES(預設 15)筆候選,並剝離停用詞以免 BM25 分數塌陷 - 交叉編碼重排:預設經 OpenRouter 呼叫
cohere/rerank-4-fast精排至top_k(facts 取 8、chunks 取 3),沿用 LLM 所需的OPENROUTER_API_KEY;設為RERANK_ENABLED=false後回退 LanceDB 的 RRF 融合
資料規模約 1 萬筆以上時,重排是精度的最大槓桿,成本約 $0.002 /
次檢索。RERANK_BASE_URL、RERANK_API_KEY 可切回 Cohere
直連(更便宜且無需重試);laplace-eval-retrieval 可量化 recall@k /
MRR,比對改動前後的回歸
直播回憶與場次目錄
除了依語意檢索約 5 分鐘的直播片段,Stream archive 還維護一份場次目錄(stream_catalog.json,由 laplace-build-stream-catalog 從已精煉的轉錄在本機建立,不產生任何模型或嵌入請求)。目錄依日期與 BV 號歸組錄影分段,為每場直播保留活動統計、代表性摘錄與全部場景摘要
有了這一層,分身能區分「整場回顧」與「某個話題」兩類問題:
- 整場回顧(「上週直播都播了什麼」)直接讀取符合的目錄記錄,略過嵌入、重排與人設事實檢索
- 限定時間的話題查詢沿用混合檢索,並疊加一層中繼資料預過濾
- 複雜或需要模型判斷的時間指代,交由
recall_streams工具處理:它接受latest、last_week、past_days、date_range等時段,或上下文中已給出的場次 ID;每個使用者回合最多兩次查詢
日期與時間區間依 PERSONA_TIMEZONE 解析——「上週」指上一個週一至週日,「最近七天」含今天。目錄缺失或無法讀取時安靜降級,原有的片段檢索照常運作
自行架設
僅需執行:
docker compose up --build映像檔內建 Pipecat 執行環境與已建好的 LanceDB 索引,監聽 7860 連接埠;端點 POST /api/offer 處理 SmallWebRTC SDP 信令,前端可直接對接,亦可使用 @pipecat-ai/client-js 與 @pipecat-ai/small-webrtc-transport
正式部署需在反向代理中終止 TLS,並視需要設定 TURN 以穿透對稱 NAT
建議使用 Cloudflare Realtime TURN,機器人會依需求簽發憑證並自動輪替(前端經
GET /api/ice-servers 取用),免費額度約 1
TB/月中繼流量,涵蓋大多數自行部署的情境
設定 TURN 後伺服器端只收集中繼候選:來源站的公開 IP 不再出現在觀眾的 SDP 中,各類 NAT 也都能在第一個候選上連通,代價是全部媒體走中繼、TURN 成為硬相依。僅設定 STUN 時維持原有行為
HTTPS 是瀏覽器取得麥克風權限的硬性要求,本機 localhost
之外的任何環境都需設定有效憑證
觀眾身分與限流
可選的 loginSyncToken 驗證由 LAPLACE Login Sync 完成:辨識出的 Bilibili UID 用作 Mem0 命名空間,並跳過匿名限流;未攜帶或驗證失敗的訪客則依 IP 進入滑動視窗配額,預設每小時 5 次連線嘗試
已登入的訪客還可自助管理屬於自己的 Mem0 記憶——GET /api/memories 列出、POST /api/memories/forget 清除,兩者均以 WebRTC 握手所攜帶的同一組 loginSyncToken 驗證身分,並嚴格限定在呼叫者本人的 Bilibili UID 範圍內
執行時聲線切換
分身可在通話中切換 Fish Audio 聲線:當訪客說出「溫柔一點」「活潑一點」等口令時,change_voice_style 工具會即時換用對應的 reference_id。設定 FISH_VOICE_ID_GENTLE、FISH_VOICE_ID_LIVELY 即可啟用,全部留空則關閉該能力
此外,前端可依音訊品質逐工作階段選擇 Opus 編碼位元率(EIDOLON_OPUS_BITRATE_*,預設 96 kbps),在網路不佳與音質之間取捨
公共 MCP 服務
機器人在 /mcp/ 額外開放一個唯讀的 Model Context Protocol 端點(Streamable HTTP),向任意 MCP 用戶端提供與語音管線相同的兩層 RAG 索引——search_persona_facts 與 search_streams。公開實例位於 https://eidolon.vrp.moe/mcp,預設匿名開放。詳見 MCP 服務
離線工具
建立索引、批次下載與轉錄均以獨立 console script 提供,全部透過 uv run 呼叫:
| 指令 | 功能 |
|---|---|
laplace-pipeline | 端到端管線:下載 → 轉錄 → 精煉 → 入庫 |
laplace-download | 基於 yt-dlp 的批次 VOD 下載 |
laplace-transcribe | Whisper / Soniox 轉錄 |
laplace-refine | 將原始轉錄精煉為結構化記憶 JSON |
laplace-ingest-streams | 建立 stream_chunks LanceDB 索引 |
laplace-ingest-facts | 建立 persona_facts LanceDB 索引 |
laplace-build-stream-catalog | 建立場次目錄 stream_catalog.json |
laplace-search | LanceDB 索引的 REPL 式檢索 |
laplace-eval-retrieval | 量化檢索 recall@k / MRR,回歸比對基準 |
laplace-extract-users | 從彈幕 JSON 中挖掘高頻使用者名稱 |
laplace-export-mem0 | 將 Mem0 雲端帳號匯出為 JSON |
laplace-import-mem0 | 將匯出的 JSON 冪等匯入 Qdrant |
原始碼
最後更新於 2026年9月10日