| name | topic-modeler |
| description | Use this skill, which uses BERTopic, whenever a user wants to discover what a collection of text documents is about, OR when refining an existing topic model (merging topics, reducing to fewer themes, consolidating groups). This is the default skill for finding topics, themes, patterns, or subject groupings in any text dataset (CSV, JSONL, JSON, TXT). Invoke it for requests like: "what are people complaining about", "find the main topics", "what themes exist", "group by subject", "find patterns in these documents", "explore this dataset", "merge topics 2 and 4", "reduce to 5 topics", or explicit "topic modeling". Covers: complaint analysis, survey exploration, ticket categorization, review analysis, article grouping — any "what's in these documents" task. Not for: sentiment scoring, named entity extraction, predefined label classification, translation, or word frequency counts.
|
| allowed-tools | ["Read","Write","Edit","Grep","Glob","AskUserQuestion","TaskOutput","KillShell","Agent","Bash(python:*)","WebFetch(domain:maartengr.github.io)"] |
BERTopic Topic Modeling
Iterative topic modeling using BERTopic. Understand intent, fit a model, assess quality, label, refine, then deepen with temporal or class-based analysis when the data supports it.
Setup
Resolve the full path to this skill's scripts/ directory from where this SKILL.md is located. Run the setup script once per session before any other commands:
bash $SETUP
$SETUP = full path to scripts/setup.sh; $SCRIPT = full path to scripts/bertopic_analyze.py. Setup is idempotent.
Quick Reference
| Command | Example |
|---|
| Fit model | python $SCRIPT fit data.csv --column text |
| List topics | python $SCRIPT topics --top-n 10 --detail |
| Get labeling data | python $SCRIPT label --max-docs 5 --doc-length 500 |
| Merge topics | python $SCRIPT merge '[[1,2],[3,4]]' |
| Reduce to N | python $SCRIPT reduce 10 |
| Reduce outliers | python $SCRIPT reduce-outliers --strategy c-tf-idf |
| Topics over time | python $SCRIPT over-time --timestamps created_at |
| Topics per class | python $SCRIPT per-class --classes category |
| Search by query | python $SCRIPT search "climate policy" --top-n 5 |
| Visualize | python $SCRIPT visualize --type barchart |
All commands output JSON to stdout. Check status field: "success" or "error". For full CLI options, consult references/cli-reference.md.
Workflow
1. Understand Intent
Before touching the data, understand what the user wants to learn. This shapes the entire approach.
| User intent | Workflow emphasis |
|---|
| "what are people complaining about" | Standard: fit > label > refine |
| "how have topics changed over time" | Temporal: fit > label > over-time analysis |
| "compare topics across [groups]" | Per-class: fit > label > per-class breakdown |
| "explore this dataset" | Full: fit > label > offer all options |
Ask the user if their intent is ambiguous. Don't assume "standard" by default.
2. Pre-Flight
Assess the dataset before fitting:
- Identify format (CSV/JSON/JSONL/TXT/directory) and text column/field
- Detect metadata: look for timestamp columns (dates,
created_at, timestamp) and category columns (labels, types, departments). Tell the user: "I see a created_at column — we can analyze how topics evolve over time after fitting."
- Size: under 10k fit directly; 10k-50k consider
--sample-n 10000; 50k+ strongly recommend --sample-n 10000 --sample-strategy stratified
- Long documents (articles, reports): consider splitting into paragraphs before fitting
- Non-English: use
--no-stop-words (default stop words are English only)
Tell the user what you found and your plan before fitting.
3. Fit
Default fitting uses English stop word removal, cross-topic frequent word reduction, and MMR diversity (0.3).
If the user specified a target topic count (e.g., "consolidate to 3 themes"), use --nr-topics N on the first fit rather than fitting repeatedly and merging afterward. This is faster and produces cleaner results than post-hoc reduction.
Report: number of topics, outlier count/percentage, top topics by size, embedding model and min_topic_size used.
Tuning when defaults are poor:
- First try: adjust
--min-topic-size
- Short texts:
--umap-n-neighbors 5 --umap-min-dist 0.1
- Topics merging that should separate:
--umap-min-dist 0.1 or --umap-n-components 10
- Noisy data with high outliers:
--hdbscan-min-samples 1
4. Quality Gate
After fitting, assess quality before labeling. Present findings as a decision point.
- Outlier %: if >20%, offer outlier reduction via
reduce-outliers. If >40%, suggest re-fitting with smaller --min-topic-size
- Topic count: flag if <3 (too coarse — try smaller
--min-topic-size or better embedding model) or >30 for small corpora (too granular — suggest --nr-topics or reduce)
- Topic overlap: run
visualize --type heatmap, flag topic pairs with high similarity as merge candidates
Present: "Here's what I found — [concrete options to proceed]". Don't silently continue past quality issues.
5. Label & Analyze
Get representative docs and generate labels:
python $SCRIPT label --max-docs 5 --doc-length 500
The label command returns keywords AND representative documents for each topic. Use both to:
- Generate 2-5 word natural-language labels
- Provide concrete analysis grounded in actual documents
Present a topic table, then analyze each topic using the representative docs as evidence. Quote or paraphrase specific documents to illustrate what each topic is about, what the sub-themes are, and what patterns you notice. This is what makes the analysis useful — not just "Shipping & Delivery (11 docs)" but what people are actually saying about shipping.
| Topic | Count | Label | Key Terms |
|---|
| 0 | 1234 | [Your label] | word1, word2, word3 |
For each topic, write a short analysis paragraph that includes:
- What the topic covers (synthesized from representative docs, not just keywords)
- Notable sub-themes or patterns within the topic
- 2-3 representative quotes that illustrate the range of content within the topic
Flag topics that look similar as merge candidates.
6. Refine
Offer refinement options based on what the Quality Gate and labeling revealed:
- Reduce outliers:
python $SCRIPT reduce-outliers --strategy c-tf-idf — when outlier % is high
- Merge:
python $SCRIPT merge '[[1,2],[3,4]]' — when topics overlap. Use visualize --type hierarchy for structured tree data to make merge suggestions concrete
- Reduce:
python $SCRIPT reduce <nr_topics> — when too many small topics or user wants higher-level view
After each operation: re-run label, present updated table, loop back through quality assessment. Ask: "Want to refine further, or are these topics good?"
7. Deepen
Once topics are stable, offer analysis based on metadata detected in Pre-Flight:
- If timestamps detected: "Want to see how these topics trend over time?" →
python $SCRIPT over-time --timestamps <col>
- If categories detected: "Want to compare topic distributions across [column]?" →
python $SCRIPT per-class --classes <col>
- If neither: skip to Summarize
This step is the key branch point — let the data shape what analysis you offer.
8. Summarize
End with a concise summary:
- Final topic table with labels and counts
- Key findings: cross-topic patterns, surprising themes, sentiment splits — grounded in specific examples from the data
- Outlier notes (what percentage remained unclassified, what that means)
- Actionable insights relevant to the user's original intent, with concrete evidence from representative documents
Output Handling
- Stdout: structured JSON summary
- Full detail:
.bertopic_workspace/topics_detail.json
- Visualizations:
.bertopic_workspace/visualizations/
- Workspace persists across invocations for iterative refinement
Behavioral Modes
User-invoked (direct topic modeling request): Be thorough — full topic tables, proactive refinement suggestions, relevant visualizations.
Part of larger workflow: Be efficient — fit, label, concise summary. Skip visualizations unless relevant. Focus on insights, not process. The Quality Gate still applies — don't skip iteration just because this is part of a larger task. If outliers are >20% or topics are too coarse, fix it before reporting results.
Error Recovery
- Model not found: run
fit first
- Merge fails on unknown topic IDs: run
topics to check current IDs
- Fit too slow: suggest smaller
--sample-n or larger --min-topic-size
- Topics too coarse: try smaller
--min-topic-size or better embedding model
References