
專案背景
追蹤美國選舉時,相關資訊通常分散在全國性媒體、地方媒體、候選人動態與不同州的選舉報導中。當選舉進入初選、特別選舉或期中選舉階段後,要持續掌握每一州正在發生什麼事情,需要花大量時間閱讀與整理不同來源。
Election Radar 希望把這些資訊集中到同一個平台,透過 AI 搜尋並整理選舉相關事件,再依照州別、時間與事件類型呈現,讓使用者可以快速掌握目前值得注意的美國選舉動態。
核心功能
App 會使用 GraphQL 獲取已完成審核的選舉資料,再依照不同使用情境組織與呈現事件內容。
美國互動地圖
首頁以美國地圖作為主要入口,使用者可以直接選擇不同州,查看該州目前的選舉事件與相關資訊。選擇州之後,畫面會滑到該州相關的事件與內容。
以地圖出發的呈現方式讓事件直接與地理位置連結,使用者不需要先知道特定候選人或選舉名稱,也能從州別開始探索目前正在發生的選舉事件。
每週選舉事件摘要
Election Radar 會以週為單位整理近期的重要選舉動態,收錄美國各州的重要選舉事件,並提供每個事件的標題、摘要與來源,讓使用者快速掌握這段期間發生的主要選舉動態。
每個選舉事件都會保留來源連結,使用者在閱讀 AI 摘要的內容後,可以直接前往原始來源了解完整報導。

選舉時間軸
時間軸會依照時間順序整理特定州的選舉事件,讓使用者可以從較早的事件一路查看到最近的發展。
時間軸使用與每週選舉內容相同的事件資料,讓使用者可以從每週摘要掌握近期動態,也可以透過時間軸查看特定州較長時間範圍內的事件發展,例如候選人宣布參選、初選結果、背書、法院裁定或其他後續變化。

選舉趨勢
Election Radar 也整合州別事件統計與選舉相關的影片內容,讓使用者除了閱讀文字事件之外,也可以透過統計數字及影片了解候選人、選舉事件與近期政治發展。

跨平台開發
Election Radar 以 SwiftUI 開發,並使用 Skip 框架將同一套 Swift / SwiftUI 專案帶到 Android。
Skip 讓 Swift 程式碼可以在 iOS 與 Android 之間共用,並在 Android 上將 SwiftUI 介面對應至 Jetpack Compose,同時保留兩個平台的原生執行方式。
在實際開發中,部分平台功能仍需要針對 iOS 與 Android 分別處理。例如 WebView 的生命週期與 Render Process 行為在兩個平台上具有不同實作,因此這些部分會透過平台條件與個別處理方式完成。在開發此專案時,我 fork 了 Skip 的 skip-web 套件,並自行處理 Skip 尚未提供客製化支援的 WebView termination 問題。
Election Radar 如何運作
Election Radar 背後有一套自動化的選舉事件處理與半自動化的審核流程,從事件蒐集、AI 整理、資料處理到人工審核,再將通過審核的內容提供給 App 顯示。
整體流程為:
- Google Apps Script 依照排程定時啟動流程
- AI 蒐集本週美國選舉事件
- AI 整理選舉事件內容與相關資料,並以結構化輸出
- 進行事件去重、圖片取得等資料處理
- 將資料標記為未審核並存入資料庫
- 進行半自動化人工審核
- 通過審核後將資料標記為已審核
- App 讀取已審核資料並顯示給使用者

這套流程讓選舉事件可以定期更新,並在進入 App 前完成資料整理與審核,維持固定的資料格式與內容品質。
後端負責從排程啟動到事件進入資料庫及完成審核的整套資料處理流程。
Google Apps Script 會依照設定的排程啟動流程,接著由 AI 蒐集近期的美國選舉事件,再整理成 Election Radar 使用的結構化資料,包括標題、摘要、州別、分類與來源資訊。
AI 整理完成後,系統會繼續進行事件去重、圖片取得等資料處理,再將結果標記為未審核並存入資料庫。資料接著進入半自動化人工審核流程,通過審核後才會標記為已審核,提供 App 讀取。
使用 Redis 改善資料讀取穩定性
後端從 Supabase 讀取資料時,曾出現回應時間過長與頻繁發生 503 Service Unavailable 的情況。為了降低這類服務異常對 App 的影響,我加入 Redis 作為快取層。當快取中沒有對應資料時,後端會從 Supabase 取得資料並寫入 Redis;快取建立後,後續請求便會直接從 Redis 取得資料。即使 Supabase 暫時回應較慢,已快取的資料仍能快速且穩定地提供給 App。
來源與內容整理
同一個選舉事件可能同時被多個來源報導,因此系統在整理時也需要處理來源選擇與重複事件。
Election Radar 會以事件本身作為主要內容單位,再將對應的來源保存下來,避免同一件事情因為不同來源各自報導而重複出現在使用者面前。
審核後台
使用 Next.js 開發的審核後台,系統會將完成 AI 整理與資料處理的事件標記為未審核,並提供後台進行半自動化人工審核。完成審核的事件會標記為已審核,之後才會提供給 App 顯示。

