quarto/dist/Writing/Obsidian-RAG.html
Nicole Dresselhaus ce0c52a66a initial
2025-05-09 21:47:18 +02:00

1604 lines
120 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="de" xml:lang="de"><head>
<meta charset="utf-8">
<meta name="generator" content="quarto-1.7.23">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze Nicole Dresselhaus</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */
vertical-align: middle;
}
/* CSS for citations */
div.csl-bib-body { }
div.csl-entry {
clear: both;
margin-bottom: 0em;
}
.hanging-indent div.csl-entry {
margin-left:2em;
text-indent:-2em;
}
div.csl-left-margin {
min-width:2em;
float:left;
}
div.csl-right-inline {
margin-left:2em;
padding-left:1em;
}
div.csl-indent {
margin-left: 2em;
}</style>
<script src="../site_libs/quarto-nav/quarto-nav.js"></script>
<script src="../site_libs/quarto-nav/headroom.min.js"></script>
<script src="../site_libs/clipboard/clipboard.min.js"></script>
<script src="../site_libs/quarto-search/autocomplete.umd.js"></script>
<script src="../site_libs/quarto-search/fuse.min.js"></script>
<script src="../site_libs/quarto-search/quarto-search.js"></script>
<meta name="quarto:offset" content="../">
<script src="../site_libs/quarto-html/quarto.js" type="module"></script>
<script src="../site_libs/quarto-html/tabsets/tabsets.js" type="module"></script>
<script src="../site_libs/quarto-html/popper.min.js"></script>
<script src="../site_libs/quarto-html/tippy.umd.min.js"></script>
<script src="../site_libs/quarto-html/anchor.min.js"></script>
<link href="../site_libs/quarto-html/tippy.css" rel="stylesheet">
<link href="../site_libs/quarto-html/quarto-syntax-highlighting-dark-2c84ecb840a13f4c7993f9e5648f0c14.css" rel="stylesheet" class="quarto-color-scheme quarto-color-alternate" id="quarto-text-highlighting-styles">
<link href="../site_libs/quarto-html/quarto-syntax-highlighting-6cf5824034cebd0380a5b9c74c43f006.css" rel="stylesheet" class="quarto-color-scheme" id="quarto-text-highlighting-styles">
<script src="../site_libs/bootstrap/bootstrap.min.js"></script>
<link href="../site_libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
<link href="../site_libs/bootstrap/bootstrap-ec71cb1e120c0dd41819aca960e74e38.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme" id="quarto-bootstrap" data-mode="light">
<link href="../site_libs/bootstrap/bootstrap-dark-6ed95ce66646ab2447a87e45f81c21f3.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme quarto-color-alternate" id="quarto-bootstrap" data-mode="dark">
<link href="../site_libs/bootstrap/bootstrap-ec71cb1e120c0dd41819aca960e74e38.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme-extra" id="quarto-bootstrap" data-mode="light">
<script id="quarto-search-options" type="application/json">{
"location": "navbar",
"copy-button": false,
"collapse-after": 3,
"panel-placement": "end",
"type": "overlay",
"limit": 50,
"keyboard-shortcut": [
"f",
"/",
"s"
],
"show-item-context": false,
"language": {
"search-no-results-text": "Keine Treffer",
"search-matching-documents-text": "Treffer",
"search-copy-link-title": "Link in die Suche kopieren",
"search-hide-matches-text": "Zusätzliche Treffer verbergen",
"search-more-match-text": "weitere Treffer in diesem Dokument",
"search-more-matches-text": "weitere Treffer in diesem Dokument",
"search-clear-button-title": "Zurücksetzen",
"search-text-placeholder": "",
"search-detached-cancel-button-title": "Abbrechen",
"search-submit-button-title": "Abschicken",
"search-label": "Suchen"
}
}</script>
<meta name="mermaid-theme" content="default">
<script src="../site_libs/quarto-diagram/mermaid.min.js"></script>
<script src="../site_libs/quarto-diagram/mermaid-init.js"></script>
<link href="../site_libs/quarto-diagram/mermaid.css" rel="stylesheet">
<meta property="og:title" content="RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze Nicole Dresselhaus">
<meta property="og:description" content="Ramblings of a madwoman">
<meta property="og:image" content="https://nicole.dresselhaus.cloud/thumbs/writing_obsidian-rag.png">
<meta property="og:site_name" content="Nicole Dresselhaus">
<meta property="og:image:height" content="400">
<meta property="og:image:width" content="555">
<meta name="citation_title" content="RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze">
<meta name="citation_author" content="GPT-4.5 ">
<meta name="citation_author" content="cogito-v1-preview ">
<meta name="citation_author" content="Claude 3.7 Sonnet">
<meta name="citation_author" content="Nicole Dresselhaus">
<meta name="citation_publication_date" content="2025-04-24">
<meta name="citation_cover_date" content="2025-04-24">
<meta name="citation_year" content="2025">
<meta name="citation_online_date" content="2025-04-24">
<meta name="citation_fulltext_html_url" content="https://nicole.dresselhaus.cloud/Writing/Obsidian-RAG.html">
<meta name="citation_language" content="de">
<meta name="citation_reference" content="citation_title=Ollama - Chroma Cookbook;,citation_publication_date=2024-04;,citation_cover_date=2024-04;,citation_year=2024;,citation_fulltext_html_url=https://cookbook.chromadb.dev/integrations/ollama/embeddings/;">
<meta name="citation_reference" content="citation_title=Just wanted to mention that the smart connections plugin is incredible. : r/ObsidianMD;,citation_publication_date=2024-10;,citation_cover_date=2024-10;,citation_year=2024;,citation_fulltext_html_url=https://www.reddit.com/r/ObsidianMD/comments/1fzmkdk/just_wanted_to_mention_that_the_smart_connections/;">
<meta name="citation_reference" content="citation_title=Khoj: An AI powered Search Assistant for your Second Brain - Share &amp;amp;amp; showcase - Obsidian Forum;,citation_publication_date=2023-07;,citation_cover_date=2023-07;,citation_year=2023;,citation_fulltext_html_url=https://forum.obsidian.md/t/khoj-an-ai-powered-search-assistant-for-you-second-brain/53756;">
<meta name="citation_reference" content="citation_title=Supercharging Obsidian Search with AI and Ollama;,citation_author=undefined @airabbitX;,citation_publication_date=2024-11;,citation_cover_date=2024-11;,citation_year=2024;,citation_fulltext_html_url=https://medium.com/@airabbitX/supercharging-obsidian-search-with-local-llms-a-personal-journey-1e008eb649a6;">
<meta name="citation_reference" content="citation_title=Export to common graph formats - Plugins ideas - Obsidian Forum;,citation_publication_date=2020-02;,citation_cover_date=2020-02;,citation_year=2020;,citation_fulltext_html_url=https://forum.obsidian.md/t/export-to-common-graph-formats/4138;">
<meta name="citation_reference" content="citation_title=Personal Knowledge Graphs in Obsidian;,citation_author=Volodymyr Pavlyshyn;,citation_publication_date=2024-03;,citation_cover_date=2024-03;,citation_year=2024;,citation_fulltext_html_url=https://volodymyrpavlyshyn.medium.com/personal-knowledge-graphs-in-obsidian-528a0f4584b9;">
<meta name="citation_reference" content="citation_title=How to export your Obsidian Vault to RDF;,citation_author=Volodymyr Pavlyshyn;,citation_publication_date=2024-03;,citation_cover_date=2024-03;,citation_year=2024;,citation_fulltext_html_url=https://volodymyrpavlyshyn.medium.com/how-to-export-your-obsidian-vault-to-rdf-00fb2539ed18;">
<meta name="citation_reference" content="citation_title=AI empowered Zettelkasten with NER and Graph LLM - Knowledge management - Obsidian Forum;,citation_publication_date=2024-03;,citation_cover_date=2024-03;,citation_year=2024;,citation_fulltext_html_url=https://forum.obsidian.md/t/ai-empowered-zettelkasten-with-ner-and-graph-llm/79112;">
<meta name="citation_reference" content="citation_title=Build your second brain with Khoj AI;,citation_publication_date=2024-06;,citation_cover_date=2024-06;,citation_year=2024;,citation_fulltext_html_url=https://dswharshit.medium.com/build-your-second-brain-with-khoj-ai-high-signal-ai-2-87492730d7ce;">
<meta name="citation_reference" content="citation_title=Second Brain Assistant with Obsidian;,citation_publication_date=2025-03;,citation_cover_date=2025-03;,citation_year=2025;,citation_fulltext_html_url=https://www.ssp.sh/brain/second-brain-assistant-with-obsidian-notegpt/;">
<meta name="citation_reference" content="citation_title=Basic Memory | AI Conversations That Build Knowledge;,citation_fulltext_html_url=https://basicmachines.co/;">
<meta name="citation_reference" content="citation_title=Local (Free) RAG with Question Generation using LM Studio, Nomic embeddings, ChromaDB and Llama 3.2 on a Mac mini M1;,citation_author=Oscar Galvis;,citation_publication_date=2024-10;,citation_cover_date=2024-10;,citation_year=2024;,citation_fulltext_html_url=https://lomaky.medium.com/local-free-rag-with-question-generation-using-lm-studio-nomic-embeddings-chromadb-and-llama-3-2-9758877e93b4;">
<meta name="citation_reference" content="citation_title=privateGPT / llama.cpp based scripts;,citation_publication_date=2025-03;,citation_cover_date=2025-03;,citation_year=2025;,citation_fulltext_html_url=https://www.ssp.sh/brain/second-brain-assistant-with-obsidian-notegpt/;">
</head>
<body class="nav-sidebar docked nav-fixed slimcontent quarto-light"><script id="quarto-html-before-body" type="application/javascript">
const toggleBodyColorMode = (bsSheetEl) => {
const mode = bsSheetEl.getAttribute("data-mode");
const bodyEl = window.document.querySelector("body");
if (mode === "dark") {
bodyEl.classList.add("quarto-dark");
bodyEl.classList.remove("quarto-light");
} else {
bodyEl.classList.add("quarto-light");
bodyEl.classList.remove("quarto-dark");
}
}
const toggleBodyColorPrimary = () => {
const bsSheetEl = window.document.querySelector("link#quarto-bootstrap:not([rel=disabled-stylesheet])");
if (bsSheetEl) {
toggleBodyColorMode(bsSheetEl);
}
}
window.setColorSchemeToggle = (alternate) => {
const toggles = window.document.querySelectorAll('.quarto-color-scheme-toggle');
for (let i=0; i < toggles.length; i++) {
const toggle = toggles[i];
if (toggle) {
if (alternate) {
toggle.classList.add("alternate");
} else {
toggle.classList.remove("alternate");
}
}
}
};
const toggleColorMode = (alternate) => {
// Switch the stylesheets
const primaryStylesheets = window.document.querySelectorAll('link.quarto-color-scheme:not(.quarto-color-alternate)');
const alternateStylesheets = window.document.querySelectorAll('link.quarto-color-scheme.quarto-color-alternate');
manageTransitions('#quarto-margin-sidebar .nav-link', false);
if (alternate) {
// note: dark is layered on light, we don't disable primary!
enableStylesheet(alternateStylesheets);
for (const sheetNode of alternateStylesheets) {
if (sheetNode.id === "quarto-bootstrap") {
toggleBodyColorMode(sheetNode);
}
}
} else {
disableStylesheet(alternateStylesheets);
enableStylesheet(primaryStylesheets)
toggleBodyColorPrimary();
}
manageTransitions('#quarto-margin-sidebar .nav-link', true);
// Switch the toggles
window.setColorSchemeToggle(alternate)
// Hack to workaround the fact that safari doesn't
// properly recolor the scrollbar when toggling (#1455)
if (navigator.userAgent.indexOf('Safari') > 0 && navigator.userAgent.indexOf('Chrome') == -1) {
manageTransitions("body", false);
window.scrollTo(0, 1);
setTimeout(() => {
window.scrollTo(0, 0);
manageTransitions("body", true);
}, 40);
}
}
const disableStylesheet = (stylesheets) => {
for (let i=0; i < stylesheets.length; i++) {
const stylesheet = stylesheets[i];
stylesheet.rel = 'disabled-stylesheet';
}
}
const enableStylesheet = (stylesheets) => {
for (let i=0; i < stylesheets.length; i++) {
const stylesheet = stylesheets[i];
if(stylesheet.rel !== 'stylesheet') { // for Chrome, which will still FOUC without this check
stylesheet.rel = 'stylesheet';
}
}
}
const manageTransitions = (selector, allowTransitions) => {
const els = window.document.querySelectorAll(selector);
for (let i=0; i < els.length; i++) {
const el = els[i];
if (allowTransitions) {
el.classList.remove('notransition');
} else {
el.classList.add('notransition');
}
}
}
const isFileUrl = () => {
return window.location.protocol === 'file:';
}
window.hasAlternateSentinel = () => {
let styleSentinel = getColorSchemeSentinel();
if (styleSentinel !== null) {
return styleSentinel === "alternate";
} else {
return false;
}
}
const setStyleSentinel = (alternate) => {
const value = alternate ? "alternate" : "default";
if (!isFileUrl()) {
window.localStorage.setItem("quarto-color-scheme", value);
} else {
localAlternateSentinel = value;
}
}
const getColorSchemeSentinel = () => {
if (!isFileUrl()) {
const storageValue = window.localStorage.getItem("quarto-color-scheme");
return storageValue != null ? storageValue : localAlternateSentinel;
} else {
return localAlternateSentinel;
}
}
const toggleGiscusIfUsed = (isAlternate, darkModeDefault) => {
const baseTheme = document.querySelector('#giscus-base-theme')?.value ?? 'light';
const alternateTheme = document.querySelector('#giscus-alt-theme')?.value ?? 'dark';
let newTheme = '';
if(darkModeDefault) {
newTheme = isAlternate ? baseTheme : alternateTheme;
} else {
newTheme = isAlternate ? alternateTheme : baseTheme;
}
const changeGiscusTheme = () => {
// From: https://github.com/giscus/giscus/issues/336
const sendMessage = (message) => {
const iframe = document.querySelector('iframe.giscus-frame');
if (!iframe) return;
iframe.contentWindow.postMessage({ giscus: message }, 'https://giscus.app');
}
sendMessage({
setConfig: {
theme: newTheme
}
});
}
const isGiscussLoaded = window.document.querySelector('iframe.giscus-frame') !== null;
if (isGiscussLoaded) {
changeGiscusTheme();
}
};
const queryPrefersDark = window.matchMedia('(prefers-color-scheme: dark)');
const darkModeDefault = queryPrefersDark.matches;
document.querySelector('link.quarto-color-scheme-extra').rel = 'disabled-stylesheet';
let localAlternateSentinel = darkModeDefault ? 'alternate' : 'default';
// Dark / light mode switch
window.quartoToggleColorScheme = () => {
// Read the current dark / light value
let toAlternate = !window.hasAlternateSentinel();
toggleColorMode(toAlternate);
setStyleSentinel(toAlternate);
toggleGiscusIfUsed(toAlternate, darkModeDefault);
};
queryPrefersDark.addEventListener("change", e => {
if(window.localStorage.getItem("quarto-color-scheme") !== null)
return;
const alternate = e.matches
toggleColorMode(alternate);
localAlternateSentinel = e.matches ? 'alternate' : 'default'; // this is used alongside local storage!
toggleGiscusIfUsed(alternate, darkModeDefault);
});
// Switch to dark mode if need be
if (window.hasAlternateSentinel()) {
toggleColorMode(true);
} else {
toggleColorMode(false);
}
</script>
<div id="quarto-search-results"></div>
<header id="quarto-header" class="headroom fixed-top">
<nav class="navbar navbar-expand-lg " data-bs-theme="dark">
<div class="navbar-container container-fluid">
<div class="navbar-brand-container mx-auto">
<a class="navbar-brand" href="../index.html">
<span class="navbar-title">Nicole Dresselhaus</span>
</a>
</div>
<div id="quarto-search" class="" title="Suchen"></div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" role="menu" aria-expanded="false" aria-label="Navigation umschalten" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav navbar-nav-scroll me-auto">
<li class="nav-item">
<a class="nav-link" href="../index.html"> <i class="bi bi-house" role="img">
</i>
<span class="menu-text">Home</span></a>
</li>
<li class="nav-item">
<a class="nav-link active" href="../About/index.html" aria-current="page"> <i class="bi bi-file-person" role="img">
</i>
<span class="menu-text">About</span></a>
</li>
</ul>
<ul class="navbar-nav navbar-nav-scroll ms-auto">
<li class="nav-item compact">
<a class="nav-link" href="../index.xml"> <i class="bi bi-rss" role="img">
</i>
<span class="menu-text"></span></a>
</li>
</ul>
</div> <!-- /navcollapse -->
<div class="quarto-navbar-tools">
<a href="" class="quarto-color-scheme-toggle quarto-navigation-tool px-1" onclick="window.quartoToggleColorScheme(); return false;" title="Dunkelmodus umschalten"><i class="bi"></i></a>
<a href="" class="quarto-reader-toggle quarto-navigation-tool px-1" onclick="window.quartoToggleReader(); return false;" title="Lesemodus umschalten">
<div class="quarto-reader-toggle-btn">
<i class="bi"></i>
</div>
</a>
</div>
</div> <!-- /container-fluid -->
</nav>
<nav class="quarto-secondary-nav">
<div class="container-fluid d-flex">
<button type="button" class="quarto-btn-toggle btn" data-bs-toggle="collapse" role="button" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Seitenleiste umschalten" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<i class="bi bi-layout-text-sidebar-reverse"></i>
</button>
<nav class="quarto-page-breadcrumbs" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item">Serious</li><li class="breadcrumb-item"><a href="../Writing/documentation.html">Writing</a></li><li class="breadcrumb-item"><a href="../Writing/Obsidian-RAG.html">RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze</a></li></ol></nav>
<a class="flex-grow-1" role="navigation" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Seitenleiste umschalten" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
</a>
</div>
</nav>
</header>
<!-- content -->
<div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-article page-navbar">
<!-- sidebar -->
<nav id="quarto-sidebar" class="sidebar collapse collapse-horizontal quarto-sidebar-collapse-item sidebar-navigation docked overflow-auto">
<div class="sidebar-menu-container">
<ul class="list-unstyled mt-1">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" role="navigation" aria-expanded="true">
<span class="menu-text">Serious</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" role="navigation" aria-expanded="true" aria-label="Abschnitt umschalten">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-1" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" role="navigation" aria-expanded="true">
<span class="menu-text">Writing</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" role="navigation" aria-expanded="true" aria-label="Abschnitt umschalten">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-2" class="collapse list-unstyled sidebar-section depth2 show">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../Writing/documentation.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../Writing/ner4all-case-study.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Case Study: Local LLM-Based NER with n8n and Ollama</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../Writing/Obsidian-RAG.html" class="sidebar-item-text sidebar-link active">
<span class="menu-text">RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" role="navigation" aria-expanded="false">
<span class="menu-text">Coding</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" role="navigation" aria-expanded="false" aria-label="Abschnitt umschalten">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-3" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-4" role="navigation" aria-expanded="false">
<span class="menu-text">Haskell</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-4" role="navigation" aria-expanded="false" aria-label="Abschnitt umschalten">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-4" class="collapse list-unstyled sidebar-section depth3 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../Coding/Haskell/Advantages.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Talks und Posts zu Haskell</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../Coding/Haskell/FFPiH.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Fortgeschrittene funktionale Programmierung in Haskell</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../Coding/Haskell/Lenses.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Lenses</span></a>
</div>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-5" role="navigation" aria-expanded="false">
<span class="menu-text">Code Snippets</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-5" role="navigation" aria-expanded="false" aria-label="Abschnitt umschalten">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-5" class="collapse list-unstyled sidebar-section depth4 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../Coding/Haskell/Code Snippets/Monoid.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Monoid? Da war doch was…</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../Coding/Haskell/Code Snippets/Morphisms.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">*-Morpisms</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a href="../Coding/Haskell/Webapp-Example/index.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Webapp-Development in Haskell</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-6" role="navigation" aria-expanded="false" aria-label="Abschnitt umschalten">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-6" class="collapse list-unstyled sidebar-section depth4 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../Coding/Haskell/Webapp-Example/Main.hs.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Webapp-Example: Main.hs</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../Coding/Haskell/Webapp-Example/MyService_Types.hs.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Webapp-Example: MyService/Types.hs</span></a>
</div>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-7" role="navigation" aria-expanded="false">
<span class="menu-text">Health</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-7" role="navigation" aria-expanded="false" aria-label="Abschnitt umschalten">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-7" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../Health/Issues.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Mental Health</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-8" role="navigation" aria-expanded="false">
<span class="menu-text">Uni</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-8" role="navigation" aria-expanded="false" aria-label="Abschnitt umschalten">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-8" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../Uni/Lernerfolg_an_der_Uni.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Wie lerne ich richtig an der Uni?</span></a>
</div>
</li>
</ul>
</li>
</ul>
</li>
<li class="px-0"><hr class="sidebar-divider hi "></li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-9" role="navigation" aria-expanded="true">
<span class="menu-text">Fun</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-9" role="navigation" aria-expanded="true" aria-label="Abschnitt umschalten">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-9" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-10" role="navigation" aria-expanded="false">
<span class="menu-text">Opinions</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-10" role="navigation" aria-expanded="false" aria-label="Abschnitt umschalten">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-10" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../Opinions/Editors.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Editors</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../Opinions/Keyboard-Layout.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Keyboard-Layout</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-11" role="navigation" aria-expanded="false">
<span class="menu-text">Stuff</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-11" role="navigation" aria-expanded="false" aria-label="Abschnitt umschalten">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-11" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../Stuff/Bielefeldverschwoerung.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Die Bielefeld-Verschwörung</span></a>
</div>
</li>
</ul>
</li>
</ul>
</li>
<li class="px-0"><hr class="sidebar-divider hi "></li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-12" role="navigation" aria-expanded="true">
<span class="menu-text">Info</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-12" role="navigation" aria-expanded="true" aria-label="Abschnitt umschalten">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-12" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a href="../About/index.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">About me</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-13" role="navigation" aria-expanded="false" aria-label="Abschnitt umschalten">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-13" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../About/Experience.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Highlights of my experiences in the programming world</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../About/Extracurricular.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Studium generale / University-Life</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../About/Work.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Work-Experience</span></a>
</div>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</nav>
<div id="quarto-sidebar-glass" class="quarto-sidebar-collapse-item" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item"></div>
<!-- margin-sidebar -->
<div id="quarto-margin-sidebar" class="sidebar margin-sidebar">
<nav id="TOC" role="doc-toc" class="toc-active">
<h2 id="toc-title">Auf dieser Seite</h2>
<ul>
<li><a href="#hintergrund-und-zielsetzung" id="toc-hintergrund-und-zielsetzung" class="nav-link active" data-scroll-target="#hintergrund-und-zielsetzung">Hintergrund und Zielsetzung</a></li>
<li><a href="#vektorbasierter-ansatz-semantic-search-mit-embeddings" id="toc-vektorbasierter-ansatz-semantic-search-mit-embeddings" class="nav-link" data-scroll-target="#vektorbasierter-ansatz-semantic-search-mit-embeddings">1. Vektorbasierter Ansatz: Semantic Search mit Embeddings</a>
<ul class="collapse">
<li><a href="#prinzip" id="toc-prinzip" class="nav-link" data-scroll-target="#prinzip">Prinzip</a></li>
<li><a href="#implementierung" id="toc-implementierung" class="nav-link" data-scroll-target="#implementierung">Implementierung</a></li>
<li><a href="#integration-mit-lokalen-llms" id="toc-integration-mit-lokalen-llms" class="nav-link" data-scroll-target="#integration-mit-lokalen-llms">Integration mit lokalen LLMs</a></li>
<li><a href="#leistung" id="toc-leistung" class="nav-link" data-scroll-target="#leistung">Leistung</a></li>
<li><a href="#nachteile-und-aufwand" id="toc-nachteile-und-aufwand" class="nav-link" data-scroll-target="#nachteile-und-aufwand">Nachteile und Aufwand</a></li>
<li><a href="#zusammenfassung-ansatz-1-vektordatenbank-embeddings" id="toc-zusammenfassung-ansatz-1-vektordatenbank-embeddings" class="nav-link" data-scroll-target="#zusammenfassung-ansatz-1-vektordatenbank-embeddings">Zusammenfassung Ansatz 1: Vektordatenbank (Embeddings)</a></li>
</ul></li>
<li><a href="#knowledge-graph-ansatz-strukturierte-graphdatenbank" id="toc-knowledge-graph-ansatz-strukturierte-graphdatenbank" class="nav-link" data-scroll-target="#knowledge-graph-ansatz-strukturierte-graphdatenbank">2. Knowledge-Graph-Ansatz: Strukturierte Graphdatenbank</a>
<ul class="collapse">
<li><a href="#prinzip-1" id="toc-prinzip-1" class="nav-link" data-scroll-target="#prinzip-1">Prinzip</a></li>
<li><a href="#erstellung-des-graphen" id="toc-erstellung-des-graphen" class="nav-link" data-scroll-target="#erstellung-des-graphen">Erstellung des Graphen</a></li>
<li><a href="#nutzung-für-llm-aufgaben" id="toc-nutzung-für-llm-aufgaben" class="nav-link" data-scroll-target="#nutzung-für-llm-aufgaben">Nutzung für LLM-Aufgaben</a></li>
<li><a href="#integration-mit-llms" id="toc-integration-mit-llms" class="nav-link" data-scroll-target="#integration-mit-llms">Integration mit LLMs</a></li>
<li><a href="#zusammenfassung---ansatz-2-graphdatenbank" id="toc-zusammenfassung---ansatz-2-graphdatenbank" class="nav-link" data-scroll-target="#zusammenfassung---ansatz-2-graphdatenbank">Zusammenfassung - Ansatz 2: Graphdatenbank</a></li>
<li><a href="#fazit-graphdatenbank" id="toc-fazit-graphdatenbank" class="nav-link" data-scroll-target="#fazit-graphdatenbank">Fazit Graphdatenbank</a></li>
</ul></li>
<li><a href="#hybrid-ansatz-kombination-aus-graph-und-vektor-rag" id="toc-hybrid-ansatz-kombination-aus-graph-und-vektor-rag" class="nav-link" data-scroll-target="#hybrid-ansatz-kombination-aus-graph-und-vektor-rag">3. Hybrid-Ansatz: Kombination aus Graph und Vektor-RAG</a>
<ul class="collapse">
<li><a href="#vor-nachteile" id="toc-vor-nachteile" class="nav-link" data-scroll-target="#vor-nachteile">Vor-/Nachteile</a></li>
<li><a href="#integrationsmöglichkeiten" id="toc-integrationsmöglichkeiten" class="nav-link" data-scroll-target="#integrationsmöglichkeiten">Integrationsmöglichkeiten</a></li>
<li><a href="#zusammenfassung-ansatz-3-hybrid-lösung" id="toc-zusammenfassung-ansatz-3-hybrid-lösung" class="nav-link" data-scroll-target="#zusammenfassung-ansatz-3-hybrid-lösung">Zusammenfassung Ansatz 3: Hybrid-Lösung</a></li>
<li><a href="#fazit-hybrid-lösung" id="toc-fazit-hybrid-lösung" class="nav-link" data-scroll-target="#fazit-hybrid-lösung">Fazit Hybrid-Lösung</a></li>
</ul></li>
<li><a href="#datenaufbereitung-yaml-metadaten-extrahieren-und-normalisieren" id="toc-datenaufbereitung-yaml-metadaten-extrahieren-und-normalisieren" class="nav-link" data-scroll-target="#datenaufbereitung-yaml-metadaten-extrahieren-und-normalisieren">4. Datenaufbereitung: YAML-Metadaten extrahieren und normalisieren</a>
<ul class="collapse">
<li><a href="#extraktion" id="toc-extraktion" class="nav-link" data-scroll-target="#extraktion">Extraktion</a></li>
<li><a href="#normalisierung" id="toc-normalisierung" class="nav-link" data-scroll-target="#normalisierung">Normalisierung</a></li>
<li><a href="#nutzung-durch-llm" id="toc-nutzung-durch-llm" class="nav-link" data-scroll-target="#nutzung-durch-llm">Nutzung durch LLM</a></li>
<li><a href="#beispiel-workflows" id="toc-beispiel-workflows" class="nav-link" data-scroll-target="#beispiel-workflows">Beispiel-Workflows</a></li>
<li><a href="#vor--nachteile" id="toc-vor--nachteile" class="nav-link" data-scroll-target="#vor--nachteile">Vor- &amp; Nachteile</a></li>
<li><a href="#zusammenfassung-ansatz-4-extraktion" id="toc-zusammenfassung-ansatz-4-extraktion" class="nav-link" data-scroll-target="#zusammenfassung-ansatz-4-extraktion">Zusammenfassung Ansatz 4: Extraktion</a></li>
</ul></li>
<li><a href="#automatisierungstools-und-workflows" id="toc-automatisierungstools-und-workflows" class="nav-link" data-scroll-target="#automatisierungstools-und-workflows">5. Automatisierungstools und Workflows</a>
<ul class="collapse">
<li><a href="#obsidian-plugins-in-app-ki-features" id="toc-obsidian-plugins-in-app-ki-features" class="nav-link" data-scroll-target="#obsidian-plugins-in-app-ki-features">Obsidian-Plugins (In-App KI-Features)</a></li>
<li><a href="#externe-anwendungen-skripte" id="toc-externe-anwendungen-skripte" class="nav-link" data-scroll-target="#externe-anwendungen-skripte">Externe Anwendungen / Skripte</a></li>
</ul></li>
<li><a href="#zusammenfassende-empfehlung" id="toc-zusammenfassende-empfehlung" class="nav-link" data-scroll-target="#zusammenfassende-empfehlung">Zusammenfassende Empfehlung</a></li>
<li><a href="#quellen" id="toc-quellen" class="nav-link" data-scroll-target="#quellen">Quellen</a></li>
</ul>
</nav>
</div>
<!-- main -->
<main class="content page-columns page-full" id="quarto-document-content">
<header id="title-block-header" class="quarto-title-block default"><nav class="quarto-page-breadcrumbs quarto-title-breadcrumbs d-none d-lg-block" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item">Serious</li><li class="breadcrumb-item"><a href="../Writing/documentation.html">Writing</a></li><li class="breadcrumb-item"><a href="../Writing/Obsidian-RAG.html">RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze</a></li></ol></nav>
<div class="quarto-title">
<h1 class="title">RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze</h1>
<div class="quarto-categories">
<div class="quarto-category">Article</div>
<div class="quarto-category">RAG</div>
<div class="quarto-category">ML</div>
</div>
</div>
<div class="quarto-title-meta-author">
<div class="quarto-title-meta-heading">Autor:innen</div>
<div class="quarto-title-meta-heading">Zugehörigkeiten</div>
<div class="quarto-title-meta-contents">
<p class="author"><a href="https://chatgpt.com">GPT-4.5</a> </p>
</div>
<div class="quarto-title-meta-contents">
<p class="affiliation">
<a href="https://openai.com">
OpenAI
</a>
</p>
</div>
<div class="quarto-title-meta-contents">
<p class="author"><a href="https://www.deepcogito.com/research/cogito-v1-preview">cogito-v1-preview</a> </p>
</div>
<div class="quarto-title-meta-contents">
<p class="affiliation">
<a href="https://www.deepcogito.com">
DeepCogito
</a>
</p>
</div>
<div class="quarto-title-meta-contents">
<p class="author"><a href="https://claude.ai">Claude 3.7 Sonnet</a> </p>
</div>
<div class="quarto-title-meta-contents">
<p class="affiliation">
<a href="https://www.anthropic.com">
Antrhopic
</a>
</p>
</div>
<div class="quarto-title-meta-contents">
<p class="author">Nicole Dresselhaus <a href="https://orcid.org/0009-0008-8850-3679" class="quarto-title-author-orcid"> <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo1N0NEMjA4MDI1MjA2ODExOTk0QzkzNTEzRjZEQTg1NyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozM0NDOEJGNEZGNTcxMUUxODdBOEVCODg2RjdCQ0QwOSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozM0NDOEJGM0ZGNTcxMUUxODdBOEVCODg2RjdCQ0QwOSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkZDN0YxMTc0MDcyMDY4MTE5NUZFRDc5MUM2MUUwNEREIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjU3Q0QyMDgwMjUyMDY4MTE5OTRDOTM1MTNGNkRBODU3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+84NovQAAAR1JREFUeNpiZEADy85ZJgCpeCB2QJM6AMQLo4yOL0AWZETSqACk1gOxAQN+cAGIA4EGPQBxmJA0nwdpjjQ8xqArmczw5tMHXAaALDgP1QMxAGqzAAPxQACqh4ER6uf5MBlkm0X4EGayMfMw/Pr7Bd2gRBZogMFBrv01hisv5jLsv9nLAPIOMnjy8RDDyYctyAbFM2EJbRQw+aAWw/LzVgx7b+cwCHKqMhjJFCBLOzAR6+lXX84xnHjYyqAo5IUizkRCwIENQQckGSDGY4TVgAPEaraQr2a4/24bSuoExcJCfAEJihXkWDj3ZAKy9EJGaEo8T0QSxkjSwORsCAuDQCD+QILmD1A9kECEZgxDaEZhICIzGcIyEyOl2RkgwAAhkmC+eAm0TAAAAABJRU5ErkJggg=="></a></p>
</div>
<div class="quarto-title-meta-contents">
<p class="affiliation">
<a href="https://hu-berlin.de">
Humboldt-Universität zu Berlin
</a>
</p>
</div>
</div>
<div class="quarto-title-meta">
<div>
<div class="quarto-title-meta-heading">Veröffentlichungsdatum</div>
<div class="quarto-title-meta-contents">
<p class="date">24. April 2025</p>
</div>
</div>
</div>
</header>
<section id="hintergrund-und-zielsetzung" class="level2">
<h2 class="anchored" data-anchor-id="hintergrund-und-zielsetzung">Hintergrund und Zielsetzung</h2>
<p>Der Nutzer verfügt über eine Obsidian-Wissensdatenbank, in der Markdown-Dateien mit <strong>typisierten</strong> Inhalten (FileClasses wie <code>Person</code>, <code>Projekt</code>, <code>Deliverable</code> etc.) verwaltet werden. Die Notizen enthalten strukturierte <strong>YAML-Metadaten</strong> (unterstützt durch Plugins wie <em>Metadata Menu</em>) und sind durch viele <strong>Wiki-Links</strong> miteinander vernetzt. Standardisierte Templates (via <em>Templater</em>) sorgen dafür, dass z.B. Personenseiten immer ähnliche Felder (Name, ORCID, etc.) aufweisen.</p>
<p><strong>Ziel</strong> ist es, mithilfe eines Language Models (LLM) wiederkehrende Aufgaben zu erleichtern, zum Beispiel: automatisch YAML-Felder ausfüllen (etwa fehlende ORCID iDs bei Personen ergänzen), neue Entitätsseiten anhand von Templates befüllen oder sinnvolle Verlinkungen zwischen Notizen vorschlagen. Dabei reicht ein tägliches Neu-Einlesen der Obsidian-Daten (via Cronjob o.Ä.) aus eine Echtzeit-Synchronisation ist optional. Die Obsidian-internen Wikilinks (<code>[[...]]</code>) müssen im LLM-Ausgabeformat nicht unbedingt klickbar sein (es genügt, wenn sie referenziert werden).</p>
<p>Um diese Funktionen umzusetzen, bieten sich verschiedene <strong>technische Ansätze</strong> an. Im Folgenden werden fünf Optionen untersucht: (1) Nutzung eines <strong>Vektorspeichers</strong> für semantische Suche, (2) Aufbau eines <strong>Knowledge Graph</strong> aus den Notizen, (3) eine <strong>Hybrid-Lösung</strong> aus Graph und Vektor, (4) Extraktion &amp; Normalisierung der <strong>YAML-Metadaten</strong> und (5) existierende <strong>Tools/Workflows</strong> zur Automatisierung. Jede Option wird mit Funktionsweise, Vorund Nachteilen, Aufwand, Integrationsmöglichkeiten (insb. mit lokalen LLMs wie LLaMA, Deepseek, Cogito etc.) sowie konkreten Tool-Empfehlungen dargestellt.</p>
</section>
<section id="vektorbasierter-ansatz-semantic-search-mit-embeddings" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="vektorbasierter-ansatz-semantic-search-mit-embeddings">1. Vektorbasierter Ansatz: Semantic Search mit Embeddings</h2>
<section id="prinzip" class="level3">
<h3 class="anchored" data-anchor-id="prinzip">Prinzip</h3>
<p>Alle Markdown-Notizen (bzw. deren Inhalt) werden in kleinere Chunks zerlegt und durch einen Embedding-Modell in hochdimensionale Vektoren umgewandelt. Diese Vektoren werden in einem <strong>Vektorstore</strong> (wie ChromaDB oder Weaviate) gespeichert. Bei Anfragen des LLM (z.B. <em>“Welche Projekte hat Person X?”</em> oder <em>“Erstelle eine neue Organisation XYZ basierend auf ähnlichen Einträgen”</em>), können mittels <strong>ähnlichkeitssuche</strong> semantisch passende Notiz-Abschnitte abgerufen und dem LLM als Kontext mitgegeben werden (Retrieval-Augmented Generation).</p>
</section>
<section id="implementierung" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="implementierung">Implementierung</h3>
<p>In der Praxis ließe sich z.B. ein Workflow mit <strong>Ollama</strong> + <strong>Nomic Embeddings</strong> + <strong>Chroma</strong><a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> aufbauen. Ollama stellt ein lokales LLM-Serving bereit und bietet auch eine API für Embeddins <span class="citation" data-cites="ollama_chroma_cookbook">[<a href="#ref-ollama_chroma_cookbook" role="doc-biblioref">1</a>]</span>. Man könnte ein spezialisiertes Embeddin-Modell wie <code>nomic-embed-text</code> verwenden, welches kompakte 1024-dimensionale Textvektoren liefert <span class="citation" data-cites="ollama_chroma_cookbook">[<a href="#ref-ollama_chroma_cookbook" role="doc-biblioref">1</a>]</span>. Die Notizen des Obsidian Vault würden per Skript täglich eingelesen, in Sinnabschnitte (Chunks) aufgeteilt (z.B. nach Überschriften oder einer festen Token-Länge) und über Ollamas Embedding-API in Vektoren umgewandelt <span class="citation" data-cites="ollama_chroma_cookbook">[<a href="#ref-ollama_chroma_cookbook" role="doc-biblioref">1</a>]</span>. Diese Vektoren speichert man in einer lokalen DB wie Chroma. Anfragen an das LLM werden dann zunächst an den Vektorstore gestellt, um die relevantesten Notiz-Abschnitte zu finden, welche dann zusammen mit der eigentlichen Frage an das LLM gegeben werden (klassischer RAG-Pipeline). Dieses Verfahren ist vergleichbar mit dem <em>Smart Connections</em> Obsidian-Plugin: Dort wird ebenfalls ein “Text Embedding Model” auf den Vault angewendet, um zu einer Nutzerfrage automatisch thematisch passende Notizen zu finden und dem LLM bereitzustellen <span class="citation" data-cites="smart_connections_plugin">[<a href="#ref-smart_connections_plugin" role="doc-biblioref">2</a>]</span>. So konnte im Beispiel ein lokales LLaMA-basiertes Modell Fragen zum eigenen Vault korrekt beantworten, indem es zuvor den passenden Ausschnitt (hier: eine Styleguide-Notiz) über Embeddings gefunden hatte <span class="citation" data-cites="smart_connections_plugin">[<a href="#ref-smart_connections_plugin" role="doc-biblioref">2</a>]</span>.</p>
<div class="no-row-height column-margin column-container"><div id="fn1"><p><sup>1</sup>&nbsp;Alle diese Teile laufen bereits individuell in der Arbeitsgruppe bzw. werden schon genutzt.</p></div></div></section>
<section id="integration-mit-lokalen-llms" class="level3">
<h3 class="anchored" data-anchor-id="integration-mit-lokalen-llms">Integration mit lokalen LLMs</h3>
<p>Ein Vorteil dieses Ansatzes ist, dass er schon heute mit lokalen Open-Source-LLMs funktioniert. Beispielsweise ließ sich in <em>Smart Connections</em> ein lokal gehostetes LLaMA-Model (3B Instruct) via text-generation-webui einbinden <span class="citation" data-cites="smart_connections_plugin">[<a href="#ref-smart_connections_plugin" role="doc-biblioref">2</a>]</span>. Alternativ kann man auch <em>LLM-as-a-service</em> Tools wie <strong>Ollama</strong> nutzen, um ein Modell wie Llama&nbsp;2 bereitzustellen. Die Open-Source-Tools <strong>LangChain</strong> oder <strong>LlamaIndex</strong> bieten Module, um Vektorstores anzubinden und mit LLM-Abfragen zu kombinieren dies kann man auch mit lokal eingebundenen Modellen (z.B. über LlamaCpp oder GPT4All) verwenden. Zahlreiche fertige Projekte demonstrieren dieses Vorgehen: z.B. <em>privateGPT</em> kombiniert LangChain, GPT4All (lokales LLM) und Chroma, um komplett offline Fragen über lokale Dateien zu beantworten <span class="citation" data-cites="second_brain_assistant_with_obsidian">[<a href="#ref-second_brain_assistant_with_obsidian" role="doc-biblioref">3</a>]</span>. Auch <strong>Khoj</strong> verfolgt einen ähnlichen Pfad: Es indexiert den Vault und erlaubt semantische <strong>Natürliche Sprache Suche</strong> über Markdown-Inhalte sowie <em>“ähnliche Notizen finden”</em> <span class="citation" data-cites="khoj_plugin">[<a href="#ref-khoj_plugin" role="doc-biblioref">4</a>]</span>.</p>
</section>
<section id="leistung" class="level3">
<h3 class="anchored" data-anchor-id="leistung">Leistung</h3>
<p>Dank moderner Embedding-Modelle können semantisch ähnliche Inhalte gefunden werden, selbst wenn die Schlagwörter nicht exakt übereinstimmen. Das löst das in Obsidian bekannte Problem, dass die eingebaute Suche nur exakte Worttreffer findet <span class="citation" data-cites="supercharging_obsidian_search">[<a href="#ref-supercharging_obsidian_search" role="doc-biblioref">5</a>]</span>. Der Ansatz skaliert auch auf größere Wissensbasen; Vektordatenbanken wie Weaviate oder Chroma sind für zehntausende Einträge ausgelegt. Eine tägliche Aktualisierung ist machbar, da nur neue/geänderte Notizen re-embedded werden müssen.</p>
</section>
<section id="nachteile-und-aufwand" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="nachteile-und-aufwand">Nachteile und Aufwand</h3>
<p>Die Einrichtung erfordert mehrere Komponenten. Man benötigt Pipeline-Schritte für das Chunking, Embedding und das Handling des Vektorstores dies bedeutet anfängliche Komplexität und Rechenaufwand <span class="citation" data-cites="supercharging_obsidian_search">[<a href="#ref-supercharging_obsidian_search" role="doc-biblioref">5</a>]</span>. Insbesondere das Generieren der Embeddings kann bei großen Vaults zeitund speicherintensiv sein (je nach Modell und Hardware) <span class="citation" data-cites="supercharging_obsidian_search">[<a href="#ref-supercharging_obsidian_search" role="doc-biblioref">5</a>]</span>. Laufende Kosten sind bei rein lokaler Verarbeitung allerdings kein Thema außer CPU/GPU-Last. Ein potenzieller Nachteil ist, dass rein embeddings-basierte Suche keine <strong>strukturierte</strong> Abfrage erlaubt das Modell findet zwar thematisch passende Textpassagen, aber um z.B. <strong>eine bestimmte Eigenschaft</strong> (wie eine fehlende ORCID) gezielt abzufragen, müsste man dennoch im Text suchen oder zusätzliche Logik anwenden. Das LLM kann aus den gefundenen Texten zwar implizit Fakten entnehmen, hat aber kein explizites Wissen über die Datenstruktur. Zudem können irrelevante Kontextstücke eingebunden werden, wenn das semantische Matching fehlerhaft ist (dies erfordert ggf. Feintuning der Chunk-Größe oder Filtern per Dateityp/-klasse)<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a>.</p>
<div class="no-row-height column-margin column-container"><div id="fn2"><p><sup>2</sup>&nbsp;Und diese Nachteile machen dies zu einem Deal-Breaker. Gerade in Tabellen oder Auflistungen kann der Attention-Mechanismus der LLM schnell zu einem Mischen oder Verwechseln von präsentierten Informationen führen. Besonders kleine Netze (meist bis ~7b) sind hier anfällig.</p></div></div></section>
<section id="zusammenfassung-ansatz-1-vektordatenbank-embeddings" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="zusammenfassung-ansatz-1-vektordatenbank-embeddings">Zusammenfassung Ansatz 1: Vektordatenbank (Embeddings)</h3>
<table class="caption-top table">
<colgroup>
<col style="width: 6%">
<col style="width: 93%">
</colgroup>
<thead>
<tr class="header">
<th></th>
<th><strong>Details</strong></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><strong>Vorgehen</strong></td>
<td>Inhalte aller Markdown-Dateien in semantische Vektoren kodieren (z.B. mit <code>nomic-embed-text</code> (<span class="citation" data-cites="ollama_chroma_cookbook">[<a href="#ref-ollama_chroma_cookbook" role="doc-biblioref">1</a>]</span>)) und in einer Vektor-DB speichern. LLM-Anfragen per Similarity Search mit relevantem Kontext anreichern.</td>
</tr>
<tr class="even">
<td><strong>Stärken</strong></td>
<td><em>Semantische Suche</em> (findet thematisch passende Infos, nicht nur exakte Worttreffer) <span class="citation" data-cites="supercharging_obsidian_search">[<a href="#ref-supercharging_obsidian_search" role="doc-biblioref">5</a>]</span>. Skaliert auf große Textmengen. Bereits heute mit lokalen LLMs erprobt (z.B. <em>Smart Connections</em> Plugin) <span class="citation" data-cites="smart_connections_plugin">[<a href="#ref-smart_connections_plugin" role="doc-biblioref">2</a>]</span>. Gut geeignet für Q&amp;A, Textzusammenfassungen und Link-Vorschläge basierend auf Ähnlichkeit.</td>
</tr>
<tr class="odd">
<td><strong>Schwächen</strong></td>
<td>Komplexeres Setup (Embedding-Model + DB + Pipeline) <span class="citation" data-cites="supercharging_obsidian_search">[<a href="#ref-supercharging_obsidian_search" role="doc-biblioref">5</a>]</span>. Hoher Rechenaufwand für Embeddings bei großen Vaults. Kein explizites Modell von Beziehungen/Metadaten strukturierte Abfragen (z.B. <em>“zeige alle Personen ohne ORCID”</em>) nur mit Zusatzlogik. Kontexttreffer können ungenau sein (erfordert ggf. Feinjustierung).</td>
</tr>
<tr class="even">
<td><strong>Integrations-Optionen</strong></td>
<td>Lokale LLM-Einbindung möglich (z.B. LLaMA 2 über Ollama-API). Tools: <strong>ChromaDB</strong>, <strong>Weaviate</strong> oder <strong>FAISS</strong> als Vektorstore; <strong>LangChain/LlamaIndex</strong> für Pipeline-Management. Obsidian-Plugins: <em>Smart Connections</em> (komplett integriert mit lokalem Embedder+LLM) <span class="citation" data-cites="smart_connections_plugin">[<a href="#ref-smart_connections_plugin" role="doc-biblioref">2</a>]</span>; <em>Khoj</em> (separater Suchassistent mit Embeddings) <span class="citation" data-cites="khoj_plugin">[<a href="#ref-khoj_plugin" role="doc-biblioref">4</a>]</span>.</td>
</tr>
</tbody>
</table>
<div class="cell page-columns page-full" data-layout-align="default">
<div class="cell-output-display column-screen-inset-right">
<div>
<p></p><figure class="figure"><p></p>
<div>
<pre class="mermaid mermaid-js">graph LR
A[Obsidian Vault] --&gt; B[Chunking]
B --&gt; C[Embedding Model]
C --&gt; D[(Vector Database)]
E[User Query] --&gt; F[Query Embedding]
F --&gt; G[Similarity Search]
D --&gt; G
G --&gt; H[Relevant Chunks]
H --&gt; I[LLM]
E --&gt; I
I --&gt; J[Response]
</pre>
</div>
<p></p></figure><p></p>
</div>
</div>
</div>
</section>
</section>
<section id="knowledge-graph-ansatz-strukturierte-graphdatenbank" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="knowledge-graph-ansatz-strukturierte-graphdatenbank">2. Knowledge-Graph-Ansatz: Strukturierte Graphdatenbank</h2>
<section id="prinzip-1" class="level3">
<h3 class="anchored" data-anchor-id="prinzip-1">Prinzip</h3>
<p>Statt (oder zusätzlich zu) freiem Text wird der Informationsgehalt des Vaults als <strong>Graph</strong> modelliert. Jede Notiz entspricht einem <strong>Knoten</strong> im Graphen (mit Typ-Label gemäß FileClass, z.B. <code>Person</code>, <code>Projekt</code> etc.). Relationen zwischen Notizen implizit durch Obsidian-Wikilinks gegeben werden zu expliziten <strong>Kanten</strong> im Graph (z.B. eine Person “arbeitet in” Organisation, ein Projekt “liefert” ein Deliverable). Auch Metadaten aus YAML können als Knoten oder Properties modelliert werden (z.B. ORCID als Attribut eines Person-Knotens, Tags als Relationen <em>“hat Schlagwort”</em> usw.). Das Ergebnis ist ein <strong>Wissensgraph</strong>, der ähnlich einem klassischen RDF-Triple-Store oder Neo4j-Property-Graph komplexe Abfragen und Analysen ermöglicht.</p>
</section>
<section id="erstellung-des-graphen" class="level3">
<h3 class="anchored" data-anchor-id="erstellung-des-graphen">Erstellung des Graphen</h3>
<p>Eine Möglichkeit ist die Obsidian-Daten nach RDF zu exportieren. So beschreibt Pavlyshyn (2023) ein Verfahren, einen Vault ins RDF-Format zu überführen, um “komplexe Abfragen mit klassischen Semantic-Tools” zu ermöglichen <span class="citation" data-cites="export_obsidian_to_rdf">[<a href="#ref-export_obsidian_to_rdf" role="doc-biblioref">6</a>]</span>. Alternativ kann man direkt in einer Graphdatenbank wie <strong>Neo4j</strong> modellieren. Ein Community-Plugin (<em>obsidian-neo4j-stream</em>) hat beispielsweise versucht, den Obsidian-Linkgraph in Neo4j importierbar zu machen <span class="citation" data-cites="export_to_common_graph_formats">[<a href="#ref-export_to_common_graph_formats" role="doc-biblioref">7</a>]</span>. Konkret würde man pro Markdown-Datei einen Node mit dessen YAML-Feldern als Properties anlegen. Bestehende Wiki-Links zwischen Dateien werden als ungerichtete oder gerichtete Edges abgebildet (hier kann man, sofern man mehr Semantik will, Link-Typen einführen z.B. im Text <code>[[Albert Einstein|Autor]]</code> könnte der Alias “Autor” als Kanten-Label genutzt werden). Da Obsidian standardmäßig keine typisierten Kanten unterstützt, bleiben Relationstypen begrenzt Plugins wie <em>Juggl</em> oder <em>Graph-Link-Types</em> erlauben allerdings das Hinzufügen von Link-Metadaten, was für eine genauere Graph-Modellierung hilfreich sein könnte <span class="citation" data-cites="personal_knowledge_graphs_in_obsidian">[<a href="#ref-personal_knowledge_graphs_in_obsidian" role="doc-biblioref">8</a>]</span>. YAML-Inhalte, die auf andere Notizen referenzieren, können ebenfalls als Kanten kodiert werden (Beispiel: In einer Projekt-Notiz listet das YAML-Feld <code>team:</code> mehrere Personen diese Verweise werden im Graph als Kanten <em>Projekt —hatTeam→ Person</em> umgesetzt). Nicht referenzielle Metadaten (etwa ein ORCID-Wert) bleiben einfach als Datenfeld am Knoten.</p>
</section>
<section id="nutzung-für-llm-aufgaben" class="level3">
<h3 class="anchored" data-anchor-id="nutzung-für-llm-aufgaben">Nutzung für LLM-Aufgaben</h3>
<p>Ein solcher Graph erlaubt <strong>strukturierte Abfragen</strong> und <strong>Schlussfolgerungen</strong>. Für wiederkehrende Aufgaben kann man den Graph gezielt auswerten. Beispielsweise ließen sich <em>“alle Personen ohne ORCID”</em> mittels einer einfachen Graph-Query ermitteln. Das LLM könnte diese Liste als Input erhalten und dann (ggf. mittels Tools oder Wissensbasis) die fehlenden IDs ergänzen. Auch <em>Link-Vorschläge</em> können aus dem Graph gezogen werden: Durch Graph-Analysen wie das Finden von gemeinsamen Nachbarn oder kürzesten Pfaden entdeckt man Verbindungen, die im Vault noch nicht als direkte Links existieren. So könnte man z.B. feststellen, dass zwei Personen an vielen gleichen Meetings teilgenommen haben und dem Nutzer vorschlagen, diese Personen direkt miteinander zu verknüpfen. Oder man erkennt durch <em>link prediction</em> Algorithmen neue mögliche Beziehungen. Forschung und Community sehen hier großes Potential: Eine AI-gestützte Graphanalyse kann helfen, verborgene Zusammenhänge im eigenen Zettelkasten zu finden <span class="citation" data-cites="ai_empowered_zettelkasten_with_ner_and_graph_llm">[<a href="#ref-ai_empowered_zettelkasten_with_ner_and_graph_llm" role="doc-biblioref">9</a>]</span>. Mit Graph-basiertem Reasoning ließe sich sogar <strong>neues Wissen entdecken</strong> oder logisch konsistente Antworten generieren <span class="citation" data-cites="ai_empowered_zettelkasten_with_ner_and_graph_llm">[<a href="#ref-personal_knowledge_graphs_in_obsidian" role="doc-biblioref">8</a>]</span> etwas, das rein embeddings-basierte Ansätze so nicht leisten.</p>
</section>
<section id="integration-mit-llms" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="integration-mit-llms">Integration mit LLMs</h3>
<p>Die Integration eines Graphen erfordert meist eine <strong>Zwischenschicht</strong>. Ein LLM kann nicht direkt “in” einer Neo4j-Datenbank suchen, aber man kann ihm eine Schnittstelle anbieten. Zwei Strategien sind denkbar:</p>
<ol type="1">
<li><strong>Verbalize &amp; Prompt:</strong> Informationen aus dem Graph gezielt ins Prompt einbetten. Z.B. könnte man bei einer Frage wie “In welcher Organisation arbeitet Alice?” erst eine Graphdatenbank-Anfrage (z.B. in Cypher oder SPARQL) ausführen und das Ergebnis (etwa: “Alice arbeitetBei → AcmeCorp”) in Textform dem Modell vorgeben, bevor es antwortet. Solche Abfragen könnte ein LLM theoretisch sogar selbst generieren (LangChain bietet z.B. Agents, die Cypher-Queries formulieren und ausführen können). Für definierte Use-Cases kann man aber auch feste Query-Vorlagen verwenden.</li>
<li><strong>LLM-in-the-Loop Graph Reasoning:</strong> Neuere Libraries wie LlamaIndex ermöglichen es, LLMs als Reasoner über Graphen einzusetzen. Der Graph wird dabei intern z.B. als Tripel-Liste gehalten, und das LLM kann mittels promptbasierter Logik Kettenschlüsse durchführen. Allerdings muss der Graph dafür in das Prompt passen (bei sehr vielen Knoten unrealistisch) es ist also eher für Teilgraphen oder summarische Beziehungen geeignet<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a>.</li>
</ol>
<div class="no-row-height column-margin column-container"><div id="fn3"><p><sup>3</sup>&nbsp;Via Tool Use in Modernen LLM könnte das LLM selbst eine Suche auslösen und so den Teilgraphen wählen. Aber alleine die formulierung der Suche führt dann direkt zu dem hybriden Ansatz unten.</p></div></div><p>Eine andere interessante Möglichkeit ist die Nutzung <strong>graphbasierter KI-Modelle</strong> (Graph Neural Networks o.ä.), die aber in unserem Kontext (persönlicher Vault) noch experimentell sind. Erwähnenswert ist z.B. MyKin.ai, ein Projekt, das einen privaten KI-Assistenten baut, der gemeinsam mit dem Nutzer einen persönlichen Wissensgraphen aufbaut und nutzt <span class="citation" data-cites="personal_knowledge_graphs_in_obsidian">[<a href="#ref-personal_knowledge_graphs_in_obsidian" role="doc-biblioref">8</a>]</span>. Hier übernimmt die KI das “heavy lifting” der Graph-Pflege, während der Nutzer chattet ein hybrider Ansatz aus Conversation und Graphaufbau. Für unseren Anwendungsfall wäre jedoch eher ein statischer Graph sinnvoll, den wir periodisch aktualisieren.</p>
</section>
<section id="zusammenfassung---ansatz-2-graphdatenbank" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="zusammenfassung---ansatz-2-graphdatenbank">Zusammenfassung - Ansatz 2: Graphdatenbank</h3>
<table class="caption-top table">
<colgroup>
<col style="width: 4%">
<col style="width: 95%">
</colgroup>
<thead>
<tr class="header">
<th></th>
<th><strong>Details</strong></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><strong>Vorgehen</strong></td>
<td>Konvertiere Vault-Inhalte in einen strukturierten Graphen (Knoten = Notizen/Entitäten; Kanten = Obsidian-Links oder abgeleitete Relationen). Nutzen von Graph-DB (Neo4j, RDF-Store) für Abfragen und Analysen.</td>
</tr>
<tr class="even">
<td><strong>Stärken</strong></td>
<td>Explizite <strong>Struktur</strong>: ermöglicht genaue Abfragen, z.B. Finde fehlende Werte oder alle Verknüpfungen eines Knotens auf einen Blick. <strong>Logische Inferenzen</strong> möglich (Graph Reasoning) unterstützt Link-Empfehlungen und Konsistenzprüfungen <span class="citation" data-cites="ai_empowered_zettelkasten_with_ner_and_graph_llm">[<a href="#ref-personal_knowledge_graphs_in_obsidian" role="doc-biblioref">8</a>]</span>. Gute Ergänzung zu YAML-Typisierung: FileClass-Struktur wird vollständig nutzbar. Persistenz: Graph kann unabhängig von Obsidian analysiert, versioniert, mit anderen Daten gemappt werden (z.B. ORCID-Abgleich via externen Datensatz).</td>
</tr>
<tr class="odd">
<td><strong>Schwächen</strong></td>
<td>Erheblicher <strong>Initialaufwand</strong>: Datenmodell entwerfen, Export-Skripte schreiben oder Tools einrichten <span class="citation" data-cites="export_to_common_graph_formats">[<a href="#ref-export_to_common_graph_formats" role="doc-biblioref">7</a>]</span>. Keine fertige Out-of-the-box-Lösung für Obsidian↔Graph (bislang nur Ansätze in der Community). Laufende Synchronisation nötig (Vault-Änderungen -&gt; Graph-Update). Die LLM-Integration ist komplexer erfordert Query-Tool oder das Einbetten von Graph-Daten ins Prompt. Für offene Fragen (Freitext) allein nicht ausreichend, da der Graph primär Fakten repräsentiert, nicht Fließtext.</td>
</tr>
<tr class="even">
<td><strong>Integrations-Optionen</strong></td>
<td><strong>Neo4j</strong> (mit APOC/Neosemantics für RDF-Unterstützung) eignet sich für Property-Graph-Modell; <strong>Apache Jena</strong> oder GraphDB für RDF-Triple-Store. <em>LangChain</em> bietet Memory/Agent, um Wissensgraphen abzufragen (z.B. ConversationKGMemory). <em>LlamaIndex</em> hat einen KnowledgeGraphIndex, der Tripel extrahieren und durchs LLM traversieren kann. Diese Lösungen sind aber noch experimentell. Evtl. Kombination mit Obsidian-Plugin: Ein früher Plugin-Prototyp streamte Obsidian-Daten nach Neo4j <span class="citation" data-cites="export_to_common_graph_formats">[<a href="#ref-export_to_common_graph_formats" role="doc-biblioref">7</a>]</span> dieser könnte als Ausgangspunkt dienen.</td>
</tr>
</tbody>
</table>
<div class="cell page-columns page-full" data-layout-align="default">
<div class="cell-output-display column-screen-inset-right">
<div>
<p></p><figure class="figure"><p></p>
<div>
<pre class="mermaid mermaid-js">graph LR
A[Obsidian Vault] --&gt; B[Entity Extraction]
A --&gt; C[Relationship Extraction]
B --&gt; D[Graph Construction]
C --&gt; D
D --&gt; E[(Graph Database)]
F[User Query] --&gt; G[Query Parser]
G --&gt; H[Graph Traversal]
E --&gt; H
H --&gt; I[Structured Facts]
I --&gt; J[LLM]
F --&gt; J
J --&gt; K[Response]
</pre>
</div>
<p></p></figure><p></p>
</div>
</div>
</div>
</section>
<section id="fazit-graphdatenbank" class="level3">
<h3 class="anchored" data-anchor-id="fazit-graphdatenbank">Fazit Graphdatenbank</h3>
<p>Ein Wissensgraph spielt seine Stärken vor allem bei <strong>strukturbezogenen Aufgaben</strong> aus. Für das automatische Ausfüllen von YAML-Feldern oder das Prüfen von Verlinkungen ist er ideal, da solche Fragen direkte Graphabfragen ermöglichen. Auch für neuartige Verknüpfungen (Link-Vorschläge) lässt sich ein Graph analytisch nutzen (z.B. “Link Prediction” auf Basis von Graph-Nachbarschaft). Allerdings ist die Umsetzung deutlich komplexer als beim Vektorstore, und viele RAG-Anwendungsfälle (Zusammenfassungen, inhaltliche Q&amp;A) erfordern trotzdem den Rückgriff auf die eigentlichen Texte was wiederum den Vektoransatz benötigt. Daher bietet sich oft eine Kombination beider Methoden an.</p>
</section>
</section>
<section id="hybrid-ansatz-kombination-aus-graph-und-vektor-rag" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="hybrid-ansatz-kombination-aus-graph-und-vektor-rag">3. Hybrid-Ansatz: Kombination aus Graph und Vektor-RAG</h2>
<p>Dieser Ansatz versucht, <strong>semantische Textsuche</strong> und <strong>strukturierte Graph-Abfragen</strong> zu vereinen, um die Vorteile beider Welten auszuschöpfen. In der Praxis gibt es mehrere Möglichkeiten, wie ein hybrides System ausgestaltet sein kann:</p>
<ul>
<li><strong>Parallelbetrieb mit separaten Pipelines:</strong> Vektorstore und Knowledge Graph werden beide gepflegt. Je nach Anfrage oder Teilaufgabe wird das eine oder andere genutzt. Beispiel: Für eine Q&amp;A-Frage holt das System erst relevante Text-Passagen via Vektorstore, <strong>und</strong> prüft zusätzlich im Graph, welche Entitäten darin vorkommen und ruft deren Beziehungen ab. Das LLM bekäme dann sowohl inhaltliche Ausschnitte als Kontext als auch strukturierte Fakten (z.B. <em>“Alice arbeitetBei AcmeCorp”</em>) als Knowledge-Panel. Für die Aufgabe <em>Link-Vorschläge</em> könnte das System sowohl einen Embedding-Vergleich zwischen Notizen nutzen (um thematisch ähnliche Notes zu finden), als auch den Graphen auswerten (um strukturell nahe, aber unverbundene Knoten zu entdecken). Die finalen Vorschläge wären die <strong>Schnittmenge</strong> bzw. <strong>Union</strong> beider Methoden das erhöht Präzision und Reichweite der Empfehlungen.</li>
<li><strong>Integration innerhalb einer Datenplattform:</strong> Moderne Vector-Datenbanken wie <em>Weaviate</em> erlauben es, semantische Vektorsuche mit symbolischen Filtern zu kombinieren. Man kann Objekte (hier: Notizen) mit ihren strukturierten Feldern in Weaviate speichern und neben dem Vektorindex auch die Metadaten abfragen. Z.B. könnte man eine Query formulieren: <em>“Gib mir die 5 ähnlichsten Notizen zu <code>[Text]</code>, die vom Typ Projekt sind und nach 2020 erstellt wurden.”</em> Weaviate würde erst nach Ähnlichkeit filtern, dann die Metadaten-Bedingungen anwenden. So eine <strong>hybride Suche</strong> könnte man nutzen, um etwa bei Template-Befüllung <strong>nur vergleichbare Objekte</strong> zum Prompt hinzuzufügen (z.B. nur andere Organisationen, keine Meeting-Notizen). Auch ChromaDB arbeitet an Feature-Filterfunktionen, die so etwas erlauben würden. Alternativ kann man den Graphen selbst mit Vektor-Embeddings anreichern: Man könnte jedem Knotentyp einen eigenen Vektor zuordnen, der den gesamten Inhalt der zugehörigen Notiz(en) repräsentiert. Diese Vektoren ließen sich im Graphen als Attribut halten und für Ähnlichkeitssuchen zwischen Knoten verwenden (<em>knowledge graph embeddings</em>). Allerdings ist das experimentell man müsste z.B. bei Kanten-Traversierung dynamisch Nachbarschaftsvektoren kombinieren, was nicht trivial ist.</li>
<li><strong>LLM als Orchestrator:</strong> Hier steuert das LLM, wann welcher Ansatz gezogen wird. Beispielsweise könnte man ein System bauen, in dem das LLM zunächst entscheidet: <em>“Brauche ich strukturiertes Wissen?”</em> Wenn ja, könnte es per Tool-Use einen Graph-Query durchführen (z.B. via Cypher) eine Technik, die mit LangChain Agents umsetzbar wäre. Danach würde es ggf. einen zweiten Schritt machen: <em>“Benötige ich noch Detailinformationen oder Zitate?”</em> dann die Vektor-Datenbank abfragen, relevante Textstücke holen, und schließlich alles in einer konsolidierten Antwort formulieren. Dieser agentenbasierte Ansatz ist sehr flexibel, aber auch am anspruchsvollsten in der Implementierung (er erfordert zuverlässig trainierte/verfeinerte LLM-Prompts, die wissen, wann und wie die jeweiligen Werkzeuge zu benutzen sind).</li>
</ul>
<section id="vor-nachteile" class="level3">
<h3 class="anchored" data-anchor-id="vor-nachteile">Vor-/Nachteile</h3>
<p>Die Hybridlösung verspricht <strong>maximale Abdeckung</strong> der Anwendungsfälle. Strukturierte Fakten und unstrukturierte Inhalte können gemeinsam dem LLM präsentiert werden, was sowohl präzise Faktenkenntnis als auch reichhaltigen Kontext ermöglicht. Gerade für komplexe Aufgaben etwa das automatisierte Erstellen einer neuen Entitätenseite wären wohl beide Aspekte wichtig: das LLM müsste sich an vorhandenen ähnlichen Seiten <strong>inhaltlich</strong> orientieren (Vektorsuche nach ähnlichen Organisations-Beschreibungen) und zugleich <strong>korrekte Verknüpfungen</strong> setzen (Graph checken, ob z.B. die neue Organisation bereits Personen im Vault hat, die als Mitarbeiter verknüpft werden sollten). Ein solches System könnte also dem Nutzer sehr viel Arbeit abnehmen und dabei konsistente, vernetzte Notizen erzeugen.</p>
<p>Dem steht jedoch ein hoher <strong>Architekturund Wartungsaufwand</strong> gegenüber. Man muss im Grunde zwei Systeme aufbauen und aktuell halten. Zudem ist die Logik, wie die Ergebnisse zusammenfließen, nicht trivial. Ohne gutes Design kann es passieren, dass der Graph-Teil und der Vektor-Teil widersprüchliche oder redundante Informationen liefern. Auch muss man Performance beachten doppelte Abfragen kosten mehr Zeit. In vielen Fällen mag auch ein einzelner Ansatz ausreichen, sodass die Zusatzkomplexität nicht immer gerechtfertigt ist.</p>
</section>
<section id="integrationsmöglichkeiten" class="level3">
<h3 class="anchored" data-anchor-id="integrationsmöglichkeiten">Integrationsmöglichkeiten</h3>
<p>Auf technischer Seite ist so ein hybrides System durchaus machbar. Beispielsweise ließe sich <strong>LlamaIndex</strong> verwenden, um unterschiedliche Indexe (VectorIndex, KnowledgeGraphIndex) zu kombinieren es gibt Konzepte wie “Composable Indices”, mit denen man hierarchische Abfragen bauen kann. So könnte man erst den Graph nach relevanten Knoten filtern und dann nur die zugehörigen Dokumente vektor-suchen (oder umgekehrt). Weaviate als All-in-one-Lösung wurde bereits erwähnt. In kleineren Umgebungen kann man auch pragmatisch vorgehen: Ein Python-Skript, das bei bestimmten Fragen zuerst einen Neo4j-Query absetzt und dessen Ergebnis dem LLM als Teil des Prompts voranstellt, während es parallel eine Chroma-Query macht, wäre eine einfache implementierbare Variante.</p>
</section>
<section id="zusammenfassung-ansatz-3-hybrid-lösung" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="zusammenfassung-ansatz-3-hybrid-lösung">Zusammenfassung Ansatz 3: Hybrid-Lösung</h3>
<table class="caption-top table">
<colgroup>
<col style="width: 5%">
<col style="width: 94%">
</colgroup>
<thead>
<tr class="header">
<th></th>
<th><strong>Details</strong></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><strong>Vorgehen</strong></td>
<td>Kombination beider Ansätze: Pflege einer Graph-Struktur <strong>und</strong> eines Vektorindex. Nutzung je nach Bedarf entweder separat oder durch orchestrierte Abfragen, um sowohl strukturiertes Wissen als auch relevante Texte bereitzustellen.</td>
</tr>
<tr class="even">
<td><strong>Stärken</strong></td>
<td>Sehr <strong>leistungsfähig</strong> deckt sowohl faktische als auch kontextuelle Fragen ab. Kann die <strong>höchste Antwortqualität</strong> liefern (Konsistenz durch Graph-Fakten, Detail durch Textauszüge). Hilft, sowohl “Known-item” Suchen (explizite Werte) als auch “Open-ended” Suchen (Texte) zu bedienen. Für Link-Vorschläge ideal: Kombination aus semantischer Ähnlichkeit und Graph-Nachbarschaft erhöht Trefferquote sinnvoll.</td>
</tr>
<tr class="odd">
<td><strong>Schwächen</strong></td>
<td><strong>Sehr komplex</strong> in Umsetzung und Wartung. Erfordert doppelte Infrastruktur. Koordination zwischen Graph und Vectorstore nötig potenziell fehleranfällig. Höhere Latenz durch Mehrfach-Abfragen. Nur lohnend, wenn wirklich vielfältige Aufgaben automatisiert werden sollen; für rein textliche Q&amp;A overkill.</td>
</tr>
<tr class="even">
<td><strong>Integrations-Optionen</strong></td>
<td>Weaviate (Vectors + strukturierte Class-Properties in einem System), oder Kombination aus Neo4j + Chroma. LangChain Agents könnten Graphund Vektor-Tools parallel nutzen. <strong>LlamaIndex</strong> bietet experimentell kombinierbare Indizes. Workflows müssen sorgfältig entworfen werden (z.B. zuerst Graph-Query, dann Vector-Query auf Untermenge).</td>
</tr>
</tbody>
</table>
<div class="cell page-columns page-full" data-layout-align="default">
<div class="cell-output-display column-screen-inset-right">
<div>
<p></p><figure class="figure"><p></p>
<div>
<pre class="mermaid mermaid-js">graph LR
A[Obsidian Vault] --&gt; B[Chunking]
A --&gt; C[Entity Extraction]
B --&gt; D[Embedding Model]
C --&gt; E[Graph Construction]
D --&gt; F[(Vector Database)]
E --&gt; G[(Graph Database)]
H[User Query] --&gt; I[Query Embedding]
H --&gt; J[Graph Query]
I --&gt; K[Vector Search]
J --&gt; L[Graph Traversal]
F --&gt; K
G --&gt; L
K --&gt; M[Text Context]
L --&gt; N[Structured Facts]
M --&gt; O[Combined Context]
N --&gt; O
H --&gt; P[LLM]
O --&gt; P
P --&gt; Q[Enriched Response]
</pre>
</div>
<p></p></figure><p></p>
</div>
</div>
</div>
</section>
<section id="fazit-hybrid-lösung" class="level3">
<h3 class="anchored" data-anchor-id="fazit-hybrid-lösung">Fazit Hybrid-Lösung</h3>
<p>Die Hybrid-Lösung ist die <strong>ambitionierteste</strong>, aber auch zukunftsträchtigste Option. Sie empfiehlt sich, wenn sowohl inhaltliche Assistenz (Texte zusammenfassen, beantworten) als auch datenbankartige Operationen (Felder validieren, Beziehungen auswerten) gefragt sind was hier der Fall ist. Oft kann man auch schrittweise vorgehen: zunächst mit einem Vektor-RAG starten (geringerer Aufwand) und dann gezielt Graph-Features ergänzen, sobald z.B. Link-Empfehlungen oder Konsistenzprüfungen wichtiger werden.</p>
</section>
</section>
<section id="datenaufbereitung-yaml-metadaten-extrahieren-und-normalisieren" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="datenaufbereitung-yaml-metadaten-extrahieren-und-normalisieren">4. Datenaufbereitung: YAML-Metadaten extrahieren und normalisieren</h2>
<p>Unabhängig vom gewählten Retrieval-Ansatz ist es essenziell, die in YAML front matter steckenden strukturierten Informationen effektiv zu nutzen. Die Obsidian-Plugins <em>Metadata Menu</em> und <em>Templater</em> stellen sicher, dass viele wichtige Daten bereits sauber in den Notizen vorliegen (z.B. hat eine Personenseite Felder wie <code>fullname</code>, <code>birthdate</code>, <code>ORCID</code> usw.). Ein LLM könnte zwar theoretisch auch direkt im Markdown nach diesen Mustern suchen, aber es ist deutlich effizienter, die Daten einmalig zu <strong>extrahieren</strong> und in einer leichter nutzbaren Form vorzuhalten.</p>
<section id="extraktion" class="level3">
<h3 class="anchored" data-anchor-id="extraktion">Extraktion</h3>
<p>Ein möglicher Schritt im täglichen Refresh ist ein Skript, das alle Dateien durchläuft und die YAML-Blöcke parst (z.B. mit einem YAML-Parser in Python oder JavaScript). Die extrahierten Felder können dann in eine <strong>normale Datenbank</strong> (SQLite/CSV/JSON) oder direkt als Knoten/Properties in den Knowledge Graph überführt werden. Damit erhält man z.B. eine Tabelle aller Personen mit ihren ORCID-IDs, eine Liste aller Projekte mit Start-/Enddatum etc.</p>
</section>
<section id="normalisierung" class="level3">
<h3 class="anchored" data-anchor-id="normalisierung">Normalisierung</h3>
<p>Oft müssen die Rohwerte etwas vereinheitlicht werden. Beispielsweise sollten Datumsangaben ein konsistentes Format haben, Personennamen evtl. in Vor-/Nachname zerlegt, und fehlende Felder explizit als <code>null</code> markiert werden. Außerdem kann man hier foreign-key-Bezüge auflösen: Wenn z.B. im YAML einer Publikation <code>author: "[[Doe, John]]"</code> steht, könnte das Skript erkennen, dass dies die Person mit UID XYZ ist, und entsprechend in der extrahierten Struktur statt des Link-Codes einen eindeutigen Verweis (auf die Person John Doe) speichern. Diese Normalisierung erleichtert nachfolgende Analysen enorm insbesondere kann man einfache Regeln ableiten, die dann vom LLM geprüft oder genutzt werden. Zum Beispiel: <em>“Wenn <code>person.ORCID</code> leer ist, schlage vor, ihn zu ergänzen”</em> das kann das LLM dann direkt als Aufforderung bekommen. Oder: <em>“Beim Erstellen einer neuen Person fülle Felder X,Y nach Vorlage aus”</em> hier weiß man aus der YAML-Definition bereits, welche Felder existieren müssen.</p>
</section>
<section id="nutzung-durch-llm" class="level3">
<h3 class="anchored" data-anchor-id="nutzung-durch-llm">Nutzung durch LLM</h3>
<p>Der aufbereitete YAML-Datensatz kann auf zwei Weisen eingebunden werden:</p>
<ul>
<li><strong>Inline im Prompt:</strong> Für bestimmte Aufgaben kann man dem LLM direkt Ausschnitte aus dieser strukturierten Sammlung geben. Etwa: <em>“In unserer Datenbank fehlt für <code>Person[42]</code> der ORCID. Hier ist eine Liste aller Personennamen mit ORCID, finde anhand des Namens den passenden ORCID und trage ihn ein.”</em> Falls die Person woanders erwähnt wurde, könnte das Modell es herausfinden. (Eher unwahrscheinlich ohne Internetzugriff ORCID erfordert eher einen API-Call, aber zumindest könnte das LLM erkennen, <em>dass</em> es fehlt und ggf. den Nutzer nach der ID fragen). Für Link-Empfehlungen könnte man dem LLM eine Liste aller Titel geben oder besser direkt die Graph-Info wie <em>“Person A und Person B haben 3 gemeinsame Projekte”</em> siehe Hybrid-Ansatz.</li>
<li><strong>Programmatisch außerhalb des LLMs:</strong> Viele Routineaufgaben lassen sich erkennen, ohne das LLM zu bemühen. Man könnte einen Teil der Automatisierung rein mit Skripten vorab erledigen. Z.B. neue Links: Ein Skript könnte alle Personennamen im Fließtext durchsuchen und prüfen, ob sie bereits als <code>[[Link]]</code> markiert sind; wenn nicht, die Stelle hervorheben und dem LLM als <em>“Kandidat für Verlinkung”</em> präsentieren. Oder bei einer neuen Organisation könnten automatisch Felder aus externen APIs gezogen und ins Template eingetragen werden (sofern erlaubt). Das LLM hätte dann eher die Rolle, die zusammengestellten Infos in schönen Prosa-Text zu gießen, anstatt die Fakten selbst zu suchen.</li>
</ul>
</section>
<section id="beispiel-workflows" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="beispiel-workflows">Beispiel-Workflows</h3>
<ul>
<li>YAML-Exports lassen sich mit vorhandenen Tools unterstützen. Es gibt z.B. das Obsidian-Plugin <em>Dataview</em>, welches Abfragen auf YAML ermöglichen kann allerdings nur innerhalb Obsidian. Man könnte aber ein Dataview JS-Skript<a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a> schreiben, das alle Einträge eines Typs ausgibt, und diese Output-Datei dann weiterverarbeiten. Alternativ direkt auf Dateisystemebene arbeiten: Python mit <code>os</code> und <code>pyyaml</code> kann alle <code>.md</code> Files scannen.</li>
<li>Die extrahierten Daten kann man mit dem Graph-Ansatz koppeln: etwa alle Personen ohne ORCID als Cypher-Query generieren lassen und automatisch in eine “ToDo”-Liste (Obsidian Note) schreiben, die vom LLM oder Nutzer geprüft wird.</li>
<li>Durch Templates sind die Felder pro FileClass ja bekannt. Diese Knowledge kann ins Prompt fließen: <em>“Eine Organisation hat die Felder Name, Typ, Beschreibung, Mitarbeiter, etc. Fülle basierend auf den folgenden Infos…”</em> Das Modell weiß dann genau, welche YAML-Spalten es ausgeben soll.</li>
</ul>
<div class="no-row-height column-margin column-container"><div id="fn4"><p><sup>4</sup>&nbsp;oder Plugins wie <em>Dataview-Publisher</em> benutzen, die die Ergebnisse als Markdown-Tabell in ein Dokument schreiben</p></div></div></section>
<section id="vor--nachteile" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="vor--nachteile">Vor- &amp; Nachteile</h3>
<p>Die <strong>Vorteile</strong> der strukturierten Extraktion liegen auf der Hand Performance und Präzision. Man muss nicht jedes Mal den gesamten Markdown-Text durchsuchen, um z.B. den Wert eines bestimmten Feldes zu finden; man hat ihn direkt. Außerdem reduziert es die Abhängigkeit vom LLM für einfache Aufgaben (Daten finden, vergleichen). Für die meisten Menschen ist es auch leichter zu verstehen und zu prüfen, wenn man z.B. eine CSV mit allen ORCIDs hat, als wenn man dem LLM blind glauben muss.<br>
Als <strong>Nachteil</strong> kann gesehen werden, dass es zusätzlicher Implementierungsaufwand ist und eine gewisse Duplizierung der Daten (die YAML-Inhalte leben dann in zwei Formen: im Markdown und in der extrahierten Sammlung). Die Synchronisation muss bei Änderungen immer gewährleistet sein (Cronjob). Allerdings ist das, verglichen mit dem Aufwand der LLM-Integration, relativ gering und gut automatisierbar.</p>
<div class="cell page-columns page-full" data-layout-align="default">
<div class="cell-output-display column-screen-inset-right">
<div>
<p></p><figure class="figure"><p></p>
<div>
<pre class="mermaid mermaid-js">graph LR
A[Obsidian Vault] --&gt; B[FileClass Detection]
B --&gt; C[Type-Specific Extraction]
C --&gt; D[YAML Parser]
D --&gt; E[Data Validation]
E --&gt; F[Type Normalization]
F --&gt; G[(Typed Collections)]
H[Task Request] --&gt; I[Schema Lookup]
I --&gt; J[Targeted Data Fetch]
G --&gt; J
J --&gt; K[Context Assembly]
H --&gt; K
K --&gt; L[LLM Processing]
L --&gt; M[Schema-Aware Output]
</pre>
</div>
<p></p></figure><p></p>
</div>
</div>
</div>
</section>
<section id="zusammenfassung-ansatz-4-extraktion" class="level3">
<h3 class="anchored" data-anchor-id="zusammenfassung-ansatz-4-extraktion">Zusammenfassung Ansatz 4: Extraktion</h3>
<p>In jedem Fall sollte man eine Pipeline vorsehen, die die YAML-<strong>Metadaten extrahiert</strong> und in eine strukturierte Form bringt. Diese bildet das Rückgrat für den Knowledge-Graph-Ansatz (ohne diese wären die Knoten nackte Titel ohne Attribute) und ist auch für Vektor-RAG nützlich (z.B. als Filter oder zur post-processing der LLM-Antworten). Insbesondere dank der FileClass-Typisierung im Vault kann man hier sehr <strong>zielgerichtet</strong> vorgehen etwa nur definierte Entitätstypen verarbeiten. In Community-Diskussionen wurde vorgeschlagen, YAML-Metadaten zu nutzen, um AI-Aufgaben einzuschränken: z.B. NER-Modelle nur auf bestimmten Notizen laufen zu lassen, die laut YAML einen bestimmten Typ haben <span class="citation" data-cites="ai_empowered_zettelkasten_with_ner_and_graph_llm">[<a href="#ref-ai_empowered_zettelkasten_with_ner_and_graph_llm" role="doc-biblioref">9</a>]</span>. Solche Optimierungen werden durch saubere strukturelle Aufbereitung erst möglich.</p>
</section>
</section>
<section id="automatisierungstools-und-workflows" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="automatisierungstools-und-workflows">5. Automatisierungstools und Workflows</h2>
<p>Für die Umsetzung der oben beschriebenen Ansätze gibt es bereits einige Tools, Projekte und Best Practices, die man nutzen oder von denen man lernen kann. Hier eine strukturierte Übersicht samt Empfehlungen:</p>
<section id="obsidian-plugins-in-app-ki-features" class="level3">
<h3 class="anchored" data-anchor-id="obsidian-plugins-in-app-ki-features">Obsidian-Plugins (In-App KI-Features)</h3>
<ul>
<li><em>Smart Connections:</em> Plugin, das innerhalb Obsidian mit lokalen Embeddings arbeitet, um <strong>ähnliche Notizen</strong> zu finden, und einen Chatbot bereitstellt. Es kann ein lokales LLM (oder OpenAI API) einbinden und versorgt es automatisch mit Kontext aus dem Vault <span class="citation" data-cites="smart_connections_plugin">[<a href="#ref-smart_connections_plugin" role="doc-biblioref">2</a>]</span>. Vorteil: einfache Installation, enge Vault-Integration (Antworten können direkt als Notiz eingefügt werden). Nachteil: begrenzt anpassbar der Workflow ist vordefiniert (hauptsächlich Q&amp;A Chat). Für den Start aber exzellent, um ein Gefühl für RAG im eigenen Vault zu bekommen.</li>
<li><em>Khoj:</em> Ein Open-Source Projekt, bestehend aus einem lokalen Backend <span class="citation" data-cites="khoj_plugin">[<a href="#ref-khoj_plugin" role="doc-biblioref">4</a>]</span> und Obsidian-Plugin. Ermöglicht <strong>natürliche Sprachsuche</strong> und Chat über die eigenen Notizen <span class="citation" data-cites="khoj_plugin">[<a href="#ref-khoj_plugin" role="doc-biblioref">4</a>]</span>. Es kann sowohl online-Modelle (GPT-4 etc.) als auch lokale Modelle nutzen <span class="citation" data-cites="build_your_second_brain_with_khoj_ai">[<a href="#ref-build_your_second_brain_with_khoj_ai" role="doc-biblioref">10</a>]</span>. Khoj fokussiert auf schnelle semantische Suche; der Chat-Teil ist vor allem QA-orientiert. Als persönlicher Suchassistent ist es sehr interessant etwa um via Obsidian Command Palette Fragen ans Vault zu stellen. Es ist weniger darauf ausgelegt, automatisch Links zu erzeugen oder YAML zu verändern (dafür wäre wiederum ein LLM mit Schreibrechten nötig).</li>
<li><em>Obsidian Copilot / GPT-Assistant:</em> Es existieren mehrere Plugins, die GPT-3/4 in Obsidian integrieren (teils auch lokal via LLaMA). Diese sind im Prinzip UI-Verbesserungen, um das LLM “im Editor” zu nutzen. Für RAG kann man sie einsetzen, indem man manuell Kontext reinkopiert, aber automatisches Retrieval bieten sie nicht ohne weiteres.</li>
<li><em>Obsidian Neo4j Plugin (Experimentell):</em> Das erwähnte <em>obsidian-neo4j-stream</em> von @HEmile <span class="citation" data-cites="export_to_common_graph_formats">[<a href="#ref-export_to_common_graph_formats" role="doc-biblioref">7</a>]</span> könnte als Ausgangspunkt dienen, falls man die Graph-Route ausprobieren will. Es war dazu gedacht, den Vault als kontinuierlichen Stream in Neo4j zu spiegeln. Leider wurde es nicht fertiggestellt/maintained. Dennoch ließe sich der Code evtl. anpassen, um zumindest einmalig einen Export durchzuführen. Alternativ: Im Obsidian-Forum gibt es auch Beispiele, wie man mit ein paar Skriptzeilen alle Links extrahieren kann. Zusammen mit den YAML-Daten könnte man so einen Basic-Graphen schon bekommen.</li>
</ul>
</section>
<section id="externe-anwendungen-skripte" class="level3 page-columns page-full">
<h3 class="anchored" data-anchor-id="externe-anwendungen-skripte">Externe Anwendungen / Skripte</h3>
<ul>
<li><em>LlamaIndex (GPT Index):</em> Diese Python-Bibliothek ist eine <strong>Schweizer Taschenmesser</strong> für RAG. Man kann Dokumente laden (Markdown wird unterstützt), unterschiedliche Indizes erstellen (Vector, List, KnowledgeGraph etc.) und Abfragen mit LLM orchestrieren. Sie eignet sich, um schnell Prototypen zu bauen. Beispielsweise könnte man einen <strong>KnowledgeGraphIndex</strong> erstellen, der mittels Instruct-LLM Tripel aus den Notizen extrahiert (z.B. “Person X arbeitet für Organisation Y”). Anschließend kann man Abfragen in natürlicher Sprache stellen, die vom LLM in Graph-Traversals übersetzt werden. Oder man nutzt den simpleren VectorIndex auf Markdown-Chunks. LlamaIndex kann auch <strong>Komposition</strong>: man könnte pro FileClass einen Index bauen (z.B. alle Personen in einem VectorIndex, alle Projekte in einem anderen) und dann einen übergeordneten Query laufen lassen. Diese Flexibilität ist mächtig aber es erfordert eben etwas Programmierung. Für einen produktiven Workflow (täglicher Cronjob) müsste man ein eigenes Python-Skript schreiben, das die Indizes aktualisiert.</li>
<li><em>LangChain:</em> Ein Framework v.a. für komplexere Chains und Agenten. Es liefert Bausteine, um z.B. eine Tool-using Agent zu bauen, die mit einer <strong>Vector DB Suche</strong> und einer <strong>Graph-DB Abfrage</strong> als Tools ausgestattet ist. Damit ließe sich ein Dialogsystem kreieren, das je nach Frage entscheidet, ob es den Neo4j-Graph oder den Chroma-Vektorindex konsultiert. Allerdings setzt dies einiges an Prompt Engineering voraus, damit der Agent zuverlässig funktioniert. Alternativ kann man LangChain auch einfach nutzen, um entweder Vector-search oder Graph-DB-Queries einzeln bequemer zu machen (es gibt z.B. vorgefertigte Neo4j Retriever-Klassen etc.).</li>
<li><em>Haystack:</em> Das von deepset (evtl. in der Frage mit “Deepseek” gemeint) entwickelte Open-Source-Toolkit <strong>Haystack</strong> ist ebenfalls auf Dokumenten-QA spezialisiert. Es unterstützt das Indexieren von Markdown, verschiedene Vector-Backends und kann auch Knowledge-Graph-Komponenten integrieren. Zudem hat es Pipeline-Knoten zum z.B. Fragenklassifizieren, dass bestimmte Fragen an bestimmte Reader geleitet werden. Für einen produktiven Einsatz mit lokalem UI ggf. eine Option. Allerdings eher heavy-weight und auf QA fokussiert, weniger auf Wissensbasis-Pflege.</li>
<li><em>privateGPT / llama.cpp based scripts:</em> Für einfache Frage-Antwort-Systeme auf dem eigenen Vault kann man vorhandene Lösungen wie <em>privateGPT</em> oder <em>GPT4All</em> (mit UI) verwenden <span class="citation" data-cites="second_brain_assistant_with_obsidian">[<a href="#ref-second_brain_assistant_with_obsidian" role="doc-biblioref">3</a>]</span>. Diese bringen einen Großteil der Vector+LLM Pipeline schon fertig mit. Sie indexieren Ordner voller Dokumente (auch Markdown) und erlauben dann Queries an ein lokales Modell. Der Anpassungsspielraum (z.B. andere Tasks als reines QA) ist aber gering. Als <strong>Baseline</strong> sind sie nützlich man könnte damit z.B. testen, wie gut ein LLM mit den eingebetteten Obsidian-Notizen Fragen beantwortet, und daraus Anforderungen ableiten.</li>
<li><em>Basic Memory (basicmachines):</em> Ein innovativer Ansatz ist hier zu erwähnen: <strong>Basic Memory</strong> speichert AI-Konversationen als Markdown in Obsidian und baut daraus sukzessive einen semantischen Wissensgraph <span class="citation" data-cites="basic_memory_ai_conversations_that_build_knowledge">[<a href="#ref-basic_memory_ai_conversations_that_build_knowledge" role="doc-biblioref">11</a>]</span>. D.h. wenn man mit dem LLM chatbasiert arbeitet, erstellt das Tool automatisch Notizen und verbindet sie (z.B. werden erkannte Entitäten verlinkt). Es ist quasi das Gegenstück zu unserem Problem statt einen bestehenden Vault zu nutzen, erzeugt es einen Vault. Dennoch kann man sich dort Konzepte abschauen: z.B. wie strukturierte Notizen aus LLM-Ausgaben generiert werden können, oder wie man <em>bi-direktional</em> arbeitet (User editiert Notiz, KI liest Änderungen beim nächsten Mal). Basic Memory setzt auf lokale Dateien und betont Privatsphäre, was dem hiesigen Anforderungsprofil ähnelt. Für die konkreten Aufgaben (ORCID-Suche, Link-Vorschlag) liefert es zwar keine fertige Lösung, aber die <strong>Idee, KI beim Nutzer Notizen anlegen/ändern zu lassen,</strong> ist hier praktisch umgesetzt.</li>
<li><strong>Externe APIs / Datenquellen:</strong><br>
Für bestimmte Felder wie ORCID wird ein rein lokales LLM kaum die Werte erraten können, sofern sie nicht schon irgendwo im Vault stehen. Falls Internetzugriff eine Option ist, könnte man ein Plugin oder einen Workflow integrieren, der <strong>ORCID API</strong> Abfragen durchführt (z.B. über den Namen der Person) und die ID zurückliefert. Ein LLM-Agent könnte auch so einen API-Call ausführen (via Tools in LangChain). Alternativ: Alle bekannten ORCID-IDs der eigenen Personen könnte man in einer Datei sammeln; wenn das LLM eine Lücke findet, bittet es den Nutzer um Input. Hier muss man die Limitierungen eines LLM realistisch sehen und ggf. klassische Automatisierung (API-Skripte) kombinieren.</li>
</ul>
<div class="cell page-columns page-full" data-layout-align="default">
<div class="cell-output-display column-screen-inset-right">
<div>
<p></p><figure class="figure"><p></p>
<div>
<pre class="mermaid mermaid-js">graph LR
subgraph Obsidian
A[Vault] --&gt; B[Plugins]
B --&gt; C[Templater]
B --&gt; D[Metadata Menu]
B --&gt; E[AI Assistant]
end
subgraph External Processing
A --&gt; F[Daily Export]
F --&gt; G[Data Processing]
G --&gt; H[LLM Analysis]
H --&gt; I[Automation Scripts]
end
subgraph Integration
I --&gt; J[Change Proposals]
J --&gt; K[User Review]
K --&gt; L[Accepted Changes]
L --&gt; M[Vault Updates]
M --&gt; A
end
</pre>
</div>
<p></p></figure><p></p>
</div>
</div>
</div>
</section>
</section>
<section id="zusammenfassende-empfehlung" class="level2">
<h2 class="anchored" data-anchor-id="zusammenfassende-empfehlung">Zusammenfassende Empfehlung</h2>
<p>Für einen ersten Prototypen empfiehlt es sich, mit dem <strong>Vektorstore-Ansatz (1)</strong> zu beginnen, da dieser am schnellsten sichtbare Erfolge bringt. Man kann z.B. mit ChromaDB + einem lokalen LLM experimentieren, oder direkt das Smart-Connections-Plugin ausprobieren, um ein Gefühl für semantische Suche im Vault zu bekommen. Die YAML-Daten sollte man von Anfang an <strong>mit-extrahieren (4)</strong>, da sie die Grundlage für weitere Strukturierungsmaßnahmen bilden. Anschließend kann man gezielt <strong>Graph-Features (2)</strong> ergänzen: etwa den exportierten Vault in Neo4j laden und ein paar Abfragen formulieren, um Missing Links oder fehlende Felder aufzuspüren. Mittelfristig dürfte eine <strong>Kombination (3)</strong> notwendig sein, um sowohl Inhalt als auch Struktur abzudecken dies kann man Schritt für Schritt angehen (z.B. zunächst Vector-RAG für inhaltliche Fragen, und separate Tools/Reports für strukturierte Checks; später dann Integration zu einem einheitlichen KI-Assistenten). Unterstützend sollte man vorhandene <strong>Tools (5)</strong> nutzen, wo möglich z.B. Khoj für ad-hoc Fragen, oder LlamaIndex für schnelle Implementierung von Prototypen. Generell gilt: lokale LLMs sind inzwischen leistungsfähig genug für solche Aufgaben, wie die genannten Beispiele zeigen (Chat mit Vault über LLaMA etc.). Wichtig ist es, die <strong>Vault-Organisation</strong> konsequent weiterzuführen (FileClasses, Templates), da ein sauber strukturiertes Wissen die Grundlage für jede erfolgreiche RAG-Lösung ist egal ob Vektor, Graph oder hybrid.</p>
</section>
<section id="quellen" class="level2">
<h2 class="anchored" data-anchor-id="quellen">Quellen</h2>
<p>Die Analyse basiert auf aktuellen Erkenntnissen aus der Obsidian-Community und KI-Fachwelt, u.a. Erfahrungen mit semantischer Suche <span class="citation" data-cites="smart_connections_plugin">[<a href="#ref-smart_connections_plugin" role="doc-biblioref">2</a>]</span>, Diskussionen zu Knowledge Graphs in PKM <span class="citation" data-cites="ai_empowered_zettelkasten_with_ner_and_graph_llm">[<a href="#ref-ai_empowered_zettelkasten_with_ner_and_graph_llm" role="doc-biblioref">9</a>]</span> und Berichten über lokale RAG-Implementierungen <span class="citation" data-cites="local_free_rag_with_question_generation">[<a href="#ref-smart_connections_plugin" role="doc-biblioref">2</a>]</span>.</p>
</section>
<div id="quarto-appendix" class="default"><section id="methodik-llms-als-autoren" class="level2 appendix"><h2 class="anchored quarto-appendix-heading">Methodik / LLMs als Autoren</h2><div class="quarto-appendix-contents">
<p>Erstellt wurde der initial draft mittels Websuche und “Deep-Research” von <code>gpt-4.5 (preview)</code>. Systematische Überarbeitungen (Extraktion Bibliographie, Überarbeitung Metadaten) mittels <code>cogito-v0.1</code> im Editor. Übernahme nach manueller Prüfung. Erstellung der Mermaid-Diagramme mittels <code>Claude 3.7 Sonnet</code>. Abschließendes Korrekturlesen/inhaltliche Prüfung/Layouting durch Nicole Dresselhaus.</p>
</div></section><section class="quarto-appendix-contents" role="doc-bibliography" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">Literatur</h2><div id="refs" class="references csl-bib-body" data-entry-spacing="0" role="list">
<div id="ref-ollama_chroma_cookbook" class="csl-entry" role="listitem">
<div class="csl-left-margin">1. </div><div class="csl-right-inline"><a href="https://cookbook.chromadb.dev/integrations/ollama/embeddings/">Ollama - Chroma Cookbook</a>. 2024.</div>
</div>
<div id="ref-smart_connections_plugin" class="csl-entry" role="listitem">
<div class="csl-left-margin">2. </div><div class="csl-right-inline"><a href="https://www.reddit.com/r/ObsidianMD/comments/1fzmkdk/just_wanted_to_mention_that_the_smart_connections/">Just wanted to mention that the smart connections plugin is incredible. : r/ObsidianMD</a>. 2024.</div>
</div>
<div id="ref-second_brain_assistant_with_obsidian" class="csl-entry" role="listitem">
<div class="csl-left-margin">3. </div><div class="csl-right-inline"><a href="https://www.ssp.sh/brain/second-brain-assistant-with-obsidian-notegpt/">Second Brain Assistant with Obsidian</a>. 2025.</div>
</div>
<div id="ref-khoj_plugin" class="csl-entry" role="listitem">
<div class="csl-left-margin">4. </div><div class="csl-right-inline"><a href="https://forum.obsidian.md/t/khoj-an-ai-powered-search-assistant-for-you-second-brain/53756">Khoj: An AI powered Search Assistant for your Second Brain - Share &amp; showcase - Obsidian Forum</a>. 2023.</div>
</div>
<div id="ref-supercharging_obsidian_search" class="csl-entry" role="listitem">
<div class="csl-left-margin">5. </div><div class="csl-right-inline">@airabbitX. 2024. <a href="https://medium.com/@airabbitX/supercharging-obsidian-search-with-local-llms-a-personal-journey-1e008eb649a6">Supercharging Obsidian Search with AI and Ollama</a>.</div>
</div>
<div id="ref-export_obsidian_to_rdf" class="csl-entry" role="listitem">
<div class="csl-left-margin">6. </div><div class="csl-right-inline">Pavlyshyn, Volodymyr. 2024. <a href="https://volodymyrpavlyshyn.medium.com/how-to-export-your-obsidian-vault-to-rdf-00fb2539ed18">How to export your Obsidian Vault to RDF</a>.</div>
</div>
<div id="ref-export_to_common_graph_formats" class="csl-entry" role="listitem">
<div class="csl-left-margin">7. </div><div class="csl-right-inline"><a href="https://forum.obsidian.md/t/export-to-common-graph-formats/4138">Export to common graph formats - Plugins ideas - Obsidian Forum</a>. 2020.</div>
</div>
<div id="ref-personal_knowledge_graphs_in_obsidian" class="csl-entry" role="listitem">
<div class="csl-left-margin">8. </div><div class="csl-right-inline">Pavlyshyn, Volodymyr. 2024. <a href="https://volodymyrpavlyshyn.medium.com/personal-knowledge-graphs-in-obsidian-528a0f4584b9">Personal Knowledge Graphs in Obsidian</a>.</div>
</div>
<div id="ref-ai_empowered_zettelkasten_with_ner_and_graph_llm" class="csl-entry" role="listitem">
<div class="csl-left-margin">9. </div><div class="csl-right-inline"><a href="https://forum.obsidian.md/t/ai-empowered-zettelkasten-with-ner-and-graph-llm/79112">AI empowered Zettelkasten with NER and Graph LLM - Knowledge management - Obsidian Forum</a>. 2024.</div>
</div>
<div id="ref-build_your_second_brain_with_khoj_ai" class="csl-entry" role="listitem">
<div class="csl-left-margin">10. </div><div class="csl-right-inline"><a href="https://dswharshit.medium.com/build-your-second-brain-with-khoj-ai-high-signal-ai-2-87492730d7ce">Build your second brain with Khoj AI</a>. 2024.</div>
</div>
<div id="ref-basic_memory_ai_conversations_that_build_knowledge" class="csl-entry" role="listitem">
<div class="csl-left-margin">11. </div><div class="csl-right-inline"><a href="https://basicmachines.co/">Basic Memory | AI Conversations That Build Knowledge</a>.</div>
</div>
<div id="ref-local_free_rag_with_question_generation" class="csl-entry" role="listitem">
<div class="csl-left-margin">12. </div><div class="csl-right-inline">Galvis, Oscar. 2024. <a href="https://lomaky.medium.com/local-free-rag-with-question-generation-using-lm-studio-nomic-embeddings-chromadb-and-llama-3-2-9758877e93b4">Local (Free) RAG with Question Generation using LM Studio, Nomic embeddings, ChromaDB and Llama 3.2 on a Mac mini M1</a>.</div>
</div>
</div></section></div></main> <!-- /main -->
<script id="quarto-html-after-body" type="application/javascript">
window.document.addEventListener("DOMContentLoaded", function (event) {
// Ensure there is a toggle, if there isn't float one in the top right
if (window.document.querySelector('.quarto-color-scheme-toggle') === null) {
const a = window.document.createElement('a');
a.classList.add('top-right');
a.classList.add('quarto-color-scheme-toggle');
a.href = "";
a.onclick = function() { try { window.quartoToggleColorScheme(); } catch {} return false; };
const i = window.document.createElement("i");
i.classList.add('bi');
a.appendChild(i);
window.document.body.appendChild(a);
}
window.setColorSchemeToggle(window.hasAlternateSentinel())
const icon = "";
const anchorJS = new window.AnchorJS();
anchorJS.options = {
placement: 'right',
icon: icon
};
anchorJS.add('.anchored');
const isCodeAnnotation = (el) => {
for (const clz of el.classList) {
if (clz.startsWith('code-annotation-')) {
return true;
}
}
return false;
}
const onCopySuccess = function(e) {
// button target
const button = e.trigger;
// don't keep focus
button.blur();
// flash "checked"
button.classList.add('code-copy-button-checked');
var currentTitle = button.getAttribute("title");
button.setAttribute("title", "Kopiert");
let tooltip;
if (window.bootstrap) {
button.setAttribute("data-bs-toggle", "tooltip");
button.setAttribute("data-bs-placement", "left");
button.setAttribute("data-bs-title", "Kopiert");
tooltip = new bootstrap.Tooltip(button,
{ trigger: "manual",
customClass: "code-copy-button-tooltip",
offset: [0, -8]});
tooltip.show();
}
setTimeout(function() {
if (tooltip) {
tooltip.hide();
button.removeAttribute("data-bs-title");
button.removeAttribute("data-bs-toggle");
button.removeAttribute("data-bs-placement");
}
button.setAttribute("title", currentTitle);
button.classList.remove('code-copy-button-checked');
}, 1000);
// clear code selection
e.clearSelection();
}
const getTextToCopy = function(trigger) {
const codeEl = trigger.previousElementSibling.cloneNode(true);
for (const childEl of codeEl.children) {
if (isCodeAnnotation(childEl)) {
childEl.remove();
}
}
return codeEl.innerText;
}
const clipboard = new window.ClipboardJS('.code-copy-button:not([data-in-quarto-modal])', {
text: getTextToCopy
});
clipboard.on('success', onCopySuccess);
if (window.document.getElementById('quarto-embedded-source-code-modal')) {
const clipboardModal = new window.ClipboardJS('.code-copy-button[data-in-quarto-modal]', {
text: getTextToCopy,
container: window.document.getElementById('quarto-embedded-source-code-modal')
});
clipboardModal.on('success', onCopySuccess);
}
var localhostRegex = new RegExp(/^(?:http|https):\/\/localhost\:?[0-9]*\//);
var mailtoRegex = new RegExp(/^mailto:/);
var filterRegex = new RegExp("https:\/\/nicole\.dresselhaus\.cloud");
var isInternal = (href) => {
return filterRegex.test(href) || localhostRegex.test(href) || mailtoRegex.test(href);
}
// Inspect non-navigation links and adorn them if external
var links = window.document.querySelectorAll('a[href]:not(.nav-link):not(.navbar-brand):not(.toc-action):not(.sidebar-link):not(.sidebar-item-toggle):not(.pagination-link):not(.no-external):not([aria-hidden]):not(.dropdown-item):not(.quarto-navigation-tool):not(.about-link)');
for (var i=0; i<links.length; i++) {
const link = links[i];
if (!isInternal(link.href)) {
// undo the damage that might have been done by quarto-nav.js in the case of
// links that we want to consider external
if (link.dataset.originalHref !== undefined) {
link.href = link.dataset.originalHref;
}
// target, if specified
link.setAttribute("target", "_blank");
if (link.getAttribute("rel") === null) {
link.setAttribute("rel", "noopener");
}
// default icon
link.classList.add("external");
}
}
function tippyHover(el, contentFn, onTriggerFn, onUntriggerFn) {
const config = {
allowHTML: true,
maxWidth: 500,
delay: 100,
arrow: false,
appendTo: function(el) {
return el.parentElement;
},
interactive: true,
interactiveBorder: 10,
theme: 'quarto',
placement: 'bottom-start',
};
if (contentFn) {
config.content = contentFn;
}
if (onTriggerFn) {
config.onTrigger = onTriggerFn;
}
if (onUntriggerFn) {
config.onUntrigger = onUntriggerFn;
}
window.tippy(el, config);
}
const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
for (var i=0; i<noterefs.length; i++) {
const ref = noterefs[i];
tippyHover(ref, function() {
// use id or data attribute instead here
let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href');
try { href = new URL(href).hash; } catch {}
const id = href.replace(/^#\/?/, "");
const note = window.document.getElementById(id);
if (note) {
return note.innerHTML;
} else {
return "";
}
});
}
const xrefs = window.document.querySelectorAll('a.quarto-xref');
const processXRef = (id, note) => {
// Strip column container classes
const stripColumnClz = (el) => {
el.classList.remove("page-full", "page-columns");
if (el.children) {
for (const child of el.children) {
stripColumnClz(child);
}
}
}
stripColumnClz(note)
if (id === null || id.startsWith('sec-')) {
// Special case sections, only their first couple elements
const container = document.createElement("div");
if (note.children && note.children.length > 2) {
container.appendChild(note.children[0].cloneNode(true));
for (let i = 1; i < note.children.length; i++) {
const child = note.children[i];
if (child.tagName === "P" && child.innerText === "") {
continue;
} else {
container.appendChild(child.cloneNode(true));
break;
}
}
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(container);
}
return container.innerHTML
} else {
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(note);
}
return note.innerHTML;
}
} else {
// Remove any anchor links if they are present
const anchorLink = note.querySelector('a.anchorjs-link');
if (anchorLink) {
anchorLink.remove();
}
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(note);
}
if (note.classList.contains("callout")) {
return note.outerHTML;
} else {
return note.innerHTML;
}
}
}
for (var i=0; i<xrefs.length; i++) {
const xref = xrefs[i];
tippyHover(xref, undefined, function(instance) {
instance.disable();
let url = xref.getAttribute('href');
let hash = undefined;
if (url.startsWith('#')) {
hash = url;
} else {
try { hash = new URL(url).hash; } catch {}
}
if (hash) {
const id = hash.replace(/^#\/?/, "");
const note = window.document.getElementById(id);
if (note !== null) {
try {
const html = processXRef(id, note.cloneNode(true));
instance.setContent(html);
} finally {
instance.enable();
instance.show();
}
} else {
// See if we can fetch this
fetch(url.split('#')[0])
.then(res => res.text())
.then(html => {
const parser = new DOMParser();
const htmlDoc = parser.parseFromString(html, "text/html");
const note = htmlDoc.getElementById(id);
if (note !== null) {
const html = processXRef(id, note);
instance.setContent(html);
}
}).finally(() => {
instance.enable();
instance.show();
});
}
} else {
// See if we can fetch a full url (with no hash to target)
// This is a special case and we should probably do some content thinning / targeting
fetch(url)
.then(res => res.text())
.then(html => {
const parser = new DOMParser();
const htmlDoc = parser.parseFromString(html, "text/html");
const note = htmlDoc.querySelector('main.content');
if (note !== null) {
// This should only happen for chapter cross references
// (since there is no id in the URL)
// remove the first header
if (note.children.length > 0 && note.children[0].tagName === "HEADER") {
note.children[0].remove();
}
const html = processXRef(null, note);
instance.setContent(html);
}
}).finally(() => {
instance.enable();
instance.show();
});
}
}, function(instance) {
});
}
let selectedAnnoteEl;
const selectorForAnnotation = ( cell, annotation) => {
let cellAttr = 'data-code-cell="' + cell + '"';
let lineAttr = 'data-code-annotation="' + annotation + '"';
const selector = 'span[' + cellAttr + '][' + lineAttr + ']';
return selector;
}
const selectCodeLines = (annoteEl) => {
const doc = window.document;
const targetCell = annoteEl.getAttribute("data-target-cell");
const targetAnnotation = annoteEl.getAttribute("data-target-annotation");
const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation));
const lines = annoteSpan.getAttribute("data-code-lines").split(",");
const lineIds = lines.map((line) => {
return targetCell + "-" + line;
})
let top = null;
let height = null;
let parent = null;
if (lineIds.length > 0) {
//compute the position of the single el (top and bottom and make a div)
const el = window.document.getElementById(lineIds[0]);
top = el.offsetTop;
height = el.offsetHeight;
parent = el.parentElement.parentElement;
if (lineIds.length > 1) {
const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
const bottom = lastEl.offsetTop + lastEl.offsetHeight;
height = bottom - top;
}
if (top !== null && height !== null && parent !== null) {
// cook up a div (if necessary) and position it
let div = window.document.getElementById("code-annotation-line-highlight");
if (div === null) {
div = window.document.createElement("div");
div.setAttribute("id", "code-annotation-line-highlight");
div.style.position = 'absolute';
parent.appendChild(div);
}
div.style.top = top - 2 + "px";
div.style.height = height + 4 + "px";
div.style.left = 0;
let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
if (gutterDiv === null) {
gutterDiv = window.document.createElement("div");
gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
gutterDiv.style.position = 'absolute';
const codeCell = window.document.getElementById(targetCell);
const gutter = codeCell.querySelector('.code-annotation-gutter');
gutter.appendChild(gutterDiv);
}
gutterDiv.style.top = top - 2 + "px";
gutterDiv.style.height = height + 4 + "px";
}
selectedAnnoteEl = annoteEl;
}
};
const unselectCodeLines = () => {
const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"];
elementsIds.forEach((elId) => {
const div = window.document.getElementById(elId);
if (div) {
div.remove();
}
});
selectedAnnoteEl = undefined;
};
// Handle positioning of the toggle
window.addEventListener(
"resize",
throttle(() => {
elRect = undefined;
if (selectedAnnoteEl) {
selectCodeLines(selectedAnnoteEl);
}
}, 10)
);
function throttle(fn, ms) {
let throttle = false;
let timer;
return (...args) => {
if(!throttle) { // first call gets through
fn.apply(this, args);
throttle = true;
} else { // all the others get throttled
if(timer) clearTimeout(timer); // cancel #2
timer = setTimeout(() => {
fn.apply(this, args);
timer = throttle = false;
}, ms);
}
};
}
// Attach click handler to the DT
const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
for (const annoteDlNode of annoteDls) {
annoteDlNode.addEventListener('click', (event) => {
const clickedEl = event.target;
if (clickedEl !== selectedAnnoteEl) {
unselectCodeLines();
const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active');
if (activeEl) {
activeEl.classList.remove('code-annotation-active');
}
selectCodeLines(clickedEl);
clickedEl.classList.add('code-annotation-active');
} else {
// Unselect the line
unselectCodeLines();
clickedEl.classList.remove('code-annotation-active');
}
});
}
const findCites = (el) => {
const parentEl = el.parentElement;
if (parentEl) {
const cites = parentEl.dataset.cites;
if (cites) {
return {
el,
cites: cites.split(' ')
};
} else {
return findCites(el.parentElement)
}
} else {
return undefined;
}
};
var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]');
for (var i=0; i<bibliorefs.length; i++) {
const ref = bibliorefs[i];
const citeInfo = findCites(ref);
if (citeInfo) {
tippyHover(citeInfo.el, function() {
var popup = window.document.createElement('div');
citeInfo.cites.forEach(function(cite) {
var citeDiv = window.document.createElement('div');
citeDiv.classList.add('hanging-indent');
citeDiv.classList.add('csl-entry');
var biblioDiv = window.document.getElementById('ref-' + cite);
if (biblioDiv) {
citeDiv.innerHTML = biblioDiv.innerHTML;
}
popup.appendChild(citeDiv);
});
return popup.innerHTML;
});
}
}
});
</script>
</div> <!-- /content -->
</body></html>