Safety and compliance
Template v1.0 · updated 2026-09-03Video Moderation for Training Sets
Reduce raw footage to the subset you can defend training on. Every excluded clip carries its reason, every uncertain one goes to a person, and the thresholds are yours.
A cleared training set, with an audit trail for every clip that did not make it.
Teams preparing video corpora for model training who have to prove what was excluded and why.
82%
of minors caught at a cutoff of 18
on 650 labelled faces, 320 minors and 330 adults, single frame; 4.8% of adults sent to review
100%
recall is reachable, and the page says what it costs
a cutoff of 30 catches every minor and sends 94.5% of adult footage to review; the whole curve is in the evaluation tab
0.70
face detector gate
cleared six phantom minors the detector found in non-faces while keeping 85% of genuine faces
39%
of detections routed to review as unaged
no usable face, under 24px, or detector confidence under 0.70. None of them is passed as adult.
What it looks like
Frame in, decisions fire, a verdict lands, a reviewer's call feeds back.
Simulated walkthrough · illustrative frames, scripted decisions
face 0.93 · est. 16 to 18 · borderlinereviewfaces -> age-estimation -> person-verdictsStill: Pexels / Ron Lachframe 1 of 4
Decision path
- face detected 0.93
- grouped across 3 looks
- age estimate: borderline
- route: review
within 6 years of the cutoff, and 3 looks
Running tally
0
cleared
1
review
0
excluded
Reviewer feedback
When the frames are done, a reviewer's call on the borderline case feeds back into the thresholds.What goes in
- source-videoYour footage, read from your own object storage. Nothing is copied out.
- brand-mark-refsLogo and mark variants you supply, one file per variant, with a hard-fail or contextual rule each.
- person-refsPublic-figure reference imagery you supply and hold the rights to. Several poses per person.
Where the decisions fire
Illustrative frames; boxes show the decision path, not live model output
face 0.93 · est. 16 to 18 · borderlinereview
brand mark · match 0.88 · rule: contextualcleared
staged 0.81 · real 0.19 · margin +0.62cleared
onscreen_text · BREAKING NEWS · staticcleared What comes out
- segment-verdictsOne row per segment: decision, the reason, and counts of people, minors, unaged and borderline faces.
- review-queueEverything routed to a human, ranked, with the decision chain attached to each item.
- cleared-set-exportThe segments you can train on, as a filterable set ready to hand to a training job.
How the namespace is wired
3 buckets, 8 collections, 2 clean views, 6 retrievers. The diagram generates the manifest below; they cannot drift apart.
SourceBucketCollectionClean viewRetrieverClick a node to inspect it
One file spins up the namespace. Generated from the diagram above. Also served at /templates/video-moderation.namespace.yaml.
# One file spins up the namespace. Wiring comes from the diagram:
# edges are sync targets, source.bucket_ids, view sources and retriever scope.
namespace: video-moderation
sources:
- name: source-video
kind: s3
cadence: continuous
filters: your footage, in your account
syncs_into: [source-video]
- name: brand-marks
kind: manual
cadence: one-shot
filters: customer-supplied logo and mark variants
syncs_into: [brand-mark-refs]
- name: person-references
kind: manual
cadence: one-shot
filters: customer-supplied public-figure reference imagery
syncs_into: [person-refs]
buckets:
- name: source-video
skip_duplicates: true
schema:
content: { type: video }
- name: brand-mark-refs
skip_duplicates: true
schema:
content: { type: image }
- name: person-refs
skip_duplicates: true
schema:
content: { type: image }
collections:
- name: video-segments
source: { type: bucket, bucket_ids: [source-video] }
feature_extractor:
feature_extractor_name: multimodal_extractor
version: v2
input_mappings:
video: content
field_passthrough:
- { source_path: file_location }
- { source_path: segment_id }
- { source_path: start_time }
- { source_path: end_time }
- name: brand-mark-index
source: { type: bucket, bucket_ids: [brand-mark-refs] }
feature_extractor:
feature_extractor_name: image_extractor
version: v1
input_mappings:
image: content
field_passthrough:
- { source_path: mark_name }
- { source_path: rule }
- name: person-ref-index
source: { type: bucket, bucket_ids: [person-refs] }
feature_extractor:
feature_extractor_name: face_identity_extractor
version: v1
input_mappings:
image: content
field_passthrough:
- { source_path: person_name }
- { source_path: rights_note }
- name: faces
source: { type: collection, collection_id: video-segments }
feature_extractor:
feature_extractor_name: face_identity_extractor
version: v1
input_mappings:
video: content
field_passthrough:
- { source_path: segment_id }
- { source_path: bbox }
- { source_path: detection_score }
- { source_path: quality_score }
- name: keyframes
source: { type: collection, collection_id: video-segments }
feature_extractor:
feature_extractor_name: image_extractor
version: v1
input_mappings:
image: content
field_passthrough:
- { source_path: segment_id }
- { source_path: frame_ts }
- name: onscreen-text
source: { type: collection, collection_id: video-segments }
feature_extractor:
feature_extractor_name: scrolling_text_extractor
version: v1
input_mappings:
video: content
field_passthrough:
- { source_path: segment_id }
- name: age-estimation
source: { type: collection, collection_id: faces }
feature_extractor:
feature_extractor_name: age_estimator # template-provided extractor, installed with the template
version: v1
input_mappings:
face: content
field_passthrough:
- { source_path: person_id }
- { source_path: age_median }
- { source_path: aged }
- { source_path: n_looks }
- name: violence-context
source: { type: collection, collection_id: keyframes }
feature_extractor:
feature_extractor_name: context_scorer # template-provided extractor, installed with the template
version: v1
input_mappings:
image: content
field_passthrough:
- { source_path: segment_id }
- { source_path: staged_score }
- { source_path: real_score }
- { source_path: margin }
clean_views: # retriever_transform collections, the downstream contract
- name: person-verdicts
source: { type: collection, collection_ids: [faces, age-estimation] }
retriever_transform:
unique_id_field: person_id
write_back_fields:
- { source_field: age_median, target_field: age_median }
- { source_field: aged, target_field: aged }
- { source_field: n_looks, target_field: n_looks }
- name: segment-verdicts
source: { type: collection, collection_ids: [person-verdicts, violence-context, onscreen-text, video-segments] } # via retriever pipeline
retriever_transform:
retriever: roll-up-by-segment # the pipeline defines transform + doc identity
unique_id_field: segment_id
write_back_fields:
- { source_field: decision, target_field: decision }
- { source_field: reason, target_field: reason }
- { source_field: n_person, target_field: n_person }
- { source_field: n_minor, target_field: n_minor }
- { source_field: n_unaged, target_field: n_unaged }
- { source_field: n_borderline, target_field: n_borderline }
retrievers:
- name: brand-mark-match
collections: [brand-mark-index]
feature_search:
fusion: rrf
top_k: 5
searches:
- { feature_uri: "mixpeek://image_extractor@v1/google_siglip_base_v1", query: { input_mode: text, text: "{{INPUT.query}}" } }
- name: public-figure-match
collections: [person-ref-index]
feature_search:
fusion: rrf
top_k: 5
searches:
- { feature_uri: "mixpeek://face_identity_extractor@v1/insightface__arcface", query: { input_mode: text, text: "{{INPUT.query}}" } }
- name: roll-up-by-segment
collections: [person-verdicts, violence-context, onscreen-text, video-segments]
stages: # pipeline retriever: groups documents, materialises into the collection on its out-edge
- stage_name: group
config: { stage_id: group_by, parameters: { group_by_field: segment_id } }
- name: review-queue
collections: [segment-verdicts]
feature_search:
fusion: rrf
top_k: 100
searches:
- { feature_uri: "mixpeek://multimodal_extractor@v2/gemini-embedding-2", query: { input_mode: text, text: "{{INPUT.query}}" } }
- name: moderation-search
collections: [segment-verdicts]
feature_search:
fusion: learned
top_k: 25
searches:
- { feature_uri: "mixpeek://multimodal_extractor@v2/gemini-embedding-2", query: { input_mode: text, text: "{{INPUT.query}}" } }
- { feature_uri: "mixpeek://multimodal_extractor@v2/multilingual_e5_large_instruct_v1", query: { input_mode: text, text: "{{INPUT.query}}" } }
- { feature_uri: "mixpeek://multimodal_extractor@v2/multilingual_e5_large_instruct_ocr_v1", query: { input_mode: text, text: "{{INPUT.query}}" } }
learning_config: { shadow_mode: true, rollout_pct: 0.0, context_features: [INPUT.session_id] } # flip via retriever PATCH when the signals justify it
- name: cleared-set-export
collections: [segment-verdicts]
feature_search:
fusion: rrf
top_k: 1000
searches:
- { feature_uri: "mixpeek://multimodal_extractor@v2/gemini-embedding-2", query: { input_mode: text, text: "{{INPUT.query}}" } }