AI Integration
zawk works with AI in three directions:
- LLMs write zawk:
zawk llmbuilds a prompt from your data and question;zawk lintchecks the answer. - Agents use zawk: a Claude Code skill and machine-readable tooling make zawk a natural tool for agents.
- zawk asks LLMs:
jevfunctions make typed decisions per record, andhttp_postcalls any model API.
1. Let an LLM write the program: zawk llm
zawk llm [options] FILE... QUESTION prints a prompt for an LLM. It contains:
- the command line of the run and what its options mean (input format,
FS, header, output format) - the first lines of every input file
- the schema and a summary of each column (
zawk inspect) - the zawk language notes and builtin functions
- how to check the program with
zawk lint - your question
Pipe it to any LLM tool. With llm, -x extracts the first code block, which zawk
runs directly:
zawk -i csv -H "$(zawk llm -i csv -H demo.csv 'how many adults?' | llm -x)" demo.csv
zawk can also run the first ```awk block of a Markdown answer, so the raw answer works too:
zawk -i csv -H "$(zawk llm -i csv -H demo.csv 'average age by decade' | llm)" demo.csv
With an agent
An AI agent writes the program, checks it with zawk lint, runs it and answers the question:
# Claude Code
zawk llm -i csv -H demo.csv 'what is the average age of adults?' | claude -p --dangerously-skip-permissions
# Qoder CLI
zawk llm -i csv -H demo.csv 'how many adults?' | qodercli -p --dangerously-skip-permissions
--dangerously-skip-permissions lets the agent run commands without asking. Only use it with data and
questions you trust.
2. zawk as a tool for agents
Claude Code skill
The repository ships a skill at
skills/zawk/SKILL.md. Copy it into
~/.claude/skills/zawk/ (or your project's .claude/skills/), and Claude Code will prefer zawk over
awk/sed/cut pipelines or one-off Python scripts for line-oriented data work.
mkdir -p ~/.claude/skills/zawk
curl -fsSL https://raw.githubusercontent.com/linux-china/zawk/master/skills/zawk/SKILL.md -o ~/.claude/skills/zawk/SKILL.md
There is also an llms.txt for tools that read it.
Machine-readable tooling
Everything an agent needs is available as structured output:
zawk functions --json json_ # signatures, params, return types and examples as JSON Lines
zawk inspect -o markdown data.csv # schema, sample rows and column summary as Markdown
zawk lint -i csv -H -f report.awk # diagnostics as JSON, exit code 1 on errors
zawk -o md '...' data.csv # results as a Markdown table for the reply
zawk lint reports a JSON document:
{
"valid": false,
"errors": 1,
"warnings": 0,
"diagnostics": [
{
"code": "E301", "severity": "error", "message": "Call to unknown function \"foo\"",
"file": "demo.awk", "line": 3, "column": 9,
"source": " print foo(1)", "caret": " ^"
}
]
}
See Tooling for all diagnostics codes.
3. Ask an LLM from a program
Typed decisions: jev
jev functions ask a typed question about a record (a text, or an array sent as a JSON object) using the
TypeSafe API. Set TYPESAFE_API_KEY (in the environment or .env).
# filter: 1 when the probability of "yes" is above jev_threshold (default 0.5)
zawk 'jev($0, "the name is European")' names.txt
zawk -v jev_threshold=0.8 'jev($0, "the customer is angry")' tickets.txt
# rank: probability of yes, 0..1
zawk '{ printf "%.2f\t%s\n", jev_prob($0, "the customer is angry"), $0 }' tickets.txt | sort -rn
# classify: pick one option
zawk '{ print jev_choice($0, "which team should handle this?", "billing,technical,security,sales"), $0 }' tickets.txt
# score: probability-weighted position on ordered levels, 0 .. length(levels) - 1
zawk '{ print jev_score($0, "how luxurious is this product?", "budget,mid-range,premium,luxury"), $0 }' products.txt
# structured record: an array is sent as JSON
zawk -i csv -H '{ r["title"] = $FI["title"]; r["body"] = $FI["body"]; if (jev(r, "this is a bug report")) print $FI["id"] }' issues.csv
Options can describe themselves: for an array entry whose key and value differ, the value is the description of the key:
BEGIN {
teams["billing"] = "invoices, refunds, payment methods"
teams["security"] = "account access, passwords, suspicious activity"
teams["technical"] = "bugs, errors, performance"
}
{ print jev_choice($0, "which team should handle this ticket?", teams), $0 }
Any model API with http_post
Combine f-strings, escape("json", ...) and json_value to call a chat completion API per record. For example
with the Claude API:
BEGIN {
h["x-api-key"] = ENVIRON["ANTHROPIC_API_KEY"]
h["anthropic-version"] = "2023-06-01"
}
{
prompt = escape("json", "Summarize in one short sentence: " $0)
body = f"{{\"model\":\"claude-haiku-5-5\",\"max_tokens\":100,\"messages\":[{{\"role\":\"user\",\"content\":\"{prompt}\"}}]}}"
r = http_post("https://api.anthropic.com/v1/messages", body, h)
print r["status"] == 200 ? json_value(r["text"], "$.content[0].text") : "error " r["status"]
}
zawk -f summarize.awk reviews.txt
escape("json", s) returns the escaped content without the surrounding quotes, so it fits inside a JSON string
literal in an f-string. Check with zawk 'BEGIN { print escape("json", "say \"hi\"") }'.
Fuzzy matching without an LLM
Not every "smart" match needs a model: similar(a, b) (Jaro-Winkler) catches typos and name variants locally:
zawk -i csv -H 'similar(tolower($FI["company"]), "anthropic") > 0.9' leads.csv