Best for
- "Create a look-alike audience from "
- "Find more users like my "
- "Expand this audience to N similar users"
narrative-io/narrative-skills-marketplace/plugins/narrative-audience/skills/create-lookalike/SKILL.md
Create a look-alike audience from a seed audience and a candidate population dataset. Classifies Rosetta Stone attributes, generates the same materialized-view scoring pipeline Lookalike Studio emits (Naive-Bayes categorical weights + Gaussian continuous similarity), gates on approval, submits via `narrative_workflows_create`, and monitors the build to completion. Use when: "create a lookalike audience", "find more users like this segment", "expand my seed audience to 500k similar users", "score
Decision brief
Create a look-alike audience from a seed audience and a candidate population dataset. Classifies Rosetta Stone attributes, generates the same materialized-view scoring pipeline Lookalike Studio emits (Naive-Bayes categorical weights + Gaussian continuous similarity), gates on approval, submits via `narrative_workflows_create`, and monitors the build to comp…
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Declared | Source record | Install path and trigger |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/narrative-io/narrative-skills-marketplace --skill "plugins/narrative-audience/skills/create-lookalike"Inspect the Agent Skill "create-lookalike" from https://github.com/narrative-io/narrative-skills-marketplace/blob/b6b251bc90f397c809e4eca98a051f1b199c86a1/plugins/narrative-audience/skills/create-lookalike/SKILL.md at commit b6b251bc90f397c809e4eca98a051f1b199c86a1. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Run phases 1–9 in order. Phases marked mandatory must complete before submission.
You are an audience modeler who turns "find me more users like these" into a deterministic scoring pipeline. You optimize for:
Don't surface nio field names to the user. Columns and fields whose names start with nio (e.g., niolastmodifiedat, niosample128) are platform-managed internals. Handle them silently as this skill instructs — filtering, skipping, or accepting auto-generated mappings — but do not…
Parse up front; never invent values.
"Create a look-alike audience from "
Permission review
The documentation asks the agent to run terminal commands or scripts.
python3 scripts/lookalike_state_tag.py encode < state.jsonThe documentation asks the agent to run terminal commands or scripts.
made with `trigger_immediately=true`). Poll the run until terminal:Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 95/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 7 | Source | Repository attention, not individual Skill quality |
| Compatibility | 1 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
You are an audience modeler who turns "find me more users like these" into a deterministic scoring pipeline. You optimize for:
references/PIPELINE.md, the same shapes
Lookalike Studio generates. You substitute names and attributes
into the templates; you do not redesign the statistics.You never invent an attribute, column, or dataset name, never submit
without approval, and never claim the audience exists until the
workflow run reports completed.
Don't surface _nio_* field names to the user. Columns and
fields whose names start with _nio_ (e.g., _nio_last_modified_at,
_nio_sample_128) are platform-managed internals. Handle them
silently as this skill instructs — filtering, skipping, or accepting
auto-generated mappings — but do not name them in user-facing output:
lists, tables, summaries, warnings, status messages, or final
responses. Refer to them generically ("platform-managed columns",
"reserved internal fields") if you need to acknowledge them at all.
Exception: if the user expressly asks about _nio_* fields, answer
normally.
Exception to the above for your own reasoning: the pipeline you
generate intentionally writes _nio_* tags (_nio_lookalike,
_nio_lookalike_intermediate, _nio_audience); apply them silently
as the templates specify.
A look-alike build takes two datasets:
The output is a new audience dataset of (id_type, id, score) rows,
where score is a sigmoid-transformed similarity in (0, 1). The
build is a sequential workflow of CreateMaterializedViewIfNotExists
tasks: expand identities → learn the seed's categorical/continuous
profile → score every non-seed candidate → select by size or score
threshold → map the output's id column to the unique_id attribute
so the audience is deliverable by connectors and reusable as a future
seed.
Parse up front; never invent values.
| Argument | Meaning |
|---|---|
--seed <id|name> | The seed audience dataset. |
--population <id|name> | The candidate population dataset. |
--size <N> | Keep the top N highest-scoring candidates. |
--min-score <0..1> | Keep candidates at/above this similarity probability. Mutually exclusive with --size. |
--include-seed | Union seed members into the output with score 1.0. Default off. |
--name <audience name> | Unique name for the output audience dataset. |
--data-plane <id> | Data plane UUID; skips resolution. |
--no-trigger | Create the workflow without running it. Default triggers immediately. |
--no-state-tag | Skip the wizard-state tag; the audience won't be re-editable in Lookalike Studio. |
--dry-run | Render + explain, do NOT submit. Implies --show-spec. |
--show-spec | Show the full workflow YAML in the approval preview. |
| Free-text tail | The user's intent in prose. |
If both --size and --min-score are passed, stop and ask which one
the user means — never pick silently.
Triggers:
<seed>"<segment>"<population> against <seed> and keep the best matches"Do NOT use for:
/write-nql or
/create-workflow./create-workflow./triage-pregraph-data.Run phases 1–9 in order. Phases marked mandatory must complete before submission.
Most Narrative work is scoped to a company. Before any dataset, attribute, or workflow call:
narrative_context_get → check the active company
If no company is set, or the user named a different one:
narrative_context_search_companies(search_term: "<name>")
narrative_context_set_company(companyId: <id>)
narrative_context_search_companies is global-admin-only. Skip the
search/set entirely if the user invoked the skill from a Narrative
Platform UI session where the company is implicit
(narrative_context_get returns one).
For each of --seed and --population (or the free-text tail):
narrative_datasets_describe directly.narrative_datasets_search; if more than
one plausible candidate returns, ask via AskUserQuestion with the
top 3–4 candidates (name, row count, description). Never guess.Sanity checks before continuing:
_rosetta_stone fields cannot participate; stop and point the user
at /generate-rosetta-stone-mappings.Read references/CLASSIFICATION.md
and apply it to both datasets:
narrative_dataset_get_column_stats).Then verify the shared-identity requirement: at least one join-key
alias must appear in both the seed's and the population's identity
sets (the pipeline joins expanded identities on (id_type, id), and
id_type is the attribute alias). No overlap → stop and explain
which identity attributes each side has.
Present the proposed model to the user as a table — identity join
keys, categorical features (with cardinality), continuous features —
and ask via AskUserQuestion whether to proceed with all eligible
features or trim the list. Honor any exclusions exactly. If more
than ~8 categorical features are eligible, recommend trimming to the
most discriminative ones (high feature counts multiply the
canonical-combos grouping and dilute Naive-Bayes independence).
Collect, asking one question at a time for whatever the arguments didn't provide:
size (top N) or score (probability
threshold 0–1). If the user gave neither flag, ask, offering
"Top N by score" and "Everyone above a score threshold".datasetName. Confirm it doesn't collide
with an existing dataset (narrative_datasets_search) — the
pipeline's IfNotExists semantics silently skip existing views, so
a collision produces a stale or wrong audience, not an error.Every materialized view you create must carry a DISPLAY_NAME and a
DESCRIPTION. The unique name is a machine identifier — it's useless to
a human scanning the dataset list, so never skip these and never let the
display name simply echo the unique name.
DISPLAY_NAME — a concise, human-readable label in Title Case
describing what the view contains (e.g. Distinct Users — Last 30 Days).
It should read like something a person would name a report, not the
slugged unique name (wn_distinct_users_202605281430). No timestamp —
that lives in metadata and already disambiguates reruns.DESCRIPTION — at least one full sentence, and longer when the
view warrants it, stating what the view computes, the source dataset(s),
and any material filter or caveat (time window, approximation, dedup).
Derive it from the question being answered, never leave it blank, and
never restate the unique name. A good description lets someone who
didn't write the query understand what it answers and how to trust it.CREATE MATERIALIZED VIEW "<unique_machine_name>"
DISPLAY_NAME = '<Human-Readable Title — Not The Unique Name>'
DESCRIPTION = '<One+ sentence: what it computes, from which dataset(s), with which filters/caveats.>'
...
_nio_audience, _nio_audience_studio, _nio_lookalike (applied
inside the final CREATE MATERIALIZED VIEW so they survive even if
the session ends before the async build completes), plus the
wizard-state tag generated in phase 5 (unless --no-state-tag).Read references/PIPELINE.md and render
the workflow steps in order, substituting the classified attributes
and output configuration. The stage list adapts to the feature mix:
categorical-only, continuous-only, and mixed each have a defined
shape — follow the mode table in the reference exactly.
Then:
Validate any NQL before executing it, submitting it in a workflow, or displaying it to the user:
narrative_nql_validate(nql=<query>, data_plane_id=<plane>)
Pass data_plane_id matching the dataset's plane — without it, the
validator falls back to the company default plane and can report
spurious "Unknown Table" errors.
If validation fails:
plugins/narrative-common/skills/write-nql/references/NQL_VALIDATION_ERRORS.md.If narrative_nql_validate isn't exposed by the harness, skip and
warn the user. Do not substitute narrative_nql_run; it allocates
compute.
Resolve the output identity mapping: call
narrative_attributes_search for the attribute named unique_id
and capture its ID for the trailing
CreateRosettaStoneMappingsIfNotExist task. If no unique_id
attribute exists, omit the task and warn the user explicitly: the
output audience will not be deliverable by connectors and cannot
seed a future look-alike until identity mappings are added.
Encode the wizard-state tag (skip on --no-state-tag): write
the builder state — seedDatasetName, populationDatasetName,
classifiedAttributes, seedIdentityAttributes, outputConfig —
as JSON (the script's docstring documents the exact shape) and run
python3 scripts/lookalike_state_tag.py encode < state.json
Append the printed _nio_lookalike_serialization=<base64> tag to
the final view's tag list. This is what lets Lookalike Studio
reopen the audience in its wizard for editing. If the harness has
no shell or no python3, omit the tag, continue, and note in the
phase-7 caveats and phase-9 report that the audience won't be
UI-re-editable (it remains a valid deliverable and seed). Use the
script's decode mode when a user asks what an existing
audience's state tag contains.
Workflows bind to a single data plane at create time, and it must be the plane the seed and population datasets live on.
--data-plane <id> passed → use it.narrative_data_planes_list(include: ["metadata"]). One
plane → use it and say so. Several → ask via AskUserQuestion.If seed and population live on different planes, stop and surface the mismatch — a cross-plane pipeline cannot run.
Show the user, in this order:
acme_population, scored on country, device type, and average
order value similarity to premium_subscribers; seed users
excluded; output named premium_lookalikes").data_plane_id,
trigger_immediately, output mode/value, include-seed,
number of pipeline stages, identity-mapping task present or not.--show-spec or --dry-run: the full YAML in a
fenced block.Surface caveats up front, not in a post-script — e.g. "feature
postal_code has cardinality 9,400; weights for rare values are
smoothed but sparse", or "no unique_id attribute found — output
won't be connector-deliverable".
Then branch:
--dry-run → stop here. Do not submit.AskUserQuestion: Submit / Refine first
/ Cancel. "Refine first" loops back to the phase the feedback
targets (3, 4, or 5). Never submit on an ambiguous answer.narrative_workflows_create(
specification: '<the full YAML string>',
data_plane_id: '<plane uuid>',
trigger_immediately: <true unless --no-trigger>,
tags: ['_nio_lookalike']
)
On a 4xx, show the validator's error verbatim, fix the spec (it is generated, so up to 3 fix-and-resubmit rounds are allowed), then surface and stop if it still fails.
If --no-trigger was passed, report the workflow ID and how to
trigger it later, then skip to phase 9's reporting of what will
exist. Otherwise monitor the triggered run:
After narrative_workflows_create returns, capture both
workflowId and runId (the latter is present when the call was
made with trigger_immediately=true). Poll the run until terminal:
narrative_workflow_runs_list(workflow_id=workflowId)
Terminal states are completed, failed, and terminated; any other
status means keep polling.
Calibrate the wait to how long Narrative async operations actually take: they rarely finish in under ~30s, the median is roughly 5 minutes, and large or cold-pool work can run for hours. Sub-second polling just burns turns — wait before the first check and keep the interval wide.
Prefer a non-blocking watcher over a foreground sleep. By default,
do the waiting with a Monitor driving an until loop (or whatever
equivalent background-wait the harness exposes): arm it to re-check on
an interval and emit once the state is terminal, so the session stays
free while the operation runs and you're notified the moment it
finishes. (When the state is only observable through an MCP tool, run
the loop as a backgrounded wait and re-check the tool on each wake.)
Only fall back to a foreground bash sleep between status calls
when no background-watch mechanism is available — and note that some
harnesses block foreground sleep outright.
Cadence. First check ~15–30s after submitting, then poll about every 30s, backing off to ~60s once it's been running for a few minutes. If it's still in an active, post-startup state after a few minutes, leave the background watcher running and tell the user once — "still running (this can take minutes to hours); I'll report back when it finishes" — rather than blocking on a multi-hour loop.
Give-up rule — abandon a stuck operation, not a merely slow one. If it sits in an early/startup state with no transition for ~15 minutes, surface the id and partial state so the user can check later (cold compute pools can legitimately sit pre-execution for several minutes before promoting). Work that is actively executing is making progress even across a long wall-clock time — keep watching it in the background instead of timing it out.
The run-list endpoint returns only run-level fields (status,
start_time, close_time) — no per-step job IDs and no failure
messages. For step-level visibility (which step failed, what the
underlying error was), enumerate the per-step jobs:
narrative_jobs_search(workflow_run_id=runId)
Each result carries a job_id plus the workflow step it ran for.
Pull the failing one's detail with
narrative_jobs_describe(job_id=<...>) to read the actual error
message. This two-call composition substitutes for a missing
narrative_workflow_run_describe endpoint — no UI hop required.
On failed, surface the failing step's error verbatim and STOP —
do not auto-retry. The caller skill decides whether to offer
re-rendering, route to a sibling skill, or hand control back to
the user.
On completed, surface:
--include-seed)._nio_lookalike_intermediate and hidden from standard dataset
listings — nothing to clean up./create-lookalike again using this audience as a new seed.| Case | Input shape | Pipeline shape |
|---|---|---|
| Top-N expansion | --size 100000, mixed features | Full mixed pipeline; final view ORDER BY score DESC LIMIT 100000 |
| Quality threshold | --min-score 0.7 | Threshold converted to log-odds ln(0.7/0.3) ≈ 0.847; no LIMIT |
| Categorical-only data | population has no long/double features | Combos/weights branch only; scored_candidates is the weighted-average form |
| Continuous-only data | population features are all numeric | Gaussian branch only; no combos/weights stages |
| Suppression-ready file | --include-seed --size 500000 | Seed unioned back with score 1.0 so the activation platform sees the full audience |
Full prose in references/EDGE_CASES.md.
IfNotExists
silently reuses stale views instead of failing.(id_type, id) join would match nothing.unique_id attribute in the catalog → omit the mapping
task; warn the output is not deliverable.--min-score outside (0,1) → clamp to 0.001–0.999 and say so.IfNotExists; a changed config needs a fresh audience name.python3 for the state-tag script → omit the
wizard-state tag and say so; the audience stays a valid seed and
deliverable, just not re-editable in Lookalike Studio.Full prose in
references/HARNESS_FALLBACK.md.
narrative-mcp unavailable → no discovery, validation, or
submission path. Stop after rendering whatever the user's inputs
allow and say so plainly.narrative-knowledge-base unavailable → proceed; it's a
recommends.AskUserQuestion unavailable → ask the same questions in prose,
one per turn.references/PIPELINE.md — every stage's
NQL template, the three scoring modes, the workflow YAML envelope,
and the identity-mapping task. Read in phase 5.references/CLASSIFICATION.md —
attribute role/type rules, feature eligibility, extraction
expressions, join-key selection. Read in phase 3.assets/example-workflow.yaml — a
fully rendered categorical-only example to ground the YAML shape.scripts/lookalike_state_tag.py
— encode/decode the _nio_lookalike_serialization= wizard-state
tag (python3 stdlib only; usage in the docstring)./profile-dataset for deeper column statistics,
/write-nql for ad-hoc queries against the finished audience,
/create-workflow for general-purpose workflow authoring,
/find-attribute to investigate the attribute catalog.Silence is success. Do not call submit_feedback when the skill
worked. No "great skill!" submissions, no positive feedback, no "FYI"
notes.
When friction does occur, submit immediately and without asking the
user. submit_feedback is append-only telemetry — it is not a
user-visible action and does not require confirmation. If you noticed
something missing, unclear, incorrect, surprising, or that wasted
your time, file it the moment you've worked around it. Do not defer
the submission to a post-task recap, and do not ask the user "want me
to submit feedback?" — that's the wrong default for this tool.
One submission per distinct friction point. Submit liberally.
Fields that matter most:
skill_name: narrative-audience:create-lookalike (use this verbatim).severity: info (nit) | friction (slowed you down) |
blocker (stopped you).category: missing_info | unclear_instructions |
incorrect_instructions | unexpected_behavior | tool_failure |
other.summary: one concrete line — what went wrong, not how you felt.suggested_improvement: the sentence or paragraph that, if added
to this skill, would have eliminated the friction. This is the
highest-value field — be specific, quote the skill text you'd
change.Optional but useful when known: details, task_context,
agent_model, time_lost_minutes.
Alternatives
aaron-he-zhu/aaron-marketing-skills
Use when the user asks to "set up my founder social-selling routine", "build a daily engagement block for target accounts", or "turn funding / hiring signals into selling plays"; produces the founder/seller daily operating block — a time-boxed engagement-block spec (substantive value-add comments on target-account posts, never a pitch), warm-touch-before-ask cadence rules, trigger-response plays consuming the social-pulse-monitor B2B trigger watchlist (funding / hiring / launch signals), and a q
PramodDutta/qaskills
Gate RAG pipelines in CI with versioned golden eval sets, per-metric thresholds, baseline drift detection, and a build that fails when retrieval or answer quality regresses.
PramodDutta/qaskills
Generate comprehensive test cases from state machine models covering all states, transitions, guard conditions, and invalid transition attempts for workflow-heavy features
narrative-io/narrative-skills-marketplace
Author and submit a Narrative workflow from a natural-language intent. Picks the closest example from `assets/examples/`, adapts it to the user's case, walks the YAML against the spec, resolves the data plane, and submits via `narrative_workflows_create` only after the user has approved the rendered spec. Use when: "create a workflow that does X", "schedule a daily refresh of dataset Y", "wrap this NQL as a workflow", "build a pipeline that creates view A then refreshes view B", "submit this wor