Posted on
Artificial Intelligence

Artificial Intelligence Agent Projects for Linux

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

Artificial Intelligence Agent Projects for Linux (Bash-first, Local-first)

If you’ve been watching AI “agent” demos and wondering, “How do I run something like that on my own Linux box—no cloud, no subscriptions, just Bash and open models?” this is for you. In this guide we’ll build practical, local AI agents that slot into your command line and automate real ops/dev work without sending your data anywhere.

You’ll learn why local agents are worth it, how to set them up, and how to ship 3–4 useful projects today—plus copy‑paste snippets you can adapt.


Why Linux AI agents are worth your time

  • Privacy and control: Keep logs, code, and credentials on your machine. No third-party uploads.

  • Cost and speed: Run fast models locally; avoid recurring API bills and rate limits.

  • Composability: Unix philosophy meets AI—pipe, compose, and automate with standard tools.

  • Observability: Everything is files and processes you can inspect, log, and audit with Bash.


Prerequisites (install once)

We’ll use:

  • A local LLM runtime: Ollama

  • Basic CLI tools: curl, jq, git, tmux

  • Optional Python for one project

Install system packages:

Debian/Ubuntu (apt):

sudo apt update
sudo apt install -y python3 python3-venv python3-pip git curl jq tmux

Fedora/RHEL/CentOS (dnf):

sudo dnf install -y python3 python3-pip git curl jq tmux

openSUSE (zypper):

sudo zypper refresh
sudo zypper install -y python3 python3-pip git curl jq tmux

Install Ollama (local LLM engine). The official script supports apt, dnf, and zypper:

curl -fsSL https://ollama.com/install.sh | sh

Start the Ollama service (or run it in your shell):

# As a system service (recommended)
sudo systemctl enable --now ollama

# Or run in foreground if you prefer
# ollama serve

Pull a model that runs well on CPUs/GPUs. Examples:

# Smaller, fast general model
ollama pull mistral:7b

# Llama 3 family (choose what fits your VRAM/CPU)
ollama pull llama3:8b
ollama pull llama3.1:8b

Quick sanity check:

curl http://localhost:11434/api/generate -d '{
  "model": "mistral:7b",
  "prompt": "Respond with the single word: ready.",
  "stream": false
}'

You should see JSON with “ready.” in the response.

Tip: Set OLLAMA_HOST to point at a remote box with more VRAM if needed:

export OLLAMA_HOST="http://gpu-server:11434"

Project 1: Log triage agent (tail, summarize, suggest fixes)

Problem: Logs are noisy; you need quick, actionable context.

Solution: Pipe logs to a small agent that summarizes bursts of errors and suggests next steps.

Create a script logsage.sh:

#!/usr/bin/env bash
# Summarize bursts of log lines via Ollama and append to a triage file.

MODEL="${MODEL:-mistral:7b}"
BATCH_LINES="${BATCH_LINES:-80}"
LOG_INPUT="${1:-/var/log/syslog}"
OUT="${OUT:-$HOME/log-triage.out}"

echo "[*] Reading from $LOG_INPUT, batching $BATCH_LINES lines, model $MODEL"
touch "$OUT"

buffer=""
count=0

consume() {
  [ -z "$buffer" ] && return 0
  prompt="You are a Linux SRE assistant. Summarize the following logs into:

- Main issue(s)

- Probable root cause(s)

- 2-3 concrete next actions (systemctl/journalctl/grep commands)
Only return plain text. Logs:
$buffer"

  resp=$(curl -s http://localhost:11434/api/generate \
    -H "Content-Type: application/json" \
    -d "$(jq -n --arg m "$MODEL" --arg p "$prompt" '{model:$m, prompt:$p, stream:false}')" \
    | jq -r '.response')

  ts="$(date -Is)"
  {
    echo "----- [$ts] triage via $MODEL -----"
    echo "$resp"
    echo
  } >> "$OUT"

  buffer=""
  count=0
}

