20260914 會議記錄
:::info
-
所有會議記錄 編輯當週文件 準備 總結 - 9/14 (一) 20:00 線上(bil 在國外,本次改線上)
- 線上出席:bil, mrorz, nonumpa, ggm, yutin
- https://meet.google.com/mrz-dgrd-pri :::
上週待辦
- 追蹤 devops#12:開放 user-driven 爬蟲存取政策
- 追蹤 devops#17:rumors-site / line-bot-zh / url-resolver 記錄 request header,並修 rumors-api GraphQL log header 迴歸
- 追蹤 devops#18:記錄 Cloudflare Analytics retention(31 天 / 3 天)與 Logpush Enterprise-only 限制
- cofacts.ai session 週報:GGM 手動貼到 #collab;MrOrz 準備 DC bot 之後自動化
- nonumpa:繼續研究 FB / IG preview 仍是登入畫面的問題(threads shared URL 的 oembed 做法可參考)
- MrOrz:cofacts.ai 前端先打底(frontend skill + storybook),之後請 yutin 接手
- rumors-api#388 vector search:補 userId / appId backfill(rumors-db submodule 更新已 merge #402)
- 推進 rumors-db#80 review/approve(ES9 breaking change,卡住 hybrid search schema 的 deploy)
- MrOrz 把 DB mapping 送到 staging & production DB
- 推進 ai#118(URL resolver pre-flight 檢查,補
hyperlinks參考邏輯) - Cofacts.ai 回報入口:撈過去決策紀錄,確認新網站當初為何不放 message author 與 profile page
- 下週討論是否要把「誰回報了什麼」做在 profile page
- 回報入口 placeholder:詢問 AI accessibility 建議、確認過長 example 是否影響 lighthouse 分數
- 追蹤 cofacts/ai#141:V1 editorial preference memory(Memory Bank,接續 #125)
- 排在其前面先修的 4 個 bug:佐證資料連結/對題(推進 #118)、「查到一半停下來」訊號寫成查詢/alert、裸打 tool name 的 UI 問題、article-scoped「這篇之前查到哪」
- 實作 memory 前,把 09/10 會議記錄的分析寫成 ADR(跨
src/+adk/,新增資料存放位置)
url-resolver 研究
threads shared URL 的 oembed 做法可參考
可以直接拿到 og 的資訊
反而 oEmbed 是拿不到資訊的,要另外用 threads api https://graph.threads.net/v1.0/oembed?url=
- Meta ToS 明訂 oEmbed 內容只能前端顯示,禁止 consume / extract / persist / analytics [name=nonumpa]
- 不確定 https://dev.cofacts.tw/article/v8qpf7ybzg4u 為何成功,https://dev.cofacts.tw/article/2jef3meso7mej 為何失敗 [name=mrorz]
- 我猜是 url-resolver 的 unfurl 在處理
- 失敗的可能是被刪文 (?)
- profile 和單純貼文的資訊不同 [name=nonumpa]
- threads 要特別處理:title 不能拿 og-title,順便把還沒改完的一起改
我覺得這個還行,tiktok 好像也這樣 [name=mrorz]- 比較在意不同 social media / 不同 URL 的抓取狀態 [name=mrorz]
[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(Vultr, answerfamily):主機 OS 是 Ubuntu 16.04(2018 年 kernel 4.4),Node ≥18 不論裝在 host 或 container 內都會直接 crash(
uv_thread_createassertion failure,疑似缺rseq()syscall),無法照 devops/GCE.md 描述的方式在 host 上跑。改用 SSH local port forward(ssh -L 19200:<db 容器 IP>:9200 answerfamily)把 staging ES 接到本機,用本機的rumors-db@master(Node 22)執行 reload。 - Production(GCE, cofacts-prod):OS 較新(Ubuntu 24.04 / kernel 6.17 / glibc 2.39),Node 24 已透過 nvm 裝好,照 devops/GCE.md 流程直接在 host 上執行(
docker inspect找 ES container 內網 IP → 設定ELASTICSEARCH_URL→npm ci→npm run reload)。
結果
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 為既有流量持續運作中,未見異常)。
後續 / 待辦
- 待 rumors-api#388(hybrid search)部署後,才會開始真正寫入/讀取
embeddings,屆時需另外驗證 - Staging VPS OS 太舊(Ubuntu 16.04 / 2018 年 kernel),Node 18+ 完全跑不動(container 也一樣),建議找時間排 OS 升級,否則後續工具鏈只會愈來愈難裝
[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 預設不公開給不特定人、當事人自己決定要不要分享連結」來處理隱私疑慮,而非把回報列表做出來。
更早的背景(同一條隱私顧慮的源頭)
- 2017-12-20(20171220):orz 提議做「查這個 id 傳了哪些文章進來」,Hazel 隨即點出「但沒有意識到,會公開說是『誰會送出來』」「可能會有使用者覺得不舒服」——同一個顧慮在 4 年後(2021)幾乎逐字重演。
- 2020-08-31(rumors-api userId/appId 提案):新網站重寫時其實已經設計好技術方案——用假名 + 隨機頭像顯示 reply request 的使用者、支援「列出此使用者所回報的文章列表」,2020-11 也真的把假名/頭像資料 migrate 好了,但最終沒有接進前端 UI(”avatar 但 UI 還不會顯示”)。
- 換句話說:技術上做得到、也曾經規劃過,2021-11 是明確選擇不做的那次拍板。
給今晚討論的參考
9/10 會議提到的兩個方向都跟 2021 的結論相容:
- 「builder 用 reply request,API/open data 本來就撈得到」──沒有改變任何隱私邊界,維持現狀即可。
- 「user profile 頁面加個 tab 顯示這個帳號送過哪些訊息」──這正是 2021-11-03 決定不做的功能。如果要重提,2021 年的兩個顧慮(① 使用者不知情、會不舒服 ② 隱私要求只會更高不會更低)需要重新面對,而不是預設可以直接做。當時提出但沒採用的折衷方案是「預設只顯示 badge/contribution chart,使用者可自行選擇要不要打開回報列表 tab」,如果這次要做,這是現成可以參考的緩解方案。
Alternative:不做在 profile page,做在 /articles filter(「我送出的訊息」自助查詢)
這次真正的使用情境,其實跟 2017/2021 討論的「公開查某帳號送過什麼」不是同一件事:使用者透過網站送出訊息後,自然會想回頭找自己之前送過什麼——這是「查自己」,不是「查別人」。查了 cofacts/rumors-site 現有原始碼,這個 alternative 其實幾乎是現成的:
- rumors-api 的
ListArticleFilter(typegen/graphql.ts:1071)本來就有selfOnly: Boolean欄位:「Only list the articles created by the currently logged in user」——後端用登入 session 判斷「currently logged in user」,不是吃一個公開的userId參數,所以架構上就只能查「我自己」,不會有查到別人的疑慮。 - 但這個欄位目前在 rumors-site 完全沒有接到 UI:
/articles與/search頁面的「Filter」(components/ListPageControls/ArticleStatusFilter.tsx)只做了REPLIED_BY_ME/NOT_REPLIED_BY_ME(用articleRepliesFrom.userId查「我回應過的文章」),沒有對應「SENT_BY_ME / 我送出的訊息」選項去接這個現成的selfOnly。- 唯一用到
selfOnly的地方是/search頁面查 Reply(ListReplyFilter.selfOnly,「我寫過的回應」),跟 Article 的selfOnly是不同欄位,目前沒人接。
- 唯一用到
- 舊網站當年的
searchUserByArticleId(old.cofacts.tw/articles?searchUserByArticleId=xxx)在新 schema 裡對應到的是userId/userIds/fromUserOfArticleId——這幾個欄位仍然存在於 API,但同樣沒有暴露在網站 UI 上,只給 API/open data/community builder 用(呼應 9/10 會議「API 都撈得到、researcher 也能用 open data 拿」的現況)。
結論:selfOnly 幾乎是現成的「我送出的訊息」機制,工程量很小(在 ArticleStatusFilter 加一個 SENT_BY_ME 選項、接上 filterObj.selfOnly = true),而且因為它天生只能查「登入者自己」,完全不會重新踩到 2017/2021 那次「公開他人回報紀錄」的隱私紅線——不用做使用者可調的公開/隱藏設定,也不需要動 profile page。這跟「查別人回報了什麼」(userId/fromUserOfArticleId,維持只給 API/研究者用)是兩件事,可以分開處理。

- community builder 是公開的 [name=nonumpa]
- 是,因為下架公告要顯示 [name=mrorz]
- 惡意使用者也能用 [name=nonumpa]
- 可以考慮掛個 cloudflare Access
- 之後要做 API key 管理之前,community builder 要改成 server load data,或許也能做成要登入才看得到 [name=mrorz]
[!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。
結論先講
- 兩個症狀是同一條因果鏈:LLM 呼叫被砍 → session 沒有任何已驗證來源 → writer 照樣交件並編造出處。
- 都不是新的,跟「兩週沒 release」一致。network error 最早可追到 6/24 的 feedback,幻覺從 5 月就有人標。
- 第三個症狀(查到不相關文章)查無後端 bug,11 次
get_single_cofacts_article抓的 article_id 全部正確。
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:
8d6c9fb8…9/13 14:30:21→14:32:26 verifier(125.055s)62cffdd1…9/11 05:21:18→05:23:23 verifier(125.212s)819ac3ff…9/9 verifier(125.153s)7cf8db69…9/9 investigator(125.304s)
已排除
- Cloud Run request timeout:
timeoutSeconds: 900(#112, 7/18) - 我們自己的 client timeout:
HttpOptions(timeout=240_000)(#116)只設在 investigator/verifier,但沒設任何 timeout 的 writer 也在同樣 ~125s 被砍 - 全域延遲上限:同期有成功跑到 195.6s / 45k tokens 的呼叫
假說 ~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%) |
- 只認 verifier 會誤擋四成好回應——writer 合法引用的多來自 investigator 的 google_search grounding。
- 第三種規則誤擋沒變少、攔截率反而掉,verifier ∪ investigator 是 Pareto 最優。
- 那 3 筆誤擋可能高估:其中
edcc0c71該 session 另有 8 個倒讚含「提供不存在的出處」。實際誤擋估 2–4%,且擋下來是要求 writer 重試,不是拒絕服務。 - 明確標「提供不存在的出處」的案例:9/13 會被擋(含 9/13 那筆 5/5 全編造)。
- 擋不到的 4 筆是「網址是真的、但內容不支持該 claim」(verifier 自己幻覺說支持)。要治這類得把
verifier_confirmed從 writer 手上拿走,改由after_tool依 verifier 逐條 ✓/✗ 自動判定——建議列為第二個 PR。
需要決定
- 125s 根因調查 –> MrOrz
- provenance 檢查(verifier ∪ investigator)是否現在做?這會動到 agent contract 與 per-claim source-coverage gate,依
CLAUDE.md屬於該留 ADR 的層級。 –> 建議開 cofacts.ai 的 issue、實作時付 ADR 文件 verifier_confirmed自動化要不要排進下一輪 –> 太複雜,先不要
- 可以考慮加上 source 可信度 [name=yutin]
上週重點整理(2026-09-10 ~ 2026-09-13)
GitHub 活動
cofacts/ai
cofacts/devops
- docs(GCE): document BigQuery external tables for querying archived logs(closed 2026-09-10)
cofacts/kb
- docs(embedding): 更正混合搜尋設計文件以符合實際實作(merged 2026-09-12)
- doc: add 20260910 meeting notes(opened & merged 2026-09-13)
cofacts/rumors-api
- fix: article 更新在樂觀鎖競爭中失敗時重試(closed 2026-09-13)
cofacts/rumors-db
- fix(reloadSchema): use snake_case wait_for_completion for ES9 client(closed 2026-09-10)
- release/20260914(released 2026-09-13)
cofacts/takedowns
- 新增 2026/09/11 移除隱私資訊公告,並新增隱私下架處理規範 AGENTS.md(merged 2026-09-14)
MCP
https://github.com/cofacts/kb/pull/25
October meetup
10/3 or 10/17
華梵大學板橋中心
- mrorz & nonumpa 都可以
[!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 個服務均正常運行(✔)。近期部署:
cofacts-ai:2026-09-10 19:30(本次期間內有新部署;與 GCE 上持續運作的python main.py為不同 workload,詳見第二節勘誤)site-tw/site-en/site-ja:2026-04-29 / 04-09(未變動)site-staging-*:2026-08-17(未變動)line-bot-*:2025-05-16 / 2026-03-16(未變動)
四、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 後:
- 該小時剛好是每小時執行的
fetchStatsFromGA(GA 分析資料索引進 ES)排程,但當小時處理筆數(3,716→3,965 筆)與前後小時相比並無異常放大,判斷並非單一 job 造成,較可能是該排程與台北午後流量高峰疊加所致。 - 已確認與每週 ES 備份無關(見下)。
- 建議:若未來再次出現類似尖峰,可考慮將
fetchStatsFromGA排程錯開流量高峰時段。
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
- https://github.com/cofacts/ai/pull/143 [name=ggm]
- 7 個 agent,要有評分的方式,做了 writer & investigator 這兩個 [name=ggm]
- 拿現有 data 挑 20~30 組,看使用者的 intention 是否有達成
- test data + LLM as judge
- judge 結果:langfuse evaluation –> https://langfuse.com/docs/evaluation/experiments/experiments-via-sdk
- 參考 https://github.com/cofacts/rumors-api/blob/master/src/scripts/experimentAVTranscript.ts [name=mrorz]
- 想看的東西
- ADK 派發的每一個點的表現如何
- End user 的角度:只看輸出與結果
- 用來看是哪裡表現不好
- verifier 的話應該要用有 browser tool 的 coding agent 就能 judge [name=mrorz]
- https://cofacts-ai-eval.godgunman.workers.dev/reviews/2026-09-14/
- Score delivered drafts independently from conversation quality https://github.com/cofacts/cofacts-ai-eval/pull/13
- 之前沒有針對草稿做評分。
- agent 說「你可以看草稿」,LLM as judge 以為應該要有 URL(他不知道草稿在另一個介面)
- Evaluate conversation prefixes with swappable judge providers https://github.com/cofacts/cofacts-ai-eval/pull/12
- 使用者給 negative feedback 有時會覺得單輪不好、有時候覺得整個不好
- LLM judge 之前都是一輪一輪評分,但他應該要看 context 做評分
- 改成 judge 看得到待測一輪之前的對話
- 跑完會放上來