Posted on
Artificial Intelligence

Artificial Intelligence Workflow Templates

Author
  • User
    linuxbash
    Posts by this author
    Posts by this author

Artificial Intelligence Workflow Templates: A Bash-First Approach You Can Reuse Tomorrow

Ever spent half a day “just” trying to re-run yesterday’s AI experiment? Different folder names, missing virtualenv, stale data, and that one-off command you ran and forgot to log. The problem isn’t your model; it’s your workflow.

AI workflow templates give you a repeatable, shell-native blueprint you can copy, adapt, and hand to teammates. In this article, we’ll build a practical, Bash-first template that:

  • runs locally on any Linux distro,

  • uses sane defaults and environment variables,

  • cleanly separates stages (data, train, evaluate),

  • and is easy to extend (cron, containers, cloud later).

You’ll leave with a workflow you can paste into a new project and run right away.

Why AI workflow templates are worth your time

  • Reproducibility: The same commands produce the same artifacts. The template encodes the order of steps, inputs, and outputs.

  • Portability: Bash, Make, and Python (stdlib) are everywhere. This cuts “works on my machine” incidents.

  • Onboarding: A new teammate needs one command to see the entire pipeline at work.

  • Observability: Standard folders and logs make it obvious where to look when things break.

  • Incremental power: Start small (local, CPU), later swap in containers, schedulers, or GPUs without rewriting everything.

Prerequisites (install once)

Install the minimal runtime you’ll use across many projects: git, make, curl, and Python with venv.

  • On Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y git make curl python3 python3-venv python3-pip
  • On Fedora/RHEL/CentOS (dnf):
sudo dnf install -y git make curl python3 python3-pip
  • On openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y git make curl python3 python3-pip

Note: If python3 -m venv venv fails on your distro, install the appropriate venv subpackage (e.g., python311-venv) and retry.

The template you can paste today

The following snippet scaffolds a minimal, shell-driven AI workflow that trains a tiny Naive Bayes text classifier using only Python’s standard library. It includes Make targets for data, train, and evaluate, and an .env file for configuration.

Copy, paste, and run in an empty directory:

#!/usr/bin/env bash
set -euo pipefail

mkdir -p ai-template/{scripts,src,data/raw,models,reports,dist}

# .env example
cat > ai-template/.env.example <<'EOF'
TRAIN_FILE=data/raw/train.csv
TEST_FILE=data/raw/test.csv
MODEL_OUT=models/model.json
REPORT_OUT=reports/metrics.json
EOF

# Makefile
cat > ai-template/Makefile <<'EOF'
SHELL := /usr/bin/env bash
-include .env

PY := venv/bin/python3

## make help            # Show this help
help:
    @sed -n 's/^##//p' Makefile | sed 's/# //'

## make venv            # Create virtual environment (optional but recommended)
venv:
    python3 -m venv venv && ./venv/bin/pip install -U pip >/dev/null

## make data            # Generate example dataset
data:
    bash scripts/data.sh

## make train           # Train model
train: venv data
    $(PY) src/train.py --train "$(TRAIN_FILE)" --model_out "$(MODEL_OUT)"

## make evaluate        # Evaluate model and write metrics
evaluate: train
    $(PY) src/evaluate.py --test "$(TEST_FILE)" --model_in "$(MODEL_OUT)" --report_out "$(REPORT_OUT)"

## make package         # Create a tarball with artifacts
package: evaluate
    tar czf dist/artifacts.tgz models reports

## make clean           # Remove build artifacts
clean:
    rm -rf venv models reports dist
EOF

# scripts
cat > ai-template/scripts/data.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
mkdir -p data/raw

cat > data/raw/train.csv <<'CSV'
text,label
"win a free ticket now",spam
"call your mom",ham
"project meeting at 10",ham
"free entry in a contest",spam
"are you coming home",ham
"use this code to claim prize",spam
"meeting moved to room 3",ham
"claim your free coupon today",spam
CSV

cat > data/raw/test.csv <<'CSV'
text,label
"free prize waiting",spam
"let's have lunch",ham
"urgent claim coupon",spam
"see you at the standup",ham
CSV

echo "Wrote data/raw/train.csv and data/raw/test.csv"
EOF
chmod +x ai-template/scripts/data.sh

# src: simple Naive Bayes implementation (stdlib only)
cat > ai-template/src/train.py <<'EOF'
#!/usr/bin/env python3
import argparse, csv, json, math, os, re
from collections import Counter, defaultdict

TOKEN_RE = re.compile(r"[A-Za-z]+")

def tokenize(text):
    return [t.lower() for t in TOKEN_RE.findall(text)]

def load_dataset(path):
    rows = []
    with open(path, newline="", encoding="utf-8") as f:
        r = csv.DictReader(f)
        for row in r:
            rows.append((row["text"], row["label"]))
    return rows

def train_nb(pairs, alpha=1.0):
    # Multinomial Naive Bayes for text
    label_counts = Counter()
    word_counts = defaultdict(Counter)
    vocab = set()

    for text, label in pairs:
        label_counts[label] += 1
        for tok in tokenize(text):
            word_counts[label][tok] += 1
            vocab.add(tok)

    model = {
        "labels": list(label_counts.keys()),
        "label_counts": dict(label_counts),
        "word_counts": {lbl: dict(cnt) for lbl, cnt in word_counts.items()},
        "vocab": list(vocab),
        "alpha": alpha
    }
    return model

