20260914 會議記錄

:::info

上週待辦

url-resolver 研究

threads shared URL 的 oembed 做法可參考 可以直接拿到 og 的資訊 反而 oEmbed 是拿不到資訊的,要另外用 threads api https://graph.threads.net/v1.0/oembed?url= 但回應只有 {type, version, html(blockquote+embed.js), provider_name, provider_url, width},沒有 title / author_name / thumbnail_url / 內文。

[cofacts.ai] DB schema 更新:staging & production(vector search embeddings 欄位)

對應 TODO:MrOrz 把 DB mapping 送到 staging & production DB

目的:套用 rumors-db#78(新增 embeddings 欄位,供 rumors-api#388 hybrid search 讀寫)與 rumors-db#80(ES9 client 的 reloadSchema 修正)。兩者皆已 merge 進 rumors-db master(7bccbfd)。變更為 additive、backward-compatible,articles / replies 新增 nested embeddings.vector(768 維 dense_vector,cosine similarity,hnsw index),airesponses 新增 embeddings(object / enabled: false,做 cache 用)。

執行方式

用 rumors-db 內建的 npm run reload -- <index> 流程(建新 index → reindex → 切 alias → 刪舊 index),對 articles、replies、airesponses 三個 index 各跑一次。

結果

Staging

Index 舊 index 新 index Reindex 耗時
articles articles_v1_4_1-v9 articles_v1_5_0 260s
replies replies_v1_1_1-v9 replies_v1_2_0 202s
airesponses airesponses_v1_0_1-v9 airesponses_v1_1_0 81s

Production

Index 舊 index 新 index Reindex 耗時 文件數(前 → 後)
articles articles_v1_4_1-v9 articles_v1_5_0 131s 294,741 → 294,741 ✅
replies replies_v1_1_1-v9 replies_v1_2_0 138s 147,830 → 147,830 ✅
airesponses airesponses_v1_0_1-v9 airesponses_v1_1_0 77s 276,796 → 276,796 ✅

Production 有先抓 migration 前的文件數當 baseline,reindex 後三個 index 筆數完全一致,確認沒有資料遺失。Staging 因為是先跑完才想到要驗證,沒有抓到 migration 前的 baseline,改用「reindex 前後 _count 內部加總一致」的方式間接確認(articles 279,286 + replies 143,498 + airesponses 249,453 = 三個 index 一起查的 _count 672,237,吻合)。

