246 lines
9.7 KiB
Twig
246 lines
9.7 KiB
Twig
{% extends 'admin/base.html.twig' %}
|
|
|
|
{% block title %}Indexierungsprofile{% endblock %}
|
|
|
|
{% block body %}
|
|
{# ============================= #}
|
|
{# Strukturstatus Alert #}
|
|
{# ============================= #}
|
|
|
|
{% if structureMismatch %}
|
|
<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.
|
|
Eine globale Neuindizierung ist erforderlich oder Sie haben kein indexiertes Dokument im System.
|
|
</div>
|
|
<a href="{{ path('admin_jobs') }}"
|
|
class="btn btn-sm btn-outline-danger">
|
|
Global Reindex starten
|
|
</a>
|
|
</div>
|
|
{% else %}
|
|
<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>
|
|
|
|
{% if is_granted('ROLE_SUPER_ADMIN') %}
|
|
<a class="btn btn-sm btn-outline-info"
|
|
href="{{ path('admin_ingest_profile_create') }}">
|
|
Neues Profil anlegen
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{# ========================================================= #}
|
|
{# PROFIL SYSTEM DESCRIPTION #}
|
|
{# ========================================================= #}
|
|
<div class="card bg-dark border-secondary mb-4 shadow-sm">
|
|
<div class="card-body text-light row">
|
|
|
|
<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>
|
|
|
|
<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>
|
|
|
|
</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>
|
|
|
|
<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>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# ============================= #}
|
|
{# Profile Tabelle #}
|
|
{# ============================= #}
|
|
|
|
<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>
|
|
<th>Version</th>
|
|
<th>Chunk Size</th>
|
|
<th>Overlap</th>
|
|
<th>Embedding</th>
|
|
<th>Dim</th>
|
|
<th>Scoring</th>
|
|
<th>Status</th>
|
|
<th>Reindex</th>
|
|
<th class="text-end">Aktionen</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for p in profiles %}
|
|
<tr>
|
|
<td>v{{ p.version }}</td>
|
|
<td>{{ p.chunkSize }}</td>
|
|
<td>{{ p.chunkOverlap }}</td>
|
|
<td>{{ p.embeddingModel }}</td>
|
|
<td>{{ p.embeddingDimension }}</td>
|
|
<td>{{ p.scoringVersion }}</td>
|
|
|
|
<td>
|
|
{% if p.active %}
|
|
<span class="badge bg-success">Aktiv</span>
|
|
{% else %}
|
|
<span class="badge bg-dark border border-secondary">
|
|
Inaktiv
|
|
</span>
|
|
{% endif %}
|
|
</td>
|
|
|
|
<td>
|
|
{% if p.reindexRequired %}
|
|
<span class="badge bg-warning text-dark">
|
|
Erforderlich
|
|
</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">
|
|
Nein
|
|
</span>
|
|
{% endif %}
|
|
</td>
|
|
|
|
<td class="text-end">
|
|
|
|
{% if not p.active and is_granted('ROLE_SUPER_ADMIN') %}
|
|
|
|
<form method="post"
|
|
action="{{ path('admin_ingest_profile_activate', {id: p.id}) }}"
|
|
class="d-inline"
|
|
onsubmit="return confirm('Profil aktivieren? Global Reindex kann erforderlich sein.');">
|
|
|
|
<input type="hidden"
|
|
name="_token"
|
|
value="{{ csrf_token('activate_ingest_profile_' ~ p.id) }}">
|
|
|
|
<button class="btn btn-sm btn-outline-success me-2">
|
|
Aktivieren
|
|
</button>
|
|
</form>
|
|
|
|
<form method="post"
|
|
action="{{ path('admin_ingest_profile_remove', {id: p.id}) }}"
|
|
class="d-inline"
|
|
onsubmit="return confirm('Profil wirklich löschen?');">
|
|
|
|
<input type="hidden"
|
|
name="_token"
|
|
value="{{ csrf_token('delete_ingest_profile_' ~ p.id) }}">
|
|
|
|
<button class="btn btn-sm btn-outline-danger">
|
|
Löschen
|
|
</button>
|
|
</form>
|
|
|
|
{% endif %}
|
|
|
|
</td>
|
|
</tr>
|
|
{% else %}
|
|
<tr>
|
|
<td colspan="9" class="text-center text-secondary py-4">
|
|
Keine Profile vorhanden.
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
{# ============================= #}
|
|
{# Struktur-Diff #}
|
|
{# ============================= #}
|
|
|
|
<div class="card bg-black border-secondary">
|
|
<div class="card-body text-light">
|
|
|
|
{% if indexMeta %}
|
|
<div class="mb-3 small text-light">
|
|
Aktuell erstellte Index-Version:
|
|
<strong>{{ indexMeta.index_version }}</strong>
|
|
</div>
|
|
{% else %}
|
|
<div class="alert alert-warning">
|
|
index_meta.json nicht gefunden.
|
|
</div>
|
|
{% endif %}
|
|
|
|
<table class="table table-dark table-striped table-hover align-middle">
|
|
<thead class="table-secondary text-dark">
|
|
<tr>
|
|
<th>Parameter</th>
|
|
<th>Aktiver Meta-Index (System)</th>
|
|
<th>Aktives Meta-Profil</th>
|
|
<th>Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for key, row in diff %}
|
|
<tr>
|
|
<td>{{ key }}</td>
|
|
<td>{{ row.meta }}</td>
|
|
<td>{{ row.profile }}</td>
|
|
<td>
|
|
{% if row.equal %}
|
|
<span class="badge bg-success">Identisch</span>
|
|
{% else %}
|
|
<span class="badge bg-danger">Abweichung</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% else %}
|
|
<tr>
|
|
<td colspan="4" class="text-center text-secondary py-4">
|
|
Keine Vergleichsdaten verfügbar.
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4 small text-light">
|
|
Hinweis: Strukturänderungen (Chunking, Embedding, Scoring)
|
|
führen zu inkonsistentem Retrieval, bis eine vollständige
|
|
Neuindizierung durchgeführt wird.
|
|
</div>
|
|
|
|
{% endblock %} |