Remove XGBoost ML + spam scorer; add image-aware AI curation #80

Closed
opened 2026-08-13 04:02:12 +00:00 by agent-pi · 2 comments
Collaborator

Fenliu's XGBoost ML integration and its rule-based spam scorer are not serving their purpose. Both look only at post text and metadata, so neither can judge what actually matters when curating cat content: whether the attachment is a real photograph of a cat (versus a comic, painting, mural, meme, statue, or other artwork).

The spam score in particular has never been useful, because it reads textual spam markers while curation decisions are made on the images.

This issue proposes removing both the ML stack and the spam scorer, and replacing them with something that evaluates image content directly, plus a check that the post text isn't using a cat as an excuse to promote something (books, war victims, soldiers, fundraisers, adoption drives, etc.).

Fenliu's XGBoost ML integration and its rule-based spam scorer are not serving their purpose. Both look only at post text and metadata, so neither can judge what actually matters when curating cat content: whether the attachment is a real photograph of a cat (versus a comic, painting, mural, meme, statue, or other artwork). The spam score in particular has never been useful, because it reads textual spam markers while curation decisions are made on the images. This issue proposes removing both the ML stack and the spam scorer, and replacing them with something that evaluates image content directly, plus a check that the post text isn't using a cat as an excuse to promote something (books, war victims, soldiers, fundraisers, adoption drives, etc.).
Author
Collaborator

Plan of attack:

Approach: remove the XGBoost ML stack + rule-based spam scorer entirely, and replace them with a two-stage AI classifier using a configurable OpenAI-compatible model (default Moonshot kimi-k2.6):

  1. Text stage (inline, at fetch): classify status text + alt text for "cat used as an excuse to promote" — promotional posts are flagged and never reach the image stage.
  2. Vision stage (overnight batch, Moonshot Batch API at 60% price): classify the first image as a real-cat photograph vs comic/art/mural/meme/etc.

The result is an indexed ai_would_reject recommendation on posts, surfaced as a badge + filters in the review UI. No auto-reject at launch (behind an ai_auto_reject setting, default off).

Key files:

  • Remove: src/fenliu/training/, services/ml_inference.py, services/spam_scoring.py, cli/, ML dependencies, and all score/ML surface (columns, API endpoints, review-UI widgets, tests).
  • Add: services/ai_classification.py (client + text/vision classifiers), services/ai_batch.py (batch processor), new posts AI columns, scheduler text hook + batch job, review-UI badge/filters.
  • Cross-package: remove Zhongli's dead score field (fed by Fenliu's spam_score).
  • Also fold in leftover migration fixes: the zaojun pre-commit hook and wuming CI action still point at codeberg.org.

Spec: .superpowers/specs/2026-08-13-fenliu-vision-ai-design.md
Plan: .superpowers/plans/2026-08-13-fenliu-vision-ai.md
Branch: feat/issue-80-remove-ml-add-vision-ai

Commits are task-per-commit (gitmoji), then a PR targeting main.

Plan of attack: **Approach:** remove the XGBoost ML stack + rule-based spam scorer entirely, and replace them with a two-stage AI classifier using a configurable OpenAI-compatible model (default Moonshot `kimi-k2.6`): 1. **Text stage** (inline, at fetch): classify status text + alt text for "cat used as an excuse to promote" — promotional posts are flagged and never reach the image stage. 2. **Vision stage** (overnight batch, Moonshot Batch API at 60% price): classify the first image as a real-cat photograph vs comic/art/mural/meme/etc. The result is an indexed `ai_would_reject` recommendation on `posts`, surfaced as a badge + filters in the review UI. No auto-reject at launch (behind an `ai_auto_reject` setting, default off). **Key files:** - Remove: `src/fenliu/training/`, `services/ml_inference.py`, `services/spam_scoring.py`, `cli/`, ML dependencies, and all score/ML surface (columns, API endpoints, review-UI widgets, tests). - Add: `services/ai_classification.py` (client + text/vision classifiers), `services/ai_batch.py` (batch processor), new `posts` AI columns, scheduler text hook + batch job, review-UI badge/filters. - Cross-package: remove Zhongli's dead `score` field (fed by Fenliu's `spam_score`). - Also fold in leftover migration fixes: the zaojun pre-commit hook and wuming CI action still point at codeberg.org. **Spec:** `.superpowers/specs/2026-08-13-fenliu-vision-ai-design.md` **Plan:** `.superpowers/plans/2026-08-13-fenliu-vision-ai.md` **Branch:** `feat/issue-80-remove-ml-add-vision-ai` Commits are task-per-commit (gitmoji), then a PR targeting `main`.
Author
Collaborator

Sub-PR progress on the collecting branch (feat/issue-80-collect):

Merged so far:

  • PR #81 — remove XGBoost ML + spam scorer; add AI text/vision classifiers (refs #80)
  • PR #86 — inline vision (refs #84, #82)
  • PR #88 — review filter fixes (refs #87)
  • PR #89 — reconcile models with DB + fix test-isolation flakiness (refs #83)
  • PR #90 — rename reviewer_notes to auto_reject_reason (refs #85)
  • PR #92 — remove stale review.html template (refs #91)
  • PR #95 — wire AI auto-reject into the fetch flow (refs #94)
  • PR #98 — classify posts on manual fetch paths (refs #96)

Still open: #97 (SQLite "database is locked" under concurrent fetches).

Final PR into main will carry Closes #80, #82, #83, #84, #85, #87, #91, #94, #96, #97.

Sub-PR progress on the collecting branch (`feat/issue-80-collect`): Merged so far: - PR #81 — remove XGBoost ML + spam scorer; add AI text/vision classifiers (refs #80) - PR #86 — inline vision (refs #84, #82) - PR #88 — review filter fixes (refs #87) - PR #89 — reconcile models with DB + fix test-isolation flakiness (refs #83) - PR #90 — rename reviewer_notes to auto_reject_reason (refs #85) - PR #92 — remove stale review.html template (refs #91) - PR #95 — wire AI auto-reject into the fetch flow (refs #94) - PR #98 — classify posts on manual fetch paths (refs #96) Still open: #97 (SQLite "database is locked" under concurrent fetches). Final PR into `main` will carry `Closes #80, #82, #83, #84, #85, #87, #91, #94, #96, #97`.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
marvin8/dujiangyan#80
No description provided.