Skip to content

Latest commit

 

History

History
145 lines (96 loc) · 7.72 KB

File metadata and controls

145 lines (96 loc) · 7.72 KB

HANDOFF — 接手指南

這份文件回答一個問題:要把這個專案交給別人,需要給什麼、不用給什麼、怎麼跑起來。


TL;DR

程式碼全在 git 裡(src/ + web/ + docs)。git 沒有不該有的是大資料產物,必須另外傳(雲端硬碟 / 外接碟):

必給 是什麼 大小
data/handoff.db 清空 per-query bake 層的 SQLite:raw SO 資料 + extracted practice + authority。給接手者一個乾淨的 bake 起跑點。 ~1.5 GB
data/canonical_index/ cold-path RAG 用的語料向量(embeddings.npy + question_ids.npy ~1.4 GB

接手的人自備一份 .env(自己的 API keys,見下)。給了這兩個產物,他可以直接把站台跑起來、丟新 query 走 cold-path bake —— 不用重抓 SO 資料、不用重燒 embedding、不用重算 authority

想連開發期 bake 出來的 demo query 一起交? 那就改交完整的 data/so_data_react_2021_2026_processed.db(含已 bake 的 query,landing 立刻有東西看),見下面〈兩種交法〉。

不用給 data/so_data_react_2021_2026.db(純 raw、無 computed 表)—— 它是 processed 的子集,processed/handoff 已含同一份 raw。只有「想從乾淨 raw 自己重跑整條 pipeline」才需要它。


handoff.db 裡有什麼

一顆 shared SQLite integration bus(見 src/store/):raw 表是 source of truth,其餘都是同一顆裡算出來的衍生表。三層保留、一層清空

角色 交接
Raw(source of truth) questions / answers / comments / users / interactions(221212 / 253351 / 604681 / 187701 / 778676) 原始 SO 資料 ✅ 留
Extracted practice(group-independent) practice_extractions(~680) 一 practice 一列、按 answer keying;answer body 的純函數 ✅ 留
Authority(全圖算、與 query 無關) user_authority(~170k) / user_pagerank_yearly(~182k) / authority_run_meta network PageRank 等中心性 ✅ 留
Per-query bake 層 submissions / canonical_group / practice_clusters / query_practice / camp_signal / query_signal / query_narrative / query_viz_cache 一次 query 的 resolve→cluster→signal→narrative→viz 結果 清空(clean handoff)/ ✅ 留(完整交)

practice_extractions 留著是為了設計意圖 + 未來跨-group reuse,但 ⚠️ 目前不會幫接手的人省 bake 錢extract.py 對每個 pool 進來的 answer 都重抽(cross-group reuse 還沒接,見其 docstring)。它今天不是成本快取。

第三個產物 data/canonical_index/ 不在 db 裡,是獨立 .npy:~221k 題的 OpenAI embedding 矩陣,free-text query → canonical group 全靠它。沒有它 cold-path 進不去。


兩種交法

A. 乾淨交接(預設,交 handoff.db —— 清掉開發期的 demo bake,接手者拿到空的 landing、自己 bake。務必在副本上做,別動工作 DB(不然本機 demo 全沒、要重 bake 才能繼續開發):

cp data/so_data_react_2021_2026_processed.db data/handoff.db
sqlite3 data/handoff.db <<'SQL'
DELETE FROM submissions;        DELETE FROM canonical_group;   DELETE FROM practice_clusters;
DELETE FROM query_practice;     DELETE FROM camp_signal;       DELETE FROM query_signal;
DELETE FROM query_narrative;    DELETE FROM query_viz_cache;
DROP TABLE IF EXISTS cluster_aggregations;   -- legacy 空表
VACUUM;                                       -- raw 為大宗,縮不多(1.6G→1.5G)
SQL
# 交 data/handoff.db + data/canonical_index/

已實測:乾淨 DB 能從零跑完整條 cold-path(resolve→extract→cluster→aggregate→narrate→materialize,~88s,query_viz_cache.status='ready')。

B. 完整交接(交 ...processed.db —— 直接交工作 DB,含你已 bake 的 demo query;接手者一打開 landing 就看得到結果。沒有刪除步驟,照原檔交即可。

兩種都搭 data/canonical_index/ 一起交。


路徑都集中在 src/config.py

db / index 路徑全部 import 自一個地方,換檔名 / 指到別的 snapshot 用環境變數即可,不用改任何程式碼(相對路徑以 repo root 為基準,絕對路徑照用):

# src/config.py
DB_PATH             # 預設 data/so_data_react_2021_2026_processed.db
CANONICAL_INDEX_DIR # 預設 data/canonical_index
COMMUNITY_DB=data/handoff.db          PYTHONPATH=src uv run uvicorn serve.app:app --reload
COMMUNITY_INDEX_DIR=/mnt/big/canonical_index   PYTHONPATH=src python -m canonical.build_index

authority 模組保留舊的 AUTHORITY_DB 當 legacy override(有設就贏),沒設就跟著 src/config.py 走,確保整條 spine 讀同一顆 db。


環境設定

需求:Python ≥ 3.11、uv、Node + pnpm

uv sync                              # 1. 後端依賴
cd web && pnpm install && cd ..      # 2. 前端依賴
cp .env.example .env                 # 3. 填 API keys(.env 已 gitignored)
#   SE_API_KEY      — 只有「重抓 SO 資料」才需要
#   OPENAI_API_KEY  — bake 新 query / 重建 canonical_index 需要(LLM + embedding)

⚠️ 程式不會自動讀 .env(沒有 load_dotenv)。env 變數要實際存在於 shell 環境:用 direnv(repo 用 .envrc,已 gitignored),或臨時 set -a; . ./.env; set +a 再跑。

把上面兩個資料產物放進 data/


跑起來

兩個 process,前端固定打 http://localhost:8000、serve 的 CORS 只放行 :5173

# 後端 serve plane(warm reads + cold-path 入口)
COMMUNITY_DB=data/handoff.db PYTHONPATH=src uv run uvicorn serve.app:app --reload   # → :8000

# 前端(另開一個終端機)
cd web && pnpm dev                                                                  # → :5173

打開 http://localhost:5173

  • 乾淨交接(handoff.db):landing 是空的 —— 在輸入框丟一個 React query,走 cold-path(resolve → bake → dashboard)即生出第一個結果。
  • 完整交接(processed.db):landing 直接列出已 bake 的 groups,點進去看 force-field + narrative。

(選用)authority Streamlit dashboard —— 探索用旁支工具,需另裝 streamlit / plotly

PYTHONPATH=src streamlit run -m authority.dashboard      # 預設讀 config.DB_PATH

如果沒拿到資料產物,要從零重建

整條鏈路都在 git 裡,可以重跑,但要花 API 額度與時間:

  1. 重抓 raw(Stack Overflow API,需 SE_API_KEY,量大、耗時)—— 見 src/ingest/README.md。產物 = raw db。
  2. 建 canonical index(需 OPENAI_API_KEY,一次性 ≈ $0.66)—— PYTHONPATH=src uv run python -m canonical.build_index
  3. bake(需 OPENAI_API_KEY,每個 query 約 $0.3 量級)—— in-window query 走 serve 的 POST /queries cold-path,或用 orchestration.driver 單跑。

computed 表(practice / cluster / signal / narrative)是 LLM 產物 —— 非決定性、且要花錢。這正是直接交接 db 的價值:省下重 bake 的成本與不可重現性。


不用留 / 不用煩惱的

  • data/so_data_react_2021_2026.db(純 raw,processed 的子集,見 TL;DR)
  • data/smoketest.db 之類的拋棄式副本(驗完即刪)
  • __pycache__/.venv/web/node_modules/(重裝即得)
  • data/golden_*.jsonsrc/breakdown/data/canonical_q54069253.json(retired demo 的 fixture / snapshot,留著只為能重跑舊 demo)
  • 任何 *.npy(embedding,gitignored)/ .env(keys 各自帶)