{"id":50531909,"url":"https://github.com/timcsy/matcher","last_synced_at":"2026-06-03T14:30:39.228Z","repository":{"id":360645857,"uuid":"1251010572","full_name":"timcsy/matcher","owner":"timcsy","description":"可解釋、公平、可重現的「參與者↔對象」配對工具（規則篩選 + 公平抽籤 + 完整稽核）","archived":false,"fork":false,"pushed_at":"2026-05-27T09:24:47.000Z","size":1025,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-27T10:13:02.101Z","etag":null,"topics":["auditable","fairness","fastapi","lottery","matching","python","school"],"latest_commit_sha":null,"homepage":"https://matcher.tew.tw/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/timcsy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-27T06:54:50.000Z","updated_at":"2026-05-27T09:24:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/timcsy/matcher","commit_stats":null,"previous_names":["timcsy/matcher"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/timcsy/matcher","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timcsy%2Fmatcher","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timcsy%2Fmatcher/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timcsy%2Fmatcher/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timcsy%2Fmatcher/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/timcsy","download_url":"https://codeload.github.com/timcsy/matcher/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timcsy%2Fmatcher/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33870025,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["auditable","fairness","fastapi","lottery","matching","python","school"],"created_at":"2026-06-03T14:30:37.391Z","updated_at":"2026-06-03T14:30:39.219Z","avatar_url":"https://github.com/timcsy.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# matcher\n\n[![tests](https://github.com/timcsy/matcher/actions/workflows/ci.yml/badge.svg)](https://github.com/timcsy/matcher/actions/workflows/ci.yml)\n\n依參與者屬性媒合對象的工具——以可解釋規則篩出資格集合、以可驗證隨機程序在集合內公平分配，並產出可重現的稽核紀錄。\n\n## 快速開始\n\n### 安裝\n\n需要 Python 3.11+ 與 [uv](https://github.com/astral-sh/uv)：\n\n```bash\nuv venv --python 3.11\nuv pip install -e \".[dev]\"\n```\n\n### 執行基準場景（教師-班級配對）\n\n```bash\nuv run matcher run \\\n  --rules  examples/teacher-class/rules.yaml \\\n  --roster examples/teacher-class/roster.yaml \\\n  --seed   123456 \\\n  --output audit.json\n```\n\n預期輸出：規則摘要、資格集合大小、最終配對、稽核檔已寫入。\n\n### 驗證可重現性\n\n同樣輸入跑兩次，稽核紀錄應逐位元組相同：\n\n```bash\nuv run matcher run --rules examples/teacher-class/rules.yaml \\\n                   --roster examples/teacher-class/roster.yaml \\\n                   --seed 123456 --output /tmp/a.json\nuv run matcher run --rules examples/teacher-class/rules.yaml \\\n                   --roster examples/teacher-class/roster.yaml \\\n                   --seed 123456 --output /tmp/b.json\ndiff /tmp/a.json /tmp/b.json \u0026\u0026 echo \"✅ 完全相同\"\n```\n\n### 使用內建模板\n\n```bash\n# 列出所有內建模板\nuv run matcher template list\n\n# 檢視單一模板\nuv run matcher template show teacher-class\n\n# 用內建模板執行媒合\nuv run matcher run --template teacher-class \\\n                   --roster examples/teacher-class/roster.yaml \\\n                   --seed 123456 --output audit.json\n\n# 匯出模板為檔案以分享\nuv run matcher template export teacher-class --output tc.yaml\n\n# 用匯出的檔案執行（取代內建 id）\nuv run matcher run --template-file tc.yaml \\\n                   --roster examples/teacher-class/roster.yaml \\\n                   --seed 123456 --output audit.json\n```\n\n「研習分組」模板含 `preferences_schema`，但本階段 M0 機制下，若名單帶有非空 preferences → 拒絕並提示等待階段 4。\n\n### 撰寫自訂模板（含 AI 助手 prompt）\n\n需要做新場景的模板？可參考 [`docs/template-authoring-guide.md`](docs/template-authoring-guide.md)——一份完整的 YAML schema 規格 + 6 個 expr 算子說明 + 2 個 worked example + Self-check checklist。\n\n**最省力的做法**：把整份指南複製貼給 Claude / ChatGPT，描述你的場景（參與者、對象、規則），AI 會依規格產出 YAML。指南最後 §13 附有現成的 prompt 填空模板。\n\n產出的 YAML 可用 `--template-file my.yaml` 跑 CLI，或放到 `src/matcher/templates/builtin/` 後重啟讓 Web UI 也看得到。\n\n### Web UI\n\n啟動本地 server：\n\n```bash\nuv run matcher serve\n# 預設綁定 127.0.0.1:8000（不對外）\n# 開發模式：uv run matcher serve --reload\n# 對外（LAN）：uv run matcher serve --host 0.0.0.0\n```\n\n開啟瀏覽器訪問 \u003chttp://127.0.0.1:8000/\u003e，可：\n\n- **新建媒合**：4 步驟向導（選模板 → 上傳 CSV/Excel → 設定種子 → 選分配機制 M0/M1/M2 → 執行）；若模板含志願 schema 且名單未填志願，自動跳到「填志願表單」中介頁——每位參與者 N 個下拉、無需手工準備 CSV preferences 欄；M1/M2 結果頁顯示處理順序與每位的志願排名，個別查詢頁顯示「您被分到第 N 志願」或「由公平抽籤分到」\n- **模板瀏覽**：查看內建模板的完整規則與屬性 schema\n- **過去媒合**：查看歷次媒合紀錄、重新下載 audit\n\n媒合紀錄持久化於 `data/matches/\u003cid\u003e.json`（已加入 `.gitignore`）。\n\n#### PDF 報告匯出\n\n結果頁與個別查詢頁皆提供「下載 PDF 報告」按鈕；CLI 亦可用 `matcher report --audit \u003cfile\u003e --output \u003cpdf\u003e [--role-id \u003cid\u003e]` 從 audit JSON 產出。\n\n需安裝 WeasyPrint 系統依賴：\n- macOS：`brew install pango glib`（並設 `export DYLD_FALLBACK_LIBRARY_PATH=/opt/homebrew/lib`）\n- Debian/Ubuntu：`apt install libpango-1.0-0 libcairo2 libgobject-2.0-0 libharfbuzz0b`\n\n未安裝時 Web PDF 端點回 503、CLI report 指令 exit 50；既有功能（媒合、結果頁、audit JSON 下載）不受影響。\n\n#### 個別查詢視圖\n\n媒合完成後，admin 結果頁底部「個別查詢連結」區段列出每位被媒合者的專屬 URL（`/match/\u003crecord_id\u003e/role/\u003crole_id\u003e`）。行政可將這些連結個別發送給對應的當事人；當事人開啟後可獨立查看：\n\n- 自己的基本資訊\n- 是否被分配（被分到哪個對象 / 或為什麼未分配）\n- 依模板規則的判定說明（用語面向一般教師，避免技術名詞）\n- 下載個人稽核紀錄 JSON（`/match/\u003crecord_id\u003e/role/\u003crole_id\u003e/audit.json`）\n\n頁面為純讀取——同一 URL 多次訪問結果完全一致。\n\n### 分配機制\n\n```bash\n# M0 純抽籤（預設；無偏好）\nuv run matcher run --template teacher-class \\\n                   --roster examples/teacher-class/roster.yaml \\\n                   --seed 123456 --output audit.json\n\n# M1 RSD 隨機輪流挑（含志願；先隨機洗牌處理順序、再逐位選最高未滿志願）\nuv run matcher run --template study-group \\\n                   --roster-csv examples/study-group/roster-m1.csv \\\n                   --seed 2026 --mechanism M1 --output audit-m1.json\n```\n\n「研習分組」範例 `roster-m1.csv` 含每位學生的志願組別（分號分隔），可用 M1 跑出含「處理順序 + 每人志願滿足度」的稽核紀錄。\n\n```bash\n# M2 Boston 層級填滿（先全塞第 1 志願超額抽籤、剩餘退到第 2 志願以此類推）\nuv run matcher run --template study-group \\\n                   --roster-csv examples/study-group/roster-m1.csv \\\n                   --seed 2026 --mechanism M2 --output audit-m2.json\n```\n\n- 規則：M0 不接受任何 preferences；M1 / M2 至少需一位提供 preferences。\n- M1 / M2 + 全空 preferences → 拒絕（exit 40）；建議改用 M0。\n- M1 vs M2：兩者皆為「公平的志願序」但定義不同——M1 強調「處理順序公平」，M2 強調「同層級內滿足度最大化」。同 roster + 同 seed 下兩者結果可能不同。\n\n### 從 CSV / Excel 匯入名單\n\n支援 CSV（UTF-8 / UTF-8-BOM / CP950 三種編碼自動偵測）與 Excel（.xlsx）：\n\n```bash\n# CSV 匯入（中文表頭、自動對齊到模板的 aliases）\nuv run matcher run --template teacher-class \\\n                   --roster-csv examples/teacher-class/roster.csv \\\n                   --seed 123456 --output audit.json\n\n# Excel 匯入（單一工作表自動使用）\nuv run matcher run --template study-group \\\n                   --roster-xlsx examples/study-group/roster.xlsx \\\n                   --seed 2026 --output audit.json\n\n# Excel 多工作表 → 須指定 --sheet\nuv run matcher run --template study-group \\\n                   --roster-xlsx examples/study-group/roster-multi.xlsx \\\n                   --sheet \"報名表\" \\\n                   --seed 2026 --output audit.json\n```\n\n格式要求：\n\n- 第一列為表頭；模板宣告的 `aliases` 自動對齊中文表頭（例「姓名」→ `name`）。\n- 可選 `id`／`編號` 欄位指定參與者 id；否則自動生成 `R001`、`R002`...\n- list 型別欄位以分號 `;` 分隔（例：`G1;G2;G3`）。\n- targets 由旁檔 `\u003cbasename\u003e.targets.yaml` 提供。\n\n### 只跑過濾階段\n\n```bash\nuv run matcher filter \\\n  --rules  examples/teacher-class/rules.yaml \\\n  --roster examples/teacher-class/roster.yaml \\\n  --output qualified.json\n```\n\n### 跑測試\n\n```bash\nuv run pytest\n```\n\n## 概念\n\n- **參與者（Role）**：待媒合的個體（如老師）。\n- **對象（Target）**：被分配的容器，具屬性與容量上限（如班級）。\n- **規則（Rule）**：定義「哪些參與者屬性 vs. 對象屬性符合資格」，附自然語言說明。\n- **資格集合（Qualified Set）**：規則篩選後的合法配對候選。\n- **分配機制**：支援 M0 純抽籤、M1 RSD（隨機輪流挑）、M2 Boston（層級填滿）；CLI 與 Web 三入口皆可選。\n- **稽核紀錄**：包含規則快照、名單快照、資格集合、seed、每步隨機決策、最終配對。\n\n## 部署到 K8s\n\n容器化部署（單一映像、PVC 持久化、無 DB）見 [`deploy/README.md`](deploy/README.md)。\n摘要：`docker buildx --platform linux/amd64 --push` 推映像到 ghcr → 由本機 `.env` 灌 Secret →\n`kubectl apply -f deploy/k8s/` → `kubectl port-forward -n matcher svc/matcher 8765:8765`。\n網域 / TLS / Ingress 自理（附 `deploy/k8s/ingress.example.yaml`）。\n\n## 文件\n\n- 規格：`specs/001-core-allocator/spec.md`\n- 計畫：`specs/001-core-allocator/plan.md`\n- 任務清單：`specs/001-core-allocator/tasks.md`\n- 知識文件：`knowledge/{principles,vision,experience}.md`\n- Constitution：`.specify/memory/constitution.md`\n\n## 範圍邊界\n\nmatcher 不是 scheduler、不是 voter、也不是動態調整器。多輪排程、投票選舉、事後重新洗牌請開立獨立專案；詳見 `knowledge/vision.md`「範圍邊界」段。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimcsy%2Fmatcher","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimcsy%2Fmatcher","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimcsy%2Fmatcher/lists"}