- Posted on
- • Artificial Intelligence
Writing Better Artificial Intelligence Prompts
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Writing Better AI Prompts from the Command Line (for Bash Users)
If you’ve ever piped a vague prompt into an AI model and gotten something “close but wrong,” you already know the pain: you spend more time fixing outputs than you would writing it yourself. The good news is that better prompts yield better results—reliably and repeatably—especially when you work from Bash, where you can script tests, validate outputs, and iterate fast.
This post explains why prompt quality matters, then gives you practical, Bash-friendly techniques and examples you can paste right into your terminal. You’ll finish with a tiny reusable shell harness you can adapt for any model with an OpenAI-compatible API.
Prerequisites (with install commands)
We’ll use curl to call an API and jq to validate and parse model outputs.
Debian/Ubuntu (apt):
sudo apt update sudo apt install -y curl jqFedora/RHEL/CentOS (dnf):
sudo dnf install -y curl jqopenSUSE/SLE (zypper):
sudo zypper refresh sudo zypper install -y curl jq
You’ll also need an API endpoint. Many services (OpenAI, OpenRouter, local servers like Ollama or llama.cpp HTTP servers) expose an OpenAI-compatible API. We’ll keep it generic and let you point to any base URL.
Why better prompts matter (even more in Bash)
Language models are generalists. Without clear instructions, they guess what you want. You control quality by narrowing the task and constraints.
Ambiguity is expensive. Bad prompts waste tokens, time, and attention. Good prompts reduce retries and make outputs parseable.
Bash is great for guardrails. You can require JSON, validate with jq, and fail CI if the model drifts from spec.
Actionable techniques (with real examples)
1) Be explicit: role, goal, constraints, and format
Tell the model who it is, what to do, how to do it, and how to format the answer. Avoid open-ended phrasing.
Example: asking for a safe file deletion command (dry-run only)
SYSTEM="You are a Linux shell expert. Prefer safety and clarity over cleverness."
USER_PROMPT=$(cat <<'EOF'
Task: Propose a SAFE command to remove *.tmp files under /var/tmp modified >30 days ago.
Constraints:
- Do not execute anything.
- Show a single command line.
- Use portability-friendly tools commonly available on Linux.
- Add brief inline comments as shell comments (#) explaining flags.
Output format:
- First line: the command.
- Following lines: comments only. No prose paragraphs.
EOF
)
What’s happening:
You specified a role (Linux shell expert).
You constrained risk (do not execute).
You constrained style (single command, comment lines only).
You clarified output format.
This converts “please help me clean tmp files” into a task the model can nail.
2) Provide concrete context and delimit it
Give the model the exact data it needs—and tell it to ignore everything outside your delimiters.
Example: summarizing recent syslog lines
DATA="$(sudo tail -n 120 /var/log/syslog 2>/dev/null || dmesg | tail -n 120)"
USER_PROMPT=$(cat <<EOF
Task: Summarize key issues in the logs below for a Linux admin. Focus on repeated errors and actionable next steps.
Data (between triple backticks):
\`\`\`
$DATA
\`\`\`
Constraints:
- Bullet points only.
- No commands unless safely copy/pasteable.
- Include a one-line "Most likely root cause".
EOF
)
Tips:
Limit to the relevant slice of data (e.g., last 120 lines).
Use clear boundaries (triple backticks) so the model knows what to read.
Say what not to do (“No commands unless safely copy/pasteable”).
3) Demand structured output and validate it with jq
If you want machine-usable results, ask for JSON and enforce a schema in prose. Then verify with jq.
Prompt template:
USER_PROMPT=$(cat <<'EOF'
Task: Extract a deployment plan from the description below.
Input:
"""
We need to roll a new container on two hosts, keep zero downtime, and back out on failure...
"""
Output:
- Valid JSON only.
- Keys: ["summary", "steps", "rollback", "risk_level"]
- steps: array of strings, max 8 items.
- risk_level: one of ["low","medium","high"].
No additional prose. Output JSON only.
EOF
)
Then, parse and enforce:
resp="$(curl -sS -X POST "$OPENAI_BASE_URL/chat/completions" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg m "$MODEL" --arg sys "$SYSTEM" --arg u "$USER_PROMPT" '{
model: $m,
temperature: 0.2,
messages: [
{role:"system", content:$sys},
{role:"user", content:$u}
]
}')")"
content="$(echo "$resp" | jq -r '.choices[0].message.content')"
# Validate it's strictly JSON and meets our contract
echo "$content" | jq 'has("summary") and has("steps") and has("rollback") and has("risk_level")' | grep -q true \
&& echo "$content" | jq . \
|| { echo "Model did not follow the JSON contract." >&2; exit 1; }
Why this works:
You eliminate “chatty” explanations.
You get stable machine-readable output.
You fail fast if the model wanders.
4) Control style, determinism, and cost
Lower temperature for reliability:
temperature: 0.0..0.3 # pick 0.2 for most deterministic behaviorKeep prompts short and specific; include only needed context.
Capture token usage to monitor cost and drift:
echo "$resp" | jq '.usage'
# Example output:
# { "prompt_tokens": 423, "completion_tokens": 118, "total_tokens": 541 }
Small changes in phrasing can shift outputs a lot. Use explicit constraints instead of adjectives like “clever” or “concise.” Prefer unambiguous checklists and schemas.
5) Build a tiny Bash harness you can reuse
This wrapper lets you change base URLs and models without editing code. It works with OpenAI and with many self-hosted or proxy services that implement the same API.
#!/usr/bin/env bash
set -euo pipefail
# Configuration via env:
: "${OPENAI_API_KEY:?Set OPENAI_API_KEY}"
OPENAI_BASE_URL="${OPENAI_BASE_URL:-https://api.openai.com/v1}"
MODEL="${MODEL:-gpt-4o-mini}"
TEMPERATURE="${TEMPERATURE:-0.2}"
require() { command -v "$1" >/dev/null || { echo "Missing: $1" >&2; exit 127; }; }
require curl
require jq
SYSTEM_DEFAULT="You are a precise, security-conscious assistant for Linux and Bash. Prefer safe, portable answers."
send_prompt() {
local system="${1:-$SYSTEM_DEFAULT}"
local user="${2:?Missing user prompt}"
local body
body="$(jq -n \
--arg model "$MODEL" \
--arg sys "$system" \
--arg usr "$user" \
--argjson temp "$TEMPERATURE" \
'{model:$model, temperature:$temp, messages:[{role:"system",content:$sys},{role:"user",content:$usr}] }')"
curl -sS -X POST "$OPENAI_BASE_URL/chat/completions" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d "$body"
}
# Example usage:
# resp="$(send_prompt "$SYSTEM_DEFAULT" "$USER_PROMPT")"
# echo "$resp" | jq -r '.choices[0].message.content'
Environment overrides you can set once in your shell profile:
export OPENAI_API_KEY="YOUR_KEY"
export OPENAI_BASE_URL="https://api.openai.com/v1" # or your local/proxy endpoint
export MODEL="gpt-4o-mini" # or any supported model
export TEMPERATURE="0.2"
Real-world prompt examples you can adapt
Safe command synthesis (dry-run deletion) ``` SYSTEM="You are a Linux shell expert. Never produce destructive commands without dry-run or explicit confirmation." USER_PROMPT=$(cat <<'EOF' Task: Propose a dry-run command to show which *.log files under /var/log exceed 50MB. Constraints:
Single command line.
Add shell comments to explain flags.
Do not perform deletion or modification. Output format:
First line: command
Following lines: comments only EOF ) ```
Log triage to JSON for alerting ``` USER_PROMPT=$(cat <<'EOF' Task: From the log snippet below, extract distinct error types with counts and a one-sentence fix hint. Data:
<insert 50–150 lines of logs here>
Output:
JSON only: { "errors": [ { "type": "...", "count": N, "hint": "..." } ] }
Max 5 distinct types. EOF ) ```
Config transformation with strict schema ``` USER_PROMPT=$(cat <<'EOF' Task: Convert the following INI-style settings into JSON with keys: ["service","port","env","features"]. Rules:
"port" is a number.
"env" is one of ["dev","staging","prod"].
"features" is an array of strings. Input:
SERVICE=web
PORT=8080
ENV=staging
FEATURES=auth,metrics
Output:
Valid JSON only.
No comments or extra text. EOF ) ```
Pipe the content through the harness and validate with jq, failing your script if the output isn’t valid JSON.
Common pitfalls (and fixes)
Pitfall: Asking for “the best way” without constraints.
- Fix: State trade-offs, environment assumptions, and the success criteria.
Pitfall: Allowing prose around JSON.
- Fix: “Output JSON only. No additional text.” Then validate with jq.
Pitfall: Overloading the context with entire files.
- Fix: Include only relevant excerpts and tell the model the limits.
Conclusion and next steps
Clear, structured prompts turn AI from a guesser into a reliable collaborator—especially when you wrap them in Bash. Start by:
Defining role, goal, constraints, and format.
Supplying only the data that matters, clearly delimited.
Requiring structured output and validating with jq.
Using a small shell harness to iterate quickly and monitor cost.
Your next step: install curl and jq (if you haven’t), drop the harness into a script, and convert one of your “fuzzy” prompts into a precise, validated workflow today.
Debian/Ubuntu:
sudo apt update && sudo apt install -y curl jqFedora/RHEL/CentOS:
sudo dnf install -y curl jqopenSUSE/SLE:
sudo zypper refresh && sudo zypper install -y curl jq
Have a favorite prompt pattern or a tricky Bash task you want to automate? Try adapting the templates above and validate the output with jq. If it passes your checks, keep it. If not, adjust the constraints and try again.