← Back to all stories

Evaluate a review queue for user-generated content

Sort a backlog of reports and submissions for human review without letting a model make the final moderation call.

Define the policy labels, preserve the original submission, and make escalation rules visible before you automate any routing.

Make policy labels operational

Start with the downloadable, fictional three-record evaluation. The queue record below is an output design, not a deployed moderation system. Run the local input validator before any upload. Live urgent reports must bypass batches and go directly to your existing immediate escalation process.

Labels such as "unsafe" are too vague for a review queue. Define the policy area, severity, confidence, and the action a reviewer should take. Include a category for uncertain cases. It is safer than forcing every submission into a confident label.

Write escalation rules before you process a backlog. Threats, self-harm, child safety, legal requests, and account compromise need a person immediately, regardless of the model output.

Download and validate the fictional batch

Download these three files into one folder: ugc-requests.jsonl, ugc-expected.json and ugc-validate.mjs. With Node.js 22 or later, run:

Shell command
node ugc-validate.mjs input ugc-requests.jsonl

The JSONL already contains the complete policy and one fictional report per request. No prompt assembly is needed. Keep the English cases unchanged for the first run. The expected file is an answer key for local checks; never upload it as model input. The validator makes no network calls.

The file uses the OpenAI Chat Completions batch shape, endpoint /v1/chat/completions and the DigitalOcean catalog ID openai-gpt-4o-mini. Before upload, verify current batch eligibility, account access and pricing. If that model is unavailable, replace body.model on all three lines with one supported OpenAI Chat Completions model ID and rerun the validator. A local pass checks structure, not account entitlement or model quality. The request caps output at 400 tokens using max_completion_tokens; do not increase the cap to hide a validation failure.

Then follow the official DigitalOcean batch workflow: upload the validated file, create an OpenAI job with the matching endpoint, retain its ID, and download both results and any error file. This optional remote step is billed; it was not executed for this fixture. The model catalog and OpenAI batch format were checked on 8 October 2026.

Save the result file as ugc-results.jsonl in the same folder, then run:

Shell command
node ugc-validate.mjs output ugc-results.jsonl

An error file, missing response, unknown or repeated ID, incomplete completion, incorrect route, invalid field or non-null final outcome blocks acceptance. Keep failures in manual review. A completed batch status alone does not mean every request succeeded. The validator accepts documented DigitalOcean response.choices and OpenAI response.body.choices envelopes; a missing finish_reason is inconclusive and fails closed. Review every original report and evidence quote even after a local pass.

Keep the original submission beside the suggestion

A reviewer needs the submitted text or media reference, the report reason, the proposed label, the evidence phrase, and the model confidence. Do not show a bare label and ask people to trust it.

Create a small labeled sample with policy specialists. Measure false negatives by policy area. The error that hurts most is often the one a broad accuracy number hides.

Expected human checks: UGC-01 → standard_review for possible spam; UGC-02 → standard_review with missing context flagged and no enforcement recommendation; UGC-03 → needs_specialist for account takeover. All final outcomes remain unset until a reviewer acts.

The downloadable policy defines these fixture-specific fields:

  • severity: low for routine nuisance, medium for possible harm needing context, high for immediate specialist concern. The unknown context in UGC-02 does not establish harassment.
  • confidence: low when context is missing, medium for a plausible interpretation, high for clear evidence supporting the proposed label. These are uncalibrated categories, not probabilities or authority to enforce.
  • reviewer_action: standard_review, review_now or needs_specialist. Specialist rules override confidence. review_now means immediate human-review priority within standard_review.
  • queue_state: needs_specialist for a specialist route; standard_review otherwise. final_reviewer_outcome must be null at model-output time.

The validator expects spam/low for UGC-01, harassment_uncertain/medium with low confidence for UGC-02, and account_compromise/high for UGC-03. These are exercise labels, not a universal moderation policy. Confirm the definitions with your own specialists before adapting them.

JSON
{
  "submission_id": "UGC-03",
  "policy_area": "account_compromise",
  "severity": "high",
  "evidence_quote": "an unknown person took control of the account",
  "confidence": "medium",
  "reviewer_action": "needs_specialist",
  "queue_state": "needs_specialist",
  "final_reviewer_outcome": null
}

This is a handwritten expected-shape illustration, not a recorded model response. The downloadable input preserves all three original reports; join results by custom_id rather than line order.

Make each queue state explicit

A queue should tell reviewers what happened next, not merely what a model guessed. Keep the suggested action separate from the final reviewer outcome so quality checks can find unsafe routing and reversals.

New items enter needs_specialist or standard_review. A human can move a case to returned_for_policy_clarification or escalated while leaving the final outcome null. Only an authorized reviewer may mark it resolved and record a final decision, owner and timestamp.

  • needs_specialist: immediate specialist attention; do not wait for confidence to rise.
  • standard_review: normal reviewer workflow with the original submission visible.
  • returned_for_policy_clarification: the policy label or instructions do not support a safe route.
  • escalated: a human has handed the case to a named owner; record a timestamp, leaving the final outcome null until a decision is made.
  • resolved: an authorized reviewer has recorded the final outcome, owner and timestamp.

Process an old queue asynchronously

A historical review backlog is a batch problem. DigitalOcean Batch Inference accepts JSONL requests and returns results after asynchronous processing. Keep the job scoped to text requests and make sure each source record has a unique ID for the result join.

Run a limited batch and sample every label before routing a full backlog. Stop the run if reviewers see a systematic error. A queue that moves faster in the wrong direction is worse than a slow queue.

Measure reviewer outcomes, not model confidence

Track time to review, agreement with the final reviewer outcome, appeals or reversals, and the number of urgent items caught by escalation rules. Confidence is only a sorting hint.

Keep a dated copy of the policy and instruction used for each batch. That gives your team a way to explain a change in outcomes and roll back a bad revision.

Frequently asked questions

Can a model remove content automatically?

Do not start there. First use it to prepare a transparent queue, then assess the harm, policy, and appeal requirements with the people responsible.

What belongs in the test set?

Include straightforward examples, ambiguous cases, policy edge cases, and samples that require immediate specialist review.