Build a 100% Local AI Semantic Linker for Obsidian: Auto-Suggest Note Connections with Python + Ollama

Build a 100% Local AI Semantic Linker for Obsidian: Auto-Suggest Note Connections with Python + Ollama

In any serious Personal Knowledge Management (PKM) or Second Brain system, the most powerful element isn't the number of notes you've written — it's the bi-directional linking that weaves them into a living network of ideas, following the Zettelkasten or Map of Content (MOC) philosophy.

But here's the problem: as your Obsidian vault grows to hundreds or even thousands of notes, remembering what you've already written and which old note deserves a new link becomes an enormous cognitive tax (hello, Decision Fatigue). Too often, you just jot down a fresh note and walk away — leaving it orphaned, disconnected, and quietly losing the very connections that make a knowledge base valuable.

This is exactly where Cognitive Automation earns its keep: automating the analytical thinking so your brain can focus on creation instead of bookkeeping. In this article, we'll build a Semantic Linker AI Agent that runs 100% locally using Python and Ollama. It scans your most recently edited note, compares it against every other note in your vault, and recommends the most relevant links — ready for you to accept with a single click.

---

### Why Local AI for Note Relationship Mapping?

Using AI to analyze and suggest links in a personal knowledge base carries considerations that are fundamentally different from generic AI use cases:

1. Data Privacy & Sovereignty: Your Second Brain holds personal reflections, business ideas, meeting notes, and even daily journal entries. Shipping that data to a cloud API is a serious risk. Running Ollama entirely offline on your own machine is the safest option by a wide margin.

2. Preserving the Local-First Note-Taking Ecosystem: Obsidian is designed to run offline and store everything as plain .md files. A Python script running on the same machine keeps your Data Sovereignty fully intact — no external dependencies, no telemetry, no lock-in.

3. Full Control Over Prompting: You can instruct the AI to search only specific note clusters, limit relationship types, or tailor suggestions to your personal writing style. No black-box API constraints.

---

### How the AI Semantic Linker Works

The automated knowledge-linking pipeline breaks down into four clean steps:

1. Scan Files: Discover every note in your Obsidian vault.

2. Identify Target Note: Find the most recently modified file to use as the anchor.

3. LLM Reasoning: Send the target note's content plus the full list of other note titles to Ollama for semantic analysis.

4. Write-Back: Append the AI's link suggestions directly to the end of the target note.

---

### Prerequisites

Make sure your machine has the following ready to go:

* Python 3: For running the file-management and API-calling script.

* Ollama: Your local LLM runtime (download from ollama.com).

* Obsidian: Your daily note-taking and knowledge-management app.

Next, open your terminal and pull the language model we'll use for inference. In this example we'll use gemma2 (9B), which excels at contextual semantic understanding:


ollama pull gemma2

---

### Creating the Python Script

Create a file called obsidian_semantic_linker.py and paste the code below. This script uses only Python's standard library — no external packages to install:


import urllib.request
import json
import os
import glob

# Set your Obsidian vault path (edit to match your machine)
OBSIDIAN_VAULT_PATH = "/home/linn/notes"
LLM_MODEL = "gemma2"  # Swap for llama3 or any other model as needed

def get_all_notes(vault_path):
    """Find all Markdown notes in the vault."""
    search_pattern = os.path.join(vault_path, "**", "*.md")
    all_files = glob.glob(search_pattern, recursive=True)
    notes = {}
    for filepath in all_files:
        filename = os.path.basename(filepath)
        title = os.path.splitext(filename)[0]
        # Skip system or hidden files
        if not title.startswith("."):
            notes[title] = filepath
    return notes

def get_latest_modified_note(notes):
    """Find the most recently created or modified note."""
    if not notes:
        return None
    latest_file = max(notes.values(), key=os.path.getmtime)
    for title, filepath in notes.items():
        if filepath == latest_file:
            return title, filepath
    return None

def read_note_content(filepath):
    with open(filepath, "r", encoding="utf-8") as f:
        return f.read()