def save_json(obj, path):
    os.makedirs(os.path.dirname(path), exist_ok=True)
    with open(path, "w", encoding="utf-8") as f:
        json.dump(obj, f, ensure_ascii=False, indent=2)

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--train", required=True)
    ap.add_argument("--model_out", required=True)
    args = ap.parse_args()

    pairs = load_dataset(args.train)
    model = train_nb(pairs, alpha=1.0)
    save_json(model, args.model_out)
    print(f"Model written to {args.model_out} with {len(model['vocab'])} tokens and labels {model['labels']}")

if __name__ == "__main__":
    main()
EOF
chmod +x ai-template/src/train.py

cat > ai-template/src/evaluate.py <<'EOF'
#!/usr/bin/env python3
import argparse, csv, json, math, os, re
from collections import Counter

TOKEN_RE = re.compile(r"[A-Za-z]+")

def tokenize(text):
    return [t.lower() for t in TOKEN_RE.findall(text)]

def load_json(path):
    with open(path, "r", encoding="utf-8") as f:
        return json.load(f)

def load_dataset(path):
    rows = []
    with open(path, newline="", encoding="utf-8") as f:
        r = csv.DictReader(f)
        for row in r:
            rows.append((row["text"], row["label"]))
    return rows

def predict(model, text):
    labels = model["labels"]
    label_counts = model["label_counts"]
    word_counts = model["word_counts"]
    vocab = set(model["vocab"])
    alpha = model["alpha"]

    total_docs = sum(label_counts.values())
    tokens = tokenize(text)

    scores = {}
    for lbl in labels:
        prior = math.log((label_counts[lbl] + 1) / (total_docs + len(labels)))
        word_total = sum(word_counts[lbl].values())
        denom = word_total + alpha * (len(vocab) + 1)
        s = prior
        for tok in tokens:
            c = word_counts[lbl].get(tok, 0)
            s += math.log((c + alpha) / denom)
        scores[lbl] = s

    # argmax
    return max(scores.items(), key=lambda kv: kv[1])[0], scores

def save_json(obj, path):
    os.makedirs(os.path.dirname(path), exist_ok=True)
    with open(path, "w", encoding="utf-8") as f:
        json.dump(obj, f, ensure_ascii=False, indent=2)

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--test", required=True)
    ap.add_argument("--model_in", required=True)
    ap.add_argument("--report_out", required=True)
    args = ap.parse_args()

    model = load_json(args.model_in)
    pairs = load_dataset(args.test)

    correct = 0
    preds = []
    for text, gold in pairs:
        pred, _ = predict(model, text)
        correct += int(pred == gold)
        preds.append({"text": text, "gold": gold, "pred": pred})

    acc = correct / len(pairs) if pairs else 0.0
    report = {"accuracy": acc, "n": len(pairs), "predictions": preds}
    save_json(report, args.report_out)
    print(f"Accuracy: {acc:.3f}  (wrote {args.report_out})")

if __name__ == "__main__":
    main()
EOF
chmod +x ai-template/src/evaluate.py

# Copy a working .env from example
cp ai-template/.env.example ai-template/.env

echo "Scaffold complete. Next steps:
  cd ai-template
  make help
"

Now run the workflow:

cd ai-template
make venv
make evaluate

You should see a trained model saved to models/model.json and an evaluation report at reports/metrics.json with accuracy on the tiny test set.

What’s inside (and why)

  • Makefile: Declares steps and their dependencies. Run once, over and over, the same way: make data, make train, make evaluate.

  • .env: Configuration lives in one place. You can commit .env.example and keep sensitive or environment-specific values out of git.

  • scripts/: Bash scripts are the “glue” (data fetching, preprocessing, packaging). Keep them idempotent and safe (set -euo pipefail).

  • src/: Small, pure-Python utilities for model logic. You can later swap in frameworks without touching the shell wrapper.

  • models/, reports/, data/: Standardized artifacts and logs make debugging and sharing easier.

3–5 actionable tips to make this template your own

1) Parameterize everything with .env
Add paths and knobs only here, then consume them in Make/Shell:

# .env
TRAIN_FILE=data/raw/train.csv
TEST_FILE=data/raw/test.csv
MODEL_OUT=models/model.json
REPORT_OUT=reports/metrics.json

This keeps commands readable and safe to change.

2) Keep steps idempotent and explicit

  • Don’t overwrite without reason; check existence first in scripts.

  • Use Make’s dependencies to avoid re-doing expensive work:

models/model.json: $(TRAIN_FILE) venv
    $(PY) src/train.py --train "$(TRAIN_FILE)" --model_out "$@"

3) Log everything to files
Redirect long-running step outputs:

make train > reports/train.log 2>&1

When something breaks, less reports/train.log is your friend.

4) Add a packaging/export step early
Shipping artifacts matters:

make package
# dist/artifacts.tgz now contains models/ and reports/

5) Automate with cron or systemd (when ready)

  • Cron (daily at 02:30):
crontab -e
# add:
30 2 * * * cd /path/to/ai-template && /usr/bin/make -s evaluate >> reports/cron.log 2>&1
  • systemd user service gives better logs and retries; add when the pipeline matures.

Real-world extension ideas

  • Swap in your real dataset: modify scripts/data.sh to download from S3/HTTP and verify checksum.

  • Upgrade the model: Replace src/train.py with scikit-learn or PyTorch. Your shell contract stays the same (input, output, exit code).

  • Containerize later: Wrap each step in a container and keep the same Make targets. Your teammates can still just run make.

Conclusion and next steps

Templates beat troubleshooting. Start with this Bash-first AI workflow, then layer in complexity on your terms. Your next move:

  • Copy the scaffold above into your next experiment.

  • Replace the dataset in scripts/data.sh with your source.

  • Keep the interface (make data, make train, make evaluate) stable as you iterate.

When you’re ready for the next level, add dependency pinning, containers, or CI. But keep Bash as your reliable, reproducible backbone.