Spaces:
Sleeping
Sleeping
| # PoolCoach RL — hướng dẫn cho Claude Code | |
| Luận văn thạc sĩ: huấn luyện tư duy điều bi 9-ball bằng RL. | |
| Stack: `pooltool` (sim vật lý) → Gymnasium env → Stable-Baselines3 (PPO/SAC), cộng web app FastAPI + canvas gợi ý cú đánh. | |
| Trả lời và giải thích bằng **tiếng Việt**. Commit message bằng **tiếng Anh**. | |
| ## Môi trường | |
| - Python 3.12, venv ở `D:\Khoa luan\poolcoach-env` (NGOÀI repo). | |
| - Cài gói bằng **uv**, không dùng pip: | |
| `uv pip install -r requirements.txt --index-strategy unsafe-best-match` | |
| (flag bắt buộc — `panda3d==1.11.0.dev3702` chỉ có trên `archive.panda3d.org`). | |
| - 4 tầng requirements, cài tách để mạng chậm không kéo hỏng nhau: | |
| `requirements.txt` (core: pooltool + gymnasium) · `-rl.txt` (SB3 + torch) · `-app.txt` (fastapi + uvicorn) · `-dev.txt` (pytest + httpx). | |
| - Lần chạy pooltool đầu tiên mỗi process tốn **~40s Numba JIT**. Đừng tưởng treo. | |
| ## Quy ước repo — đọc trước khi viết code | |
| **Package KHÔNG được cài.** Mọi file trong `scripts/`, `tests/`, `examples/` phải tự đẩy `src/` vào path trước khi import `poolcoach_rl`: | |
| ```python | |
| ROOT = Path(__file__).resolve().parents[1] | |
| sys.path.insert(0, str(ROOT / "src")) | |
| ``` | |
| Quên dòng này là lỗi đã tái phạm (24/07). Trong `tests/` việc này nằm ở `conftest.py` — đừng dựa vào `app/main.py` chèn hộ, vì khi đó kết quả test phụ thuộc thứ tự collect của pytest. | |
| **`.bat` có hai vai trò khác nhau — đừng lẫn:** | |
| - **Launcher chạy tay** đặt ở `D:\Khoa luan\` (thư mục CHA, **ngoài git**). Script Python nằm trong `scripts/`; `.bat` chỉ là lớp bọc activate venv + `cd` + gọi script. Thêm script mới cần chạy thường xuyên → thêm `.bat` tương ứng ở thư mục cha, **đừng tạo launcher trong repo**. | |
| Hiện có: `poolcoach.bat` (mở shell đã activate) · `run_app.bat` · `run_tests_app.bat` · `run_eval_fullrack.bat` · `run_benchmark_fullrack.bat` · `run_recommend_smoke.bat` + các `run_experiments_*.bat` theo ngày. | |
| - **Bản lưu lịch sử của thí nghiệm ĐÃ CHẠY** thì ngược lại: **trong repo**, ở `scripts/experiments/` (29/07/2026). Đó là **copy byte-for-byte** của launcher đã thực sự sinh ra số liệu — bản ở thư mục cha vẫn còn nguyên để double-click. Lý do: comment đầu mỗi file ghi **lý do chạy + ngưỡng nghiệm thu**, thứ `git log` không tải nổi; và `run_qfield_20260721.bat` là lệnh tái lập chính model đang chạy trong app. Ngoài git thì mất là mất hẳn. | |
| Luật + bảng nội dung: `scripts/experiments/README.md`. Copy vào đó **sau khi** đã chạy xong, và **không sửa** bản đã lưu. | |
| `.gitattributes` khai `*.bat text eol=crlf` — `cmd` Windows cần CRLF. | |
| **`models/`, `logs/` bị gitignore.** Model cần deploy phải ép: `git add -f models/qfield_20260721/qfield.pt` (đang track qua Git LFS). | |
| ## Cấu trúc | |
| ``` | |
| src/poolcoach_rl/ | |
| envs/ SinkOneBallEnv (stage 1), PositionPlayEnv (stage 2a) | |
| recommend/ simulate.py (chỉ trả FACTS vật lý) · rules.py (luật + EV) | |
| geometry.py (đường bị chắn) · engines.py (oracle search + pool) | |
| core.py (validate + orchestrate) | |
| app/ main.py (FastAPI, 3 endpoint) · schemas.py · static/ (canvas FE) | |
| scripts/ train_* · eval_* · benchmark_* · oracle_* · gen_bc_dataset | |
| tests/ contract test (rules/fallback/api-v2/v1-compat) + conftest stub | |
| ``` | |
| Tách tầng cố ý: `simulate.py` chỉ nói *chuyện gì đã xảy ra*, `rules.py` mới phán *hợp lệ hay không*. Đừng nhét luật vào tầng sim. | |
| ## Test & nghiệm thu | |
| - `pytest` (hoặc `run_tests_app.bat`). Test kiểm **contract** — luật 9-ball, shape response, message lỗi — KHÔNG kiểm vật lý. `conftest.py` cố ý nhét stub `pooltool` rỗng, thiếu `System`/`simulate`/`EventType` để test nào lỡ chạm tầng vật lý sẽ ném `AttributeError` ngay. | |
| - Gate thật của engine là `scripts/eval_fullrack.py --tables 20`: **0 cú phạm luật** và **search < 5s/cú**. Mốc hiện tại: median 1.13s, max 2.48s. | |
| - Chỉ số RL đọc bằng eval mẫu lớn: `eval_position.py --episodes 1000`. Eval 200 cú cho Q|pot nhiễu tới ±0.05 — đã bị lừa 2 lần (14/07, 16/07). Kết luận về Q phải kèm SE. | |
| ## Bẫy đã trả giá — đừng lặp lại | |
| - **`os.cpu_count()` không thấy cgroup quota.** Trong container HF (2 vCPU) nó trả về số core host → pool 7-15 worker. Số worker khai báo tường minh qua env `POOLCOACH_WORKERS` (Dockerfile set `2` = đúng số vCPU thật của Space; `0` = serial). `sched_getaffinity` cũng không cứu. | |
| - **Quy ước spin của pooltool** (nghiệm thu từ source wheel 0.6.0): `a=+1` là mép **TRÁI** bi, `b=+1` là **đỉnh** bi. FE đã sửa `cx = 50 − side·46` cho khớp. Đổi dấu ở đâu cũng phải đối chiếu lại docstring `Cue`. | |
| - **Tín hiệu "thắng" thô bị may mắn thổi phồng.** Hai lần: `b2` rớt lỗ may mắn (20/07) và "lucky 9" trong grid search (24/07). Cách sửa đúng là **điều kiện hoá theo ngữ cảnh** (WIN_EV chỉ khi target = 9), không phải hạ hệ số. | |
| - **`.git/index.lock` sót lại** → xoá tay file đó. Nguyên nhân đã truy ra (25/07): `git status` chạy từ sandbox Linux của Cowork tạo lock rồi không unlink được qua mount Windows. Claude Code chạy native nên không dính; Cowork từ nay dùng `git --no-optional-locks status`. | |
| ## Mẫu block `HANDOFF.md` | |
| Ghi thêm một block vào `..\HANDOFF.md` sau MỖI lần chạy — xong hay dừng giữa chừng đều ghi. Chép từ `..\poolcoach-docs\Documents\PoolCoach_Workflow_Cowork_ClaudeCode.md` §5.5; bản đó là bản gốc, sửa mẫu thì sửa ở đó trước. | |
| Một block cho mỗi lần chạy, mới nhất **ở dưới cùng**: | |
| ```markdown | |
| ## <ngày giờ> — <việc gì, một dòng> | |
| **BRIEF**: <mốc chép từ BRIEF.md; "không có" nếu làm theo prompt miệng> | |
| **Trạng thái**: XONG | XONG-CÓ-LỆCH | DỪNG-GIỮA-CHỪNG | |
| **Commit** | |
| - `<hash>` <subject> (rỗng nếu không commit gì — nói rõ vì sao) | |
| **Gate** | |
| | gate | ngưỡng | đo được | ĐẠT/ĐỎ | | |
| **Lệch khỏi BRIEF** ← MỤC QUAN TRỌNG NHẤT | |
| - <làm khác gì · vì sao · ảnh hưởng thế nào tới số liệu> | |
| - ghi thẳng "không lệch gì" nếu đúng vậy — đừng bỏ trống | |
| **Bất ngờ** (thứ tự nhận ra mà BRIEF không hỏi) | |
| - <crash, hành vi lạ, phát hiện phụ, nghi ngờ chưa xác minh> | |
| **File sinh ra** | |
| | file | sinh bởi lệnh nào | commit hash lúc chạy | | |
| **Câu hỏi cho Cowork** (quyết định cố ý KHÔNG tự đưa ra) | |
| **Chưa làm** | |
| ``` | |
| Ba mục cứu được ca 27/07: **Lệch khỏi BRIEF** (khai script chữa cháy), **Bất ngờ** (khai crash), và cột **commit hash lúc chạy** trong bảng file (lộ ngay hai cấu hình chạy ở hai phiên bản code khác nhau). | |
| > **Ghi `HANDOFF.md` kể cả khi thất bại — nhất là khi thất bại.** Một lần chạy chết giữa chừng mang nhiều thông tin hơn một lần chạy trơn tru, mà nó lại chính là lần không để lại commit nào. | |
| ## Ranh giới với Cowork | |
| Claude Code sở hữu mọi thứ trong repo này. **Không sửa** `..\poolcoach-docs\research_log.md` — nhật ký nghiên cứu do phiên Cowork ghi cuối ngày, dựa trên `git log` của repo này. | |
| Design doc + implementation plan nằm ở `..\poolcoach-docs\Documents\` — đọc để lấy yêu cầu, nhưng không sửa; thiết kế được chốt ở Cowork. | |
| ## Commit | |
| Conventional Commits, tiếng Anh, cả subject lẫn body. Mỗi bước trong implementation plan = 1 commit, chỉ commit sau khi qua gate nghiệm thu của bước đó. | |
| Scope đang dùng: `rules` `recommend` `geometry` `engines` `api` `fe` `app` `envs` `eval` `deploy` `stage2a` `examples`. | |
| ``` | |
| feat(rules): WIN_EV only for direct 9-ball target (anti lucky-9) | |
| fix(app): flip spin-dot x-axis to match pooltool side-spin convention | |
| chore(deploy): dockerignore tests, HF metadata for full-rack build | |
| ``` | |