Jira AI Duplicate Detector
Python CLI that finds duplicate Jira issues by embedding their title and description and flagging pairs above a threshold - Excel reports plus usage and cost stats.
- Python
- CLI
- OpenAI
- Jira
Setup
- Problem
In a QA backlog, the same issue gets filed multiple times under different wording. The cost of a human scanning for duplicates grows with backlog size and headcount, and usually it just gets left alone.
- Context
A Python CLI built to automate the "duplicate issue" problem I hit in practice. It compares only existing issues - never creating new ones - to surface duplicate candidate pairs.
- Users
Teams running a piled-up QA/product backlog, and QA consulting situations that need to diagnose and show the "invisible cost of duplicates."
Build
- What I did
- Bulk-fetches issues via the Jira API (JQL,
MAX_ISSUESdefault 50) and excludes keyword issues like [UI] - Embeds title + description with OpenAI text-embedding-ada-002 and finds duplicate candidate pairs by cosine similarity
- Caches embeddings (
issue_embeddings_cache.json) to cut the cost of re-runs - Outputs to console, log, and Excel (.xlsx), and reports compared-pair count, API calls, tokens, and estimated cost
- Bulk-fetches issues via the Jira API (JQL,
- Product decisions
- Compare only existing issues, never create new ones - a diagnostic tool must not clutter the backlog
- Embeddings, not string matching - duplicates usually overlap in meaning rather than words, so it catches "the same meaning under different wording"
- Cache embeddings to keep cost low - even large comparisons stay in cents, removing the barrier to "just run it once"
- Print usage and cost alongside the results - the cost of the automation itself has to be transparent for an adoption call
- QA considerations
- The similarity threshold's precision/recall - lowering it yields false positives (unrelated pairs), raising it misses real duplicates; where is the tradeoff set?
- False-positive cost - flagging a non-duplicate as a duplicate actually adds human review time, so that cost is acknowledged
- The embedding cache (
issue_embeddings_cache.json) doesn't misjudge on a stale vector when an issue's body changes - Compares only existing issues, never creating new ones - a diagnostic tool that doesn't clutter the backlog (no side effects)
- The JQL bulk-fetch boundary - on a backlog capped by
MAX_ISSUES(default 50), the comparison set isn't silently truncated in a way that misses duplicates - The usage/cost printed with the results (API calls, tokens, estimated cost) matches actual consumption, so it can be trusted as the basis for an adoption call
Outcome
- Metrics
A personal CLI built out of a real need (a single Python piece). Embedding cost stays around a cent even for large comparisons. No team-adoption metrics collected.
- Retrospective
- Hard-coding the similarity threshold in config is the limit - what counts as a "duplicate" differs per project, so pulling the threshold and keyword exclusions out as CLI options is left as the next task.
- Tech stack
- Python
- OpenAI Embeddings