圖片來源與授權
Election Radar 的選舉事件通常會搭配人物、政府機關或相關地點的圖片。圖片來源同時考量內容相關性、使用成本與使用者對內容的信任感。
一般免費圖庫雖然容易取得圖片,但素材通常較為泛用,遇到特定候選人、政治人物或選舉事件時,很難找到與事件直接相關的圖片。AI 生成圖片可以提供更高的自由度,但大量生成會增加成本,放在新聞與選舉資訊產品中也會影響使用者對產品信任度的觀感。
因此 Election Radar 使用 Wikimedia Commons 作為主要圖片來源。許多政治人物、政府機關與公共建築都有相對應的 Wikipedia 頁面與 Wikimedia Commons 圖片,能讓事件直接搭配真實人物或地點的影像。
系統會先找到對應的 Wikipedia 頁面,再取得相關的 Wikimedia Commons 圖片,同時讀取圖片的作者、授權方式與其他 attribution 資訊。由於不同圖片可能使用不同的授權條款,Election Radar 會保存並顯示完整的 attribution,讓每張圖片都能依照原始授權要求使用。
核心亮點
半自動化審核流程
Election Radar 的資料流中,大部分重複性工作都由系統自動完成,包括排程啟動、事件蒐集與整理、去重、圖片取得,以及資料寫入等處理。
進入審核階段後,另一個 AI Agent 會先進行初步檢查,再由人工確認事件內容、摘要、來源與相關資料。人工確認主要負責最後一道審核,確認內容可以發布後標記為已審核,App 就會讀取並顯示該筆資料。
這樣的半自動化流程可以減少需要逐筆處理的工作,同時保留人工確認,避免 AI 產生的錯誤內容直接進入 App。
UI 設計
資訊階層設計
Election Radar 的介面資訊階層以「州別」與「事件標題」作為最優先顯示的內容,讓使用者往下瀏覽時,可以先快速掌握這段期間各地發生的重要選舉事件。
當使用者對某個事件有興趣時,可以展開該事件查看 AI 整理的摘要;需要了解完整內容時,再進一步閱讀原始來源。
漸層淡出
系統預設會以省略號「…」表示超出顯示範圍的文字。當列表中多個事件標題都超出長度時,畫面會出現大量重複的省略號,讓列表尾端的視覺較為雜亂。
因此我建立了一個自訂元件,當文字超出指定顯示範圍時,在文字尾端自動套用漸層淡出遮罩。漸層可以提示使用者文字仍有後續內容,同時避免列表中反覆出現省略號。

使用 Codex 協助開發
將 Codex 納入開發 Workflow
開發新功能時,我通常會先在 ChatGPT App 透過語音轉文字把自己的想法說出來,先與 ChatGPT 討論功能是否合理、有哪些疑點與可以改善的地方。因為 Election Radar 涉及美國選舉,我會把自己平常關注美國政治累積的領域知識帶進討論,再結合 ChatGPT 提供的意見,逐步整理出功能方向。由於 Codex 的 token 使用量較高,而 ChatGPT 有較充足的 usage,所以這類前期功能討論通常會先在 ChatGPT 完成。
方向確定後,我才會進入 Codex。因為專案程式碼很多,我會直接指定它先閱讀哪些功能區塊與現有元件,讓上下文集中在這次功能,也避免重新建立或實作專案中已經存在的東西。接著我會先要求它說明修改方向、涉及哪些檔案,以及提供主要程式區塊大概會怎麼實作的範例,再根據這些內容確認做法與複雜度,同時把功能的預期效果與需要驗證的 test case 討論清楚。因為有些需求其實只需要少量程式碼就能完成,先確認實作方式可以避免 Agent 產生過多不必要的結構,減少後續 review 成本;事先定義 test case,也能讓後面的測試直接對照原本的功能需求。
由於 Codex 的上下文接近限制時會進行 context compaction,遇到較大的功能時,我會先告訴 Codex 整體方向,以及預計拆成哪些較小的功能,但實際上一次只處理其中一個,完成並確認後才進入下一個,而每個小功能的詳細需求則等真正開始實作時再提供。
Codex 完成實作後,我會要求它依照前面定義好的 test case 執行測試,確認功能達到原本設定的效果後,再回報這次修改結果。UI 功能則會讓 Agent 實際操作模擬器進行驗證。接著我會依照前面確認過的修改方向 review 實際變更,包括修改的檔案、實作方式與最終行為是否符合預期。但因為我對 UI 的動畫與轉場細節有較高要求,Agent 雖然可以驗證功能是否正常運作,可是很難準確判斷每一段動畫與轉場在實際操作時是否足夠順暢以及符合我的期望,因此最後我仍會在實體手機上完整跑一次,同時確認整體功能、互動與實際使用體驗。
如果實際測試發現問題,我會再次描述原本希望達到的效果與目前實際結果,必要時直接附上截圖,再讓 Codex 根據這些資訊繼續修改。功能確認完成後,我最後會讓 Agent 根據本次修改內容撰寫 commit message,再由我檢查它是否準確反映這次實際變更。
使用 Codex 處理開發問題
在使用 Skip 開發 Android 端時,有時畫面呈現或元件行為會與我的預期不同,即使程式語法正確且沒有產生編譯錯誤,仍需要進一步確認目前的結果是實作上的問題,或是框架本身的預期行為。這類情況通常需要查找文件,甚至閱讀與追蹤框架原始碼。
在開發 Election Radar 的過程中,我讓 Agent 直接閱讀 Skip 的相關原始碼並分析實作,協助確認遇到的情況是否存在問題或符合框架預期。如果目前的行為符合框架設計,卻與我希望在 Android 上呈現的效果不同,我會直接描述想要的 UI 呈現效果,讓 Agent 從原始碼與 API 中找出合適的實作方式,再透過條件編譯分別處理 iOS 與 Android 所需的行為。這樣能省下許多閱讀與追蹤原始碼的時間,並提升處理跨平台開發問題的效率。