Posted on
Artificial Intelligence

Creating an Artificial Intelligence Linux Knowledge Base

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

Creating an Artificial Intelligence Linux Knowledge Base (From Your Terminal)

Ever lost 30 minutes hunting for that one command flag you used last quarter? Your knowledge is everywhere—man pages, /usr/share/doc, wikis, tickets, personal notes, and a dozen Git repos. What if your terminal had a local, privacy-preserving “brain” that lets you ask questions in natural language and instantly retrieves the most relevant snippets?

That’s exactly what we’ll build: an AI-powered, grep-like knowledge base for Linux that runs locally, indexes your docs, and answers questions using semantic search (vector embeddings). No cloud dependency, no “vendor lock-in,” just Bash and a small Python toolchain.

Why this matters

  • Reduce context switching: Stop paging through browser tabs and docs.

  • Stay private: Your data never leaves your machine.

  • Make tacit knowledge discoverable: Surface runbooks, readmes, and tribal know-how.

  • Works with what you already have: man pages, Markdown, logs, and docs.

We’ll use retrieval-augmented techniques: convert your content to text, embed it with a small open-source model, and search using a vector index—all locally.


What you’ll build

  • A content pipeline to gather docs from man pages, /usr/share/doc, wikis/READMEs.

  • A Python-based embedding + FAISS index.

  • A tiny CLI (kb) to index and query: kb index and kb query "how do I...".

  • Optional automation to keep the index fresh with systemd timers.


Prerequisites: Install system packages

We’ll need Python, Git, ripgrep, pandoc, and build tools.

Debian/Ubuntu (apt):

sudo apt update
sudo apt install -y \
  python3 python3-venv python3-pip \
  git ripgrep pandoc \
  build-essential jq curl

Fedora/RHEL (dnf):

sudo dnf install -y \
  python3 python3-pip python3-virtualenv \
  git ripgrep pandoc \
  gcc gcc-c++ make jq curl

openSUSE (zypper):

sudo zypper refresh
sudo zypper install -y \
  python3 python3-pip python3-virtualenv \
  git ripgrep pandoc \
  gcc gcc-c++ make jq curl

Step 1: Gather and normalize your sources

Create a workspace:

mkdir -p ~/kb/{raw,txt,index,bin}

1) Export man pages to plain text:

# This can take a few minutes; trims formatting with 'col -b'
man -k . | awk -F ' - ' '{print $1}' | awk '{print $1}' | sort -u | \
while read -r topic; do
  man "$topic" | col -b > "$HOME/kb/txt/man_${topic}.txt" 2>/dev/null || true
done

2) Convert docs from /usr/share/doc:

find /usr/share/doc -type f \( -name '*.gz' -o -name '*.txt' -o -name '*.md' -o -name '*.rst' \) -print0 |
while IFS= read -r -d '' f; do
  base="$(basename "${f%.gz}")"
  if [[ "$f" == *.gz ]]; then
    zcat "$f" 2>/dev/null | col -b > "$HOME/kb/txt/doc_${base}.txt" || true
  else
    cat "$f" | col -b > "$HOME/kb/txt/doc_${base}.txt"
  fi
done

3) Convert Markdown/HTML/PDF from your repos or wiki exports with pandoc:

# Example: Convert a project README to plain text
pandoc -f markdown -t plain -o "$HOME/kb/txt/repo_readme.txt" /path/to/repo/README.md

# HTML export (e.g., from Confluence or a wiki dump)
pandoc -f html -t plain -o "$HOME/kb/txt/wiki_export.txt" ~/Downloads/wiki_export.html

4) Optionally, add your notes and runbooks:

# Copy all .md and .txt from a notes directory
find ~/notes -type f \( -name '*.md' -o -name '*.txt' \) -print0 |
xargs -0 -I{} sh -c 'pandoc -f markdown -t plain -o "$HOME/kb/txt/notes_$(basename "{}").txt" "{}"'

Tip: If you have secrets in your notes, keep a separate, sanitized directory for indexing.


Step 2: Create a Python virtual environment and install libs

We’ll use a small Sentence-Transformers model and FAISS (CPU).

python3 -m venv ~/kb/venv
source ~/kb/venv/bin/activate
python -m pip install --upgrade pip
pip install "sentence-transformers==2.*" faiss-cpu tqdm

If you ever need to leave the venv:

deactivate

Step 3: Build the index (embeddings + FAISS)

