Initial commit: local SQL expert AI (Ollama + CLI + prompts + systemd timer)

This commit is contained in:
2026-01-27 21:30:15 +01:00
commit 394edc1709
18 changed files with 574 additions and 0 deletions

11
.env.example Normal file
View File

@@ -0,0 +1,11 @@
# Base model to pull and use for the expert model
BASE_MODEL=qwen2.5-coder:14b
# Optional: space-separated list of extra models to pull
EXTRA_MODELS=sqlcoder
# Name of your custom expert model (built from Modelfile)
EXPERT_MODEL=jr-sql-expert
# Ollama API endpoint (host network)
OLLAMA_URL=http://127.0.0.1:11434

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
logs/*.log
.env
.DS_Store
*.swp

31
Modelfile Normal file
View File

@@ -0,0 +1,31 @@
# NOTE: scripts will replace ${BASE_MODEL} with your .env BASE_MODEL.
FROM ${BASE_MODEL}
SYSTEM """
Du bist ein sehr erfahrener Microsoft SQL Server 2022 Engineer (T-SQL, Query Optimizer, Indexing, Execution Plans, Stored Procedures, Views, Schema-Design, Collations/UTF-8).
Du arbeitest offline und machst KEINE Annahmen über reale Daten. Keine Verbindungen, keine Ausführung nur Analyse.
Allgemeine Regeln:
- Antworte präzise, technisch, ohne Marketing.
- Wenn Informationen fehlen: liste exakt auf, was fehlt, und gib trotzdem best-effort Analyse.
- Wenn du Snippets vorschlägst: immer konkret (T-SQL/DDL) und kommentiert.
- Wenn du mehrere Optionen gibst: nenne Vor-/Nachteile und wann welche Option sinnvoll ist.
Für Query Plans:
- Erkläre Hotspots (Top Operatoren), Kardinalitätsschätzungen, Warnungen, Spills, SARGability,
fehlende Indizes (vorsichtig!), Join-Strategien, Parameter Sniffing, Stats, Memory Grants.
Für UTF-8 Migration:
- Erkläre Vorgehen über UTF-8-enabled Collations (_UTF8).
- Risiken: Vergleiche/Sortierung, Indexgrößen, Abhängigkeiten (FK/Index/Computed/Triggers),
Teststrategie, Cutover, Rollback.
Antwort-Struktur (immer):
1) Kurzfazit (36 Bulletpoints)
2) Detailanalyse (mit konkreten Snippets)
3) Risiken & Checks (Checkliste)
4) Nächste Schritte
"""
PARAMETER temperature 0.1
PARAMETER top_p 0.9

84
README.md Normal file
View File

@@ -0,0 +1,84 @@
# jr-sql-ai (Terminal-first SQL Server Expert KI, lokal)
Ziel: Lokale Expert-KI für **SQL Server 2022** (T-SQL, Views, Stored Procedures, Execution Plans, UTF-8 Migration),
aufrufbar **vom Terminal**, ohne direkte DB-Verbindung (nur Copy & Paste / Dateien).
## Features
- **Einfacher Tech-Stack:** Docker + Ollama + Bash + curl + python
- **Host Networking:** nutzt den Host-Netzwerkstack (Routing/DNS wie Host; ideal wenn nur `br0` zuverlässig ist)
- **Auto-Updates:** Runtime + Models via `systemd --user` Timer
- **Viele Logs:** jede Ausführung schreibt detaillierte Logs unter `./logs/`
## Voraussetzungen (Arch Linux)
- docker + docker compose
- curl
- python (für JSON-Quoting/Parsing)
## Quickstart
```bash
cp .env.example .env
./scripts/bootstrap.sh
echo "SELECT 1;" | ./bin/sqlai analyze-tsql
```
## Verwendung (Copy & Paste)
### T-SQL
```bash
cat query.sql | ./bin/sqlai analyze-tsql
```
### Stored Procedure
```bash
cat dbo.usp_Something.sql | ./bin/sqlai analyze-proc
```
### View
```bash
cat dbo.vw_Something.sql | ./bin/sqlai analyze-view
```
### Execution Plan
- Unterstützt Showplan XML oder Text.
```bash
./bin/sqlai analyze-plan --file showplan.xml
```
### UTF-8 Migration Plan
```bash
cat schema_snippet.sql | ./bin/sqlai utf8-migration
```
## Prompt Library (Templates)
Unter `./prompts/` findest du Copy&Paste-Templates:
- `prompts/tsql_review.md`
- `prompts/plan_review.md`
- `prompts/utf8_migration_runbook.md`
- `prompts/indexing_checklist.md`
- `prompts/sniffing_stats_playbook.md`
- `prompts/proc_refactor_template.md`
- `prompts/view_analysis_template.md`
## Logs
- CLI Logs: `./logs/sqlai-YYYY-MM-DD.log`
- Bootstrap Logs: `./logs/bootstrap-YYYY-MM-DDTHH:MM:SS.log`
- Update Logs: `./logs/update-YYYY-MM-DDTHH:MM:SS.log`
Die Logs enthalten:
- Input-Bytes, Mode, Model
- Roh-Metriken aus Ollama (Token Counts, Durations)
- Fehler inkl. curl exit codes
## Auto-Updates aktivieren (systemd --user)
```bash
mkdir -p ~/.config/systemd/user
cp systemd/user/* ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now jr-sql-ai-update.timer
systemctl --user list-timers | grep jr-sql-ai
```
## Troubleshooting
- **API nicht erreichbar**: Prüfe `docker ps` und ob Port 11434 lokal erreichbar ist (Host-Netz).
- **Langsam/OOM**: setze `BASE_MODEL` kleiner (z.B. 7b) und re-run `./scripts/update.sh`.
- **Model fehlt**: `./scripts/update.sh` ausführen und prüfen ob `ollama list` im Container das Model zeigt:
`docker exec -it ollama ollama list`

130
bin/sqlai Executable file
View File

@@ -0,0 +1,130 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ENV_FILE="${ROOT}/.env"
[[ -f "$ENV_FILE" ]] && source "$ENV_FILE"
: "${OLLAMA_URL:=http://127.0.0.1:11434}"
: "${EXPERT_MODEL:=jr-sql-expert}"
ts() { date -Is; }
log_dir="${ROOT}/logs"
mkdir -p "$log_dir"
log_file="${log_dir}/sqlai-$(date -I).log"
# log everything (stdout+stderr)
exec > >(tee -a "$log_file") 2>&1
usage() {
cat <<EOF
Usage:
sqlai <mode> [--file path]
Modes:
analyze-tsql
analyze-proc
analyze-view
analyze-plan
utf8-migration
Examples:
cat query.sql | sqlai analyze-tsql
sqlai analyze-plan --file showplan.xml
EOF
}
mode="${1:-}"
[[ -z "$mode" ]] && { usage; exit 1; }
shift || true
file=""
while [[ $# -gt 0 ]]; do
case "$1" in
--file) file="$2"; shift 2;;
*) echo "[$(ts)] ERROR: Unknown arg: $1"; usage; exit 2;;
esac
done
input=""
if [[ -n "$file" ]]; then
if [[ ! -f "$file" ]]; then
echo "[$(ts)] ERROR: file not found: $file"
exit 3
fi
input="$(cat "$file")"
else
input="$(cat)"
fi
if [[ -z "${input//[[:space:]]/}" ]]; then
echo "[$(ts)] ERROR: empty input"
exit 4
fi
case "$mode" in
analyze-tsql)
instruction="Analysiere das folgende T-SQL (SQL Server 2022). Finde Performance-Probleme, SARGability, Indizes, Statistiken, Parameter Sniffing Risiken und gib konkrete Verbesserungen."
;;
analyze-proc)
instruction="Analysiere die folgende Stored Procedure (SQL Server 2022). Finde Performance-/Correctness-Risiken, Transaktions-/Locking-Themen, Parameter Sniffing, fehlende Indizes. Gib konkrete Refactorings."
;;
analyze-view)
instruction="Analysiere die folgende View (SQL Server 2022). Prüfe SARGability, Expand/Inlining-Effekte, mögliche Indexing-Optionen (z.B. indexed view falls sinnvoll) und Plan-Auswirkungen."
;;
analyze-plan)
instruction="Analysiere den folgenden SQL Server Execution Plan (XML Showplan oder Text). Identifiziere teure Operatoren, Spills, Warnungen, Kardinalitätsfehler, fehlende Indizes und gib konkrete Fixes."
;;
utf8-migration)
instruction="Erstelle einen Migrationsplan, um Tabellen/Spalten auf UTF-8 umzustellen (UTF-8 enabled collations mit _UTF8). Berücksichtige Abhängigkeiten (FK/PK/Indexes/Computed/Triggers), Risiken und gib eine Schritt-für-Schritt Checkliste."
;;
*)
echo "[$(ts)] ERROR: unknown mode: $mode"
usage
exit 5
;;
esac
prompt=$(cat <<EOF
${instruction}
---BEGIN INPUT---
${input}
---END INPUT---
EOF
)
echo "[$(ts)] sqlai: MODE=$mode MODEL=$EXPERT_MODEL OLLAMA_URL=$OLLAMA_URL"
echo "[$(ts)] sqlai: INPUT_BYTES=$(printf "%s" "$input" | wc -c)"
payload="$(python - <<'PY'
import json, os, sys
model=os.environ.get("EXPERT_MODEL","jr-sql-expert")
prompt=sys.stdin.read()
print(json.dumps({"model": model, "prompt": prompt, "stream": False}, ensure_ascii=False))
PY
<<<"$prompt")"
echo "[$(ts)] sqlai: sending request..."
resp="$(curl -sS -X POST "${OLLAMA_URL}/api/generate" -H 'Content-Type: application/json' --data-binary "$payload")" || {
rc=$?
echo "[$(ts)] sqlai: ERROR: curl failed rc=$rc"
exit 10
}
python - <<'PY'
import json,sys
obj=json.loads(sys.stdin.read())
print("\n" + (obj.get("response","").rstrip()) + "\n")
md = {
"total_duration": obj.get("total_duration"),
"load_duration": obj.get("load_duration"),
"prompt_eval_count": obj.get("prompt_eval_count"),
"prompt_eval_duration": obj.get("prompt_eval_duration"),
"eval_count": obj.get("eval_count"),
"eval_duration": obj.get("eval_duration"),
}
print("METRICS=" + json.dumps(md, ensure_ascii=False))
PY <<<"$resp"
echo "[$(ts)] sqlai: done"

18
docker-compose.yml Normal file
View File

@@ -0,0 +1,18 @@
services:
ollama:
image: ollama/ollama:latest
container_name: ollama
restart: unless-stopped
# Uses the host network stack (routing/DNS identical to the host, incl. br0)
network_mode: "host"
volumes:
- ollama:/root/.ollama
environment:
# Keep model in memory a bit (optional)
OLLAMA_KEEP_ALIVE: "10m"
volumes:
ollama:

0
logs/.gitkeep Normal file
View File

View File

@@ -0,0 +1,25 @@
# Indexing Checklist (SQL Server 2022)
## Vor dem Index
- Welche Query/Workload? (Top N Abfragen)
- Welche Filter/Join-Spalten?
- Sort/Group By/Distinct? (ORDER BY, GROUP BY)
- Häufige Lookups? (Key Lookup / RID Lookup)
- Schreiblast hoch? (Index-Overhead)
## Plausible Index-Regeln
- Key Columns: erst Equality predicates, dann Range, dann Join keys
- Include Columns: nur für benötigte SELECT-Spalten (Covering)
- Filtered Index: wenn Predicate stabil/selektiv (z.B. Status='Active')
- Datentypen sauber (keine impliziten Konvertierungen)
- Stats: nach großen Loads/Änderungen aktualisieren (gezielt)
## Validierung
- Plan vorher/nachher
- Reads/CPU/Duration
- Index usage (DMVs) nur als Hinweis, nicht alleinige Wahrheit
- Regression Tests / Parameter Sets
---BEGIN INPUT---
<PASTE HERE>
---END INPUT---

27
prompts/plan_review.md Normal file
View File

@@ -0,0 +1,27 @@
# Execution Plan Review Template (Showplan XML/Text)
## Kontext (optional)
- Query text (falls verfügbar)
- Parameterwerte bei Capture
- CPU/Duration/Reads/Rowcount bei Capture
- Server settings: MAXDOP, Cost Threshold for Parallelism (falls bekannt)
## Aufgabe
1) Kurzfazit
2) Hotspots:
- Top Operatoren nach Kosten
- Spills (Sort/Hash), Warnings
- Memory Grant: zu hoch/zu niedrig
- Parallelism (CXPACKET/CXCONSUMER Kontext)
3) Kardinalität:
- wo weicht Estimated vs Actual ab? (wenn Actual vorhanden)
- Stats/Histogramm Hinweise
4) Fixes:
- Query Rewrite
- Index/Stats Empfehlungen
- Parameter Sniffing Mitigation
5) Checkliste für Validierung
---BEGIN PLAN---
<PASTE HERE>
---END PLAN---

View File

@@ -0,0 +1,22 @@
# Stored Procedure Review / Refactor Template
## Kontext (optional)
- Aufrufmuster (Parameter, typische Werte):
- Isolation Level / Transactions:
- Deadlocks/Blocking bekannt? (ja/nein + Hinweise)
- Ziel: Latenz, Throughput, Stabilität, Plan-Stabilität
## Aufgabe
1) Kurzfazit
2) Detailanalyse
- Parameter Sniffing
- Temp tables vs table variables
- Row-by-row (Cursor, WHILE), UDFs, RBAR
- TRY/CATCH + Fehlerbehandlung
- Transaktionsumfang (zu groß?), Lock Escalation, Timeouts
3) Konkrete Refactorings (mit T-SQL)
4) Risiken/Checks/Next steps
---BEGIN PROC---
<PASTE HERE>
---END PROC---

View File

@@ -0,0 +1,30 @@
# Parameter Sniffing & Statistics Playbook
## Symptome
- Laufzeit stark schwankend je nach Parameter
- Plan ist "gut" für einen Wert, "schlecht" für andere
- Große Estimated vs Actual Abweichungen
- Memory grants/spills je nach Parameter
## Diagnose (ohne DB-Zugriff: theoretisch/plan-basiert)
- Welche Prädikate sind parameterabhängig?
- Gibt es Skew (ein Wert sehr häufig/selten)?
- OR-Logik / optional filters (@p IS NULL OR col=@p)?
- Implizite Konvertierungen?
## Gegenmaßnahmen (geordnet von "leicht" zu "hart")
1) Query rewrite (SARGability, Splitting optional filters)
2) OPTION(RECOMPILE) selektiv
3) OPTIMIZE FOR (oder OPTIMIZE FOR UNKNOWN) bewusst
4) Zwei Queries / IF branches für stark unterschiedliche Pfade
5) Plan guides / forced plan (nur in Ausnahmefällen)
6) Stats: update + ggf. filtered stats (wenn passend)
## Checkliste
- Welche Parameter-Sets testen (min/max/typisch)
- Regression: CPU/Reads/Duration
- Concurrency/Blocking beachten
---BEGIN INPUT---
<PASTE HERE>
---END INPUT---

29
prompts/tsql_review.md Normal file
View File

@@ -0,0 +1,29 @@
# T-SQL Review Template (Copy & Paste)
> Zweck: schnelle, präzise Analyse einer Abfrage.
> Eingabe: T-SQL + (optional) Parameterwerte + (optional) Tabelleninfos.
## Kontext (optional)
- SQL Server Version: 2022
- DB Kompatibilitätslevel: ?
- Tabellenvolumen grob (Rows): ?
- Häufigkeit/Latency Ziel: ?
- Parameterwerte (typisch & worst-case): ?
## Aufgabe
Analysiere das T-SQL und liefere:
1) **Kurzfazit** (36 Bulletpoints)
2) **Detailanalyse**:
- SARGability (LIKE, Funktionen auf Spalten, CAST/CONVERT, Datentyp-Mismatches)
- Joins/Predicates (Reihenfolge, Join Typen)
- Kardinalität/Stats (wo schätzt der Optimizer falsch)
- Parameter Sniffing Risiko + konkrete Mitigation (z.B. OPTION(RECOMPILE) gezielt, OPTIMIZE FOR, plan guides nur wenn nötig)
3) **Konkrete Verbesserungen**:
- rewrite (mit kommentiertem SQL)
- Index-Vorschläge (nur wenn plausibel; include columns; Filtered Index falls geeignet)
4) **Checks**:
- welche Messwerte/Plan-Indikatoren prüfen
---BEGIN T-SQL---
<PASTE HERE>
---END T-SQL---

View File

@@ -0,0 +1,27 @@
# UTF-8 Migration Runbook (SQL Server 2022)
## Ziel
Tabellen/Spalten auf UTF-8 umstellen durch Verwendung von UTF-8-enabled Collations (`*_UTF8`).
## Input (paste)
- Aktuelles Schema (CREATE TABLE oder relevante Spalten)
- Aktuelle Collations
- Abhängigkeiten (FKs, Indexes, Computed Columns, Triggers)
- App assumptions (Sortierung, Vergleiche, Case sensitivity)
---BEGIN INPUT---
<PASTE HERE>
---END INPUT---
## Erwartete Ausgabe
1) Kurzfazit
2) Schritt-für-Schritt Plan (staging -> validate -> cutover -> rollback)
3) DDL Snippets:
- neue Collation setzen
- Rebuild Indizes/Constraints
4) Risiken:
- Sort-/Compare-Verhalten kann sich ändern
- mögliche Indexgrößenänderung
- computed columns / persisted computed columns
5) Teststrategie:
- Vergleichssuites, Known tricky strings, roundtrip tests

View File

@@ -0,0 +1,23 @@
# View Analysis Template
## Kontext (optional)
- Wird die View häufig gejoint/weitergefiltert?
- Erwartete Selectivity / typische Filter?
- Indexed view überhaupt erlaubt/gewünscht? (Schema binding etc.)
## Aufgabe
1) Kurzfazit
2) Detailanalyse:
- Expand/Inlining Effekte
- SARGability und Pushdown von Predicates
- Aggregationen/Distinct/Union
- Risiken bei Scalar UDFs / Non-deterministic Funktionen
3) Verbesserungen:
- alternative Definition
- Hinweise zu Indizes auf Basistabellen
- falls relevant: indexed view Voraussetzungen (nur als Option)
4) Risiken/Checks
---BEGIN VIEW---
<PASTE HERE>
---END VIEW---

49
scripts/bootstrap.sh Executable file
View File

@@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cp -n "${ROOT}/.env.example" "${ROOT}/.env" || true
source "${ROOT}/.env"
ts(){ date -Is; }
log_dir="${ROOT}/logs"
mkdir -p "$log_dir"
log_file="${log_dir}/bootstrap-$(date -Iseconds).log"
exec > >(tee -a "$log_file") 2>&1
echo "[$(ts)] bootstrap: starting (ROOT=$ROOT)"
echo "[$(ts)] bootstrap: docker compose up -d"
docker compose -f "${ROOT}/docker-compose.yml" up -d
echo "[$(ts)] bootstrap: waiting for Ollama API at ${OLLAMA_URL} ..."
for i in {1..90}; do
if curl -sS "${OLLAMA_URL}/api/tags" >/dev/null 2>&1; then
echo "[$(ts)] bootstrap: Ollama API is up."
break
fi
if [[ $i -eq 90 ]]; then
echo "[$(ts)] bootstrap: ERROR: API did not come up in time."
exit 1
fi
sleep 1
done
echo "[$(ts)] bootstrap: pulling base model: ${BASE_MODEL}"
docker exec -it ollama ollama pull "${BASE_MODEL}"
if [[ -n "${EXTRA_MODELS:-}" ]]; then
for m in ${EXTRA_MODELS}; do
echo "[$(ts)] bootstrap: pulling extra model: $m"
docker exec -it ollama ollama pull "$m"
done
fi
echo "[$(ts)] bootstrap: building expert model: ${EXPERT_MODEL}"
tmp="$(mktemp)"
sed "s/\${BASE_MODEL}/${BASE_MODEL}/g" "${ROOT}/Modelfile" > "$tmp"
docker exec -i ollama ollama create "${EXPERT_MODEL}" -f - < "$tmp"
rm -f "$tmp"
echo "[$(ts)] bootstrap: done"
echo "[$(ts)] bootstrap: test:"
echo " echo "SELECT 1;" | ${ROOT}/bin/sqlai analyze-tsql"

48
scripts/update.sh Executable file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
source "${ROOT}/.env"
ts(){ date -Is; }
log_dir="${ROOT}/logs"
mkdir -p "$log_dir"
log_file="${log_dir}/update-$(date -Iseconds).log"
exec > >(tee -a "$log_file") 2>&1
echo "[$(ts)] update: starting (ROOT=$ROOT)"
echo "[$(ts)] update: pulling docker image(s)"
docker compose -f "${ROOT}/docker-compose.yml" pull
echo "[$(ts)] update: restarting services"
docker compose -f "${ROOT}/docker-compose.yml" up -d
echo "[$(ts)] update: waiting for Ollama API at ${OLLAMA_URL} ..."
for i in {1..90}; do
if curl -sS "${OLLAMA_URL}/api/tags" >/dev/null 2>&1; then
echo "[$(ts)] update: Ollama API is up."
break
fi
if [[ $i -eq 90 ]]; then
echo "[$(ts)] update: ERROR: API did not come up in time."
exit 1
fi
sleep 1
done
echo "[$(ts)] update: pulling base model: ${BASE_MODEL}"
docker exec -it ollama ollama pull "${BASE_MODEL}"
if [[ -n "${EXTRA_MODELS:-}" ]]; then
for m in ${EXTRA_MODELS}; do
echo "[$(ts)] update: pulling extra model: $m"
docker exec -it ollama ollama pull "$m"
done
fi
echo "[$(ts)] update: rebuilding expert model: ${EXPERT_MODEL}"
tmp="$(mktemp)"
sed "s/\${BASE_MODEL}/${BASE_MODEL}/g" "${ROOT}/Modelfile" > "$tmp"
docker exec -i ollama ollama create "${EXPERT_MODEL}" -f - < "$tmp"
rm -f "$tmp"
echo "[$(ts)] update: complete"

View File

@@ -0,0 +1,7 @@
[Unit]
Description=Update Ollama runtime + models (jr-sql-ai)
[Service]
Type=oneshot
WorkingDirectory=%h/jr-sql-ai
ExecStart=%h/jr-sql-ai/scripts/update.sh

View File

@@ -0,0 +1,9 @@
[Unit]
Description=Daily update timer for jr-sql-ai
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target