This is written for engineering teams that want to build a spam-detection system. It gathers Matters's experience across model selection, data recycling, error amplification, and the calibration process. It does not walk through the full architecture; the focus is on how a small-to-mid-sized platform, with limited resources, keeps model errors within a reversible range.

Matters is a Chinese-language writing community that started in Hong Kong in 2018 and writes its articles into IPFS. Its detection engineering carries one constraint that never changes, and that constraint sets the direction of every technical choice that follows. Better to miss a piece of spam than to wrongly remove a piece of legitimate speech. Seventy to eighty percent of everyday content is spam, yet only about 1% is the protected civic and political writing, so the cost of misjudging legitimate content is higher. This principle is not just a statement of values; it also shapes how the model is tuned, so the system would rather let some spam through than block legitimate content. This piece explains, in order, how the model is chosen, how the data accumulates, how errors are amplified, and how the team calibrates. The material comes from the team's own accounts and two public code repositories; wherever a figure is the platform's own self-assessment and has not been independently verified by a third party, the text says so plainly.

Define the task first, then choose a comprehension model

Content filtering did not rely on language models from the start. Early anti-abuse ran along two tracks. The behavioral layer is gatekept by a reCAPTCHA/Turnstile A little quiz or background check used at sign-up or login to tell whether “you're a real person or a bot.” — first Google reCAPTCHA, later switched to Cloudflare Turnstile — placed at sign-up, login, and article appreciation (tipping) to block large numbers of bot accounts at the source. The content layer, for a while, could only rely on keyword filtering. The problem is that keyword rules cannot read context內容過濾機制(工程端)Traditional keyword filtering alone also cannot effectively judge context. The wording of bot posts keeps changing, hiding violation signals inside long posts that look perfectly normal, and traditional rules struggle to catch them.

In August 2024, the content-analysis model went live. The first thing to decide was the model type. This task requires reading the meaning of text and judging the relationships between words, which makes it a fit for a Encoder An AI model built specifically to “read and understand” the meaning of text — a different job from the chat-style models that “generate” text; BERT is a well-known example of this kind. (a BERT-family model) rather than a Decoder (a GPT-family model) whose main job is generating text. Beyond matching the task, comprehension models are usually smaller, faster at inference, and have a lower hardware bar內容過濾機制(工程端)BERT models are small, the parameters that need tuning are simpler, inference is fast, and the hardware bar is lower — a good fit for a company the size of Matters, which makes them the more pragmatic choice for a small company.

Having settled on a comprehension model, the team tested different models against hand-labeled samples, on the basis that the closer a model comes to human judgment, the better the fit內容過濾機制(工程端)the closer a model's evaluation comes to human recognition, the better the fit; the currently chosen model is: ibm-granite/granite-embedding-107m-multilingual. It finally settled on ibm-granite/granite-embedding-107m-multilingual, then trained it with LoRA A low-cost way to fine-tune a model: by adjusting only a small fraction of the parameters, you can train a general-purpose model to specialize in one particular task. . The samples came from day-to-day operations. On-duty staff labeled violating and normal articles one by one in the backend tooling, accumulating more than 40,000 positive and negative samples early on內容過濾機制(工程端)an initial cumulative total of more than 40,000 positive and negative sample articles. In other words, the model's so-called “correct answers” all come from human judgment — and that fact shapes every training result that follows.

The handling principle: don't delete, only lower visibility

Before discussing the detection system, we have to explain what consequences the model's output brings, or there is no way to weigh the cost of a misjudgment. The handling principle Matters established in mid-2024 is to not delete content, only lower its visibility對抗濫用內容的演算法筆記The handling principle is ‘do not delete the content, it just stops being seen.’ High-scoring suspect articles are excluded from recommendations, trending, tags, and channels, but the content, page, and existing links are all kept. An article judged to be in violation is not forcibly hidden; it simply stops appearing in the homepage picks, trending, and the various channel lists. The article itself, its page, and its existing links are all still there, and it still opens via a direct URL.