兩邊都確認 articles mapping 已正確出現 embeddings(nested, vector: 768 維 dense_vector, cosine, hnsw)。兩邊目前都還 0 筆文件有 embeddings 值——這次只更新 schema,實際寫入/讀取 embeddings 的 hybrid search 程式碼(rumors-api#388)尚未部署,屬預期中狀態。

兩邊網站與既有搜尋功能皆確認正常(staging 由使用者手動驗證;production 為既有流量持續運作中,未見異常)。

後續 / 待辦

[cofacts.ai] 過去決策紀錄:訊息作者 / 回報者是否顯示在 profile page

承 20260910 TODO:「先撈過去的決策紀錄,看為啥當時新網站決定不要放 message author 與 profile page」

主決策:2021-11-03 會議

20211103 會議記錄「連結同一人回報的文章」一節:

2. 在個人頁面實作「回報過的訊息」列表?

  • 有人可能以為新網站沒有「查看該用戶回報的所有文章」功能 [name=bil]
  • 但 2020 年新版網頁上線前,Cofacts 網站一直都有「查看該用戶回報的所有文章」功能 [name=mrorz]
  • 時間過去了,大家對隱私的保護應該會更高 [name=bil]
  • 我們不應該列出編輯寫過的回應、卻不列出 LINE 使用者送過的訊息 [name=mrorz]
    • 之前的 usability report: https://g0v.hackmd.io/lIedGsQWTeScBTWfr4Zq0w
  • 那我們應該同時保護編輯與 LINE 使用者的隱私 [name=bil]
  • 設計很漂亮不給看有點可惜,應該讓人點進去看。但如果會在意的話,可以讓使用者決定 profile page 什麼要顯示、什麼不顯示 [name=nonumpa]
    • 預設可以看到 badge 但不會看到文章
    • 編輯可以選擇打開

結論:先保持現狀

  • 編輯可以自己選擇分享個人頁面 URL 給他人
  • 不特定人無法直接點入他人的 profile 頁面
  • 未來讓使用者點入時,預設只呈現 contribution chart,讓使用者可以選擇打開什麼 tab

重點:舊網站(2020 改版前)本來就有「查某帳號回報過哪些文章」的功能(old.cofacts.tw/articles?searchUserByArticleId=xxx),2020 重寫新網站時沒有搬過去。2021-11-03 這次是「事後發現少了這功能」的討論——mrorz 還指出這樣不對稱(編輯的回應列表有,使用者的回報列表沒有),但 bil 認為時代演進、隱私要求更高了,最後選擇不重建這功能,改用「profile page 預設不公開給不特定人、當事人自己決定要不要分享連結」來處理隱私疑慮,而非把回報列表做出來。

更早的背景(同一條隱私顧慮的源頭)

給今晚討論的參考

9/10 會議提到的兩個方向都跟 2021 的結論相容:

  1. 「builder 用 reply request,API/open data 本來就撈得到」──沒有改變任何隱私邊界,維持現狀即可。
  2. 「user profile 頁面加個 tab 顯示這個帳號送過哪些訊息」──這正是 2021-11-03 決定不做的功能。如果要重提,2021 年的兩個顧慮(① 使用者不知情、會不舒服 ② 隱私要求只會更高不會更低)需要重新面對,而不是預設可以直接做。當時提出但沒採用的折衷方案是「預設只顯示 badge/contribution chart,使用者可自行選擇要不要打開回報列表 tab」,如果這次要做,這是現成可以參考的緩解方案。

Alternative:不做在 profile page,做在 /articles filter(「我送出的訊息」自助查詢)

這次真正的使用情境,其實跟 2017/2021 討論的「公開查某帳號送過什麼」不是同一件事:使用者透過網站送出訊息後,自然會想回頭找自己之前送過什麼——這是「查自己」,不是「查別人」。查了 cofacts/rumors-site 現有原始碼,這個 alternative 其實幾乎是現成的:

結論:selfOnly 幾乎是現成的「我送出的訊息」機制,工程量很小(在 ArticleStatusFilter 加一個 SENT_BY_ME 選項、接上 filterObj.selfOnly = true),而且因為它天生只能查「登入者自己」,完全不會重新踩到 2017/2021 那次「公開他人回報紀錄」的隱私紅線——不用做使用者可調的公開/隱藏設定,也不需要動 profile page。這跟「查別人回報了什麼」(userId/fromUserOfArticleId,維持只給 API/研究者用)是兩件事,可以分開處理。

示意圖

[!Note] TODO 開票:

  • rumors-site: 新增「我送出的」(票裡放示意圖)
  • community builder:新增改寫的票

[cofacts.ai] /report 表單 placeholder 的 accessibility 評估

對應 20260910 會議記錄 的 TODO,範圍是 cofacts/ai PR #138 /report 表單裡 「為何您覺得這是謠言?」的說明 textarea。

Q1:一般 accessibility 建議

現在的用法沒犯大忌:問題文字是常駐可見的 <label>,placeholder 只當範例輔助,不是唯一說明, 所以不會有「打字後說明消失」的問題。原則上 placeholder 不能取代 label、對比度門檻一樣適用, 但這兩點這裡本來就沒踩到。

Q2:長 example 會不會拖累 Lighthouse 分數?

不會,實測過。 把現在的短 placeholder(51 字)跟刻意寫長的 example(281 字,5.5 倍長)各自 做成真實 markup 跑 Lighthouse accessibility 稽核:兩者都是 100 分,color-contrast、 label 等 audit 完全沒差異。

原因:Lighthouse 用的 axe-core 規則根本不檢查 ::placeholder 的內容,所以長度本來就不在 它的稽核範圍內。連帶推論:Lighthouse 綠燈也不代表 placeholder 對真人可讀夠不夠——這件事工具測 不到,得靠人工判斷。

附帶發現(本次不處理,設計端會接手)

實測順便量到:/report 這兩個欄位(url / reason)沒套用專案共用元件都有的 placeholder:text-muted-foreground,吃的是瀏覽器預設灰色 #757575,對白底對比度 4.6:1 (剛壓過 AA 門檻 4.5:1,margin 很薄);若套用專案既有的顏色 #666666 則是 5.7:1。 nonumpa 說的「對比度較低」有實測根據,但這次會議不排入待辦。

[cofacts.ai] 老鶴回報:Network error 與幻覺

@老鶴:最近都沒辦法用cofacts.ai好好的進行查核,有時候查核到一半出現”連線錯誤:network error”;有時候佐證資料出現幻覺,提供的網址實際上跟查核內容無關的新聞;有時候給它cofacts文章的連結,會查核到不相關的文章。

回報內容(2026-09-13 Discord):查核到一半出現「連線錯誤:network error」、佐證資料出現幻覺、給 cofacts 連結會查到不相關文章。 回報者 user id sPyB-IYBC7Q3lHuU-YQl,9/1 起共 9 個 session / 19 個 trace。

結論先講


1. Network error:LLM 呼叫在 125 秒被硬砍

現象 trace 中斷,最後一個 call_llm span 停在 124.8–125.4 秒,usage = 0 tokens、output = null、timeToFirstToken = null、沒有記到任何 exception。連第一個 byte 都沒拿到就被中止,不是模型跑太慢。

證據(357 個 GENERATION span 抽樣,9/1 以後)

  正常完成 被砍
筆數 327 30
latency 1s–195.6s 連續分布 124.8–125.4s,擠在 0.6 秒內
tokens 23k–46k 全部 0

代表性 trace:

已排除

假說 ~120s 的 first-byte / idle deadline(+~5s 連線)在我們的 client 之上。嫌疑最大的是 GOOGLE_CLOUD_LOCATION=global 走的 Vertex global endpoint。會吐 thinking token 的請求把連線餵活就能跑到 195s;verifier 讀大量 url_context 時整段沉默就被切。切掉後 ADK task 直接消失,handle_writer_tool_error 接不到,SSE 斷在半路 → 前端顯示「連線錯誤: network error」。

影響範圍 9/1 以來 33 個中斷 trace 有 25 個是這個指紋,橫跨 4 個使用者。不是特定使用者或特定內容。

待辦 假說要靠 Cloud Run / Vertex log 才能定案,本機無 gcloud。交接 prompt 已寫好。止血選項:GOOGLE_CLOUD_LOCATION 由 global 改為 asia-east1 做區分實驗;另外不論根因為何,都該讓「sub-agent 被取消」變成使用者看得到的「verifier 逾時,請重試」,而不是整條連線斷掉。


2. 佐證幻覺:source-coverage gate 是自己發證書給自己

個案 session e92faa5d-0ef3-4a3d-9678-05d673cec455(雪山隧道那則)

時間 turn 發生什麼
9/11 05:20 貼上 cofacts 連結 investigator 跑完,verifier 在 125s 被砍,未交件(trace)
9/13 14:22 「提交最終提案」 完全沒呼叫 investigator / verifier,交出 5 條 claim_sources 全標 verifier_confirmed: true(trace)
9/13 14:26 — 老鶴 👎「☑ 出處摘要錯誤」
9/13 14:27 「再確認一次佐證資料內容」 12 秒、0 次 tool call,回「已確認」(trace)
9/13 14:28 「不要鏡週刊」 這次真的查了,又在 125s 被砍(trace)

5 條引用在整個 session 的所有工具輸出裡一次都沒出現過。造假方式是真域名假路徑——investigator 真的找到 mirrormedia.mg/story/20220613bus001,交出去的卻是 …/20170525bus001、…bus002,另外兩條 udn / cna 完全憑空。

為什麼 gate 沒擋 tools.py:544 只檢查 source_url 有沒有出現在 references 字串裡,而 references 也是 writer 自己寫的。log 顯示 14:22:37 第一次被退(not present in references),writer 把假網址貼進 references,14:22:45 第二次就 success: True。verifier_confirmed 也是 writer 自己填的 boolean。

提案 claim_sources[].source_url 必須落在該 session 累積的 verifier ∪ investigator sources[] 裡。

來源不放 ADK state(state 會被 list session API 附在 session list 裡帶出去),改成在 gate 內遍歷對話紀錄:ADK 1.26.0 的 ToolContext 即 Context,繼承 ReadonlyContext,其 .session.events 就是完整 event list,走訪各 event 的 function_response、取出 investigator / verifier 回傳的 sources[] 即可。唯讀、零額外儲存。

跨 turn 已實證可行:9/13 14:22 那個 turn 的 writer 第一個 call_llm input 裡就帶著 9/11 那個 turn 的 investigator function_response 與 sources[](events 由 Postgres SessionService 載回)。這點很關鍵,因為該 case 的證據是兩天前收集的。同一份 input 裡 sources 只出現一次——9/11 的 verifier 被 125s 砍掉沒留下 function_response——正是要擋的情境。

實作上 draft_factcheck_response 目前是純函式,需加 tool_context: ToolContext 參數(ADK 會自動注入,不會出現在給 LLM 的 schema 裡)。

回測結果(歷史 user-thumbs 資料,session 層級重建來源集合)

規則 👍 正面回饋誤擋 (n=69) 👎 出處相關負評攔截 (n=21)
只比對 verifier sources 27 (39%) ❌ 14 (67%)
verifier ∪ investigator 3 (4%) ✅ 11 (52%)
再加使用者貼的 + cofacts 文章內網址 3 (4%) 8 (38%)

需要決定

  1. 125s 根因調查 –> MrOrz
  2. provenance 檢查(verifier ∪ investigator)是否現在做?這會動到 agent contract 與 per-claim source-coverage gate,依 CLAUDE.md 屬於該留 ADR 的層級。 –> 建議開 cofacts.ai 的 issue、實作時付 ADR 文件
  3. verifier_confirmed 自動化要不要排進下一輪 –> 太複雜,先不要

上週重點整理(2026-09-10 ~ 2026-09-13)

GitHub 活動

cofacts/ai

cofacts/devops

cofacts/kb

cofacts/rumors-api

cofacts/rumors-db

cofacts/takedowns

MCP

https://github.com/cofacts/kb/pull/25

October meetup

10/3 or 10/17

華梵大學板橋中心

[!Note] TODO 比鄰問場地

Production 環境狀態報告

報告期間:2026-09-10 ~ 2026-09-14(距上次會議 9/10 這幾天;09-14 為當日,僅有即時快照) 比較基準:2026-09-01 ~ 2026-09-09(前一期間,取自歷史 metrics summary)

一、GCE 主機狀態(cofacts-prod)

現況快照(2026-09-14 19:10)

項目 數值
Uptime 130 天(未重啟)
Load Average 0.96 / 1.23 / 1.06(1/5/15 分鐘)
記憶體(RAM) 21 GB used / 31 GB total(~68%)
Swap 1.9 GB used / 4 GB total(~48%,穩定)
磁碟 46 GB / 67 GB(68%,與 9/10 清理後持平,見第七節)

GCE CPU / 記憶體 / Swap 逐日明細

格式:min | avg | p95 | max

日期 CPU(%) Mem(%) Swap(%)
09-10 7.01 | 14.86 | 21.33 | 36.64 63.10 | 65.39 | 67.10 | 69.37 47.06 | 48.63 | 50.75 | 52.61 ✅
09-11 6.90 | 16.76 | 24.58 | 44.62 63.87 | 66.60 | 68.96 | 69.68 44.47 | 46.36 | 49.75 | 51.13 ✅
09-12 10.26 | 16.29 | 25.35 | 38.80 63.31 | 66.15 | 68.35 | 72.95 45.84 | 47.30 | 49.98 | 53.60 ✅
09-13 10.79 | 20.09 | 52.44 | 64.15 63.32 | 67.34 | 71.41 | 76.20 45.47 | 47.09 | 49.65 | 49.95 ✅
09-14 當日進行中,無 sar 歷史資料,僅有即時快照(見上表) — —

觀察:09-13 CPU 出現單日尖峰(p95/max 約為其他天的 2 倍),已個別調查,詳見第八節。其餘天數與 RAM(66–67%)、Swap(45–49%)皆與前期水準一致,無異常。


二、GCE 程序層級資源(Top 消耗者,與前一期間 9/1–9/9 比較)

CPU(原始計數器單位,僅比較相對量級)

程序 前期 peak / avg 範圍 本期 peak / avg 範圍 觀察
java(Elasticsearch) 511,262 / 211,352–340,657 898,010(9/13 尖峰) / 196,286–269,233 🟡 peak 顯著上升,見第八節調查
node server.js(rumors-api) 393,345 / 158,649–210,029 716,575(同一 9/13 尖峰) / 148,311–219,299 🟡 與 ES 同時尖峰,見第八節
node build/index.js(rumors-site) 189,009 / 112,216–149,793 298,631 / 112,902–131,470 ➡️ 略升,無異常波動
Chromium renderer(Puppeteer) 未進 Top5 95,155 / 87,755(單日採樣) ➡️ 短暫 spike,截圖類任務,屬預期行為
cloudflared tunnel 未進 Top5 60,377 / 16,732–21,638 ➡️ 正常

RSS 記憶體(MB)

程序 前期 peak / avg 範圍 本期 peak / avg 範圍 觀察
java(Elasticsearch) 19,563 / 19,155–19,265 19,360 / 19,074–19,202 ➡️ 持平,極穩定(std < 120)
node server.js(rumors-api) 1,450 / 289–322 1,087 / 271–340 ➡️ 持平略降
node build/index.js(rumors-site) 725 / 141–146 781 / 141–147 ➡️ 持平
pm2-runtime(ecosystem.config.js) 518 / 106–111 359 / 103–201 🟡 std 上升,波動略增
pm2-runtime(start ecosystem …) 346 / 115–196 353 / 166–196 ➡️ 持平

勘誤:python main.py(cofacts-ai)因記憶體用量低(遠低於 Top 20 門檻 ~280MB),未出現在上方 Top 15/20 摘要中,但製程本身持續在 GCE 上正常運作,並未遷出。查完整(未截斷)製程資料確認:RSS avg/max 本期 ~86MB/259MB、前期 ~92MB/255MB;CPU avg/max 本期 ~4,334/14,993、前期 ~4,607/13,847,兩期表現一致、穩定。(先前版本此處誤判為「已遷出 GCE」,已更正。)


三、Cloud Run 狀態

所有 11 個服務均正常運行(✔)。近期部署:


四、Cloud Run CPU 比較(p50 / p95 / p99,vCPU)

服務 前期 9/1–9/9(p50 | p95 | p99) 本期 9/10–9/13(p50 | p95 | p99) 觀察
site-tw(主力) 0.042–0.138 | 0.649–0.798 | 0.823–1.012 0.049–0.072 | 0.668–0.730 | 0.823–0.954 ➡️ p50/p99 略降,持續貼近 CPU 上限(長期穩態)
cofacts-ai 0.015–0.016 | 0.152–0.367 | 0.245–0.370 0.015 | 0.135–0.344 | 0.329–0.360 ➡️ 穩定(Cloud Run 服務本身表現,與 GCE 上的 python main.py 為並存的不同 workload)
line-bot-en 0.006–0.015 | 0.061–0.292 | 0.132–0.339 0.006–0.008 | 0.104–0.152 | 0.120–0.238 ➡️ 略降,正常
line-bot-ja 0.005–0.012 | 0.075–0.310 | 0.110–0.381 0.005–0.033 | 0.110–0.128 | 0.231–0.731 🟡 p99 單日尖峰(0.731,9/12),其餘正常

五、Cloud Run 記憶體比較(p50,GB)

服務 前期 p50 範圍 本期 p50 範圍 變化
site-tw 0.764–0.787 0.764–0.787 ➡️ 持平
line-bot-en 0.399–0.423 0.376–0.399 ➡️ 持平略降
line-bot-ja 0.387–0.423 0.387–0.569 🟡 略升(對應上述 9/12 尖峰)
cofacts-ai 0.056–0.065 0.055–0.064 ➡️ 持平

六、Cloud Run 網路流出量(site-tw,每日 egress)

日期 Bytes 備註
09-10 3.91 GB  
09-11 4.60 GB 本期最高點
09-12 4.07 GB  
09-13 3.30 GB  

前期(9/1–9/9)約 2.75–6.85 GB/日,本期落在 3.3–4.6 GB,屬同一穩定區間,無異常。


七、磁碟使用

09-10 已執行清理(清除 44 個 dangling docker image),使用率由 88% 降至 68%(45GB/67GB)。本期(09-14)快照仍為 68%(46GB/67GB),清理效果持續維持,未見回升跡象。


八、本週個案調查

1. 09-13 CPU 尖峰

Elasticsearch(java)與 node server.js(rumors-api)在同一小時(15:59–16:59 台北時間)同步出現 CPU 尖峰(java: 898,010,平常 ~230,000;node: 716,575,平常 ~190,000)。查證 cron log 後:

2. 每週備份確認

Production 上確實有每週自動備份:db(Elasticsearch)容器於每週日 08:20(台北時間,UTC 00:20)執行 snapshot 至 GCS(_snapshot/gcs/<date>),過去 5 週(8/16、8/23、8/30、9/6、9/13)皆準時執行。該時段 ES CPU 為平常水準(~230,000),未造成資源異常,與上述 09-13 尖峰(發生於 7.5 小時後的午後)無關。

另,devops/backups/ 本地資料夾的 docker-compose.yml + env-files 備份為手動執行(skill: backup-env-settings),非自動排程,上次執行為 07-15,已超過 2 個月未更新,如需最新設定備份建議手動跑一次。


九、整體健康評估

維度 狀態 說明
CPU 🟡 待觀察 09-13 出現尖峰(已調查,判斷為排程+流量疊加,非異常故障)
RAM 🟢 正常 65–67%,穩定
Swap 🟢 正常 45–53%,穩定
磁碟 🟢 正常 68%,09-10 清理效果持續維持
Cloud Run 🟢 全部正常 11 服務皆 ✔
site-tw CPU/Mem 🟡 持續觀察 p99 長期貼近 1 vCPU / 1GB 上限,非新異常但建議列入擴容評估
每週 ES 備份 🟢 正常運作 每週日 08:20(台北時間)執行,無異常負載
env 設定備份 🟢 已更新 09-14 已手動重跑 backup-env-settings(上次為 07-15),見 backups/rumors-deploy_20260914_033537

Scientist update