Build Observable CLI Scripts

Fail clearly

A few small Bash habits make automated scripts calling an LLM dramatically more trustworthy. set -euo pipefail at the top of a script makes it stop immediately on any failed command (-e), on any reference to an unset variable (-u), and - critically for pipelines - on a failure occurring anywhere inside a chain of piped commands, not just the last one (pipefail), which bash doesn't check by default. mktemp creates a safely-named temporary file that won't collide with another process's temp file, and a trap - a handler registered to run automatically when the script exits, whether normally or due to an error - is the reliable way to guarantee that temporary file actually gets cleaned up either way.

#!/usr/bin/env bash
set -euo pipefail
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT

curl -sf --max-time 30 http://127.0.0.1:11434/api/generate ... > "$tmp"
answer=$(jq -e -r '.response' "$tmp") || { echo "bad response" >&2; exit 1; }

It's also worth setting an explicit network timeout (--max-time 30 above) so a stuck model call can't hang the whole script forever, and checking that required tools like jq and curl are actually installed before relying on them. Before overwriting anything important with a model's response, save it somewhere temporary first and validate it - that's exactly what the jq -e check above does, refusing to proceed if the expected .response field isn't present.

For automation that needs structured output rather than free-form prose, explicitly ask the model for JSON, then validate what comes back with jq -e against the specific fields and values you expect. If that validation fails, the right move is to stop the script outright rather than guessing at what the model "probably meant."

Scenario: A summarizer occasionally returns a sentence of prose before its JSON object, like "Sure, here's the summary: {...}", instead of pure JSON. A strict validator that rejects anything not starting with { catches this immediately and stops the pipeline, rather than letting malformed, unparseable metadata quietly flow further downstream into a release process that trusted it blindly.