For a censorship-resistant Chinese-language writing platform, this design matters. It confines the consequence of “suppressing spam” to lowering reach, stopping short of deleting content, and so it keeps room to recover from mistakes the model may make in the future. The model misjudging political commentary, academic exegesis, and the like is an entirely predictable risk對抗濫用內容的演算法筆記and thereby preserves reversibility against future model bias — for instance, misjudging political or academic content, which is entirely to be expected. Acknowledging this first in engineering terms, and then using the architecture to absorb the cost of misjudgment, is the single most important safety design of this detection system.

Two model generations coexist; articles and comments are handled separately

Two model generations currently run in production. One is the earlier open-source BERT classifier, still operating to this day; the other is the new spam-detection-scaffold, which uses a Granite text-embedding model with LoRA fine-tuning and, when needed, hands cases to an LLM Large AI models like ChatGPT and Claude that can read, write, and understand language. for review. The two handle different scenarios; the new system did not directly replace the old one. For a team with limited resources, this is a common way to migrate: keep the old system for now if it still works, and put the new system first on the tasks it suits best.

The choice of comment model is especially worth noting. Comments and long-form posts are two different tasks. Comments are short, carry few signals, are highly ambiguous, and are often heavily templated, making them hard to judge from context. The team might have assumed a more complex model would be more accurate — and the opposite turned out to be true. Articles use Granite plus LoRA, because long posts carry more signal and suit learning fine-grained features; but on extremely short text like comments, Granite plus LoRA has a much higher error rate than e5對抗濫用內容的演算法筆記In fact, on extremely short text strings like comments, the error rate is much higher than e5's. In the end, comments switched to Microsoft's multilingual-e5-small — a multilingual small model of about 0.1B parameters that is good at comparing semantic similarity.