# You might need sudo for /var/log/syslog on some distros.
tail -n0 -F "$LOG_INPUT" | while IFS= read -r line; do
  buffer+="$line"$'\n'
  count=$((count+1))
  if [ "$count" -ge "$BATCH_LINES" ]; then
    consume
  fi
done

Make it executable and run:

chmod +x logsage.sh
./logsage.sh /var/log/syslog
# or a custom log
./logsage.sh /var/log/nginx/error.log

Open another terminal and watch triage appear:

tail -f $HOME/log-triage.out

Notes:

  • Tweak BATCH_LINES or feed it a filtered stream like journalctl -f -u nginx.

  • Use tmux to keep it running persistently.


Project 2: Self-healing service watchdog (JSON tool-calling)

Problem: Services crash or flap; you want a controlled, auditable “agent” that decides when to restart and logs why.

We’ll ask the model to return strict JSON with an action we allow, then execute only whitelisted actions.

Create a Python virtual environment (optional but clean):

python3 -m venv .venv
. .venv/bin/activate
pip install requests

Create watchdog.py:

#!/usr/bin/env python3
import json, subprocess, time, requests, os

MODEL = os.getenv("MODEL", "mistral:7b")
SERVICES = os.getenv("SERVICES", "nginx ssh").split()
OLLAMA = os.getenv("OLLAMA_HOST", "http://localhost:11434")

def status(service):
  try:
    out = subprocess.check_output(["systemctl", "is-active", service], text=True).strip()
  except subprocess.CalledProcessError as e:
    out = e.output.strip() if e.output else "unknown"
  return out

def propose_action(context):
  prompt = f"""
You are a cautious Linux ops agent. Given the service states, respond with strict JSON:
{{
  "decision": "noop" | "restart" | "investigate",
  "target": "<service name or ''>",
  "reason": "<one-sentence justification>"
}}
Rules:

- Never choose restart if ssh is inactive.

- Prefer investigate unless a single well-known service is inactive.
Context:
{context}
"""
  resp = requests.post(f"{OLLAMA}/api/generate", json={
      "model": MODEL,
      "prompt": prompt,
      "stream": False
  }, timeout=120)
  txt = resp.json()["response"]
  # extract JSON if model adds prose
  start = txt.find("{"); end = txt.rfind("}")
  if start == -1 or end == -1:
    return {"decision":"noop","target":"","reason":"Could not parse model output"}
  try:
    return json.loads(txt[start:end+1])
  except Exception:
    return {"decision":"noop","target":"","reason":"Invalid JSON from model"}

def main():
  interval = int(os.getenv("INTERVAL", "20"))
  logf = os.getenv("LOGFILE", os.path.expanduser("~/watchdog.log"))
  allowed = {"noop", "restart", "investigate"}

  while True:
    states = {s: status(s) for s in SERVICES}
    ctx = json.dumps(states, indent=2)
    plan = propose_action(ctx)
    decision = plan.get("decision","noop")
    target = plan.get("target","")
    reason = plan.get("reason","")

    line = f"[{time.strftime('%F %T')}] states={states} plan={plan}"
    print(line)
    with open(logf, "a") as f:
      f.write(line + "\n")

    if decision in allowed and decision == "restart" and target in SERVICES:
      # Final safeguard: never restart ssh from here
      if target == "ssh":
        pass
      else:
        subprocess.call(["sudo", "systemctl", "restart", target])
        with open(logf, "a") as f:
          f.write(f"[{time.strftime('%F %T')}] action=restart target={target}\n")

    time.sleep(interval)

if __name__ == "__main__":
  main()

Run it:

# Example: watch nginx and postgresql every 20s
SERVICES="nginx postgresql" INTERVAL=20 python3 watchdog.py

Inspect decisions:

tail -f ~/watchdog.log

Tips:

  • Keep the action set tiny (“noop”, “restart”, “investigate”).

  • Add your own guardrails (e.g., only restart if failed 1x in last N minutes).

  • Always log every decision.


Project 3: aicmd — a safe CLI copilot (propose, confirm, run)

