ARCHITECTURE

功能架構

三個核心容器,外加一個 mDNS 服務,不需要獨立的訊息中介軟體,也不依賴雲端服務。照片透過上傳或從 NAS 目錄匯入,依次完成後設資料提取、縮圖生成、語意向量化、人臉與文字辨識,最終成為可按自然語言、人物和地點檢索的相簿。

3 + 1個容器

Go 後端、AI 服務、PostgreSQL,和一個廣播區域網路網域的 mDNS

5類後台任務

metadata、thumbnail、clip、face、ocr

512–1024維向量

Chinese-CLIP 圖文向量(按模型等級為 512、768 或 1024 維);ArcFace 人臉特徵為 512 維

HNSW向量索引

pgvector 餘弦距離索引,搜尋和人臉比對在資料庫裡進行

SYSTEM

系統架構

瀏覽器只連線 Go 後端,由後端負責上傳、任務排程、縮圖和權限管理,並將預覽圖交給 AI 服務推論,結果統一寫入 PostgreSQL。AI 服務無狀態,不存取資料庫、不儲存檔案,可獨立部署到效能更強的機器上。

客戶端電腦瀏覽器網頁,內嵌在 Go 程式裡手機瀏覽器iPhone · Androidwanmory.local區域網路網域,不用記 IPHTTP :8080DOCKER COMPOSEGo 後端 · serverAPI、上傳、任務佇列、縮圖、權限,網頁靜態REST APItus 斷點上傳任務佇列 Worker帳號 · 登入日誌libvips 縮圖ExifTool 後設資料FFmpeg 影片地點反查(可選)相簿 · 智慧相簿連拍 · 相似AI 精選 · 那年今天目錄掃描 15 分鐘mdns廣播 wanmory.localAI 服務 · mlFastAPI + ONNX Runtime / PyTorch,無狀態SCRFD 人臉檢測ArcFace 人臉特徵Chinese-CLIPPP-OCR 文字辨識PostgreSQL 17 + pgvector應用資料、任務佇列、向量都在一個庫裡,不需要 Redisassetsfaces · peoplejobs 佇列settingsHNSW 索引SQL/predict · 1440px 預覽圖儲存 · 擴充套件/data上傳的原圖、縮圖、1440px 預覽圖/photos/*(可以只讀 :ro)NAS 上已有的照片目錄,可以掛多個./models模型檔案,在網頁設定裡下載遠端 AI 服務(可選)Mac(CoreML / MPS)· NVIDIA CUDA權杖鑑權,只收 1440px 預覽圖,不碰原圖讀寫掃描 · 只讀載入HTTP + 權杖
後端只有一個 Go 程式

網頁打包後嵌在程式裡,部署不需要 Node。啟動時自動執行資料庫遷移,升級就是換映像。

AI 服務異地部署

內建 AI 服務執行在 Docker 內部網路中,使用 CPU 推論;在網頁中填入遠端位址,即可切換到家中帶顯示卡的電腦或 Mac。

一個資料庫,統一儲存所有資料

照片資訊、人物、設定、任務佇列和向量統一存入 PostgreSQL,應用資料與任務狀態可以一起備份;照片檔案需要另外備份。

PIPELINE

照片處理處理管線

照片匯入後,先提取後設資料、生成縮圖,再平行計算語意向量、辨識人臉和文字。各步驟獨立入隊,重啟或 AI 服務掉線也不會丟失任務,恢復後繼續處理。

01ingest
匯入
  • 網頁上傳走 tus,斷網能續傳
  • /photos 目錄每 15 分鐘掃一遍
  • 算 SHA-1 校驗和,寫進 assets
後端
02metadata
後設資料
  • ExifTool 讀尺寸、拍攝時間
  • GPS、相機、鏡頭、光圈快門
  • 沒有時區的按設定的時區算
後端
03thumbnail
縮圖
  • libvips 生成縮圖
  • 再生成 1440px 預覽圖
  • 影片用 FFmpeg 取幀、轉碼
後端
04clip
語意向量
  • Chinese-CLIP 算 1024 維向量
  • 自然語言搜尋、自動分類
  • 美學評分、相似照片
AI · 並行
05face
人臉
  • SCRFD 找臉、5 個關鍵點對齊
  • ArcFace 算 512 維特徵
  • 增量歸到人物裡
AI · 並行
06ocr
文字
  • PP-OCR 認出照片裡的字
  • 按字搜照片
  • 判斷證件、文件、螢幕截圖
AI · 並行
SCHEDULING

後台任務排程

沒有獨立的訊息中介軟體:任務佇列就是 PostgreSQL 中的一張 jobs 表,與照片資料在同一事務中寫入,確保照片入庫、任務入隊。worker 透過 SKIP LOCKED 領取任務,用租約機制避免卡死,失敗後按平方退避重試。

pending排隊中running被 worker 領走done完成failed次數用完SKIP LOCKED 領取成功重試次數用完失敗:等 n² × 30 秒再試AI 連不上:15 秒後再試,不算失敗租約 10 分鐘:超時沒交回結果,每 30 秒回收一次,退回 pending
jobs/worker.go · claim
-- 領取一個任務(server/internal/jobs/worker.go)
UPDATE jobs SET status = 'running', attempts = attempts + 1,
       locked_by = $1, locked_until = now() + $2::interval
WHERE id = (
  SELECT id FROM jobs
  WHERE status = 'pending' AND run_after <= now() AND type = ANY($3)
  ORDER BY priority DESC, run_after
  FOR UPDATE SKIP LOCKED   -- 多個 worker 同時領也不會搶到同一個
  LIMIT 1
)
RETURNING id, type, asset_id, payload, attempts, max_attempts;
排程規則
  • 同一張照片的同一類任務已經在排隊或執行時不再重複入隊。
  • 佇列空時每秒查一次;按 priority 從高到低、到點時間從早到晚領。
  • 每個任務的租約(也是超時)10 分鐘;程式重啟時把自己名下沒做完的任務退回佇列。
讓路和限流
  • 有人在上傳照片時,AI 任務暫停領取,頻寬和 CPU 先給上傳。
  • AI 服務連不上時整個 AI worker 停下等,不會把任務一個個領出來失敗、白白用掉重試次數。
  • 「自動」等級會先檢測 AI 伺服器硬體,選定合適的模型後再領取任務,有助於避免在樹莓派等裝置上載入超出可用記憶體的模型。
AI SCHEDULING

AI 排程

AI 服務只負責接收預覽圖,返回人臉、向量和文字。後端決定服務位址、模型和併發數;AI 服務根據硬體選擇 CUDA、CoreML / MPS、OpenVINO 或 CPU。

AI 任務佇列clip · face · ocr併發數按設定,限流可調Go 後端 · AI 客戶端選服務 · 選等級 · 核對 model_ids· 每 15 秒檢查一次 /health· 連不上 → 任務留在佇列等· 等級自動 → 按硬體推薦領取內建 AI 服務Docker 內部網路 · CPU遠端 AI 服務家裡的 Mac · 顯示卡電腦遠端工作節點主動領取任務(規劃中)二選一CPUONNX RuntimeCoreML / MPS · CUDA自動挑可用的加速OpenVINOIntel 核顯
三檔模型組合

選「自動」時按 AI 伺服器的硬體推薦:有可用顯示卡用高精度;只有 CPU 時,記憶體不到 6 GB 用輕量,6 GB 及以上用均衡。

等級適合的硬體人臉語意搜尋
quality · 高精度Apple 晶片 Mac、NVIDIA 顯示卡SCRFD-10G + ArcFace R100(antelopev2)Chinese-CLIP ViT-H/14 · 1024 維
balanced · 均衡Intel i5 等級的 NASSCRFD-10G + ArcFace R100(antelopev2)Chinese-CLIP ViT-L/14 · 768 維
lite · 輕量N100、樹莓派 5SCRFD + ArcFace R50(buffalo_l)Chinese-CLIP ViT-B/16 · 512 維
AI 服務介面 · /predict
POST /predict   (multipart: image + tasks)
Authorization: Bearer <權杖>

tasks = {
  "face": {"model": "antelopev2", "min_score": 0.5},
  "clip": {"model": "chinese-clip-vit-h-14"},
  "ocr":  {}
}

→ {
  "face": [{"box": [x, y, w, h], "score": 0.93,
            "embedding": [512 個數], "yaw": 0.12, "blur": 340}],
  "clip": {"embedding": [1024 個數]},
  "ocr":  {"text": "...", "lines": [...]},
  "model_ids": {"face": "antelopev2", "clip": "chinese-clip-vit-h-14"}
}
ALGORITHMS

核心演算法

搜尋、人物、連拍和 AI 精選背後用到的模型、演算法和閾值,有的靠 AI 模型,有的是向量計算和規則。

自然語言搜尋

Chinese-CLIP 把照片和文字放進同一個向量空間:照片入庫時算好圖片向量,搜尋時把「寶寶三歲生日」算成文字向量,在 pgvector 的 HNSW 索引裡按餘弦距離找最近的照片。

sim(q, p) = q · p / (‖q‖ ‖p‖)
  • 三路結果合併:照片裡認出這些字的排最前,再是在這個地名拍的,最後是意思最像的。
  • 只返回明顯對得上的:要和最像的那張差不多像,還要明顯比一般照片像,有助於過濾掉相關性弱的照片,而不是返回一堆沾邊的結果。
  • 搜尋詞正好是分類名(風景、美食、螢幕截圖……)時直接返回這一類。
人臉歸類

SCRFD 找到臉和 5 個關鍵點,按關鍵點對齊裁成 112×112,ArcFace 算出 512 維特徵(歸一化後點積就是餘弦相似度)。每張照片處理完就增量歸一次,不用等全部算完。

d(a, b) = 1 − a · b  好臉 d < 0.55 · 模糊側臉 d < 0.45
  • 夠清楚、夠正、夠大的「好臉」找最像的臉:有已歸人物的按票數歸過去;都沒歸、但像的夠 3 張就新增一個人物。
  • 模糊、側臉、太小的臉不新增人物,只在很像某個人時跟著歸進去,偶爾出現一次的路人不建。
  • 幾個任務同時歸類時拿同一把資料庫諮詢鎖,不會把一個人建成兩個。封面挑清楚(拉普拉斯方差)、正臉(yaw)、夠大的單人臉。
連拍與相似
同一相機 Δt ≤ 2s 或 Δt ≤ 60s 且 sim ≥ 0.93
  • 同一場景接著拍的幾張 CLIP 相似度通常在 0.94–0.98,換了場景或姿勢一般在 0.7–0.89;以 0.93 作為相似度閾值,有助於找出可能是連拍的照片。
  • 推薦保留的那張:有臉時看臉清楚、睜眼看鏡頭;沒臉時看檔案大小(同一台相機同樣設定,糊的照片細節少、壓出來小)。
  • 同一個檔案存了幾份的單獨放進「重複」,不用挑。「保留」把其餘的放進最近刪除,在永久刪除之前都能恢復。
AI 精選的美學分

不另外跑模型,複用 CLIP 向量:用一組「攝影作品」的描述和一組「普通生活照」的描述各算平均向量,相減得到一個「好看」的方向,照片向量在這個方向上的投影就是它的分。

score(p) = p · (μ作品 − μ生活照)
  • 只用來排先後,不當絕對分數,首頁的精選輪播按它挑照片。
  • 另有一組描述專門分出「拍壞了」的照片(糊、過曝之類),和「好不好看」分開算。
TECH STACK

用到的技術

都是成熟的開源元件,沒有私有服務,換任何一塊都有替代品。

後端
  • Go
    API、任務佇列、縮圖排程
  • libvips
    縮圖和預覽圖
  • FFmpeg
    影片取幀、轉碼
  • ExifTool
    讀寫照片後設資料
  • tus
    斷點續傳上傳
AI 服務
  • Python + FastAPI
    無狀態 HTTP 服務
  • ONNX Runtime
    人臉、文字辨識推論
  • PyTorch
    Chinese-CLIP
  • InsightFace
    SCRFD + ArcFace
  • RapidOCR
    PP-OCR 文字辨識
資料
  • PostgreSQL 17
    應用資料和任務佇列
  • pgvector
    向量儲存
  • HNSW
    餘弦距離索引
  • ip2region
    登入日誌的 IP 位置(離線)
  • Nominatim
    地點反查(可關閉)
前端與部署
  • React + TypeScript
    網頁
  • Vite + Tailwind CSS
    構建和樣式
  • MapLibre GL
    衛星地球
  • Docker Compose
    amd64 / arm64 映像
  • mDNS
    wanmory.local 區域網路網域
PRIVACY

隱私與安全

預設情況下,照片和 AI 結果都留在你的私有網路內。如果設定了遠端 AI 服務,預覽圖會傳送到該伺服器處理。

最少資料

AI 服務只拿到 1440 像素的預覽圖,不儲存任何檔案,不接觸資料庫、人名和原圖。

鑑權

內建 AI 服務只在 Docker 內部網路裡,只有後端連得到;遠端 AI 服務設了權杖後,每個請求都要帶 Bearer 權杖。

權限

成員只能刪除自己上傳的照片,管理員能管全部;隱藏的照片只有自己看得到;登入日誌按 IP 顯示大概位置。

原檔案只讀

NAS 目錄可以用 :ro 只讀掛載,相簿改不了、刪不掉裡面的檔案。

不聯網也能用

模型下載完以後辨識和搜尋都在本地;地點反查、衛星圖這些需要聯網的功能可以關掉。

模型授權

InsightFace 人臉模型僅限非商業用途;模型不進映像,下載前在設定頁展示每個模型的授權。