ajq brings semantic matching and bounded classification to JSON streams, using the jq language you already know. It runs the ordinary parts of your query through a real jq engine and only calls a language model for explicit fuzzy operators, one small field value at a time.
So most of your pipeline stays byte-for-byte reproducible, and what you pay tracks how many fuzzy decisions you actually make, not how big your input is.
Deterministic core
Real jq semantics, powered by gojq. Most of every
pipeline is byte-reproducible: the same input gives you the same bytes on every run. Pure
jq paths never contact AI backends; only explicit semantic operators reach for a model.
Semantic operators
select(.x =~ "spec") and sem_classify are jq-shaped operators for fuzzy filters and
labels. sem_score and sem_norm are available only in supported contexts, while
standalone sem_extract and sem_redact are registered but unsupported. There is no new
grammar to learn, so semantic predicates compose with ordinary jq pipelines.
Local-first and cheap
A provisioned ~1 GB local model runs fuzzy decisions on your machine via
--backend local, while --cloud selects the shipped Anthropic backend. Identical field
values get deduplicated and written to the persistent judgement cache, so a second run can
replay repeated decisions without another model call.
How split execution works
A real ajq query is mostly plain jq. Only the explicit fuzzy operator needs a model:
cat data.json | ajq --backend local '.users[] | select(.feedback =~ "angry/frustrated") | .id'
└────────┬───────┘ └────────────┬────────────┘ └┬┘
deterministic path semantic predicate proj
(pure gojq) (LLM, per value) (pure gojq)
ajq parses your query, runs everything deterministic through jq in process, and only calls the model on the smallest slices of data it can get away with. Because the model sees one field value at a time, the context stays tiny and a 1.5B model is plenty. There’s more in Split execution.
How ajq compares
| ajq | llm-jq | jq / awk | Ad-hoc LLM script | grep / ripgrep | |
|---|---|---|---|---|---|
| Fuzzy, semantic matching | Yes | Prompted jq | No | Yes | No |
| Deterministic, byte-reproducible core | Yes | After generation | Yes | No | Yes |
| Structured JSON in and out | Yes | Yes | jq only | DIY | Lines only |
| Cost tracks fuzzy decisions, not input size | Yes | One prompt + sample | n/a | No, per row | n/a |
| Runs locally, no API key | Local backend | Depends on LLM | Yes | Usually cloud | Yes |
| Structural output guarantee (schema, enum) | GBNF-constrained | Generated jq | Exact | Prompt-hope | Exact |
| One-line pipeline ergonomics | Yes | Yes | Yes | No | Yes |
Here’s the gap ajq fills. grep, awk, and jq are deterministic but literal. llm-jq
uses an LLM to write a jq program from your prompt, then runs that program over the data.
A hand-rolled LLM script can be fuzzy, but it pays per row, drifts over time, and leaves
you to plumb the JSON yourself. ajq keeps jq’s ergonomics and adds explicit fuzzy JSON
filters and classification, deduplicated and cached.
Performance
Local-model latency depends on the model, runtime, hardware, and repeated-value ratio. These are medians from five independent cold-start runs; see the raw reports before comparing them with your own workload.
b4666107…5896f8c), 64-record sem_match array with
8 post-dedup judgements. Cold start ranged from 611–916 ms; warm latency from
84–95 ms. These are workload-specific measurements, not a product guarantee.
Inspect the five raw JSON reports.It’s open source
ajq is MIT-licensed and built in the open. Issues, ideas, and pull requests are all welcome.
Install it now
Install with the release script or go install, then run ajq provision to fetch the
default model and locate a llama-server engine — no API key required for local work.