---
name: anonymizer
description: Use when the user wants to detect and replace, hash, redact, annotate, or rewrite PII (names, emails, phone numbers, locations, ...) in a CSV or Parquet dataset using the NeMo Anonymizer plugin.
argument-hint: [describe the dataset and how PII should be handled]
---

# Before You Start

Do not explore the workspace first. The workflow's Learn step gives you everything you need.

**Source of truth.** For anything you're unsure about, prefer the in-tree CLI docs over your memory: `docs/anonymizer/index.md`, `docs/anonymizer/cli.md`, and `docs/anonymizer/tutorials/{index,preview,run}.md`. The [NVIDIA NeMo Anonymizer library docs](https://github.com/NVIDIA-NeMo/Anonymizer/tree/main/docs) own detection, replacement strategy parameters, and rewrite-mode semantics.

# Goal

Anonymize a tabular text dataset using the NeMo Anonymizer plugin so it matches this description:

$ARGUMENTS

The plugin wraps the [NVIDIA NeMo Anonymizer library](https://github.com/NVIDIA-NeMo/Anonymizer) and exposes:

- An `anonymizer.preview` streaming function (small samples, fast iteration). Use `nemo anonymizer preview run` for local execution and `nemo anonymizer preview submit` for platform execution.
- An `anonymizer.run` job for full-dataset execution. Use `nemo anonymizer run run` for local execution and `nemo anonymizer run submit` for Jobs-worker execution.
- A `nemo anonymizer validate` command (synchronous config validation).

# Workflow

Use **Autopilot** mode if the user implies they don't want to answer questions — e.g., they say something like "be opinionated", "you decide", "make reasonable assumptions", "just anonymize it", "surprise me", etc. Otherwise, use **Interactive** mode (default).

Read **only** the workflow file that matches the selected mode, then follow it:

- **Interactive** → read `workflows/interactive.md`
- **Autopilot** → read `workflows/autopilot.md`

# Rules

- Prefer CLI surfaces. Generate YAML specs and run `nemo anonymizer ...` commands unless the user explicitly asks for Python.
- Always iterate via `nemo anonymizer preview run` or `nemo anonymizer preview submit` before running the full job. Previews are cheap and stream a small sample (default 10 records) with full detection traces.
- When you include `config`, pick exactly one of `replace` (Annotate/Hash/Redact/Substitute) or `rewrite` on the `AnonymizerConfig`. Not both. Do not claim `config` is required for every flow; the Anonymizer library owns default config behavior and strategy semantics. See `references/replace-strategies.md` for plugin request formatting and the [library docs](https://github.com/NVIDIA-NeMo/Anonymizer/tree/main/docs) for semantics.
- The input must be a single CSV or Parquet file. `text_column` defaults to `text`; set it explicitly when the free-text column has another name. If the dataset has a stable record id, also set `id_column`. See `references/inputs.md`.
- `model_configs` is optional for local execution (`preview run` / `run run`); when omitted, the Anonymizer library defaults are used.
- The current plugin-service / Jobs paths (`preview submit`, `run submit`) require `model_configs` so requests route through the NeMo Platform Inference Gateway. See `references/model-configs.md`.
- `selected_models` overrides are only valid when `model_configs` is also supplied; aliases must resolve against that pool.
- Local file paths only work for local execution. Plugin-service / Jobs execution requires an `http(s)` URL or a fileset reference (`<workspace>/<fileset>#<path>` or `fileset://...`).
- If a spec file matching the user's description already exists in the working directory, ask whether to edit it or create a new one.

# Usage Tips and Common Pitfalls

- **Replacement strategies need a discriminated payload.** Hand-written YAML specs must include `kind: redact` (or `annotate` / `hash` / `substitute`) inside the `replace` block.
- **Substitute and rewrite need LLM-backed model aliases.** For plugin-service / Jobs execution they must be backed by providers declared in `model_configs`. For library-level details, refer to the [Anonymizer library docs](https://github.com/NVIDIA-NeMo/Anonymizer/tree/main/docs) or library skills.
- **Spec files are YAML, not JSON.** `nemo anonymizer preview run --spec-file <path>` and `nemo anonymizer run run --spec-file <path>` both load YAML.
- **Run results are artifacts.** The job writes an artifacts directory containing `dataset.parquet`, `trace.parquet`, `metadata.json`, and optional `failed_records.json`.
- **Fileset refs use `#` to point at a file.** `<workspace>/<fileset>#<path>`, `<fileset>#<path>` (uses request workspace), or `fileset://<workspace>/<fileset>#<path>`. The `#` fragment must point at a `.csv` or `.parquet` file.
- **Detection labels.** Keep the Anonymizer library default label set unless the user asks to restrict detection. Refer to the [Anonymizer library docs](https://github.com/NVIDIA-NeMo/Anonymizer/tree/main/docs) or library skills for supported label/config details.
- **Preview record cap.** `num_records` defaults to 10 and is bounded by the service's `preview_num_records.max` setting. Use a small value while iterating.

# Troubleshooting

- **`nemo anonymizer` CLI not found:** The plugin isn't installed in this environment. From the repo root, run `uv sync`; the root workspace includes the Anonymizer plugin. Confirm with `nemo anonymizer --help`. Do not install anything without the user's permission.
- **`nemo anonymizer preview submit` returns 404:** The plugin service isn't mounted on the gateway. `nemo setup` does not auto-mount it. Re-run `nemo services run` (no `--services` flag) and verify the routes show up under `/apis/anonymizer/` in the OpenAPI listing. See `docs/anonymizer/tutorials/index.md` Prerequisites.
- **`model_configs are required for remote execution`:** `preview submit` and `run submit` go through plugin-service / Jobs paths. Add `model_configs` referencing an Inference Gateway provider; use the inference/model-provider docs or skill for provider discovery.
- **`Input source ... is a local path`:** Plugin-service execution rejects local paths. Either upload the file to a fileset, use an `http(s)` URL, or switch to `preview run` / `run run` (local execution).
- **`Fileset input ... must resolve to a .csv or .parquet file`:** The `#<path>` fragment points at a directory or a non-CSV/Parquet file. Point it at a single file.
- **Config validation failed (HTTP 422):** Run `nemo anonymizer validate --config <yaml> [--model-configs <yaml>]` to surface the exact error synchronously. Common causes: mixing `replace` and `rewrite`, picking `Substitute` without a `replacement_generator` alias in `model_configs`, fileset path missing the `#<file>` fragment.
- **`selected_models requires model_configs ...`:** The user passed `selected_models` overrides without an explicit model pool. Either drop the overrides or define `model_configs` with the aliases the overrides reference.
- **User asks to run remotely:** Use `nemo anonymizer run submit`. Ensure `data.source` is an HTTP(S) URL or fileset reference and `model_configs` is present.
- **Empty preview dataset / "No preview dataset received":** Check the log frames printed by the preview stream. Most commonly the detection model alias is wrong, or the dataset has zero rows after column resolution.

# Output Template

Generate a YAML spec file in the current directory describing the request. Name it descriptively (e.g., `redact_records_preview.yaml` or `redact_records_run.yaml`).

**Preview spec** — fast iteration over a small sample:

```yaml
# Local:  nemo anonymizer preview run    --spec-file ./<this_file>.yaml --workspace <ws>
# Remote: nemo anonymizer preview submit --spec-file ./<this_file>.yaml --workspace <ws>
config:
  replace:
    kind: redact            # one of: redact, annotate, hash, substitute
    format_template: "[REDACTED_{label}]"
data:
  source: "anonymizer-inputs#anonymizer-input.csv"   # local path, http(s) URL, or fileset ref
  text_column: biography
  id_column: id
num_records: 5
# Required for plugin-service execution (`preview submit`),
# optional for local `preview run`:
model_configs:
  - alias: gliner-pii-detector
    provider: nvidia-build
    model: nvidia/gliner-pii
  - alias: gpt-oss-120b
    provider: nvidia-build
    model: openai/gpt-oss-120b
  - alias: nemotron-30b-thinking
    provider: nvidia-build
    model: nvidia/nemotron-3-nano-30b-a3b
# selected_models:
#   detection:
#     entity_detector: gliner-pii-detector
#     entity_validator: gpt-oss-120b
#   replace:
#     replacement_generator: gpt-oss-120b
```

**Run spec** — full-dataset job:

```yaml
# Local:  nemo anonymizer run run    --spec-file ./<this_file>.yaml
# Remote: nemo anonymizer run submit --spec-file ./<this_file>.yaml --workspace <ws>
config:
  replace:
    kind: redact
    format_template: "[REDACTED_{label}]"
data:
  source: "anonymizer-inputs#anonymizer-input.csv"
  text_column: biography
  id_column: id
# Required for `run submit`, optional for `run run`:
model_configs:
  - alias: gliner-pii-detector
    provider: nvidia-build
    model: nvidia/gliner-pii
  - alias: gpt-oss-120b
    provider: nvidia-build
    model: openai/gpt-oss-120b
  - alias: nemotron-30b-thinking
    provider: nvidia-build
    model: nvidia/nemotron-3-nano-30b-a3b
```

Include only the bits the task requires — e.g., omit `model_configs` for purely local previews, omit `selected_models` unless overrides are needed, and use `Substitute` / `rewrite` only when the user wants LLM-generated replacements or holistic rewriting.
