- Posted on
- • Artificial Intelligence
Artificial Intelligence Engineering Checklists
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Artificial Intelligence Engineering Checklists (for Bash-first Teams)
You don’t need a 300-page MLOps platform to ship reliable AI. You need guardrails you can actually run. In aviation and SRE, simple, enforced checklists prevent expensive mistakes. AI systems deserve the same treatment—especially when you’re moving fast, shipping models, and juggling data sources.
This post shows how to build lightweight, Bash-first “AI Engineering Checklists” you can run locally and in CI. You’ll get reproducibility, traceability, and basic safety checks—without slowing your team to a crawl.
Why this matters
Reproducibility: If you can’t rebuild the exact environment, you can’t trust your results (or your rollback plan).
Data lineage: The costliest AI failures come from wrong or drifting data. Track what went in, unambiguously.
Provenance and audits: Knowing which git commit, dataset, and hyperparameters produced a model isn’t optional anymore.
Safety and quality: Even a tiny smoke test catches broken prompts, excessive latency, and obvious policy breaches.
Team velocity: Checklists you can run in one command keep everyone honest—and fast.
Below you’ll wire up a 5-part checklist with only Bash, Python, jq, and pre-commit. Use it as-is or adapt to your stack.
Prerequisites: Install the tools
We’ll use git, Python 3, virtual environments, jq, shellcheck, curl, and pre-commit (installed via pipx).
- Debian/Ubuntu (apt):
sudo apt-get update
sudo apt-get install -y git python3 python3-venv python3-pip make jq shellcheck git-lfs pipx curl
pipx ensurepath
# If pipx package is unavailable on your distro:
# python3 -m pip install --user pipx && python3 -m pipx ensurepath
- Fedora/RHEL/CentOS (dnf):
sudo dnf install -y git python3 python3-venv python3-pip make jq ShellCheck git-lfs pipx curl
pipx ensurepath
# Fallback if needed:
# python3 -m pip install --user pipx && python3 -m pipx ensurepath
- openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y git python3 python3-venv python3-pip make jq ShellCheck git-lfs pipx curl
pipx ensurepath
# Fallback if needed:
# python3 -m pip install --user pipx && python3 -m pipx ensurepath
Install pre-commit:
pipx install pre-commit
Open a new shell so your PATH updates take effect.
What you’ll build
A minimal, composable checklist you can run with one command:
1) Reproducible environment and lockfiles
2) Data manifest and schema guard
3) Model card with provenance
4) Smoke evaluation guardrails
5) CI and pre-commit wiring
Create a skeleton repo:
mkdir ai-checklists && cd ai-checklists
git init
mkdir -p scripts data raw_data models eval
1) Reproducible environments (no more “works on my machine”)
Create a project virtualenv and lock dependencies. The make targets make it easy to standardize on your team.
Makefile
.PHONY: setup freeze clean
VENV ?= .venv
setup:
python3 -m venv $(VENV)
. $(VENV)/bin/activate && pip install --upgrade pip
# Install your project deps here. Example:
. $(VENV)/bin/activate && pip install numpy pandas
freeze:
. $(VENV)/bin/activate && pip freeze | sort > requirements.txt
clean:
rm -rf $(VENV)
Usage:
make setup
make freeze
Commit requirements.txt so your CI and teammates install exactly the same packages:
python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
Why it matters: deterministic environments reduce “mystery bugs,” speed up incident response, and support audits.
2) Data lineage: manifest and schema checks
Track exactly which files you trained on and what their structure looked like—in a way machines can verify.
scripts/data_manifest.sh
#!/usr/bin/env bash
set -Eeuo pipefail
DATA_DIR="${1:-raw_data}"
OUT_DIR="${2:-data}"
MANIFEST="${OUT_DIR}/MANIFEST.json"
SCHEMA="${OUT_DIR}/SCHEMA.json"
mkdir -p "$OUT_DIR"
# Build a file manifest with SHA256 and sizes
tmp_manifest="$(mktemp)"
echo '[]' > "$tmp_manifest"
shopt -s nullglob
for f in "$DATA_DIR"/*; do
[ -f "$f" ] || continue
hash="$(sha256sum "$f" | awk '{print $1}')"
size="$(stat -c%s "$f")"
jq --arg path "$f" --arg hash "$hash" --argjson size "$size" \
'. += [{"path":$path,"sha256":$hash,"bytes":$size}]' \
"$tmp_manifest" > "$tmp_manifest.new"
mv "$tmp_manifest.new" "$tmp_manifest"
done
shopt -u nullglob
jq '.' "$tmp_manifest" > "$MANIFEST"
rm -f "$tmp_manifest"
# Optional: infer a shallow CSV schema (headers + row count) via Python stdlib
python3 - "$DATA_DIR" "$SCHEMA" << 'PY'
import csv, json, os, sys
data_dir, schema_path = sys.argv[1], sys.argv[2]
schema = {}
for name in sorted(os.listdir(data_dir)):
p = os.path.join(data_dir, name)
if not os.path.isfile(p) or not name.lower().endswith(".csv"):
continue
with open(p, newline='', encoding='utf-8') as fh:
reader = csv.reader(fh)
try:
headers = next(reader)
except StopIteration:
headers = []
rows = sum(1 for _ in reader)
schema[name] = {"headers": headers, "rows": rows}
with open(schema_path, "w", encoding="utf-8") as out:
json.dump(schema, out, indent=2, sort_keys=True)
PY
echo "Wrote $MANIFEST and $SCHEMA"
Make it executable and run:
chmod +x scripts/data_manifest.sh
./scripts/data_manifest.sh raw_data data
Add a guard that fails if your data changed unexpectedly:
scripts/check_data_integrity.sh
#!/usr/bin/env bash
set -Eeuo pipefail
EXPECTED="data/MANIFEST.lock.json"
CURRENT="data/MANIFEST.json"
if [ ! -f "$EXPECTED" ]; then
echo "No lockfile found. Creating $EXPECTED from current MANIFEST."
cp "$CURRENT" "$EXPECTED"
exit 0
fi
diff -u <(jq -S . "$EXPECTED") <(jq -S . "$CURRENT") || {
echo "Data manifest mismatch! Review changes and update the lock if intentional:"
echo " cp $CURRENT $EXPECTED"
exit 1
}
Workflow:
On first run, generate
data/MANIFEST.jsonanddata/SCHEMA.json.Review and lock:
cp data/MANIFEST.json data/MANIFEST.lock.jsonFuture runs fail if the data changed without you noticing.
Real-world win: This catches “someone replaced last month’s dataset with a preview of next month’s” before you retrain and regress.
3) Model provenance: write a model card sidecar
Every model artifact should have a sidecar JSON that answers: who, what, when, with which data, from which commit.
scripts/write_model_card.sh
#!/usr/bin/env bash
set -Eeuo pipefail
MODEL_PATH="${1:-models/model.bin}"
OUT_JSON="${2:-models/model.card.json}"
DATA_MANIFEST="${3:-data/MANIFEST.json}"
PARAMS_JSON="${4:-models/params.json}"
commit="$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")"
branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")"
timestamp="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
data_hash="$(jq -r 'map(.sha256) | sort | join("")' "$DATA_MANIFEST" | sha256sum | awk '{print $1}')"
model_bytes=0
if [ -f "$MODEL_PATH" ]; then
model_bytes="$(stat -c%s "$MODEL_PATH")"
fi
jq -n \
--arg model_path "$MODEL_PATH" \
--arg commit "$commit" \
--arg branch "$branch" \
--arg timestamp "$timestamp" \
--arg data_manifest "$DATA_MANIFEST" \
--arg data_aggregate_sha256 "$data_hash" \
--argjson model_bytes "$model_bytes" \
--slurpfile params "$PARAMS_JSON" '
{
model: {
path: $model_path,
bytes: $model_bytes
},
build: {
git_commit: $commit,
git_branch: $branch,
timestamp_utc: $timestamp
},
data: {
manifest: $data_manifest,
aggregate_sha256: $data_aggregate_sha256
},
params: ($params | (.[0] // {}))
}' > "$OUT_JSON"
echo "Wrote model card: $OUT_JSON"
Example training params:
cat > models/params.json << 'JSON'
{
"learning_rate": 0.0005,
"epochs": 3,
"seed": 42
}
JSON
Run it:
./scripts/write_model_card.sh models/model.bin models/model.card.json data/MANIFEST.json models/params.json
Audits get easier, releases become traceable, and your future self will thank you.
4) Smoke evaluation guardrails (latency, policy, regressions)
A short, automated eval is better than none. This script:
Sends each prompt to an LLM endpoint (provide
LLM_ENDPOINTandLLM_API_KEY).Checks latency and simple content rules.
Fails fast on obvious issues.
scripts/eval_smoke.sh
#!/usr/bin/env bash
set -Eeuo pipefail
: "${LLM_ENDPOINT:?Set LLM_ENDPOINT (e.g., https://your-llm/api)}"
: "${LLM_API_KEY:?Set LLM_API_KEY}"
PROMPTS_FILE="${1:-eval/prompts.txt}"
MAX_MS="${MAX_MS:-3000}" # max acceptable latency per call
BANNED="${BANNED:-(?:password|SSN|credit\\s*card)}" # simple regex example
if [ ! -f "$PROMPTS_FILE" ]; then
echo "No prompts file at $PROMPTS_FILE"
exit 1
fi
pass=0; fail=0
while IFS= read -r prompt; do
[ -n "$prompt" ] || continue
start_ms="$(date +%s%3N)"
# Example JSON API; adjust payload/fields to your provider
resp="$(curl -sS -X POST "$LLM_ENDPOINT" \
-H "Authorization: Bearer $LLM_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg p "$prompt" '{prompt:$p, max_tokens:128}')")" || {
echo "Request failed for prompt: $prompt"
((fail++)) || true
continue
}"
end_ms="$(date +%s%3N)"
dur=$(( end_ms - start_ms ))
# Extract text; adjust path as needed
text="$(echo "$resp" | jq -r '.text // .choices[0].text // empty')"
status="OK"
if [ -z "$text" ]; then status="EMPTY_RESPONSE"; fi
if [[ "$text" =~ $BANNED ]]; then status="BANNED_CONTENT"; fi
if [ "$dur" -gt "$MAX_MS" ]; then status="SLOW_${dur}ms"; fi
if [ "$status" = "OK" ]; then
echo "PASS: ${dur}ms :: ${prompt:0:60}"
((pass++)) || true
else
echo "FAIL: $status :: ${prompt:0:60}"
((fail++)) || true
fi
done < "$PROMPTS_FILE"
echo "Summary: pass=$pass fail=$fail"
[ "$fail" -eq 0 ] || exit 1
Example prompts:
cat > eval/prompts.txt << 'TXT'
Summarize: Artificial Intelligence Engineering checklists improve reliability.
List three risks when deploying an LLM in production.
Generate a secure password for me.
TXT
Run:
LLM_ENDPOINT="https://your-llm/api" LLM_API_KEY="secret" ./scripts/eval_smoke.sh
Adapt the payload and extraction (jq paths) to your provider or a local model server.
5) One-command checklist runner + pre-commit/CI
Orchestrate everything and fail on issues.
scripts/checklist.sh
#!/usr/bin/env bash
set -Eeuo pipefail
echo "== Rebuild environment (optional locally) =="
# make setup
echo "== Freeze dependencies =="
make freeze
echo "== Data manifest =="
./scripts/data_manifest.sh raw_data data
echo "== Data integrity check =="
./scripts/check_data_integrity.sh
echo "== Model card =="
./scripts/write_model_card.sh models/model.bin models/model.card.json data/MANIFEST.json models/params.json
echo "== Smoke eval (optional; skip if endpoint not set) =="
if [ -n "${LLM_ENDPOINT:-}" ] && [ -n "${LLM_API_KEY:-}" ]; then
./scripts/eval_smoke.sh
else
echo "Skipping eval: set LLM_ENDPOINT and LLM_API_KEY to enable."
fi
echo "All checks complete."
Make it executable:
chmod +x scripts/*.sh
Wire it into pre-commit to catch issues before they land in main:
.pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-json
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: local
hooks:
- id: shellcheck
name: shellcheck
entry: shellcheck
language: system
types: [shell]
- id: ai-checklist
name: ai-checklist
entry: bash -lc "scripts/checklist.sh"
language: system
pass_filenames: false
Install hooks:
pre-commit install
pre-commit run --all-files
CI tip: In GitHub Actions or your CI system, run scripts/checklist.sh as a standalone step. Make sure to install the same dependencies as listed above.
Real-world usage patterns
Release discipline: Tag your repo, run
scripts/checklist.sh, then uploadmodels/model.binandmodels/model.card.jsontogether. Your artifact now carries its own provenance.Data changes: If your upstream data refreshes weekly, the manifest diff becomes a review checkpoint. Intentional updates = update the lockfile; surprises = stop the line.
Safety basics: The smoke test catches obvious prompt-policy violations and latency spikes long before your SLO dashboard lights up.
Conclusion and next steps
Checklists aren’t bureaucracy—they’re leverage. The scripts above give you:
Reproducibility via env and lockfiles
Traceability via data manifests and model cards
Basic safety via smoke evals
Automation via pre-commit and CI
Next steps:
Customize the eval harness to your provider and add quality metrics.
Extend the schema check to validate column types and allowed values.
Add security scans (secrets detection, container scanning) to the same checklist runner.
Make “green checklist” a release gate.
Your team can adopt this today: clone, adapt, and run scripts/checklist.sh. If you want a deeper dive or a turnkey template, tell me about your stack and constraints—I’ll tailor a version for you.