Problem: You know what you want, not the exact flags. Ask in plain English; get a proposed command; confirm before execution.

Add this function to your ~/.bashrc or ~/.bash_profile:

aicmd() {
  local prompt="You are a Linux CLI assistant. Propose ONE safe Bash command for: $*.
Rules:

- Output ONLY the command on a single line, no explanations.

- Use common GNU utils available on most distros.

- Never use destructive commands without explicit flags (no rm -rf /).
"
  local model="${AICMD_MODEL:-mistral:7b}"
  local cmd
  cmd=$(curl -s http://localhost:11434/api/generate \
    -H "Content-Type: application/json" \
    -d "$(jq -n --arg m "$model" --arg p "$prompt" '{model:$m, prompt:$p, stream:false}')" \
    | jq -r '.response' | sed -E 's/^```bash$//; s/^```$//; s/^`|`$//g' \
    | head -n1)

  if [ -z "$cmd" ]; then
    echo "No suggestion."
    return 1
  fi
  echo "Proposed:"
  echo "  $cmd"
  read -r -p "Run this? [y/N] " ans
  if [[ "$ans" =~ ^[Yy]$ ]]; then
    eval "$cmd"
  fi
}

Reload your shell:

source ~/.bashrc

Examples:

aicmd find largest 10 files under /var/log excluding gz
aicmd show top 5 memory-hungry processes with full command lines

Security tip:

  • Always require confirmation; never auto-execute raw model output.

  • Consider a denylist (e.g., disallow rm, dd) or a sandbox.


Project 4: Conventional commit agent (summarize staged changes)

Problem: Good commit messages take time; your diffs already contain the story.

Script gen-commit.sh:

#!/usr/bin/env bash
set -euo pipefail
MODEL="${MODEL:-mistral:7b}"

if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "Not a git repo"; exit 1
fi

DIFF=$(git diff --staged)
if [ -z "$DIFF" ]; then
  echo "No staged changes"; exit 0
fi

PROMPT="You are a precise commit message generator. Given a git diff of staged changes,
produce a conventional commits message (type: feat|fix|refactor|docs|test|chore),
with:

- a one-line summary (<72 chars),

- an optional blank line,

- 1-3 bullet points.
Do not include code fences. Diff:
$DIFF
"

MSG=$(curl -s http://localhost:11434/api/generate \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg m "$MODEL" --arg p "$PROMPT" '{model:$m, prompt:$p, stream:false}')" \
  | jq -r '.response')

echo "----- Proposed commit message -----"
echo "$MSG"
echo "----------------------------------"
read -r -p "Use this message? [y/N] " ans
if [[ "$ans" =~ ^[Yy]$ ]]; then
  git commit -m "$MSG"
fi

Usage:

chmod +x gen-commit.sh
git add -A
./gen-commit.sh

Troubleshooting and performance tips

  • Model too slow? Try a smaller one: mistral:7b or quantized variants. Pull different tags and benchmark.

  • CPU-only hosts: Prefer 7B-class models; reduce max tokens by adding “Be concise” to prompts.

  • Check service: systemctl status ollama and journalctl -u ollama -f.

  • Network to remote GPU: set export OLLAMA_HOST=http://gpu-box:11434.

  • JSON hiccups: Always parse+validate model outputs; fall back to safe defaults.


What you built

  • A log triage agent that condenses noise into actions

  • A cautious watchdog that decides when to restart services

  • A CLI copilot that proposes commands but asks before running

  • A commit-message generator that respects your repo boundaries

All local. All scriptable. All observable.


Call to action

  • Pick one project and ship it this week. Start with aicmd—it’s a tiny quality-of-life upgrade.

  • Make it yours: tighten prompts, add guardrails, wire alerts to mail/Slack.

  • Share your dotfiles or scripts with your team; standardize safe patterns for agent actions.

If you want a follow-up post with GPU tuning, function-calling with structured JSON, or containerized deployments, tell me what you’re running and on which distro—and I’ll tailor the next guide.