- Posted on
- • Artificial Intelligence
Build an AI-Powered Linux Help Desk
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Build an AI‑Powered Linux Help Desk (with Bash + your docs)
If your Linux team is drowning in “quick questions” and tribal knowledge, you don’t need a heavyweight ticketing suite to get leverage. You can stand up a private, auditable, terminal‑native help desk powered by an LLM and your own docs—in under an hour—using Bash and curl.
This guide shows you how to:
Wire a local or cloud LLM to a Bash script
Automatically pull relevant context from man pages and your system
Return precise, copy‑pasteable commands for common admin tasks
Keep everything on your box, with logs and version control
No Python stack, no web UI—just a sharp, composable CLI your team will actually use.
Why this works (and why now)
Terminal‑first: Most Linux troubleshooting ends up in the shell. Keep assistance where the work happens.
Context‑aware: Generic AI often hallucinates distro/tooling details. Feeding it man pages, config snapshots, and logs reduces guesswork.
Private and auditable: Run locally (Ollama) or via an API of your choice. All prompts, context, and outputs are logged.
Incrementally better: Start with man pages and a few runtime snapshots. Add internal runbooks and postmortems over time.
Low friction: It’s one Bash script with ripgrep, jq, and curl.
What you’ll build
A single Bash script, helpdesk.sh, that: 1) Searches your local “knowledge base” (extracted man pages + runtime snapshots + optional notes) for relevant snippets. 2) Constructs a prompt with the best matching context. 3) Calls your chosen LLM (local via Ollama, or a cloud API via curl). 4) Prints a concise, actionable answer with commands for apt, dnf, and zypper when package installation is relevant.
You’ll also create a small job to refresh runtime snapshots (disk usage, failed services, etc.) so answers fit your host.
Prerequisites and installation
You’ll need curl, jq, and ripgrep. Install them with your distro’s package manager:
Debian/Ubuntu (apt):
sudo apt update sudo apt install -y curl jq ripgrepFedora/RHEL/CentOS (dnf):
sudo dnf install -y curl jq ripgrepopenSUSE (zypper):
sudo zypper refresh sudo zypper install -y curl jq ripgrep
Choose one LLM backend:
Local (recommended for privacy): Install Ollama and pull a model.
curl -fsSL https://ollama.com/install.sh | sh sudo systemctl enable --now ollama ollama pull llama3.1:8bNotes:
- Ollama runs at http://localhost:11434 by default.
- You can substitute another model (e.g., mistral, qwen) as you prefer.
Cloud (if you already have an API key):
- OpenAI example: set your key
export OPENAI_API_KEY="sk-..."- You can adapt the script to other providers with compatible chat endpoints.
Create a working directory:
mkdir -p ~/ai-helpdesk/{docs/man,docs/runtime,logs}
cd ~/ai-helpdesk
Step 1 — Prime your local knowledge base
Start by extracting man pages for core admin tools. This is fast and massively reduces hallucinations.
Create prime_kb.sh:
cat > prime_kb.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
mkdir -p docs/man
# A pragmatic starter set; extend as you like.
CMDS=(
bash grep sed awk find xargs tar rsync ssh scp systemctl journalctl dmesg
ip ss df du lsblk mount umount fstab useradd usermod groupadd chown chmod
ps kill crontab logrotate tail head less tee cut sort uniq wc tr
)
for cmd in "${CMDS[@]}"; do
if man -w "$cmd" >/dev/null 2>&1; then
echo "Extracting man $cmd"
man -P cat "$cmd" | col -b > "docs/man/${cmd}.txt"
fi
done
echo "Done. Man pages in docs/man/"
EOF
chmod +x prime_kb.sh
./prime_kb.sh
Optional: Add your own notes, runbooks, or SOPs to docs/notes (create this folder if you’ll use it). The script below will search any subdirectory of docs/.
Step 2 — Capture runtime context (kept fresh)
Snapshot frequently useful facts so the model can answer in context of your host.
Create refresh_runtime.sh:
cat > refresh_runtime.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
mkdir -p docs/runtime
# OS and kernel
{ date; uname -a; [ -f /etc/os-release ] && cat /etc/os-release; } \
> docs/runtime/system.txt 2>/dev/null || true
# Disks and mounts
{ df -h; echo; lsblk; echo; mount; } \
> docs/runtime/storage.txt 2>/dev/null || true
# Services and logs (non-privileged; adjust as needed)
{ systemctl --failed || true; echo; journalctl -p 3 -xb -n 200 || true; } \
> docs/runtime/services.txt 2>/dev/null || true
# Networking listeners (avoid PII; drop -p if you don’t want PIDs)
{ ss -tulpn || true; } > docs/runtime/network.txt 2>/dev/null || true
echo "Runtime snapshots refreshed in docs/runtime/"
EOF
chmod +x refresh_runtime.sh
./refresh_runtime.sh
Automate it with cron (adjust interval):
( crontab -l 2>/dev/null; echo "*/30 * * * * cd $HOME/ai-helpdesk && ./refresh_runtime.sh" ) | crontab -
Step 3 — The help desk script
Create helpdesk.sh:
cat > helpdesk.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
# Configuration
PROVIDER="${PROVIDER:-auto}" # auto | ollama | openai
OLLAMA_HOST="${OLLAMA_HOST:-http://127.0.0.1:11434}"
OLLAMA_MODEL="${OLLAMA_MODEL:-llama3.1:8b}"
OPENAI_MODEL="${OPENAI_MODEL:-gpt-4o-mini}"
TEMPERATURE="${TEMPERATURE:-0.2}"
CTX_LINES="${CTX_LINES:-8}" # lines of context per match
MAX_SNIPPETS="${MAX_SNIPPETS:-10}"
BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DOCS_DIR="$BASE_DIR/docs"
LOG_DIR="$BASE_DIR/logs"
mkdir -p "$LOG_DIR"
usage() {
echo "Usage: $(basename "$0") [-r] \"your question...\""
echo " -r, --refresh Refresh runtime snapshots before answering"
exit 1
}
REFRESH=0
QUERY=""
while [[ $# -gt 0 ]]; do
case "$1" in
-r|--refresh) REFRESH=1; shift ;;
-h|--help) usage ;;
*) QUERY="${QUERY} $1"; shift ;;
esac
done
QUERY="${QUERY#" "}"
[[ -z "$QUERY" ]] && usage
command -v curl >/dev/null || { echo "curl is required"; exit 1; }
command -v jq >/dev/null || { echo "jq is required"; exit 1; }
command -v rg >/dev/null || { echo "ripgrep (rg) is required"; exit 1; }
if [[ $REFRESH -eq 1 ]]; then
"$BASE_DIR/refresh_runtime.sh" || true
fi
# Auto-select provider if set to auto
if [[ "$PROVIDER" == "auto" ]]; then
if curl -sS "$OLLAMA_HOST/api/tags" >/dev/null 2>&1; then
PROVIDER="ollama"
elif [[ -n "${OPENAI_API_KEY:-}" ]]; then
PROVIDER="openai"
else
echo "No provider available. Start Ollama or export OPENAI_API_KEY."
exit 1
fi
fi
gather_context() {
local query="$1"
local ctx_file
ctx_file="$(mktemp)"
# Search all docs subdirs if present
if [[ -d "$DOCS_DIR" ]]; then
rg -n -S --no-heading -C "$CTX_LINES" -m "$MAX_SNIPPETS" \
--glob '!*.gz' -- "$query" "$DOCS_DIR" 2>/dev/null \
| head -n 3000 > "$ctx_file" || true
fi
# If we found nothing, try on-demand man for likely tokens in the query
if [[ ! -s "$ctx_file" ]]; then
for token in $(echo "$query" | tr -c '[:alnum:]_-' ' '); do
if man -w "$token" >/dev/null 2>&1; then
mkdir -p "$DOCS_DIR/man"
man -P cat "$token" | col -b > "$DOCS_DIR/man/${token}.txt"
fi
done
rg -n -S --no-heading -C "$CTX_LINES" -m "$MAX_SNIPPETS" \
-- "$query" "$DOCS_DIR" 2>/dev/null | head -n 3000 > "$ctx_file" || true
fi
echo "$ctx_file"
}
build_system_prompt() {
cat <<'SYS'
You are a Linux help desk assistant answering for experienced sysadmins.
- Prefer concise, copy-pasteable commands with brief explanations.
- When package installation is relevant, show commands for:
- Debian/Ubuntu (apt)
- Fedora/RHEL/CentOS (dnf)
- openSUSE (zypper)
- If the distro is unknown, be distro-agnostic and show alternatives.
- Warn and ask for confirmation before destructive actions.
- Use the provided context; if something is missing, say what to check next.
SYS
}
call_ollama() {
local sys user
sys="$(build_system_prompt)"
user="$1"
curl -sS "$OLLAMA_HOST/api/chat" \
-H "Content-Type: application/json" \
-d @- <<JSON | jq -r '.message.content'
{
"model": "${OLLAMA_MODEL}",
"stream": false,
"messages": [
{"role":"system","content": ${sys@Q}},
{"role":"user","content": ${user@Q}}
],
"options": { "temperature": ${TEMPERATURE} }
}
JSON
}
call_openai() {
[[ -n "${OPENAI_API_KEY:-}" ]] || { echo "OPENAI_API_KEY is not set"; exit 1; }
local sys user
sys="$(build_system_prompt)"
user="$1"
curl -sS https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${OPENAI_API_KEY}" \
-d @- <<JSON | jq -r '.choices[0].message.content'
{
"model": "${OPENAI_MODEL}",
"temperature": ${TEMPERATURE},
"messages": [
{"role":"system","content": ${sys@Q}},
{"role":"user","content": ${user@Q}}
]
}
JSON
}
main() {
local ctx_file prompt answer ts
ts="$(date +%Y%m%d-%H%M%S)"
ctx_file="$(gather_context "$QUERY")"
prompt="Question:
$QUERY
Relevant context (snippets from local docs and runtime):
$(sed -e 's/\t/ /g' "$ctx_file")
Please provide commands and a terse explanation. If multiple distros differ, show apt, dnf, and zypper."
if [[ "$PROVIDER" == "ollama" ]]; then
answer="$(call_ollama "$prompt")"
else
answer="$(call_openai "$prompt")"
fi
echo "$answer"
{
echo "=== $ts ==="
echo "Q: $QUERY"
echo "--- context ---"
cat "$ctx_file"
echo "--- answer ---"
echo "$answer"
echo
} >> "$LOG_DIR/history.log"
rm -f "$ctx_file"
}
main
EOF
chmod +x helpdesk.sh
Test it:
./helpdesk.sh "How can I tail the last 100 lines of the nginx error log and follow?"
Example output (will vary by model):
Use tail to read and follow the log:
sudo tail -n 100 -f /var/log/nginx/error.log
If nginx uses systemd-journald:
sudo journalctl -u nginx -n 100 -f -p warning
Step 4 — Real‑world usage patterns and tips
1) Free up space on /var quickly
./helpdesk.sh -r "Disk is full on /var. Safe steps to recover space?"
Typical reply should include:
Inspect largest dirs: sudo du -xhd1 /var | sort -h | tail
Rotate or inspect logs: sudo journalctl --vacuum-time=7d
Clean package caches:
- apt: sudo apt clean
- dnf: sudo dnf clean all
- zypper: sudo zypper clean --all
Caution around removing files in use; suggest lsof +L1
2) A failing service that won’t start
./helpdesk.sh -r "systemd service fails to start; how do I diagnose fast?"
Expected actions:
journalctl -u NAME -b -n 200
systemctl status NAME --no-pager
Permissions/ExecStart path checks
EnvironmentFile and WorkingDirectory pitfalls
3) Package installation guidance across distros
./helpdesk.sh "Install jq and ripgrep; show commands for apt, dnf, zypper"
Expected output:
apt:
sudo apt update sudo apt install -y jq ripgrepdnf:
sudo dnf install -y jq ripgrepzypper:
sudo zypper refresh sudo zypper install -y jq ripgrep
4) Safer mass deletes
./helpdesk.sh "Remove all *.pyc under /srv/app, confirm before delete"
Look for:
Review pass:
find /srv/app -type f -name '*.pyc' -printThen delete with confirm:
find /srv/app -type f -name '*.pyc' -exec rm -iv {} +
5) Network triage snippet
./helpdesk.sh -r "Which process is listening on port 8080 and how to stop it?"
Look for:
sudo ss -tulpn | rg ':8080'
sudo systemctl stop
or kill PID with caution
Operational notes
Security and privacy
- Avoid passing secrets into prompts. The script only uses man pages and selected runtime snapshots; expand cautiously.
- If you log outputs, remember logs are sensitive. Keep logs/history.log properly permissioned.
Extensibility
- Add docs/notes for your environment (SOPs, runbooks, app READMEs).
- Tag content in filenames (notes_backup.txt, notes_k8s.txt) to improve rg hits.
- Consider separate profiles per host or role (web, db, CI).
Performance
- ripgrep is fast, but context can still be large. Tune MAX_SNIPPETS and CTX_LINES.
- Pin a smaller/faster local model for quick iterations; switch to a larger one for tougher problems.
Reliability
- If Ollama isn’t reachable, the script can fall back to OPENAI_API_KEY (set PROVIDER=auto).
- Record model + config in logs for reproducibility.
Optional: turn it into a team command
Add this to your shell profile:
alias helpdesk="$HOME/ai-helpdesk/helpdesk.sh"
Now it’s:
helpdesk -r "How do I increase inotify watchers?"
Conclusion and next steps (CTA)
You now have a private, terminal‑native AI help desk that understands your system, cites local context, and returns copy‑pasteable Linux commands. It’s simple, auditable, and grows with your docs.
Next steps:
Add your team’s SOPs and postmortems to docs/notes
Expand prime_kb.sh with the tools you actually use
Create per‑host or per‑role runtime refresh jobs
Experiment with different Ollama models for speed/quality trade‑offs
When you’re ready to go deeper, you can add embeddings and semantic search—but you’ll be surprised how far “ripgrep + man + snapshots + LLM” takes you.
Happy hacking.