Haijun Platform Docs
ID

Content moderation is the process of checking content against a written policy before it goes out, then deciding what happens to it: publish, reject, or send to a human reviewer. The content can come from anywhere: user comments, seller listings, ad creatives, marketing campaigns that legal must clear. The policy is written in plain English by a policy, legal, or brand team, and content arrives faster than any review team can read. Every owner of this loop lives with the same tension:

policy is written by people, in prose, but enforcement needs to be consistent, cheap, and auditable.

In this guide, you'll build a content moderation pipeline where Haijun compiles that written policy into deterministic, auditable rules. You'll compile a realistic ad-clearance policy, extract typed fields from content (including creative images), and produce verdicts from a rule engine that never calls a model, then run the same pipeline over three domains and measure it against labeled samples.

ning an extraction schema whose descriptions double as extraction instructions

A small JSON rule language with

scopes

and three-valued logic

Compiling policy prose with a

validator-driven repair loop

LLM assertions

: rules like "no one who looks under 25" as model-judged fields

Extraction with structured outputs, including from images

Adding a rule from one plain-English sentence

Evaluating against labeled samples across three domains

Prerequisites

t edit

Versioned, diffable rule artifacts you can replay

This cookbook restructures the problem so Haijun runs in exactly two places, and never decides the verdict:

oin(spec["type"]) if isinstance(spec["type"], list) else spec["type"]

t = f"enum[{', '.join(spec['enum'])}]" if "enum" in spec else t

print(f"{name:<36} {t}")

print(f"\n{len(schema['properties'])} fields")

ad_category enum[alcohol, gambling, financial_services, pharma_supplements, weight_loss, cosmetics_skincare, apparel_footwear, electronics, food_beverage, other] brand_name string | null headline_text string disclaimer_present boolean disclaimer_text string | null age_gate_shown integer | null text_coverage enum[low, medium, high] before_after_imagery boolean superlative_claims array health_claim_present boolean price_or_rate_shown boolean apr_disclosed boolean | null people_depicted boolean depicted_person_apparent_minor boolean | null competitor_reference boolean landing_url_domain string | null target_min_age integer | null 17 fields The context schema Not everything a rule needs can be read out of the content itself. Where the ad will run, which channel it runs on, whether the advertiser is managed or self-serve: your platform already knows these things before any review starts. They are facts about the placement, not the content.

── California: DECISION: FLAG ✗ [flag] bonus_cap_flag T bonus_amount_usd gt 1000 (actual: 1500) – out of scope: nj_bonus_cap_block ── New Jersey — same fields, different scope: DECISION: BLOCK ✗ [flag] bonus_cap_flag T bonus_amount_usd gt 1000 (actual: 1500) ✗ [block] nj_bonus_cap_block scope: T state eq 'NJ' (actual: 'NJ') T bonus_amount_usd gt 1000 (actual: 1500) ── Bonus amount unreadable (null) — three-valued logic routes to review: DECISION: NEEDS_REVIEW ? [flag] bonus_cap_flag — could not determine: bonus_amount_usd ? [block] nj_bonus_cap_block — could not determine: bonus_amount_usd Step 4: LLM assertions The engine so far only compares values. But some policy clauses can't reduce to comparisons over extractable facts: "Alcohol creatives must not depict anyone who appears to be under 25." No field says that. Calling a model from inside the engine would break everything we just built, so instead the compiler manufactures a field: a derived_fields entry whose judge instruction is a precise yes/no question, referenced like any other field:

"and in New Jersey they are blocked outright.",

active,

schema,

context_schema,

)

show(addition)

Append as a new ruleset version (never edit in place; rulesets are versioned artifacts)

active_v2 = {

"derived_fields": {active["derived_fields"], addition["derived_fields"]},

"rules": active["rules"] + addition["rules"],

"uncompilable": active.get("uncompilable", []),

}

assert not validate_document(active_v2, schema, context_schema)

print(f"\nruleset v2: {len(active_v2['rules'])} rules")

{ "derived_fields": { "signup_bonus_over_1000": { "type": [ "boolean", "null" ], "judge": "Look at the headline, body copy, and any text in the creative image. Answer true if the ad promotes a sign-up / welcome / new-customer / registration bonus (cash, credit, bonus funds, free bets, match bonus, or equivalent) whose stated maximum value is greater than $1000 (e.g. 'Get up to $1,500 in bonus bets when you sign up'). Answer false if a sign-up bonus is offered but its stated value is $1000 or less, or if the ad promotes no sign-up bonus at all. Answer null if a sign-up bonus is clearly offered but its monetary value cannot be determined from the content (e.g. 'huge welcome bonus' with no amount, or amount given only in a non-monetary unit you cannot convert).", "source_policy": "custom" } }, "rules": [ { "id": "custom_signup_bonus_over_1000_flag", "policy_ref": "custom", "policy_text": "Sign-up bonus offers greater than $1000 must be flagged for review.", "action": "flag", "uses_llm_fields": true, "scope": { "not": { "field": "state", "op": "eq", "value": "NJ" } }, "when": { "field": "signup_bonus_over_1000", "op": "eq", "value": true } }, { "id": "custom_signup_bonus_over_1000_nj_block", "policy_ref": "custom", "policy_text": "In New Jersey, sign-up bonus offers greater than $1000 are blocked outright.", "action": "block", "uses_llm_fields": true, "scope": { "field": "state", "op": "eq", "value": "NJ" }, "when": { "field": "signup_bonus_over_1000", "op": "eq", "value": true } } ] } ruleset v2: 24 rules The BetPeak creative advertises a $1,500 bonus, so the new rule should catch it. The derived judgment wasn't in the extraction we ran earlier, so re-extract against v2's derived fields, then evaluate. Note the flag in California vs. the block in New Jersey.

On this page
Prerequisites