AG Day 43 已知限制與除錯日誌
執行需求:CPU 可跑。AG Day 42(原文連結)寫好了 README 與架構圖,也準備了接手清單與 smoke test,但有一塊刻意留白:這個專案目前「做不到」哪些事。一個誠實的技術專案不只說明能做到什麼,也應該明列做不到什麼。沒有這份限制清單,新成員很容易對專案抱持過高期待,跑下去才發現某個情境根本還沒支援。今天的任務是把這 45 天累積下來的「已知限制」整理成一份正式的 docs/limitations.md,並建立一份「除錯日誌」範本 docs/debug-log.md,讓日後碰到問題時能用一致格式留下紀錄,逐漸累積成專案自己的除錯手冊。這項工作完全在本機 CPU 上完成,不需要 API 金鑰或 Docker。
引言
寫軟體最危險的心態是「沒看到 bug 就是沒問題」。對一個 AI Agent 系統來說,這個心態尤其危險——LLM 的輸出本來就有機率性,今天跑出來的結果不一定明天還能重現;外部服務(Tavily、Langfuse)會偶爾逾時;檢索結果會被模型誤解。如果沒有系統化地記錄「什麼情境下會壞」,等到 production 出事才開始找原因,往往已經錯過最佳除錯時機。我們今天的做法是建立兩份互相搭配的紀錄檔:docs/limitations.md 列舉「目前已知做不到的事」,docs/debug-log.md 是「事後填寫的除錯紀錄」。前者是設計階段就決定的邊界,後者是營運階段才浮現的真實案例。
這份 limitations.md 對接手的人非常重要,因為它會直接告訴新成員:「不要把這個專案用在哪些情境」。例如:我們目前不支援多語言研究(僅繁體中文最佳,英文可用但需要另外評估)、不支援圖片或音訊輸入、不支援即時串流到對話介面(雖然 AG Day 19 已經做過內部串流)、不支援分散式部署(單機 SQLite 是瓶頸)。這些限制不會在程式碼裡以錯誤訊息的形式出現,新成員必須事先讀過這份清單,才能選對工具與場景。
原理/觀念
為什麼要分「設計時限制」與「執行時雷點」
限制分成兩種:設計時限制(design limitation)是這個版本刻意沒做的功能,例如「不支援多語言」「不支援分散式部署」;執行時雷點(runtime pitfall)是程式有做但容易在某個特定情境下出錯,例如「超過 8 步會強制熔斷」「Tavily 速率限制會讓搜尋失敗」。這兩種的本質不同:前者是「要不要做」的決策,後者是「怎麼做才不會壞」的經驗。把它們混在一起,會讓限制清單失去焦點。我們今天會用兩個獨立段落區分,並在每條後面標記對應的 AG 系列篇章編號,方便回查。
為什麼除錯日誌要用一致範本
很多團隊的除錯紀錄最後變成「Slack 訊息 + 半完成的 Markdown + 個人記事本」的拼貼,新人完全看不懂。一份好的除錯日誌需要具備幾個要素:發生時間(精確到時分)、環境(OS、Python 版本、相依套件版本)、重現步驟(一步步的指令)、預期行為、實際行為、可能的根因、已嘗試的解法、最後結論。我們今天會把這份範本寫成 docs/debug-log.md,並附上一個完整填寫的範例,讓接手的人能直接照抄格式開始記錄。
限制清單也是設計文件
很多人以為限制清單是「沒做到事的藉口」,其實恰恰相反。一份誠實的限制清單能幫助團隊做正確的技術選擇:如果「不支援分散式部署」是已知限制,那當使用者抱怨延遲時,你不會想辦法把單機系統改成叢集,而是會建議「先升級單機硬體,或減少並行請求」。把限制明列出來,反而能避免做出錯誤的架構決策。
完整實作
我們會在 research-agent/docs/ 下新增 limitations.md 與 debug-log.md 兩個檔案,並在 README 中建立對應的索引段落。先建立檔案骨架:
mkdir -p research-agent/docs
touch research-agent/docs/limitations.md
touch research-agent/docs/debug-log.md
第一步:撰寫 limitations.md。分成「設計時限制」「執行時雷點」「環境與資源限制」「語言與內容限制」四段,每一條都用「限制、現象、建議」三欄描述。對應的 AG 系列篇章編號放在每條後面,方便回查:
# research-agent/docs/limitations.md
> 本份清單列舉 research-agent v1.0 目前已知的限制。接手的人在評估是否採用本專案前,請先讀完。
> 每條限制對應到 AG 系列的篇章編號,方便回查當時的設計理由。
## 設計時限制(design limitations)
### L-01 不支援多語言研究(AG Day 21)
- 限制:分塊、引用、評估流程均預設繁體中文最佳;英文可運作但需要另外評估分塊粒度。
- 現象:對日文、韓文內容的分塊可能會把句子切在不該切的地方。
- 建議:跨語言情境請先實作小型評估集(AG Day 33),確認指標可接受再正式使用。
### L-02 不支援圖片、音訊、影片輸入(AG Day 1-9 範圍內未實作)
- 限制:所有內容來源限定為文字;多模態輸入未納入 v1.0 範圍。
- 現象:嘗試把圖片網址交給代理,會被視為文字字串而無法解讀。
- 建議:若需要多模態,請另開分支或等 v2.0;不要嘗試把圖片 base64 塞進 system prompt。
### L-03 不支援分散式部署(AG Day 38)
- 限制:SQLite 單檔案儲存、Chroma 單機運作;無法橫向擴展。
- 現象:當同時請求數超過單機承載,延遲會線性上升而非攤平到多節點。
- 建議:單機升級硬體(NVMe SSD、增加 RAM)比改成分散式更划算;真要分散需另寫 sharding 層。
### L-04 不支援對話介面即時串流到前端(AG Day 19)
- 限制:Streamlit 介面採用一次性送出請求、收到完整回應後再顯示;未實作 token-by-token 串流。
- 現象:使用者會感覺「按下送出後等很久」;AG Day 19 的內部串流沒串到 UI。
- 建議:若產品需要即時回饋,請改寫 `research_agent/ui.py` 串接 `stream()`。
## 執行時雷點(runtime pitfalls)
### P-01 步數超過上限會強制熔斷(AG Day 6、AG Day 30)
- 雷點:`RESEARCH_AGENT_MAX_STEPS` 預設 8;超過就會熔斷並回報「步數耗盡」。
- 重現:把 `max_steps` 設成 3,跑一個需要 5 步的研究問題。
- 解法:調高 `max_steps`,或先把問題拆小再交給代理。
### P-02 Tavily 速率限制會讓搜尋失敗(AG Day 24)
- 雷點:Tavily Search API 對單一金鑰有速率限制;超過會回 429。
- 重現:連續呼叫 50 次搜尋後不暫停。
- 解法:在 `tools.py` 加 tenacity 重試與指數退避;或在批次之間加 sleep。
### P-03 Chroma 索引膨脹導致檢索變慢(AG Day 20-21)
- 雷點:當 documents 與 chunks 累積到數萬筆後,向量檢索的召回時間會明顯上升。
- 重現:把同一份來源反覆抓取 100 次,觀察 `retrieve()` 的耗時。
- 解法:用 AG Day 24 的 `content_hash` 做冪等入庫;定期用 `delete(where=...)` 清理過時 chunks。
### P-04 supervisor 路由進入迴圈(AG Day 13、AG Day 29)
- 雷點:若 `route_to_worker` 條件邊寫錯(例如回傳相同 worker),會造成無限迴圈直到步數熔斷。
- 重現:把 `_rule_based_route` 故意寫成永遠回傳 `search_worker`。
- 解法:使用 AG Day 13 的 `recursion_limit` 與 LangGraph 的 `MAX_TEAM_STEPS` 雙重保護。
### P-05 Langfuse 觀測未設定金鑰時不報錯但也不記錄(AG Day 35)
- 雷點:如果 `LANGFUSE_PUBLIC_KEY` 與 `LANGFUSE_SECRET_KEY` 沒設,`setup_tracing()` 會靜默略過。
- 重現:不設金鑰,跑一次完整流程,然後去 Langfuse 平台看——沒有資料。
- 解法:把金鑰是否設定納入 smoke test;未設定時輸出警告而非靜默。
## 環境與資源限制
### E-01 Python 版本需 3.13+(AG Day 2)
- 限制:本系列使用 uv 與 3.13 的型別改進寫法;3.12 與 3.11 雖然可能能跑,但未驗證。
- 建議:用 uv 鎖定 Python 版本,不要混用系統 Python。
### E-02 模型費用隨用量累積(AG Day 9、AG Day 36)
- 限制:本系統不對模型呼叫費用做硬性上限;只透過 `RESEARCH_AGENT_MAX_STEPS` 做步數限制。
- 建議:串接 Langfuse 觀測(AG Day 35)並設定每日費用告警。
## 語言與內容限制
### C-01 評估集僅涵蓋繁體中文與英文(AG Day 33)
- 限制:黃金問題與評分標準以雙語撰寫;其他語言未驗證。
- 建議:跨語言上線前先擴充評估集(AG Day 33)。
這份清單的結構刻意把「設計時限制」與「執行時雷點」分開——前者是「這個版本不會做」,後者是「做了但容易在某個情境下出問題」。每條都用「限制/現象/建議」三欄描述,對應的 AG 系列編號放在標題後方便回查。請特別注意 L-03「不支援分散式部署」這條:很多人會誤以為單機 SQLite 是過渡方案,事實上 v1.0 刻意保留單機設計,因為對教學與小規模團隊使用反而更友善。把這個邊界寫清楚,能避免「為什麼不上 Postgres」之類的無效爭論。
第二步:撰寫 debug-log.md。這份是「事後填寫的除錯紀錄」範本。我們先給出空範本,再附一個「完整填寫」的範例:
# research-agent/docs/debug-log.md(除錯日誌範本)
> 每碰到一個非顯而易見的問題,請複製下面的範本填寫一份紀錄,附在檔案末尾。
> 累積的紀錄是專案最值錢的「實戰經驗」,新成員加入時請先讀這份檔案。
## [YYYY-MM-DD HH:MM] 簡短標題(症狀一句話)
**環境**
- OS:例如 macOS 15 / Ubuntu 24.04 / Windows 11
- Python:3.13.x(uv 鎖定)
- 套件版本:`uv pip freeze` 輸出(只列與問題相關的)
- 模型:`RESEARCH_AGENT_MODEL` 設定值(不寫版本號)
**重現步驟**
1. `...`
2. `...`
3. 觀察到 ...
**預期行為**
一句話寫清楚「原本應該怎樣」。
**實際行為**
一句話寫清楚「實際上怎樣」,附上錯誤訊息原文(記得轉義 `<` 與 `&`)。
**可能的根因**
依你當下的判斷寫下「我懷疑是 ...」,不必是定論。
**已嘗試的解法**
- 改 A:結果 ...
- 改 B:結果 ...
**結論**
最後的修法、或標記為「待研究」並指派給誰。
**相關 AG 篇章**
AG Day N(posts-ag/NN-ag-day-NN.html)
這個範本的關鍵在於「環境」「重現步驟」「結論」三欄必須是別人能照著做、跟著追的具體資訊,不要寫「我跑了一下」「好像這樣」這種模糊描述。「已嘗試的解法」這欄尤其重要,能避免下一個人重複犯同樣的錯。最後的「相關 AG 篇章」則是把這個除錯紀錄與教學內容連結起來——日後若有人問「為什麼 supervisor 路由會卡住」,你可以指著這份紀錄說「請看 AG Day 13 與這條除錯紀錄」。
第三步:在 debug-log.md 末尾附一個完整填寫的範例。我們虛構一個情境:「用 dry-run 模式跑研究問題時,writer_worker 沒輸出報告」。這個情境對應的根因其實是「離線模式下 chunks 為空,導致 writer 提早結束」,但剛接手的同事不見得能馬上看出來:
## [2026-08-25 14:32] dry-run 模式下 writer_worker 沒輸出報告
**環境**
- OS:macOS 15.1
- Python:3.13.1(uv 0.5.x)
- 套件版本:research-agent 0.9.x、langgraph 1.x、pydantic 2.x
- 模型:未設定 RESEARCH_AGENT_MODEL(離線模式)
**重現步驟**
1. `RESEARCH_AGENT_DRY_RUN=1 uv run research-agent "測試主題"`
3. 觀察到最終 `reports/test.md` 只寫了標題,沒有任何內容
**預期行為**
應該看到類似「測試主題(示範報告,離線模擬模式)」並列出兩到三筆示範引用段落。
**實際行為**
最終報告只有標題列,內容為空;`logs/pipeline.log` 顯示 writer_worker 報告 `status=done`,但 `retrieved_chunks` 是空串列。
**可能的根因**
retrieval_worker 在離線模式下,把 search_findings 當作輸入,但 search_findings 為空(因為離線模式沒有真實搜尋)。
**已嘗試的解法**
- 把 dry_run 改成 False 並設 `RESEARCH_AGENT_MODEL`:結果正常 → 確認問題出在離線流程
- 在 writer_worker 開頭加 print:看到 chunks 是 `[]`
**結論**
離線模式的 `retrieval_worker` 應該在 search_findings 為空時主動產生「示範 chunks」而不是回傳空串列;修正後離線模式可正常產出報告。已開 issue #142 指派給原作者,預計 AG Day 41 後修正。
**相關 AG 篇章**
AG Day 30(posts-ag/30-ag-day-30.html)、AG Day 33(posts-ag/33-ag-day-33.html)
這個範例刻意寫得「完整且具體」:每一欄都有實質內容,不是「待補」。新同事看到這個範例就知道「喔原來要寫成這種程度」。請特別注意「已嘗試的解法」這欄列出了兩個嘗試,最後一個找到真正的根因——這讓下一個碰到類似問題的人不必從頭摸索。
第四步:把這兩份檔案掛進 README 的索引。我們在 AG Day 42 寫的 README 末尾有指向「已知限制」段的超連結,今天要把那段填實:
# research-agent/README.md(補充段落)
## 已知限制與除錯
- 已知限制清單:請見 [docs/limitations.md](docs/limitations.md)。
涵蓋設計時限制、執行時雷點、環境與資源限制、語言與內容限制。
- 除錯日誌:請見 [docs/debug-log.md](docs/debug-log.md)。
碰到非顯而易見的問題時,請依範本新增紀錄。
## 維運手冊
請見 AG Day 44(posts-ag/44-ag-day-44.html)整理的維運情境。
這個補充段把 AG Day 42 留下的「未知內容」具體指向今天的兩份檔案,並預告明天的維運手冊。接手的人從 README 進來,能沿著「限制 → 除錯 → 維運」這個順序讀下去,完整覆蓋營運階段會碰到的問題。
第五步:把限制清單的關鍵字串進 smoke test。AG Day 42 的 scripts/smoke_test.sh 目前只驗證基本流程,今天在裡面加一段檢查「離線模式能跑出有效報告」:
echo "== 4. 驗證離線模式可產出有效報告(限制 L-01、P-01 觸發檢查) =="
RESEARCH_AGENT_DRY_RUN=1 uv run research-agent "smoke test topic" --max-steps 4 --output reports/smoke.md
if grep -q "示範報告" reports/smoke.md; then
echo " [OK] 離線模式報告內容有效"
else
echo " [FAIL] 離線模式報告內容為空,請參考 docs/debug-log.md"
exit 1
fi
這段檢查把「限制清單」與「smoke test」綁在一起:當離線模式跑出空報告時,smoke test 會失敗並指向 debug-log.md。這是「把限制寫進測試」的最佳實踐——限制不是寫完就忘,而是要在 CI 裡持續驗證。
第六步:建立 docs/decisions/ 目錄與一份 ADR(Architecture Decision Record)範本。雖然這不在「限制」範疇,但限制常常來自當初的設計決策,保留決策紀錄能讓未來的人理解「為什麼這樣選」:
# research-agent/docs/decisions/0001-use-sqlite.md(ADR 範本)
# ADR 0001:儲存層採用 SQLite 而非 Postgres
## 狀態
採用(v1.0)
## 背景
research-agent v1.0 需要儲存 documents / chunks / runs / events 四張表,
並支援 checkpointer 的狀態寫入(AG Day 16)。
## 選項
- SQLite(單檔案、零部署)
- Postgres(需額外服務、支援分散式)
## 決策
採用 SQLite。理由:
1. 教學與小規模團隊使用,單機即可。
2. 與 LangGraph 的 SqliteSaver(AG Day 16)原生整合。
3. 部署簡單:整個專案只有一個 .db 檔。
## 後果
- 優點:部署簡單、沒有額外服務。
- 缺點:無法橫向擴展;單機 SSD 與 RAM 是瓶頸。
- 緩解:見 docs/limitations.md L-03。
這份 ADR 看起來很短,但它做了一件很重要的事:把「為什麼選 SQLite」這個決策明文記下。日後若有人想改用 Postgres,這份 ADR 就是討論的起點;他不會從零開始質疑這個選擇,而是可以從「當時的考量是什麼」「現在的情境是否已經改變」切入。
第七步:在 README 中新增一段「延伸閱讀」索引,把本系列所有對應的 docs 檔案串起來。這是個輕量的設計——把散落的 docs 整合成一份入口清單:
# research-agent/README.md(新增:延伸閱讀)
## 延伸閱讀
- docs/env.md:環境變數清單(AG Day 42)
- docs/limitations.md:已知限制(AG Day 43)
- docs/debug-log.md:除錯日誌(AG Day 43)
- docs/decisions/:設計決策紀錄(ADR)
- AG Day 44 維運情境:常見營運問題與對應處理方式
這份清單刻意按 AG 系列編號排序(42 → 43 → 44),讓接手的人能沿著教學順序對應到 docs 內容。當系列結束後,這份清單就會是專案的「正式入口」,所有進一步的學習都從這裡開始。
常見錯誤與踩雷
第一個常見錯誤是把限制清單寫成「做不到的事」的抱怨清單,而沒有寫「建議」。一份好的限制清單每一條都應該附上「建議怎麼處理」,讓讀者知道「這個限制對我有影響嗎、要怎麼繞過」。今天的 limitations.md 每一條都有「建議」欄,這是設計上的堅持。
第二個常見錯誤是除錯日誌只寫症狀不寫解法,導致下次碰到類似問題時還是得從頭摸索。我們的範本刻意把「已嘗試的解法」與「結論」兩欄放在顯眼位置,強迫填寫的人留下完整的解題路徑。一份只有症狀沒有解法的除錯日誌,價值只有原本的三分之一。
第三個常見錯誤是限制清單與程式碼不同步。當某條限制被解除(例如後續版本支援了多語言),要記得更新 limitations.md 並在 CHANGELOG 註記。我們可以在 CI 加一支檢查腳本,掃描 limitations.md 中提到的 AG 篇章編號,比對是否都有對應的程式碼變更;但這個檢查目前先不做,等之後文件規模再大時再考慮。
第四個常見錯誤是 ADR 寫成「為什麼做這個決定」的辯護文,而不是「這個決定的取捨是什麼」。ADR 的重點在於「後果」段——列出這個決定的優缺點與未來可能的風險。沒有後果段,ADR 就只是個公告,而不是決策紀錄。
效能與實務提醒
限制清單的長度本身也是個指標。如果一份專案寫了上百條限制,代表這個專案可能設計過於複雜、需要拆成多個小專案;如果只有三到五條,代表可能沒認真想清楚邊界。我們今天的 limitations.md 有 4 個段落、共 12 條,落在合理範圍。建議每次新增限制前先停下來想:「這條能不能用設計手段避開?」例如「不支援多語言」也許能拆成「每個語言一個 adapter」來支援,這樣限制就變成了設計選項。
除錯日誌的格式要嚴格統一。範本一旦定下來就不要隨意改欄位,否則舊紀錄會和新紀錄混雜,新人看不懂。我們今天的範本有 8 個固定欄位:環境、重現步驟、預期行為、實際行為、可能的根因、已嘗試的解法、結論、相關 AG 篇章。每一欄都是必填,缺一欄就視為紀錄不完整。
ADR 的命名規則採用四位數編號 + 簡短標題(0001-use-sqlite.md),這樣可以按編號排序、未來插入新決策時不必重新命名。每次開新的 ADR 時,先檢查最高編號 + 1,不要跳號。這個慣例能讓 ADR 在檔案總管裡自然排序,不會被字母順序打亂。
最後,limitations.md 應該在每次重大版本發布時重新檢視一次。當 v2.0 加入新功能時(例如多語言支援),要把對應的限制移除並在 CHANGELOG 註記「L-01 已解除」。這個習慣讓限制清單保持「現在還有效」的狀態,而不是過期的歷史文件。
小結
今天把 AG Day 42 留下的「已知限制」段補完,並新增了一份除錯日誌範本與一份 ADR 範本。整個專案現在有了完整的三件套:README(入門指南)、limitations(邊界宣告)、debug-log(除錯手冊)。這三份檔案互相搭配:README 說明能做到的事,limitations 說明做不到的事,debug-log 紀錄「曾經壞過怎麼修」。接手的人讀完這三份,對專案的掌握度就會從 30% 提升到 80%。
新增的術語:設計時限制(design limitation,版本刻意沒做的功能)、執行時雷點(runtime pitfall,程式有做但容易出錯的情境)、ADR(Architecture Decision Record,記錄重要設計決策的簡短檔案)、除錯日誌(debug log,依統一範本紀錄的故障排除紀錄)。
結語
限制清單與除錯日誌都是「靜態」的指引;真正在 production 環境會碰到的問題,往往是資源調度、容量規劃、模型升級、依賴更新這些「動態」議題。AG Day 44(原文連結)會把這些動態議題整理成一份「維運情境手冊」:磁碟空間不足時怎麼辦、API 速率限制觸發時怎麼降級、依賴套件出新版本時怎麼評估升級、風險升高的徵兆有哪些。明天那份手冊會把限制清單中的「建議」欄延伸到「具體處理步驟」,讓接手的人碰到問題時能照表操課。
明天,我們會進入「AG Day 44 技術手冊:常見維運情境」,把目前已知的營運問題與處理方式彙整成一份可照著做的清單,並補上「如何在不中斷服務下升級」「如何處理 API 額度耗盡」「如何評估新版本 LLM 的相容性」等情境的 SOP。這會是交付篇倒數第二篇,也是 AG Day 45(原文連結)系列總結的前哨。
延伸資源
- ADR(Architecture Decision Records)參考:Michael Nygard 的原始 ADR 範本。可參考
https://github.com/joelparkerhenderson/architecture-decision-records。 - 「十二要素應用」(The Twelve-Factor App):
https://12factor.net/。process、disposability、dev/prod parity 等原則對今天的限制與維運討論深具啟發。 - Langfuse 觀測官方文件:
https://langfuse.com/docs。當 P-05 情境發生時,如何設定環境變數讓 tracing 正確啟用,以官方文件為準。 - Python 官方文件的
venv與uv環境管理:見 AG Day 2 整理的延伸資源。E-01「Python 版本需 3.13+」的限制與環境設定有關。
留言
張貼留言