optimize ux

This commit is contained in:
team 1
2026-04-05 19:37:50 +02:00
parent 41d4e1991d
commit bd818f5228
15 changed files with 323 additions and 271 deletions

View File

@@ -39,6 +39,12 @@ form, input, button, textarea {
font-size: 0.85rem !important;
}
input, textarea,select {
background-color: #121a25 !important;
color: #fff !important;
border-color: #020617 !important;
}
.container {
max-width: 900px;
margin: 0 auto;
@@ -47,6 +53,11 @@ form, input, button, textarea {
height: calc(100vh - 4rem);
}
::placeholder {
color: #556070 !important;
opacity: .5; /* wichtig v. a. für Firefox */
}
.header {
display: flex;
align-items: center;
@@ -62,7 +73,7 @@ form, input, button, textarea {
flex: 1;
overflow-y: auto;
padding: 1rem;
background: var(--panel);
background: #121a25;
border: 1px solid var(--border);
border-radius: 6px;
}
@@ -81,7 +92,7 @@ form, input, button, textarea {
}
.message.assistant .bubble {
background: var(--assistant);
background: #16202f;
border: 1px solid var(--border);
}

View File

@@ -14,7 +14,7 @@
<script src="/assets/js/purify.min.js"></script>
<script src="/assets/js/base.js"></script>
</head>
<body>
<body class="bg-black">
<div class="container">
<div class="header">

View File

@@ -47,7 +47,8 @@ final readonly class PromptBuilder
throw new \RuntimeException('No active system prompt configured.');
}
$activeSystemPrompt = str_replace('{%now%}', $now, $activePrompt->getContent());
$activeSystemPrompt = str_replace('{% now %}', $now, $activePrompt->getContent());
$systemBlock = "SYSTEM:\n" . $activeSystemPrompt;
// ------------------------------------------------------------

View File

@@ -57,7 +57,7 @@
<a class="nav-link text-light {% if route starts with 'admin_dashboard' %}active fw-bold{% endif %}"
href="{{ path('admin_dashboard') }}">
<i class="bi bi-hdd-rack"></i> Dashboard
<i class="bi bi-hdd-rack"></i> Systemübersicht
</a>
<hr class="border-secondary">
@@ -81,7 +81,7 @@
<a class="nav-link text-light {% if route starts with 'admin_system_agent' %}active fw-bold{% endif %}"
href="{{ path('admin_system_agent') }}">
<i class="bi bi-body-text"></i> Wissensbasis (Chunk-Index)
<i class="bi bi-robot"></i> Wissensbasis (Chunk-Index)
</a>
<hr class="border-secondary">
@@ -97,7 +97,7 @@
<a class="nav-link text-light {% if route starts with 'admin_ingest_profile' %}active fw-bold{% endif %}"
href="{{ path('admin_ingest_profile_list') }}">
<i class="bi bi-diagram-3-fill"></i> Indexierungsprofile (Ingest)
<i class="bi bi-search"></i> Indexierungsprofile (Ingest)
</a>
<hr class="border-secondary">
@@ -108,7 +108,7 @@
<a class="nav-link text-light {% if route starts with 'admin_model_config' %}active fw-bold{% endif %}"
href="{{ path('admin_model_config_list') }}">
<i class="bi bi-motherboard-fill"></i> LLM-Setup (Parameter)
<i class="bi bi-rocket-takeoff-fill"></i> LLM-Setup (Parameter)
</a>
<hr class="border-secondary">
<div class="text-info text-uppercase small mb-2">
@@ -117,7 +117,7 @@
<a class="nav-link text-light {% if route starts with 'admin_model_config' %}active fw-bold{% endif %}"
href="{{ path('admin_guides_index') }}">
<i class="bi bi-collection"></i> How-To & Leitfäden
<i class="bi bi-mortarboard-fill"></i> How-To & Leitfäden
</a>
<hr class="border-secondary">
<div class="text-info text-uppercase small mb-2">
@@ -125,15 +125,15 @@
</div>
<a class="nav-link text-light {% if route starts with 'admin_job' %}active fw-bold{% endif %}"
href="{{ path('admin_jobs') }}">
<i class="bi bi-binoculars-fill"></i> Indexierungs-Log (Ingest Jobs)
<i class="bi bi-terminal"></i> Indexierungs-Log (Ingest Jobs)
</a>
<a class="nav-link text-light {% if route starts with 'admin_job' %}active fw-bold{% endif %}"
href="{{ path('admin_vector_log') }}">
<i class="bi bi-binoculars-fill"></i> Vector-Log Python
<i class="bi bi-terminal"></i> Vector-Log Python
</a>
<a class="nav-link text-light {% if route starts with 'admin_job' %}active fw-bold{% endif %}"
href="{{ path('admin_system_logs_index') }}">
<i class="bi bi-binoculars-fill"></i> System-Logs
<i class="bi bi-terminal"></i> System-Logs
</a>
</nav>

View File

@@ -10,7 +10,7 @@
<!-- ===================================================== -->
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">Systemübersicht</h1>
<h1 class="h3 mb-0"><i class="bi bi-hdd-rack"></i> Systemübersicht</h1>
<span class="badge bg-secondary">RAG Enterprise</span>
</div>
@@ -34,7 +34,7 @@
<div class="col-lg-6 col-xl-3">
<div class="card bg-black border-secondary text-light h-100">
<div class="card-body">
<div class="small text-light mb-2">Chunk-Vektor</div>
<div class="small text-light mb-2"><i class="bi bi-files"></i> Chunk-Vektor</div>
{% if vectorHealth is defined %}
<h4 class="mb-0">
@@ -65,7 +65,7 @@
<div class="col-lg-6 col-xl-3">
<div class="card bg-black border-secondary text-light h-100">
<div class="card-body">
<div class="small text-light mb-2">Tag-Vektor</div>
<div class="small text-light mb-2"><i class="bi bi-tags"></i> Tag-Vektor</div>
{% if tagVectorHealth is defined %}
<h4 class="mb-0">
@@ -88,7 +88,7 @@
<div class="col-lg-6 col-xl-3">
<div class="card bg-black border-secondary text-light h-100">
<div class="card-body">
<div class="small text-light mb-2">Wissenskapazität</div>
<div class="small text-light mb-2"><i class="bi bi-robot"></i> Wissenskapazität</div>
<h4 class="mb-2">
{{ chunkCount|number_format(0, ',', '.') }}
@@ -121,7 +121,7 @@
<div class="col-lg-6 col-xl-3">
<div class="card bg-black border-secondary text-light h-100">
<div class="card-body">
<div class="small text-light mb-2">System-Governance</div>
<div class="small text-light mb-2"><i class="bi bi-shield-check"></i> System-Governance</div>
<div class="small">
<strong>Benutzer</strong><br>
@@ -148,7 +148,7 @@
<div class="col-lg-4">
<div class="card bg-black border-secondary text-light h-100">
<div class="card-body">
<h5 class="text-info mb-3">Chunk-Vektor-Details</h5>
<h5 class="text-info mb-3"><i class="bi bi-files"></i> Chunk-Vektor-Details</h5>
<div class="small text-info">NDJSON-Chunks</div>
<div class="h5 mb-3">
@@ -168,7 +168,7 @@
<div class="col-lg-4">
<div class="card bg-black border-secondary text-light h-100">
<div class="card-body">
<h5 class="text-info mb-3">Tag-Vektor-Details</h5>
<h5 class="text-info mb-3"><i class="bi bi-tags"></i> Tag-Vektor-Details</h5>
<div class="small text-info">NDJSON-Tags</div>
<div class="h5 mb-3">
@@ -188,7 +188,7 @@
<div class="col-lg-4">
<div class="card bg-black border-secondary text-light h-100">
<div class="card-body">
<h5 class="text-info mb-3">Indexierung (Ingest Jobs)</h5>
<h5 class="text-info mb-3"><i class="bi bi-search"></i> Indexierung (Ingest Jobs)</h5>
<div class="text-muted small mb-3">
Erstellt den kompletten Wissensindex neu.
@@ -216,7 +216,7 @@
<div class="col-lg-4">
<div class="card bg-black border-danger text-light h-100">
<div class="card-body">
<h5 class="text-danger mb-3">Kritische Systemoperationen</h5>
<h5 class="text-danger mb-3"><i class="bi bi-sign-stop-fill"></i> Kritische Systemoperationen</h5>
<div class="small mb-3 text-secondary">
Entfernt alle Dokumente, Versionen, Indizes und Jobs.

View File

@@ -5,7 +5,7 @@
{% block body %}
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">Dokumente</h1>
<h1 class="h3 mb-0"><i class="bi bi-card-list"></i> Dokumente</h1>
<a href="{{ path('admin_document_new') }}"
class="btn btn-sm btn-outline-info">
@@ -52,7 +52,7 @@
</td>
{# ID #}
<td class="small text-secondary">
<td class="small text-info">
{{ document.id }}
</td>

View File

@@ -15,7 +15,7 @@
</a>
</div>
<div class="card bg-black border-secondary mb-4 text-light">
<div class="card bg-dark border-secondary mb-4 text-light">
<div class="card-body">
<div class="mb-3">

View File

@@ -19,7 +19,7 @@
{# Dokument-Meta #}
{# ============================= #}
<div class="card bg-black border-secondary mb-5 text-light">
<div class="card bg-dark border-secondary mb-5 text-light">
<div class="card-body">
<div class="mb-2">
@@ -79,7 +79,7 @@
{% else %}
<div class="card bg-black border-secondary">
<div class="card-body p-0">
<div class="card-body">
<table class="table table-dark table-striped table-hover align-middle mb-0">
<thead class="table-secondary text-dark">

View File

@@ -8,7 +8,7 @@
{# Tag-Rebuild Status (Echte Live-Anzeige) #}
{# ============================================= #}
<div id="rebuild-status"></div>
<div id="rebuild-status" class="mb-5" style="min-height:54px"></div>
<script>
let polling = null;
@@ -90,10 +90,10 @@
{# Bereits zugewiesene Tags #}
{# ============================================= #}
<div class="card bg-black border-secondary mb-4">
<div class="card bg-dark border-secondary mb-4">
<div class="card-body">
<h5 class="text-info mb-3">Zugewiesene Tags</h5>
<h5 class="mb-3">Zugewiesene Tags für: <span class="text-info ">{{ document.title }}</span></h5>
{% if document.tags is empty %}
<div class="alert alert-secondary mb-0">
@@ -119,7 +119,7 @@
<div class="card bg-black border-secondary">
<div class="card-body">
<h5 class="text-info mb-3">Tags bearbeiten</h5>
<h5 class="text-info mb-3">Tags zuweisen</h5>
<form method="post"
action="{{ path('admin_document_tags_save', {id: document.id}) }}">
@@ -130,7 +130,7 @@
<div class="row">
{% for tag in allTags %}
<div class="col-md-4 mb-2">
<div class="col-md-2 mb-2">
<div class="form-check">
@@ -143,7 +143,7 @@
{% if tag in document.tags %}checked{% endif %}
>
<label class="form-check-label text-dark bg-info badge"
<label class="form-check-label bg-info text-black badge"{% if tag not in document.tags %} style="opacity: .5;"{% endif %}
for="tag_{{ tag.id }}">
{{ tag.label }}
</label>

View File

@@ -3,20 +3,12 @@
{% block title %}Indexierungsprofile{% endblock %}
{% block body %}
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3">Indexierungsprofile</h1>
<a class="btn btn-sm btn-outline-info"
href="{{ path('admin_ingest_profile_create') }}">
Neues Profil anlegen
</a>
</div>
{# ============================= #}
{# Strukturstatus Alert #}
{# ============================= #}
{% if structureMismatch %}
<div class="alert alert-danger d-flex justify-content-between align-items-center mb-4">
<div class="alert alert-danger d-flex justify-content-between align-items-center mb-5">
<div>
<strong>Strukturabweichung erkannt.</strong>
Die aktuelle Indexstruktur entspricht nicht dem aktiven Profil.
@@ -28,50 +20,63 @@
</a>
</div>
{% else %}
<div class="alert alert-success">
Die Indexstruktur entspricht dem aktiven Profil.
<div class="alert alert-success mb-5">
<i class="bi bi-check-lg"></i> Die Indexstruktur entspricht dem aktiven Profil.
</div>
{% endif %}
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3"><i class="bi bi-search"></i> Indexierungsprofile</h1>
<a class="btn btn-sm btn-outline-info"
href="{{ path('admin_ingest_profile_create') }}">
Neues Profil anlegen
</a>
</div>
{# ========================================================= #}
{# PROFIL SYSTEM DESCRIPTION #}
{# ========================================================= #}
<div class="card bg-black border-secondary mb-4 shadow-sm">
<div class="card-body text-light">
<div class="card bg-dark border-secondary mb-4 shadow-sm">
<div class="card-body text-light row">
<h5 class="text-info mb-3">Was sind Indexierungsprofile?</h5>
<div class="col-lg-6">
<h5 class="text-info mb-3">Was sind Indexierungsprofile?</h5>
<p class="small text-light mb-3">
Indexierungsprofile definieren die strukturellen Regeln des RAG-Systems.
Sie bestimmen, wie Dokumente in Chunks zerlegt, eingebettet
und später bewertet werden.
</p>
<p class="small text-light mb-3">
Indexierungsprofile definieren die strukturellen Regeln des RAG-Systems.
Sie bestimmen, wie Dokumente in Chunks zerlegt, eingebettet
und später bewertet werden.
</p>
<ul class="small text-light mb-3">
<li>Chunk-Größe und Overlap (Textsegmentierung)</li>
<li>Embedding-Modell und Dimension</li>
<li>Scoring-Version (Retrieval-Logik)</li>
</ul>
<ul class="small text-light mb-3">
<li>Chunk-Größe und Overlap (Textsegmentierung)</li>
<li>Embedding-Modell und Dimension</li>
<li>Scoring-Version (Retrieval-Logik)</li>
</ul>
<h6 class="text-info mt-3">Warum sind Profile versioniert?</h6>
</div>
<div class="col-lg-6">
<h6 class="text-info mt-3">Warum sind Profile versioniert?</h6>
<p class="small text-light mb-3">
Jede strukturelle Änderung beeinflusst die gesamte Indexarchitektur.
Deshalb ist ein Profil immer versioniert und unveränderlich.
Bei Aktivierung eines neuen Profils kann eine vollständige
Neuindizierung erforderlich werden.
</p>
<p class="small text-light mb-3">
Jede strukturelle Änderung beeinflusst die gesamte Indexarchitektur.
Deshalb ist ein Profil immer versioniert und unveränderlich.
Bei Aktivierung eines neuen Profils kann eine vollständige
Neuindizierung erforderlich werden.
</p>
<h6 class="text-info mt-3">Wie prüft das System Konsistenz?</h6>
<h6 class="text-info mt-3">Wie prüft das System Konsistenz?</h6>
<p class="small text-light mb-0">
Das System vergleicht das aktive Profil mit der aktuellen
<code>index_meta.json</code>. Weichen Parameter wie
Embedding-Dimension, Chunking oder Scoring ab,
wird eine Strukturabweichung erkannt und ein Global Reindex
empfohlen oder erzwungen.
</p>
<p class="small text-light mb-0">
Das System vergleicht das aktive Profil mit der aktuellen
<code>index_meta.json</code>. Weichen Parameter wie
Embedding-Dimension, Chunking oder Scoring ab,
wird eine Strukturabweichung erkannt und ein Global Reindex
empfohlen oder erzwungen.
</p>
</div>
</div>
</div>
@@ -79,10 +84,9 @@
{# Profile Tabelle #}
{# ============================= #}
<h2 class="text-light mb-3">Profile</h2>
<div class="card bg-black border-secondary mb-5">
<div class="card-body p-0">
<h2 class="text-light mb-3">Meta-Profile</h2>
<div class="card bg-black border-secondary mb-3">
<div class="card-body">
<table class="table table-dark table-striped table-hover align-middle mb-0">
<thead class="table-secondary text-dark">
<tr>
@@ -174,7 +178,6 @@
{% endfor %}
</tbody>
</table>
</div>
</div>
@@ -182,15 +185,12 @@
{# Struktur-Diff #}
{# ============================= #}
<h2 class="text-light mb-3">Index-Struktur Vergleich</h2>
<div class="card bg-black border-secondary">
<div class="card-body text-light">
{% if indexMeta %}
<div class="mb-3 small text-light">
Aktuelle Index-Version:
Aktuell erstellte Index-Version:
<strong>{{ indexMeta.index_version }}</strong>
</div>
{% else %}
@@ -203,8 +203,8 @@
<thead class="table-secondary text-dark">
<tr>
<th>Parameter</th>
<th>Index Meta</th>
<th>Aktives Profil</th>
<th>Aktiver Meta-Index (System)</th>
<th>Aktives Meta-Profil</th>
<th>Status</th>
</tr>
</thead>

View File

@@ -5,7 +5,7 @@
{% block body %}
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">KI Modell-Generierung</h1>
<h1 class="h3 mb-0"><i class="bi bi-rocket-takeoff-fill"></i> KI Modell-Generierung</h1>
{% if is_granted('ROLE_SUPER_ADMIN') %}
<a href="{{ path('admin_model_config_create') }}"
@@ -18,49 +18,54 @@
{# ========================================================= #}
{# MODEL CONFIG DESCRIPTION #}
{# ========================================================= #}
<div class="card bg-black border-secondary text-light mb-4 shadow-sm">
<div class="card-body">
<div class="card bg-dark border-secondary text-light mb-4 shadow-sm">
<div class="card-body row">
<h5 class="text-info mb-3">Was steuert die Modell-Konfiguration?</h5>
<div class="col-lg-6">
<h5 class="text-info mb-3">Was steuert die Modell-Konfiguration?</h5>
<p class="small text-light mb-3">
Die Modell-Konfiguration definiert die vollständige
Generierungsumgebung des Agents. Sie trennt strikt zwischen:
</p>
<p class="small text-light mb-3">
Die Modell-Konfiguration definiert die vollständige
Generierungsumgebung des Agents. Sie trennt strikt zwischen:
</p>
<ul class="small text-light mb-3">
<li>LLM-Verhalten (Sampling & Kontext)</li>
<li>Retrieval-Parameter (Vektor-Wissen)</li>
<li>Streaming-Modus</li>
</ul>
<ul class="small text-light mb-3">
<li>LLM-Verhalten (Sampling & Kontext)</li>
<li>Retrieval-Parameter (Vektor-Wissen)</li>
<li>Streaming-Modus</li>
</ul>
<h6 class="text-info mt-3">LLM Sampling</h6>
<h6 class="text-info mt-3">LLM Sampling</h6>
<p class="small text-light mb-3">
Parameter wie Temperature, TopK und TopP beeinflussen die
kreative Varianz der Antwort. Repeat Penalty steuert Wiederholungen,
während <code>numCtx</code> die maximale Kontextgröße des Modells festlegt.
</p>
<p class="small text-light mb-3">
Parameter wie Temperature, TopK und TopP beeinflussen die
kreative Varianz der Antwort. Repeat Penalty steuert Wiederholungen,
während <code>numCtx</code> die maximale Kontextgröße des Modells festlegt.
</p>
</div>
<h6 class="text-info mt-3">Retrieval Vector Wissen</h6>
<div class="col-lg-6">
<h6 class="text-info mt-3">Retrieval Vector Wissen</h6>
<p class="small text-light mb-3">
Retrieval-Parameter bestimmen, wie viele Chunks aus dem
NDJSON-Vektorindex geladen werden und wie viele
Top-K Kandidaten aus der FAISS-Suche berücksichtigt werden.
Diese Werte beeinflussen die Wissensbreite der Antwort,
nicht deren Kreativität.
</p>
<p class="small text-light mb-3">
Retrieval-Parameter bestimmen, wie viele Chunks aus dem
NDJSON-Vektorindex geladen werden und wie viele
Top-K Kandidaten aus der FAISS-Suche berücksichtigt werden.
Diese Werte beeinflussen die Wissensbreite der Antwort,
nicht deren Kreativität.
</p>
<h6 class="text-info mt-3">Architektur-Prinzip</h6>
<h6 class="text-info mt-3">Architektur-Prinzip</h6>
<p class="small text-light mb-0">
Das Retrieval ist deterministisch und vom LLM getrennt.
Das Modell erhält ausschließlich die vorselektierten
Chunks als Kontext. Änderungen hier verändern die
Wissensbasis der Antwort — nicht den gespeicherten Index.
Pro Modell kann genau eine Konfiguration aktiv sein.
</p>
</div>
<p class="small text-light mb-0">
Das Retrieval ist deterministisch und vom LLM getrennt.
Das Modell erhält ausschließlich die vorselektierten
Chunks als Kontext. Änderungen hier verändern die
Wissensbasis der Antwort — nicht den gespeicherten Index.
Pro Modell kann genau eine Konfiguration aktiv sein.
</p>
</div>
</div>
@@ -75,7 +80,7 @@
<th>Profil-Version</th>
<th>Streaming</th>
<th>LLM Sampling</th>
<th class="text-warning">Retrieval Vector Wissen</th>
<th class="">Retrieval Vector Wissen</th>
<th>Status</th>
<th class="text-end">Aktionen</th>
</tr>
@@ -186,10 +191,10 @@
Hinweis: Änderungen wirken sich unmittelbar auf Sampling- und Retrieval-Parameter
des aktiven Modells aus. Pro Modell kann nur eine Version aktiv sein.
</div>
<hr>
<h2 class="h4 mb-4">Agent Live Test</h2>
<div class="card bg-black border-info">
<div class="card bg-black border-secondary">
<div class="card-body p-0">
<iframe
src="/index.html?admin_test=1"

View File

@@ -1,11 +1,11 @@
{% extends 'admin/base.html.twig' %}
{% block title %}Retrieval Wissensbasis (Chunk-Index){% endblock %}
{% block title %}Retrieval Wissensbasis{% endblock %}
{% block body %}
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3">Retrieval Wissensbasis (Chunk-Index)</h1>
<h1 class="h3"><i class="bi bi-robot"></i> Retrieval Wissensbasis</h1>
<a href="{{ path('admin_dashboard') }}"
class="btn btn-sm btn-outline-secondary">
@@ -17,10 +17,10 @@
{# Index Meta Section #}
{# ============================= #}
<div class="card bg-black border-secondary mb-5">
<div class="card bg-dark border-secondary mb-5">
<div class="card-body">
<h5 class="text-info mb-3">Wissensdaten Generierungsmatrix Index Meta (index_meta.json)</h5>
<h5 class="text-info mb-3">Generierungsmatrix (index_meta.json)</h5>
{% if meta.error is defined %}
<div class="alert alert-danger">
@@ -35,12 +35,12 @@
<tbody>
{% for key, value in meta %}
<tr>
<th style="width:260px;" class="text-secondary">
<td style="width:260px;" class="text-info">
{{ key }}
</th>
</td>
<td>
{% if value is iterable %}
<pre class="mb-0 small text-info">
<pre class="mb-0 small text-light">
{{ value|json_encode(constant('JSON_PRETTY_PRINT')) }}
</pre>
{% else %}
@@ -75,7 +75,7 @@
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="text-info mb-0">
Chunks Index | NDJSON-Index Übersicht (index.ndjson)
Wissensdaten Chunks | Übersicht (index.ndjson)
</h5>
<div class="btn-group">
@@ -83,7 +83,7 @@
page: currentPage > 1 ? currentPage - 1 : 1,
limit: currentLimit
}) }}"
class="btn btn-sm btn-outline-light">
class="btn btn-sm btn-outline-info">
← Zurück
</a>
@@ -91,7 +91,7 @@
page: currentPage + 1,
limit: currentLimit
}) }}"
class="btn btn-sm btn-outline-light">
class="btn btn-sm btn-outline-info">
Weiter →
</a>
</div>
@@ -131,13 +131,14 @@
{% for item in ndjson.items|default([]) %}
<tr>
<td class="small">{{ item.chunk_id ?? '-' }}</td>
<td class="small text-info">{{ item.chunk_id ?? '-' }}</td>
<td>
<td class="small text-info">
{% if item.document_id %}
{{ item.document_id }}<br>
<a href="{{ path('admin_document_show', {id: item.document_id}) }}"
class="text-decoration-none text-light">
{{ item.document_id }}
class="text-light small">
ansehen
</a>
{% else %}
-

View File

@@ -6,58 +6,63 @@
<div class="container-fluid">
<!-- Flash Messages -->
{% for message in app.flashes('success') %}
<div class="alert alert-success mb-5">{{ message }}</div>
{% endfor %}
{% for message in app.flashes('danger') %}
<div class="alert alert-danger mb-5">{{ message }}</div>
{% endfor %}
<!-- HEADER -->
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3">System Prompt Verwaltung</h1>
<h1 class="h3"><i class="bi bi-chat-right-dots-fill"></i> System Prompt Verwaltung</h1>
</div>
{# ========================================================= #}
{# SYSTEM PROMPT DESCRIPTION #}
{# ========================================================= #}
<div class="card bg-black border-secondary text-light mb-4 shadow-sm">
<div class="card-body">
<div class="card bg-dark border-secondary text-light mb-4 shadow-sm">
<div class="card-body row">
<h5 class="text-info mb-3">Was steuert der System Prompt?</h5>
<div class="col-lg-6">
<h5 class="text-info mb-3">Was steuert der System Prompt?</h5>
<p class="small text-light mb-3">
Der System Prompt definiert das globale Antwortverhalten des LLM.
Er ist die oberste Steuerungsebene des Systems und beeinflusst:
</p>
<p class="small text-light mb-3">
Der System Prompt definiert das globale Antwortverhalten des LLM.
Er ist die oberste Steuerungsebene des Systems und beeinflusst:
</p>
<ul class="small text-light mb-3">
<li>Ton und Stil der Antworten</li>
<li>Struktur der Ausgaben</li>
<li>Umgang mit Unsicherheiten</li>
<li>Bezug auf Wissensquellen</li>
</ul>
<ul class="small text-light mb-3">
<li>Ton und Stil der Antworten</li>
<li>Struktur der Ausgaben</li>
<li>Umgang mit Unsicherheiten</li>
<li>Bezug auf Wissensquellen</li>
</ul>
</div>
<h6 class="text-info mt-3">Architektur-Prinzip</h6>
<div class="col-lg-6">
<h6 class="text-info mt-3">Architektur-Prinzip</h6>
<p class="small text-light mb-3">
Der System Prompt ist strikt von der Wissensebene getrennt.
Er verändert keine Dokumente, Chunks oder Vektoren,
sondern nur die Interpretation und Darstellung der Retrieval-Ergebnisse.
</p>
<p class="small text-light mb-3">
Der System Prompt ist strikt von der Wissensebene getrennt.
Er verändert keine Dokumente, Chunks oder Vektoren,
sondern nur die Interpretation und Darstellung der Retrieval-Ergebnisse.
</p>
<h6 class="text-info mt-3">Warum Versionierung?</h6>
<h6 class="text-info mt-3">Warum Versionierung?</h6>
<p class="small text-light mb-0">
Jede Änderung kann das Antwortverhalten signifikant verändern.
Daher ist der Prompt versioniert, rollbackfähig und nur eine Version kann aktiv sein.
Aktivierungen wirken unmittelbar auf alle nachfolgenden Anfragen.
</p>
<p class="small text-light mb-0">
Jede Änderung kann das Antwortverhalten signifikant verändern.
Daher ist der Prompt versioniert, rollbackfähig und nur eine Version kann aktiv sein.
Aktivierungen wirken unmittelbar auf alle nachfolgenden Anfragen.
</p>
</div>
</div>
</div>
<!-- Flash Messages -->
{% for message in app.flashes('success') %}
<div class="alert alert-success">{{ message }}</div>
{% endfor %}
{% for message in app.flashes('danger') %}
<div class="alert alert-danger">{{ message }}</div>
{% endfor %}
<div class="row g-4">
<!-- LEFT SIDE Versionen -->

View File

@@ -4,9 +4,18 @@
{% block body %}
{# ========================================================= #}
{# LIVE REBUILD STATUS (SSE) #}
{# ========================================================= #}
<div id="rebuild-status" class="mb-5">
<div class="alert alert-secondary shadow-sm">
Status wird geladen…
</div>
</div>
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">
Tag: {{ tag.label }}
<i class="bi bi-tag-fill"></i> Tag: {{ tag.label }}
</h1>
<a href="{{ path('admin_tags_index') }}"
@@ -15,15 +24,6 @@
</a>
</div>
{# ========================================================= #}
{# LIVE REBUILD STATUS (SSE) #}
{# ========================================================= #}
<div id="rebuild-status">
<div class="alert alert-secondary shadow-sm">
Status wird geladen…
</div>
</div>
<script>
const statusBox = document.getElementById('rebuild-status');
@@ -37,7 +37,7 @@
html = `
<div class="alert alert-info shadow-sm d-flex justify-content-between align-items-center">
<div>
<strong>Tag-Rebuild läuft</strong><br>
Tag-Rebuild läuft<br>
${data.startedAt ? 'Gestartet: ' + new Date(data.startedAt).toLocaleString() : ''}
</div>
<div class="spinner-border spinner-border-sm"></div>
@@ -46,19 +46,19 @@
} else if (data.status === '{{ statusQueued }}') {
html = `
<div class="alert alert-secondary shadow-sm">
<strong>Tag-Rebuild in Warteschlange</strong>
Tag-Rebuild in Warteschlange
</div>
`;
} else if (data.status === '{{ statusCompleted }}') {
html = `
<div class="alert alert-success shadow-sm">
<strong>Tag-Rebuild erfolgreich abgeschlossen</strong>
<i class="bi bi-check-lg"></i> Tag-Rebuild erfolgreich abgeschlossen
</div>
`;
} else if (data.status === '{{ statusFailed }}') {
html = `
<div class="alert alert-danger shadow-sm">
<strong>Tag-Rebuild fehlgeschlagen</strong><br>
Tag-Rebuild fehlgeschlagen<br>
${data.error ? '<code>' + data.error + '</code>' : ''}
</div>
`;
@@ -99,39 +99,64 @@
value="{{ csrf_token('assign_tag_' ~ tag.id) }}">
<div class="card bg-black border-secondary">
<div class="card-body p-0">
<table class="table table-dark table-striped table-hover mb-0 align-middle">
<thead class="table-secondary text-dark">
<tr>
<th style="width:60px;"></th>
<th>Dokument</th>
</tr>
</thead>
<tbody>
{% for doc in documents %}
<div class="card-body p-0 row">
<div class=" col-lg-6">
<table class="table table-dark table-striped table-hover mb-0 align-middle">
<thead class="table-secondary text-dark">
<tr>
<td>
<input type="checkbox"
name="documents[]"
value="{{ doc.id }}"
{% if doc.id in assignedDocIds %}checked{% endif %}>
</td>
<td>
{{ doc.title }}
</td>
<th style="width:60px;"><i class="bi bi-three-dots"></i></th>
<th>Zugewiesene Dokumente</th>
</tr>
{% else %}
<tr>
<td colspan="2" class="text-center text-muted">
Keine Dokumente vorhanden.
</td>
</tr>
{% endfor %}
</tbody>
</table>
</thead>
<tbody>
{% for doc in documents %}
{% if doc.id in assignedDocIds %}
<tr>
<td>
<input type="checkbox"
name="documents[]"
value="{{ doc.id }}"
checked>
</td>
<td>
{{ doc.title }}
</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
<div class=" col-lg-6">
<table class="table table-dark table-striped table-hover mb-0 align-middle col-lg-6">
<thead class="table-secondary text-dark">
<tr>
<th style="width:60px;"><i class="bi bi-three-dots"></i></th>
<th>Nicht zugewiesene Dokumente</th>
</tr>
</thead>
<tbody>
{% for doc in documents %}
{% if doc.id not in assignedDocIds %}
<tr>
<td>
<input type="checkbox"
name="documents[]"
value="{{ doc.id }}"
>
</td>
<td class="opacity-50">
{{ doc.title }}
</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>

View File

@@ -4,75 +4,79 @@
{% block body %}
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">Tag-Management</h1>
</div>
{# ========================================================= #}
{# LIVE REBUILD STATUS (SSE) #}
{# ========================================================= #}
<div id="rebuild-status">
<div id="rebuild-status" class="mb-5">
{% if latestJob %}
<div class="alert alert-secondary shadow-sm">
Status wird geladen…
</div>
{% endif %}
</div>
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0"><i class="bi bi-tag-fill"></i> Tag-Management</h1>
</div>
{# ========================================================= #}
{# TAG SYSTEM DESCRIPTION #}
{# ========================================================= #}
<div class="card bg-black border-secondary text-light mb-4 shadow-sm">
<div class="card-body">
<h5 class="text-info mb-3">Was machen Tags im System?</h5>
<div class="card bg-dark border-secondary text-light mb-4 shadow-sm">
<div class="card-body row">
<div class="col-lg-6">
<h5 class="text-info mb-3">Was machen Tags im System?</h5>
<p class="small text-light mb-2">
Tags dienen als semantische Routing-Ebene innerhalb des RAG-Systems.
Sie strukturieren Dokumente thematisch und beeinflussen,
welche Inhalte bei einer Nutzeranfrage priorisiert werden.
</p>
<p class="small text-light mb-2">
Tags dienen als semantische Routing-Ebene innerhalb des RAG-Systems.
Sie strukturieren Dokumente thematisch und beeinflussen,
welche Inhalte bei einer Nutzeranfrage priorisiert werden.
</p>
<ul class="small text-light mb-3">
<li>
Tags werden Dokumenten manuell zugewiesen.
</li>
<li>
Beim Rebuild wird aus allen Tags eine eigene
<code>tags.ndjson</code> erzeugt.
</li>
<li>
Zusätzlich wird ein separater Vektorindex
(<code>vector_tags.index</code>) aufgebaut.
</li>
<li>
Bei einer Anfrage erfolgt zunächst ein Tag-Matching,
danach wird das Chunk-Retrieval entsprechend gewichtet.
</li>
</ul>
<ul class="small text-light mb-3">
<li>
Tags werden Dokumenten manuell zugewiesen.
</li>
<li>
Beim Rebuild wird aus allen Tags eine eigene
<code>tags.ndjson</code> erzeugt.
</li>
<li>
Zusätzlich wird ein separater Vektorindex
(<code>vector_tags.index</code>) aufgebaut.
</li>
<li>
Bei einer Anfrage erfolgt zunächst ein Tag-Matching,
danach wird das Chunk-Retrieval entsprechend gewichtet.
</li>
</ul>
</div>
<div class="col-lg-6">
<h6 class="text-info mt-3">Wie werden Tags bewertet?</h6>
<h6 class="text-info mt-3">Wie werden Tags bewertet?</h6>
<p class="small text-light mb-2">
Die Bewertung erfolgt über einen eigenen Vektor-Similarity-Score
im Tag-Index. Das System berechnet:
</p>
<p class="small text-light mb-2">
Die Bewertung erfolgt über einen eigenen Vektor-Similarity-Score
im Tag-Index. Das System berechnet:
</p>
<ul class="small text-light">
<li>
Ähnlichkeit zwischen Nutzeranfrage und Tag-Embedding
</li>
<li>
Top-K Treffer im Tag-Index
</li>
<li>
Gewichtete Übergabe an das Chunk-Retrieval
</li>
</ul>
<ul class="small text-light">
<li>
Ähnlichkeit zwischen Nutzeranfrage und Tag-Embedding
</li>
<li>
Top-K Treffer im Tag-Index
</li>
<li>
Gewichtete Übergabe an das Chunk-Retrieval
</li>
</ul>
<p class="small text-light mt-2 mb-0">
Tags wirken somit als semantischer Verstärker.
Sie ersetzen kein Chunk-Retrieval, sondern steuern dessen Priorisierung.
</p>
<p class="small text-light mt-2 mb-0">
Tags wirken somit als semantischer Verstärker.
Sie ersetzen kein Chunk-Retrieval, sondern steuern dessen Priorisierung.
</p>
</div>
</div>
</div>
@@ -88,7 +92,7 @@
html = `
<div class="alert alert-info shadow-sm d-flex justify-content-between align-items-center">
<div>
<strong>Tag-Rebuild läuft</strong><br>
Tag-Rebuild läuft<br>
${data.startedAt ? 'Gestartet: ' + new Date(data.startedAt).toLocaleString() : ''}
</div>
<div class="spinner-border spinner-border-sm"></div>
@@ -97,19 +101,19 @@
} else if (data.status === '{{ statusQueued }}') {
html = `
<div class="alert alert-secondary shadow-sm">
<strong>Tag-Rebuild in Warteschlange</strong>
Tag-Rebuild in Warteschlange
</div>
`;
} else if (data.status === '{{ statusCompleted }}') {
html = `
<div class="alert alert-success shadow-sm">
<strong>Tag-Rebuild erfolgreich abgeschlossen</strong>
<i class="bi bi-check-lg"></i> Tag-Rebuild erfolgreich abgeschlossen
</div>
`;
} else if (data.status === '{{ statusFailed }}') {
html = `
<div class="alert alert-danger shadow-sm">
<strong>Tag-Rebuild fehlgeschlagen</strong><br>
Tag-Rebuild fehlgeschlagen<br>
${data.error ? '<code>' + data.error + '</code>' : ''}
</div>
`;
@@ -128,7 +132,7 @@
{# ========================================================= #}
<div class="card bg-black border-secondary text-light mb-4 shadow-sm">
<div class="card-body">
<h5 class="text-info mb-3">Neuen Tag erstellen</h5>
<h5 class="text-info mb-3">Neuen Tag hinzufügen</h5>
<form method="post" action="{{ path('admin_tags_create') }}" class="row g-3">
<input type="hidden" name="_token" value="{{ csrf_token('admin_tag_create') }}"/>
@@ -179,10 +183,10 @@
{# Tag Table #}
{# ========================================================= #}
<div class="card bg-black border-secondary text-light shadow-sm">
<div class="card-body p-0">
<div class="card-body">
<div class="px-3 py-2 border-bottom border-secondary">
<strong>Vorhandene Tags</strong>
<div class="mb-3">
<strong class="text-info">Vorhandene Tags:</strong>
<span class="text-muted small ms-2">
{{ tags|length }} Einträge
</span>