Creative DNA for Ad Archives
Search every scene of every ad you have run, and find the ones that keep earning spend
An ad archive you can query by what is on screen. Every creative is cut into scenes at ingest and the ad copy is searchable beside them, so a question like 'the hook where someone holds the product to camera' returns the moment rather than the file.
Performance marketing and creative teams sitting on an ad archive nobody can search
What deploys today4 ready1 need your input2 not available yetNo marketplace starter set ships with this template: it applies empty and reads Your own ad exports, so connect your own data before querying it.
What deploys today
What each part needs
The states are read from the manifest, so a part it ships commented out never shows as ready.
- ads-scenes
- Deploys and processes documents as they arrive.
- ads-text
- Deploys and processes documents as they arrive.
- brand-scenes
- Deploys and answers queries once its collections hold documents.
- copy-search
- Deploys and answers queries once its collections hold documents.
- ad-library-pull
- You upload these files yourself. There is no connection to sync.
- ads-faces
- Face recognition is turned off on Mixpeek Cloud, so this face step does not deploy. It runs on a dedicated or customer-hosted deployment, where face_identity_extractor is served.
- cast-identification
- Face recognition is turned off on Mixpeek Cloud, so this face step does not deploy. It runs on a dedicated or customer-hosted deployment, where face_identity_extractor is served.
What it looks like
Pick a frame or a search and see what fires.
cleared- ads-scenes and ads-faces, both fed from the same object
- Face indexed
- Scene embedded
- route: cleared
Face indexedScene embeddedcleared
Product in frameScene embeddedcleared
SubjectScene embeddedclearedWhat you would ask it
Example searches this namespace answers once it is applied. Each one names the retriever that serves it.
“ads holding the product up to camera”
brand-scenes
scene embedding over the ad archive, filtered by brand_slug
“which ads open on a price claim”
copy-search
the spoken and on-screen copy, searched as text
How the namespace is wired
The diagram shows 1 buckets, 3 collections, 0 clean views and 3 retrievers. The manifest below applies 1 buckets, 2 collections (clean views included) and 2 retrievers today; the other parts are commented out in it, each with the reason. The diagram generates the manifest, so they cannot drift apart.
Reward signals
How reviewer decisions move the thresholds
Thresholds at ingest drift as the corpus changes. The reviewers working the queue are the ones who see where a threshold is wrong first, so this template routes their decisions back into the model that set it.
None. No thumbs, no saves, no relevance marks are recorded by the template as shipped.
None from the application. Mixpeek records retriever executions server-side in mxp_retriever_executions, which is telemetry about queries rather than feedback about results.
mxp_retriever_executionsSystem collections in your namespace, on the same vector store as the rest of the template. They are yours to query.
Adding a learning loop means posting to /v1/retrievers/interactions when a user opens a result. That is a deliberate next step, not something this template does for you.
One file spins up the namespace. Generated from the diagram above. Also served at /templates/creative-dna.namespace.yaml.
# creative-dna: one manifest spins up the namespace.
# Platform manifest schema (GET /v1/discovery/schema). Validate with POST /v1/manifest/validate,
# apply with POST /v1/manifest/apply or the Deploy button. Wiring comes from the flow diagram:
# edges are bucket -> collection sources, collection -> retriever scope, retriever -> view.
# Applying a SECOND time, over a namespace this template already created: use
# POST /v1/manifest/apply?mode=create_missing, which creates what is missing and leaves
# what exists alone. The default, create_only, fails the WHOLE apply and rolls it back if
# any resource already exists, so an upgrade looks like a dead end without this. Use
# mode=upsert to also patch resources that exist but have drifted from this file.
version: '1.0'
metadata:
name: creative-dna
description: "Namespace template creative-dna. Generated from the flow diagram on mixpeek.com/templates/creative-dna."
namespaces:
- name: creative-dna
description: "Everything below lives in this namespace."
feature_extractors:
- name: multimodal_extractor
version: v1
- name: text_extractor
version: v1
# Data sources. A storage connection carries credentials, so it is created in Studio
# (or POST /v1/organizations/storage-connections) and synced into the bucket named here.
# ad-library-pull: manual, one-shot, your ad creatives and their copy -> bucket ad-creatives
buckets:
- name: ad-creatives
namespace: creative-dna
description: "Meant to be fed by ad-library-pull (manual, one-shot): upload files here or write them through the API. Nothing arrives until you do."
schema:
properties:
ad_id:
type: string
required: true
brand_slug:
type: string
required: true
brand_name:
type: string
source:
type: string
source_url:
type: string
headline:
type: string
primary_text:
type: string
required: true
ad_type:
type: string
creative_asset_url:
type: video
required: true
published_at:
type: string
collections:
- name: ads-scenes
namespace: creative-dna
description: "multimodal_extractor@v1 over bucket ad-creatives. Feeds brand-scenes."
source:
type: bucket
bucket: ad-creatives
feature_extractor:
name: multimodal_extractor
version: v1
input_mappings:
video: creative_asset_url
field_passthrough:
- source_path: ad_id
required: true
- source_path: brand_slug
required: true
- source_path: brand_name
required: true
- source_path: ad_type
- source_path: source
- source_path: source_url
- source_path: headline
- source_path: primary_text
- source_path: published_at
enabled: true
- name: ads-text
namespace: creative-dna
description: "text_extractor@v1 over bucket ad-creatives. Feeds copy-search."
source:
type: bucket
bucket: ad-creatives
feature_extractor:
name: text_extractor
version: v1
input_mappings:
text: primary_text
field_passthrough:
- source_path: ad_id
required: true
- source_path: brand_slug
required: true
- source_path: brand_name
required: true
- source_path: ad_type
- source_path: source
- source_path: source_url
- source_path: headline
- source_path: primary_text
- source_path: published_at
enabled: true
# NOT APPLIED. Face recognition is turned off on Mixpeek Cloud, so this face step does not deploy. It runs on a dedicated or customer-hosted deployment, where face_identity_extractor is served.
# The block below is correct and is emitted commented out so the rest of this
# manifest applies; an apply is all-or-nothing and would otherwise roll back.
# - name: ads-faces
# namespace: creative-dna
# description: "face_identity_extractor@v1 over bucket ad-creatives. Feeds cast-identification."
# source:
# type: bucket
# bucket: ad-creatives
# feature_extractor:
# name: face_identity_extractor
# version: v1
# input_mappings:
# video: creative_asset_url
# field_passthrough:
# - source_path: ad_id
# required: true
# - source_path: brand_slug
# required: true
# - source_path: brand_name
# required: true
# - source_path: ad_type
# - source_path: source
# - source_path: source_url
# - source_path: headline
# - source_path: primary_text
# - source_path: published_at
# enabled: true
retrievers:
- name: brand-scenes
namespace: creative-dna
description: "Searches ads-scenes across 1 feature index."
collections:
- ads-scenes
input_schema:
query:
type: text
required: true
description: "What to look for; searched across every index below"
stages:
- stage_name: search
stage_id: feature_search
parameters:
searches:
- feature_uri: "mixpeek://multimodal_extractor@v1/vertex_multimodal_embedding"
query:
input_mode: text
value: "{{INPUT.query}}"
top_k: 100
fusion: rrf
final_top_k: 100
tags:
- template:creative-dna
# NOT APPLIED. Face recognition is turned off on Mixpeek Cloud, so this face step does not deploy. It runs on a dedicated or customer-hosted deployment, where face_identity_extractor is served.
# - name: cast-identification
# namespace: creative-dna
# description: "Searches ads-faces across 1 feature index."
# collections:
# - ads-faces
# input_schema:
# query:
# type: text
# required: true
# description: "URL of the image or frame to match; encoded by the index below and compared against it"
# stages:
# - stage_name: search
# stage_id: feature_search
# parameters:
# searches:
# - feature_uri: "mixpeek://face_identity_extractor@v1/insightface__arcface"
# query:
# input_mode: content
# value: "{{INPUT.query}}"
# top_k: 50
# fusion: rrf
# final_top_k: 50
# tags:
# - template:creative-dna
- name: copy-search
namespace: creative-dna
description: "Searches ads-text across 1 feature index."
collections:
- ads-text
input_schema:
query:
type: text
required: true
description: "What to look for; searched across every index below"
stages:
- stage_name: search
stage_id: feature_search
parameters:
searches:
- feature_uri: "mixpeek://text_extractor@v1/multilingual_e5_large_instruct_v1"
query:
input_mode: text
value: "{{INPUT.query}}"
top_k: 50
fusion: rrf
final_top_k: 50
tags:
- template:creative-dnaDeploy this template, bring your data, and go from exploration to production.