99.7%
Post block success rate (platform's own figures)96% → 99.7%
0.13%
Share of users misjudged on articles (platform's own figures)3.1% → 0.13%
0.33%
Comment-model false positives (platform's own figures)e5-small · recall 0.88

These figures need careful reading. After the article model went live, the success rate for blocking invalid articles rose from 96% to 99.7%內容過濾機制(工程端)Since going live, the success rate for blocking invalid articles has climbed gradually from 96% to 99.7%, while the share of users' articles misjudged fell from 3.1% to 0.13%內容過濾機制(工程端)The model's original rate of misjudging users' articles fell from 3.1% to 0.13%; on the comment small-model side, false positives are just 0.33%對抗濫用內容的演算法筆記Adopted the multilingual-e5-small model … which works better than the more unwieldy model, with a false-positive rate of just 0.33%. For extremely short, templated text, a small model good at comparing semantic similarity may beat a larger model that took painstaking fine-tuning. Model size is not the only criterion; the nature of the task is what you should confirm before choosing a model.

So Matters runs two asymmetric detection lines at once. They can be summarized as follows.

ScenarioModelWhy
Long-formgranite-embedding-107m + LoRAMore signal; needs to learn fine-grained features and can bear heavier fine-tuning
Commentsmultilingual-e5-small (~0.1B)Text is extremely short and often follows fixed templates; a small model good at semantic comparison actually has a lower error rate (platform's own account)

How the training data got poisoned

For teams that want to replicate this pipeline, data poisoning is the thing that most needs handling up front. After two years, the team had planned to switch to a newer model (such as Qwen3-Embedding or BGE-M3) and retrain — only to find that the original model was in fact good enough and just needed fine-tuning. The real problem was not that the model was too old, but that the training data itself had been slowly poisoned.

The poisoning mechanism runs roughly like this. One day an account's articles get flagged as problematic; later the account goes back to normal, but its subsequent normal articles may still be recognized by the system as problematic and pass into the next round of training data. The error gets fed back to the model again and again as if it were fact — like pouring cola into dough, the more you knead the less you can tell where the cola is對抗濫用內容的演算法筆記It's like accidentally pouring cola into the dough — the more you knead, the less you can tell where the cola went. The result is that long-form posts that ought to be protected — political commentary, academic exegesis, diaries, poems — gradually come to be seen as spam.

“The misjudgments were not caused by malicious coordinated attacks (though this site really does face piles of coordinated attacks all day long) — they were caused instead by wrongful removals being amplified.”

For a system that recycles new data every day and retrains on a schedule, the biggest source of bias is often itself. The system treats its own wrong output as the next round's training labels, which is harder to guard against than an external attack. Any team planning to “automatically recycle spam and retrain periodically” has to deal with this feedback poisoning first, or the model will drift on its own with no outside attack at all.

This did not cause bigger problems because the handling architecture described earlier had already blocked the consequences. The poisoned scores only showed up in the backend and did not feed directly into ranking對抗濫用內容的演算法筆記Fortunately, thanks to a relatively safe design, these scores were seen only in the backend and were never actually applied to the algorithm's ranking. Keeping scores and ranking as separate concerns paid off here. The model can make mistakes in the backend first, but those mistakes do not immediately affect public pages. If the detection score fed straight into ranking with no human-review layer, this kind of data poisoning could turn into wrongful removal at scale.

Lowering false positives with low-cost calibration

Calibrating data costs far less than retraining a large model. Using Claude's batch-agent flow with the Sonnet model, the team pulled 5,000 suspect items and re-calibrated them, at a cost of just 1 US dollar對抗濫用內容的演算法筆記Pulled 5,000 records to re-calibrate the suspect content (at a cost of just 1 USD). After calibration, false positives dropped by more than 10x對抗濫用內容的演算法筆記After the model was calibrated, false positives dropped by more than 10x, reaching a stage ready to go into production, close to a state fit for production (all of these are the platform's own account). The calibration process also surfaced a batch of samples worth watching: many articles that look like anti-fraud public-education pieces are in fact promotional copy from loan companies.

From this the team drew a cost/effectiveness conclusion about hybrid deployment — which is also the single most valuable takeaway in this piece for outside teams.

“A hybrid pattern of ‘[a small model (0.1B) + a LoRA-fine-tuned model]’ plus ‘an LLM (possibly a giant, several-trillion-parameter one) to calibrate the quality of spam labels’ may work better than upgrading the model — both in cost and in effectiveness.”

The point of this division of labor is not to spend all your resources chasing bigger, newer foundation models. Everyday scoring goes to the cheaper small model; the large language model is called in only for high-quality label calibration and reviewing hard cases. The small model handles the bulk of routine judgments, the large model handles the cases that are harder to call. For a team on a limited budget that still needs high-quality labels, this pairing is more effective than upgrading the model for its own sake.

Using an abstention mechanism to handle uncertain cases

The new generation's handling is no longer “if the score crosses the line, pass judgment.” The decision rules of spam-detection-scaffold (policy.py) split into three outcomes: below the threshold, allow; high enough and hitting a high-risk signal, block; and anything in between or carrying suspect signals is handed to the large language model to review. The high-risk signals come from patterns common in real-world porn advertising, including external links, deliberately distorted contact handles (line/telegram/WeChat/Lai), and adult-service keywords.

On top of this, the service deployed on the serverless architecture adds one more layer: a conformal Lets the model hold back from forcing a call when it isn't sure, answering “uncertain — hand it to a human” instead of classifying reluctantly. It uses a statistical upper bound to control the risk of wrongly blocking legitimate content, but it is not a per-case hard guarantee. mechanism. What it outputs is not just a score but decision ∈ {allow, block, review}, turning “uncertain — hand it to a human” into a result the system can explicitly return, and using a statistical upper bound to control the risk of legitimate content being wrongly blocked. That upper bound is an expectation-level guarantee under finite samples and must not be read as a per-case hard guarantee. The deployment status should be stated clearly too. The older staging deployment doc states that the conformal abstention layer was merged into the mainline code on June 13, 2026 and deployed to a separate staging environment; the subsequent production deployment flow was triggered by manual confirmation on June 16, 2026, successfully updating the production CloudFormation stack article-spam-model-v20251229spam-detection-serverless/STAGING_DEPLOY.md(2026-06-13)+ GitHub Actions run 27609961704(2026-06-16)+正式環境端點煙霧測試(2026-07-07)staging is deployed and available; the CI workflow deploy-staging-conformal.yml can redeploy repeatably; conformal's ‘wrongful kill ≤ eps’ holds in expectation with finite-sample slack, not as a hard guarantee; Successfully created/updated stack - article-spam-model-v20251229; the production endpoint returns score, decision, reason, p_spam, p_ham. As of a re-check on July 7, 2026, the production endpoint returns score, decision, reason, p_spam, and p_ham. So the earlier draft's assessment that treated production as still pending acceptance is no longer accurate. The more accurate statement is that the conformal abstention layer has been deployed to production and the production service can already return the three-way decision; but the actual governance effect, the volume of human re-review, and how downstream services use decision still need further observation.

One more thing also needs a neutral description: the deployment flow came after the fact. An image-based Lambda can only be built in an environment that has Docker, so the team had the model first and only later added a deployment flow that could run in the continuous-integration environmentspam-detection-serverless/STAGING_DEPLOY.mdimage-based Lambda must be built on a machine with Docker (the local Mac has none, so it needs CI or another machine). This is a common engineering state for a real small team, not an ideal pipeline finished from the outset.

Where the labels come from

Whether this system can correct itself comes down, in the end, to label quality — and label quality depends on whether humans stay involved. Bots change how they compose and word their posts, so operations still has to manually spot violating posts that slipped through and label them as the next round's training samples內容過濾機制(工程端)The management team still keeps up the work of human recognition; when a violating article shows up on a public page, they label it as a sample for later retraining. Conversely, if a user finds that their own article is not appearing on public pages, they can raise it, and after the team confirms, it gets re-labeled. This feedback tells the model its earlier judgment was wrong. Only after several rounds of retraining did the misjudgment rate fall from 3.1% to 0.13%內容過濾機制(工程端)After several rounds of retraining, the model's original rate of misjudging users' articles fell from 3.1% to 0.13% (the platform's own account).

The comment model's feedback loop is more direct. Positive samples come from the real removal records the community volunteers accumulate, and negative samples come from normal comments, forming a data source that runs from crowd labeling to model training. Automatic handling also has strict conditions: the same spam pattern must recur, or it must match a Ring A group of accounts run by one person or team that flood the site with the same template at scale. The source code calls this a Ring. pattern, or the model score must be high enough to decide. Comments that are suspect but uncertain are never handled automatically; they go to a human. Comment prevention is stacked four layers deep對抗濫用內容的演算法筆記On comment-prevention measures, we put up several layers of protection, including volunteer clean-up, model training on data provided by volunteers plus automatic clean-up (Auto), ring detection (Ring), and an admin-judgment mechanism — four layers in all: volunteer clean-up, automatic model handling, ring detection, and admin judgment. Because spam comments are highly intrusive, the design is more aggressive than for articles.

Recycling data to retrain is meant to counter abuse methods that keep shifting. A model is fixed once trained, but the other side adjusts its tactics — moving keywords from the title deep into the body, or using AI to generate template variants — making the detection task harder and harder對抗濫用內容的演算法筆記Recently we also found spam that uses AI to generate ring variants — truly a case of the higher the wall, the taller the ladder — getting harder and harder to catch. So “periodically recycling the spam that wasn't caught and retraining” remains necessary. Yet that same loop, without a human-review layer, also becomes a source of data poisoning: it both supplies the model with fresh data and can feed errors right back into the system.

Reproducible implementation principles

Take this engine apart and there are a few lessons you can carry away.