Save this as ~/kb/bin/build_index.py:

#!/usr/bin/env python3
import argparse, json, os, sys
from pathlib import Path
from tqdm import tqdm

# Embeddings
from sentence_transformers import SentenceTransformer
import numpy as np

# FAISS
import faiss  # type: ignore

def iter_text_files(root):
    root = Path(root)
    for p in root.rglob("*.txt"):
        if p.is_file():
            yield p

def chunk_text(text, max_words=500, overlap=50):
    words = text.split()
    chunks = []
    i = 0
    n = len(words)
    while i < n:
        j = min(i + max_words, n)
        chunk = " ".join(words[i:j]).strip()
        if chunk:
            chunks.append(chunk)
        i = j - overlap if (j - overlap) > i else j
    return chunks

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--txt-dir", default=str(Path.home()/ "kb" / "txt"))
    ap.add_argument("--out-dir", default=str(Path.home()/ "kb" / "index"))
    ap.add_argument("--model", default="sentence-transformers/all-MiniLM-L6-v2")
    ap.add_argument("--max-words", type=int, default=500)
    ap.add_argument("--overlap", type=int, default=50)
    args = ap.parse_args()

    txt_dir = Path(args.txt_dir)
    out_dir = Path(args.out_dir)
    out_dir.mkdir(parents=True, exist_ok=True)

    print(f"[+] Loading model: {args.model}")
    model = SentenceTransformer(args.model)

    texts = []
    metas = []
    for path in tqdm(list(iter_text_files(txt_dir)), desc="Collecting"):
        try:
            content = Path(path).read_text(errors="ignore")
        except Exception:
            continue
        chunks = chunk_text(content, max_words=args.max_words, overlap=args.overlap)
        for idx, ch in enumerate(chunks):
            texts.append(ch)
            metas.append({"source": str(path), "chunk": idx})

    if not texts:
        print("[-] No text found to index.", file=sys.stderr)
        sys.exit(1)

    print(f"[+] Encoding {len(texts)} chunks...")
    embs = model.encode(texts, show_progress_bar=True, normalize_embeddings=True)
    embs = np.asarray(embs).astype("float32")

    dim = embs.shape[1]
    index = faiss.IndexFlatIP(dim)  # cosine similarity via normalized vectors
    index.add(embs)

    faiss.write_index(index, str(out_dir / "kb.faiss"))
    with open(out_dir / "meta.jsonl", "w") as f:
        for m in metas:
            f.write(json.dumps(m) + "\n")
    with open(out_dir / "model.txt", "w") as f:
        f.write(args.model + "\n")

    print(f"[+] Wrote {len(texts)} vectors to {out_dir/'kb.faiss'}")
    print(f"[+] Metadata saved to {out_dir/'meta.jsonl'}")

if __name__ == "__main__":
    main()

Make it executable:

chmod +x ~/kb/bin/build_index.py

Build the index:

source ~/kb/venv/bin/activate
~/kb/bin/build_index.py --txt-dir ~/kb/txt --out-dir ~/kb/index

Step 4: Query the knowledge base from Bash

Create ~/kb/bin/query_kb.py:

#!/usr/bin/env python3
import argparse, json
from pathlib import Path
import numpy as np
from sentence_transformers import SentenceTransformer
import faiss  # type: ignore

def load_meta(meta_path):
    metas = []
    with open(meta_path) as f:
        for line in f:
            metas.append(json.loads(line))
    return metas

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("query", help="question or keywords")
    ap.add_argument("-k", "--top-k", type=int, default=5)
    ap.add_argument("--index-dir", default=str(Path.home()/ "kb" / "index"))
    args = ap.parse_args()

    index_dir = Path(args.index_dir)
    index = faiss.read_index(str(index_dir / "kb.faiss"))
    metas = load_meta(index_dir / "meta.jsonl")
    model_name = (index_dir / "model.txt").read_text().strip()
    model = SentenceTransformer(model_name)

    q = model.encode([args.query], normalize_embeddings=True)
    D, I = index.search(np.asarray(q).astype("float32"), args.top_k)

    for rank, (score, idx) in enumerate(zip(D[0], I[0]), start=1):
        meta = metas[idx]
        src = meta.get("source", "unknown")
        chunk_id = meta.get("chunk", 0)
        # Read a small preview
        try:
            with open(src, "r", errors="ignore") as f:
                lines = f.read().splitlines()
            preview = " ".join(lines[max(0, chunk_id*30): (chunk_id*30)+20])[:300]
        except Exception:
            preview = "(preview not available)"
        print(f"{rank}. {src}  [score={score:.3f}]")
        print(f"   {preview}\n")