def ask_ollama_for_links(note_title, note_content, all_titles, model=LLM_MODEL):
    """Send data to Ollama for semantic link suggestions."""
    # Build the candidate list, excluding the target note to avoid self-referencing
    titles_list_str = "\n".join([f"- {t}" for t in all_titles if t != note_title])

    prompt = f"""
You are an AI Agent specializing in Personal Knowledge Management (PKM) and Second Brain systems.
Your task is to analyze semantic relationships between the current note and other notes in the vault.

Current note (just written):
Title: {note_title}
Content:
{note_content}

---
All other note titles in the vault:
{titles_list_str}

---
Instructions:
1. Select 3-5 notes from the list above that are most semantically related to the current note's content.
2. Provide a brief reason (one sentence) for why each note is related and how they should be linked.
3. Format the output as a Markdown list using Obsidian-style bi-directional links, e.g. `[[Note Title]] - reason`
4. Respond concisely and directly. No preamble, no filler.
"""

    url = "http://localhost:11434/api/generate"
    payload = {
        "model": model,
        "prompt": prompt,
        "stream": False
    }

    req = urllib.request.Request(
        url,
        data=json.dumps(payload).encode("utf-8"),
        headers={"Content-Type": "application/json"}
    )

    try:
        with urllib.request.urlopen(req) as response:
            res_data = json.loads(response.read().decode("utf-8"))
            return res_data.get("response", "").strip()
    except Exception as e:
        return f"Error connecting to Ollama: {str(e)}"

def append_suggestions(filepath, suggestions):
    """Append link suggestions to the end of the target note."""
    if not suggestions or "Error connecting" in suggestions:
        return

    with open(filepath, "r", encoding="utf-8") as f:
        content = f.read()

    # Prevent duplicate runs on the same note
    if "## 🔗 Related Notes (AI Suggested)" in content:
        print("This note already has AI-suggested links.")
        return

    with open(filepath, "a", encoding="utf-8") as f:
        f.write("\n\n---\n## 🔗 Related Notes (AI Suggested)\n")
        f.write(f"{suggestions}\n")
        f.write("\n*Auto-suggested links via Local AI Agent*")
    print(f"Link suggestions appended to '{os.path.basename(filepath)}' ✓")

def main():
    if not os.path.exists(OBSIDIAN_VAULT_PATH):
        print(f"Vault path not found: {OBSIDIAN_VAULT_PATH}")
        return

    print("Scanning vault for all notes...")
    notes = get_all_notes(OBSIDIAN_VAULT_PATH)
    if not notes:
        print("No .md notes found.")
        return

    latest = get_latest_modified_note(notes)
    if not latest:
        return

    note_title, filepath = latest
    print(f"Most recently modified note: {note_title}")

    content = read_note_content(filepath)
    all_titles = list(notes.keys())

    print("Analyzing semantic relationships via Ollama...")
    suggestions = ask_ollama_for_links(note_title, content, all_titles)

    print("\nSuggested links:")
    print(suggestions)
    print("\nUpdating note file...")
    append_suggestions(filepath, suggestions)

if __name__ == "__main__":
    main()

---

### Level It Up with an Obsidian Plugin

To make the automation feel truly seamless — no need to leave Obsidian or hunt for a terminal — you can wire this script directly into your workflow:

1. Install the community plugin "Shell Commands" in Obsidian.

2. Create a new command that runs the Python script:

```bash

python /path/to/obsidian_semantic_linker.py

```

3. Set up an Event Trigger (e.g., "on note close") or assign a dedicated Hotkey so you can fire off a semantic link suggestion with a single keystroke the moment you finish writing.

---

### The Bottom Line

Building an AI agent that proactively suggests how your ideas connect transforms your Second Brain from a collection of isolated notes into a living, interlinked organism. The larger your vault grows, the more value the AI adds — surfacing connections you might never have spotted on your own.

Choosing a Local-first architecture isn't just about protecting your thoughts from prying eyes. It's about owning your data pipeline end-to-end, with 100% control over what gets processed, where, and how. Grab this script, point it at your vault, and start watching your knowledge graph grow on its own.

🛒 TREND DEE STORE • ร้านเทรนด์ดี

🛠️ Tools for Frictionless PKM & Second Brain

Curated hardware and tools tailored for this guide