if __name__ == "__main__":
    main()

Make it executable:

chmod +x ~/kb/bin/query_kb.py

Test a query:

source ~/kb/venv/bin/activate
~/kb/bin/query_kb.py "how to add a user to a group" -k 5

Step 5: A friendly CLI wrapper

Make a simple kb script so you don’t have to remember Python paths. Save as ~/kb/bin/kb:

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

KB_DIR="${KB_DIR:-$HOME/kb}"
VENV="$KB_DIR/venv"
TXT="$KB_DIR/txt"
IDX="$KB_DIR/index"
BIN="$KB_DIR/bin"

usage() {
  cat <<EOF
Usage:
  kb index              # (re)build the index from \$HOME/kb/txt
  kb query "question"   # search the knowledge base
  kb help               # show this help
EOF
}

ensure_venv() {
  if [[ ! -d "$VENV" ]]; then
    python3 -m venv "$VENV"
    source "$VENV/bin/activate"
    python -m pip install --upgrade pip
    pip install "sentence-transformers==2.*" faiss-cpu tqdm
  else
    source "$VENV/bin/activate"
  fi
}

cmd="${1:-help}"
shift || true

case "$cmd" in
  index)
    ensure_venv
    "$BIN/build_index.py" --txt-dir "$TXT" --out-dir "$IDX"
    ;;
  query)
    ensure_venv
    if [[ $# -lt 1 ]]; then
      echo "Need a query string."
      exit 1
    fi
    "$BIN/query_kb.py" "$@"
    ;;
  help|--help|-h)
    usage
    ;;
  *)
    echo "Unknown command: $cmd"
    usage
    exit 1
    ;;
esac

Make it executable and add to PATH:

chmod +x ~/kb/bin/kb
echo 'export PATH="$HOME/kb/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

Now try:

kb query "find and replace text with sed"
kb query "systemd timer example"
kb index   # after you add new docs

Keep it fresh: automate re-indexing

Use a user-level systemd timer to rebuild the index daily.

Create ~/.config/systemd/user/kb-index.service:

[Unit]
Description=Rebuild local AI KB index

[Service]
Type=oneshot
Environment=KB_DIR=%h/kb
ExecStart=%h/kb/bin/kb index

Create ~/.config/systemd/user/kb-index.timer:

[Unit]
Description=Rebuild AI KB daily

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

Enable the timer:

systemctl --user daemon-reload
systemctl --user enable --now kb-index.timer
systemctl --user list-timers | grep kb-index

Real-world examples

  • SRE runbooks: Index on-call guides, incident postmortems, and dashboards docs. Query “rotate TLS cert on service X” and jump to the exact steps.

  • Platform engineering: Index Helm chart READMEs and Kubernetes manifests. Ask “ingress annotations for sticky sessions.”

  • Security ops: Index hardening guides and CIS benchmarks. Query “auditd rules for file integrity.”


Tips, extensions, and safety

  • Source selection: Keep public and private corpora separate if needed.

  • Sensitive data: Exclude secrets with find/grep rules or maintain a sanitized copy.

  • Speed: The default MiniLM model is fast on CPU. For larger corpora, consider a stronger model or a vector DB daemon (Qdrant, Milvus) later.

  • Quality: Chunk size matters. Too big and results get fuzzy; too small and context is lost. Start with 300–600 words.

  • Shell ergonomics: Pipe results into fzf or open files in $EDITOR:

kb query "iptables allow outbound https" | fzf

Conclusion and next steps (CTA)

You now have a local, AI-powered knowledge base that turns your Linux box into a searchable brain. Start small: index man pages and /usr/share/doc, then grow with runbooks, READMEs, and wiki exports. Make it part of your workflow:

  • Run kb index weekly (or enable the systemd timer).

  • Add new sources to ~/kb/txt.

  • Use kb query "..." whenever you reach for a browser.

Want to go further? Add a lightweight answer generator on top (e.g., call a local LLM via llama.cpp) to summarize the retrieved snippets. But even as-is, you’ll save time, reduce context-switching, and keep your know-how on your own machine.

If this helped, share it with your team and turn your collective docs into a first-class, terminal-native knowledge base.