29 KiB
Feature Data Sub-Agent: Query-Repair-Loop + Ontologie-Layer (Trustee-Pilot)
Beschreibung und Kontext
Der Feature Data Sub-Agent (featureDataAgent.py, Tool queryFeatureInstance) gibt dem Workspace-Agent strukturierten Zugriff auf Feature-Datentabellen (Trustee, RealEstate, ...) via browseTable/queryTable/aggregateTable. Heute steuert er sich aus einem Schema-Prompt (Pydantic-Felder + freie Domain-Hints in mainXxx.getAgentDomainHints()).
Bekannte Halluzinations-Klassen:
- Falsche Aggregate auf bereits aggregierten Spalten (z. B.
SUM(closingBalance)über mehrere Perioden -- 7 Jahre × 13 Perioden ≈ 90× echter Saldo). - ISO-String vs. Unix-Sekunden-Datumsfilter trotz expliziter Hint-Zeile.
- Erfundene Feldnamen (LLM rät Felder, statt
browseTablezuerst aufzurufen). - Wrong-Table-Choice (raw JournalLine statt period-bucketed AccountBalance).
Heins Forschung (Boosting Querying Accuracy with Ontology-Guided LLMs, ENTER 2025) zeigt: Vanilla Text-to-SQL erreicht ~16 % Genauigkeit; ontologie-gestützter Zugriff fast 3× bessere Werte. Sein zweiter Hebel ist deterministische Post-Validierung mit strukturiertem Re-Prompt -- einfacher als die Ontologie und sofort umsetzbar.
Business-Treiber:
- Trustee ist heute der Hot-Spot für Halluzinationen (Bling, PWG, WW-Sales-Pitch). Jede falsche Saldo-Antwort untergräbt das Vertrauen direkt im wichtigsten Feature.
- AI-Agent-Quality ist das einzige Differenzierungsmerkmal gegenüber Copilot -- ohne mess- und steuerbare Genauigkeit kein USP.
- Vorbereitung auf 90-Min-Q&A mit Andreas Hein: konkretes Pilot-Setup statt abstrakter Diskussion.
Risiko bei Nicht-Tun: Halluzinationen bleiben opportunistisch über Domain-Hint-Strings gefixt; bei jedem neuen Modell oder Edge-Case Regressionen, ohne Eval-Suite nicht messbar. Die nächsten Features (Lawyer, RealEstate-Buchhaltung) erben das Problem.
Fokus und kritische Details
- Eine zentrale Stelle:
featureDataAgent._buildSchemaContextundfeatureDataProvider-- jede Doppel-Implementierung pro Feature ist verboten (Doc-Syncb-reference/platform-core/ai-agent.md). - Token-Budget: Jede Prompt-Zeile zählt für jeden Sub-Agent-Call. Heins Paper zeigt, dass kompakte, strukturierte Constraints mehr bringen als ausgeschriebene Erklärungen. Trustee-Hints sind heute ~80 Zeilen -- hart an der Grenze.
- Repair-Loop verbraucht Runden, nicht extra AI-Calls: Wenn ein Tool-Call deterministisch invalid ist, geben wir dem LLM einen strukturierten Fehler zurück (im selben Tool-Result). Kein paralleler Mini-Agent, keine versteckten Mehrkosten.
- Ontologie ist optional pro Feature: Features ohne
getAgentOntology()laufen weiter über den heutigen 3-Schichten-Prompt. Kein Big-Bang. - Benchmark-Eval ist der eigentliche Release-Gate, nicht "Feature gebaut" -- ohne Vorher-/Nachher-Zahlen kein Merge.
- Multi-Tenant-Datenrealismus für Eval: Goldstandard-Antworten brauchen echte Tenant-Daten (anonymisiert oder synthetic-faithful). Nicht aus dem Bauch.
Ziel und Nicht-Ziele
Ziel Phase 1 (Repair-Loop, 3.3):
- Deterministischer Pre-Execute-Validator in
featureDataProvider-- liefert strukturierte Fehler statt SQL-Exceptions. - Strukturierte Repair-Hints im Tool-Result, sodass der ReAct-Loop sofort konkret weiss, was zu fixen ist.
- Validierungs-Constraints zentral konfigurierbar pro Tabelle (kein Hardcode in der Provider-Methode).
Ziel Phase 2 (Ontologie-Pilot Trustee, 3.1):
OntologyDescriptor-Datenmodell als typisierter Container für Entitäten, Beziehungen und Constraints.- Trustee-Ontologie als erste vollständige Implementation (50-100 Entitäten/Beziehungen).
getAgentOntology()-Hook pro Feature-Modul, analog zum bestehendengetAgentDomainHints().- Auto-Generierung von Validator-Constraints und Prompt-Hints aus derselben Ontologie -- Single Source of Truth.
- Heins ENTER-2025-Methodik: 19-20 Goldstandard-Fragen pro Feature, automatisierter Vorher-/Nachher-Vergleich.
Explizit NICHT:
- Ontologien für andere Features in dieser Iteration (RealEstate, CommCoach, Workspace folgen nach Trustee-Pilot-Erfolg).
- Volle OWL/RDF-Tooling -- wir bleiben bei Pydantic + JSON-LD-light. Kein neuer Dependency-Stack.
- Frontend-Editor für Ontologien -- v1 ist Code-as-Truth, UI später.
- Re-Implementierung der Tool-Signaturen -- LLM-API bleibt identisch.
- Ontologie-getriebene SQL-Generierung über Toolset hinaus -- wir bleiben beim Tool-Calling-Pattern.
Betroffene Module
-
Gateway:
platform-core/modules/serviceCenter/services/serviceAgent/datamodelAgent.py(ToolResult.errorDetails,ToolCallLog.validationFailureCode,AgentTrace-Counter)platform-core/modules/serviceCenter/services/serviceAgent/agentLoop.py(Aggregation der Repair-Counter inAgentTraceaus denToolCallLog-Einträgen am Run-Ende)platform-core/modules/serviceCenter/services/serviceAgent/featureDataAgent.py(Schema-Prompt-Build, Ontologie-Hook-Resolver, Pre-Execute-Validator-Call, Tool-Description-Ergänzung umerrorDetails-Erklärung)platform-core/modules/serviceCenter/services/serviceAgent/featureDataProvider.py(unverändert; Validator läuft davor)platform-core/modules/serviceCenter/services/serviceAgent/queryValidator.py(neu) (zentrale Validator-Engine)platform-core/modules/serviceCenter/services/serviceAgent/datamodelOntology.py(neu) (OntologyDescriptor,Constraint,Entity,Relation,QueryValidationError)platform-core/modules/features/trustee/mainTrustee.py(_AGENT_DOMAIN_HINTSersetzt durchgetAgentOntology()+ auto-generated hints; alte Hints als_AGENT_DOMAIN_HINTS_LEGACYfür 1 Sprint geparkt)platform-core/modules/features/trustee/trusteeOntology.py(neu) (Trustee-Buchhaltungs-Ontologie als Code)
-
Frontend: keine Änderungen.
-
DB-Migration: nein. Eval-Goldstandard liegt im Test-Repo, nicht in der Mandanten-DB.
-
Andere Komponenten:
platform-core/tests/integration/featureDataAgent/(neu) -- Goldstandard-Eval-Harness, läuft optional in CI als langer Job.platform-core/tests/fixtures/trusteeBenchmark/(neu) -- 19 Fragen + Goldstandard-Antworten + Python-Fixture-Loader (KEINEfixture.sql; bestehende Trustee-Tests bauen Daten konsistent via Pydantic +recordCreateauf, z. B.tests/unit/features/trustee/test_accountingDataSync_balances.py). Goldstandard-Fragen bleiben als YAML.
Entscheidungen
| Datum | Entscheidung | Begründung |
|---|---|---|
| 2026-05-15 | Phase 1 (Repair-Loop) vor Phase 2 (Ontologie) | Niedrigste Komplexität zuerst, baut Eval-Infrastruktur, die Phase 2 ohnehin braucht |
| 2026-05-15 | Validator-Engine zentral, nicht in _browseTable/_queryTable/_aggregateTable |
Wiederverwendung über alle drei Tools, einheitliches Error-Format, testbar isoliert |
| 2026-05-15 | Repair-Hints als Tool-Result, kein zweiter AI-Call | Bleibt im ReAct-Pattern, keine versteckten Kosten, einfacher zu debuggen |
| 2026-05-15 | Ontologie als Pydantic-Code, nicht als JSON-Datei | Typsicher, IDE-Refactoring, kein Loader-Code, gleiche Patterns wie restliche Plattform |
| 2026-05-15 | Trustee als Pilot-Feature | Höchste Halluzinationsrate, klarste Kunden-Sichtbarkeit, bestehende Domain-Hints liefern bereits 80 % der Ontologie-Inhalte |
| 2026-05-15 | Goldstandard mit synthetischen Daten, nicht Kunden-Snapshot | Reproduzierbar in CI, kein Datenschutz-Issue, deckt Edge-Cases bewusst ab |
| 2026-05-15 | 19 Fragen analog Hein 2025 | Empirisch validierte Sample-Grösse, vergleichbar mit Paper-Resultaten, Aufwand machbar |
Architektur
Phase 1: Query-Repair-Loop (3.3)
Heute (Soll-Ist):
LLM → Tool _queryTable → FeatureDataProvider.queryTable
→ SQL execute
→ Exception "column does not exist" → str(e) im ToolResult
→ LLM bekommt rohen SQL-Fehler, rät neue Query
Soll:
LLM → Tool _queryTable → QueryValidator.validate(args, ontology|schema)
→ strukturierter Fehler ODER ok
→ falls ok: FeatureDataProvider.queryTable
→ falls Fehler: ToolResult(
success=False,
error="FIELD_NOT_FOUND: closingBalance? (you wrote klosingBalance)",
errorDetails={"code": "FIELD_NOT_FOUND",
"field": "klosingBalance",
"suggestion": "closingBalance",
"hint": "Use browseTable to inspect schema."})
→ LLM bekommt actionable Fehler, korrigiert in nächster Runde
Datenmodell-Erweiterung ToolResult (datamodelAgent.py):
Heute ist ToolResult.error: Optional[str]. Für den strukturierten Repair-Hint kommt ein zusätzliches Feld dazu:
class ToolResult(BaseModel):
...
error: Optional[str] = None # bleibt: kurzer human-readable Text
errorDetails: Optional[Dict[str, Any]] = None # NEU: strukturierter Repair-Hint
error bleibt der kurze String, den heutige Konsumenten (Logs, Tests, Audit) sehen. errorDetails ist der maschinenlesbare Block, den der LLM-Tool-Result-Serializer einbettet. Das vermeidet JSON-in-String-Hacks (error=json.dumps(...)) und hält die bestehenden Tools rückwärtskompatibel.
Komponenten:
-
datamodelOntology.QueryValidationError(Pydantic):code: ValidationErrorCode(Enum:FIELD_NOT_FOUND,INVALID_AGGREGATE_TARGET,WRONG_TABLE_FOR_PURPOSE,TYPE_MISMATCH,OPERATOR_INCOMPATIBLE,MISSING_REQUIRED_FILTER)field: Optional[str]suggestion: Optional[str](z. B. fuzzy-matched Feldname)hint: str(kurzer korrigierender Tipp, max. 80 Zeichen)
-
queryValidator.QueryValidator-- isolierte Klasse, eine Methode pro Tool:validateBrowseQuery(args, tableSchema) -> Optional[QueryValidationError]validateQueryTable(args, tableSchema, constraints) -> Optional[QueryValidationError]validateAggregateQuery(args, tableSchema, constraints) -> Optional[QueryValidationError]- Liest
tableSchemaaus dem Pydantic-Modell (MODEL_REGISTRY). - Liest
constraintsaus optional verfügbarerOntologyDescriptor.
-
Constraint-Typen (Phase 1, Minimum):
FieldExists: Feldname existiert in Pydantic-Modell.OperatorCompatible: Operator passt zum Feldtyp (LIKEnur auf String,>=nur auf Comparable).AggregateTarget: Whitelistnumerische, nicht bereits aggregierte Felder. Blacklist via Konvention (*Balance,*Total) -- Phase 2 ersetzt das durch ontologische Markierung.OrderByValid:orderByist gültiger Feldname.
-
Integration in
_browseTable/_queryTable/_aggregateTable:- Vor
provider.xxx(...)wirdQueryValidator.validateXxx(...)aufgerufen. - Bei Fehler:
ToolResult(success=False, error="<code>: <short hint>", errorDetails={code, field, suggestion, hint}).errorbleibt der Kurz-String für Logs/Audit;errorDetailsist der maschinenlesbare Block für den LLM. - LLM-Tool-Schema (
parameters) bleibt identisch -- nur die Tool-Description bekommt einen Satz dazu, dererrorDetailserklärt. Repair-Logik bleibt sonst unsichtbar.
- Vor
-
Telemetrie:
- Heimat ist
AgentTrace/ToolCallLogindatamodelAgent.py, NICHTaiAuditLogger. Begründung:aiAuditLogger.logAiCallschreibt pro AI-Call inAiAuditLogEntry-- Repair-Metriken sind aber pro Sub-Agent-Run aggregiert (über alle Runden). ToolCallLogumvalidationFailureCode: Optional[str]erweitern (Wert = dercodeauserrorDetails, oderNonewenn ok). Damit landet die Information bei der bestehenden_persistTrace-Pipeline inagentLoop.pyund wird mit dem Rest des Traces gespeichert.AgentTracebekommt zusätzlich drei aggregierte Counter (validationFailures,repairAttempts,successAfterRepair), dieagentLoopam Run-Ende aus denToolCallLog-Einträgen aufsummiert.- Eval-Harness und der separate Vergleichsbericht lesen direkt aus
AgentTrace; kein Eingriff in den AI-Audit-Log nötig.
- Heimat ist
Phase 2: Ontologie-Layer für Trustee (3.1)
OntologyDescriptor-Modell:
class Entity(BaseModel):
name: str # "Konto", "AccountBalance", "JournalEntry"
pythonClass: Optional[str] # FK zum Pydantic-Modell, wenn DB-backed
semanticType: SemanticType # ACCOUNT, BALANCE_SNAPSHOT, TRANSACTION, ...
description: str # Mensch-lesbar
invariants: List[Invariant] # z. B. "closingBalance ist NICHT summable"
class Relation(BaseModel):
fromEntity: str
toEntity: str
cardinality: Cardinality # ONE_TO_MANY, MANY_TO_ONE, ...
via: Optional[str] # FK-Feldname
class Constraint(BaseModel):
appliesTo: str # "AccountBalance.closingBalance"
rule: ConstraintRule # NEVER_AGGREGATE, REQUIRES_FILTER_ON, ...
message: str # Hint für Repair-Loop
class CanonicalQueryPattern(BaseModel):
intent: str # "Banksaldo per Stichtag"
pattern: dict # Tool-Call-Skelett mit Platzhaltern
class OntologyDescriptor(BaseModel):
featureCode: str
entities: List[Entity]
relations: List[Relation]
constraints: List[Constraint]
canonicalPatterns: List[CanonicalQueryPattern]
Trustee-Ontologie (Inhalte):
| Block | Anzahl | Heute in Domain-Hints | Neue Form |
|---|---|---|---|
| Konten-Klassen (Aktiven, Passiven, Ertrag, Aufwand) | 9 | Hardcoded Bullet-Liste | Entity mit semanticType + Account-Number-Range-Invariant |
| Konten-Subklassen (Bank, Kasse, Forderungen, ...) | ~15 | Bullet-Liste | Entity mit parentEntity |
| Periode-Konvention | 1 | Prosa | Entity mit Invariant: periodMonth=0 → annual |
| Saldo-Aggregations-Verbot | 1 (impliziert) | Anti-Pattern-Section | Constraint(NEVER_AGGREGATE, on="*Balance, *Total") |
| Datumsformat-Pflicht | 1 | Prosa | Constraint(TYPE_MISMATCH_GUARD, on="bookingDate") |
| Canonical Patterns | 4 | Code-Blöcke im Prompt | CanonicalQueryPattern mit Skelett |
Auto-Generierung des Prompt-Texts:
_buildSchemaContextruftgetAgentOntology(featureCode).- Wenn vorhanden:
OntologyToPromptCompiler.compile(ontology, requestLang)→ kompakter, deterministisch sortierter Prompt-Block (ähnliche Länge wie heute, aber ableitbar). - Wenn nicht vorhanden: Fallback auf altes
getAgentDomainHints()(Übergangsphase).
Ontologie speist auch den Validator:
QueryValidatorlädt Constraints aus der Ontologie.INVALID_AGGREGATE_TARGETfürclosingBalance-SUM kommt nicht mehr aus Hardcoded-Regex, sondern ausConstraint(rule=NEVER_AGGREGATE, appliesTo="AccountBalance.closingBalance", message="closingBalance is already a per-period balance.").
Single Source of Truth: Constraint einmal in der Ontologie definiert → wirkt im Prompt und im Validator. Heutige Doppelpflege (Anti-Pattern in Domain-Hint UND impliziter Trust auf LLM) verschwindet.
Eval-Harness (querschneidend, Phase 1 + 2)
Goldstandard-Format (platform-core/tests/fixtures/trusteeBenchmark/questions.yaml):
- id: q01
question: "Was ist der Banksaldo per 31.12.2025?"
intent: BANK_BALANCE_AT_DATE
expectedToolPattern:
tool: queryTable
tableName: TrusteeDataAccountBalance
requiredFilters: [periodYear=2025, periodMonth=0, accountNumber LIKE 102%]
forbiddenTools: [aggregateTable] # SUM closingBalance verboten
expectedAnswerContains: ["1020", "1021", "CHF"]
expectedAnswerNumeric:
"1020.closingBalance": 152400.00
"1021.closingBalance": 28100.00
Harness (platform-core/tests/integration/featureDataAgent/test_trusteeBenchmark.py):
- Lädt das Benchmark-Fixture über einen Python-Loader (
loadTrusteeBenchmarkFixture(db, mandateId, featureInstanceId)) austests/fixtures/trusteeBenchmark/. Loader nutzt dieselben Pydantic-Modelle undrecordCreate-Pfade wie Production (kein SQL-Dump, kein Schema-Drift). - Iteriert über 19 Fragen.
- Pro Frage:
runFeatureDataAgentausführen,AgentTrace(Tool-Call-Log inkl.validationFailureCode) + finale Antwort capturen. - Bewertung pro Frage:
- Pattern-Match: richtige Tools, richtige Filter? (binär)
- Forbidden-Tools: keine verbotenen Aggregations-Calls? (binär)
- Numerische Korrektheit: Antwort enthält erwartete Zahlen? (toleranzbasiert)
- Repair-Conversion: falls Validator-Fehler getriggert -- hat das LLM in der nächsten Runde einen anderen, validen Tool-Call abgesetzt? (binär, nur für Fälle mit
repairTriggered=True)
- Aggregierte Metrik pro Run:
accuracy,patternCompliance,repairTriggeredCount,repairSucceededCount,repairConversionRate = repairSucceededCount / max(1, repairTriggeredCount),avgValidationFailuresPerRun. - Lauft als pytest-marker
@pytest.mark.eval, nicht in normaler Test-Suite -- gezielt aufrufbar, lange Laufzeit (≈ 10 Minuten + AI-Cost).
Vergleichs-Runs:
- Baseline: heutiger Code, ohne Repair-Loop, ohne Ontologie.
- Phase 1 done: mit Repair-Loop, ohne Ontologie.
- Phase 2 done: mit Repair-Loop und Ontologie.
- Resultate als Markdown-Bericht in
local/notes/eval-results-YYYY-MM-DD.md.
Umsetzungs-Checkliste
Phase 1 -- Repair-Loop (Woche 1, ca. 3-4 PT) -- DONE 2026-05-15
datamodelOntology.pymit minimalerOntologyDescriptor,Constraint,QueryValidationError(auch ohne volle Ontologie nutzbar)ToolResult.errorDetails: Optional[Dict[str, Any]]indatamodelAgent.pyergänzenToolCallLog.validationFailureCode: Optional[str]indatamodelAgent.pyergänzenAgentTraceum aggregierte Counter (validationFailures,repairAttempts,successAfterRepair) erweitern;agentLoop._computeRepairCounterssummiert ausToolCallLogam Run-Ende, Counters fliessen insAGENT_SUMMARY-Event und damit in_persistTracequeryValidator.QueryValidatormitFieldExists,OperatorCompatible,AggregateTarget,OrderByValid-Constraints (+TYPE_MISMATCHfür SUM auf nicht-numerischen Feldern)- Integration in
_browseTable,_queryTable,_aggregateTable(pre-execute-Validator) -- setzterrorDetailsund befüllterrorals Kurz-String, Provider wird nie mit known-bad Query erreicht - Tool-Descriptions in
featureDataAgent._buildSubAgentToolsergänzen: "On validation failure the tool returns success=False with errorDetails={code, field, suggestion, hint}. Use that to correct the next call." - Unit-Tests
test_queryValidator.py(24 Fälle: pro Constraint Happy + Sad Path, plus Ontologie-Override-Test als Phase-2-Readiness) - Unit-Test-Erweiterung
test_featureDataAgent_schema.pyfürToolResult.errorDetails-Format (3 neue Tests: Short-Circuit auf invalid field, Short-Circuit auf SUM(closingBalance), Sanity dass valide Calls den Provider erreichen) - Unit-Tests
test_agentTrace_repairCounters.py(8 Fälle: leerer Trace, sauberer Run, Single-Fail-No-Repair, Repair-Success, Repair-Re-Fail, Sibling-Calls-im-selben-Round, Tool-Independence, Multi-Tool-Mix) - DB-Migration: nein
- Frontend: nein
- Neutralisierung betroffen? Nein
- RBAC: nein
- Billing-Impact? Nein (Repair-Hints kosten 0 zusätzliche AI-Calls)
- Lint-Check: keine Warnings
- Regression-Test: alle 62 bestehenden serviceAgent-/featureDataAgent-schema-Tests bleiben grün
Phase 1.5 -- Eval-Harness (Woche 2, ca. 3-4 PT, parallel zu Phase 2 startbar)
platform-core/tests/fixtures/trusteeBenchmark/loadTrusteeBenchmarkFixture.py(Python-Loader, Pydantic +recordCreate; analog zutests/unit/features/trustee/test_accountingDataSync_balances.py)platform-core/tests/fixtures/trusteeBenchmark/questions.yamlmit 19 Fragen + Goldstandardtest_trusteeBenchmark.pymit pytest-markereval; misst nebenaccuracyundpatternComplianceauchrepairConversionRate(Repair-Triggered → korrigiert in nächster Runde)- Eval-Bericht-Generator (Markdown-Output)
- Baseline-Run (vor Phase 2) -- Resultate in
local/notes/eval-results-baseline.md - Phase-1-Run (nach Repair-Loop, vor Ontologie) -- Resultate in
local/notes/eval-results-phase1.md
Phase 2 -- Ontologie-Pilot Trustee (Woche 3-4, ca. 5-7 PT) -- DONE
- Ontology-Datenmodell vervollständigt (
Entity,Relation,Invariant,CanonicalQueryPattern,SemanticType-Enum -- bereits in Phase 1 vorbereitet, jetzt aktiv genutzt) platform-core/modules/features/trustee/trusteeOntology.pymit Trustee-Buchhaltungs-Ontologie (6 Entitäten inkl. BankAccount/CashAccount-Spezialisierungen, 3 Relations, 6 Constraints, 8 CanonicalPatterns)getAgentOntology()-Hook inmainTrustee.pyOntologyToPromptCompiler-- generiert kompakten Prompt-Text deterministisch (ontologyToPromptCompiler.py)_buildSchemaContextruft Ontologie-Compiler bevorzugt (_loadFeatureOntologyBlock), fällt aufgetAgentDomainHints()zurück; mitPOWERON_DISABLE_FEATURE_ONTOLOGY=1Eval-only OverrideQueryValidatorlädt Constraints aus Ontologie (NEVER_AGGREGATE via_isAggregateBlacklisted);_buildValidatorForFeaturewired Validator+Ontologie automatisch- Migration: alte Trustee-Domain-Hints als
_AGENT_DOMAIN_HINTS_LEGACYgeparkt (Sicherheitsnetz) - Unit-Tests für Ontologie-Loading + Compiler (16 Tests in
test_trusteeOntology.py); test_featureDataAgent_schema erweitert um Ontology-Pfad - Phase-2-Run des Eval-Harness -- Resultate in
local/notes/trustee-benchmark-20260515-161126.md - Vergleichs-Tabelle Baseline vs. Phase 1 vs. Phase 2 -- gleicher Report
- Side-Fix:
aggregateTableTool-Definition umfilters-Parameter erweitert (war Code-Bug, blockierte SUM-on-account-Anfragen)
Abschluss vor Hein-Q&A
- Vergleichs-Tabelle in präsentierbarem Format -- Markdown-Tabelle in
local/notes/trustee-benchmark-20260515-161126.md(Summary-Sektion) - Übersicht der häufigsten
validationFailuresaus Telemetrie -- 2×INVALID_AGGREGATE_TARGETaufdebitTotal(q13 Phase 1); Phase 2 verhindert das präemptiv durch Ontologie-Block im Prompt
Akzeptanzkriterien
| # | Kriterium (Given-When-Then) | Prio |
|---|---|---|
| 1 | Given Sub-Agent ruft queryTable mit nicht-existentem Feld, When Validator prüft vor Execute, Then Tool-Result hat success=False mit code=FIELD_NOT_FOUND und suggestion-Feld |
must |
| 2 | Given Sub-Agent ruft aggregateTable(SUM, closingBalance) ohne Periode-Filter, When Validator prüft, Then Tool-Result hat code=INVALID_AGGREGATE_TARGET mit Hint zu Periode-Filter |
must |
| 3 | Given Validator-Fehler wurde im Eval-Run getriggert (Repair-Triggered-Subset), When ReAct nächste Runde startet, Then in ≥ 80 % dieser Fälle setzt das LLM einen anderen, validen Tool-Call ab (repairConversionRate ≥ 0.8, nicht End-Accuracy) |
must |
| 4 | Given Trustee-Feature exportiert getAgentOntology(), When _buildSchemaContext läuft, Then verwendet es die Ontologie und nicht getAgentDomainHints() |
must |
| 5 | Given getAgentOntology() fehlt für ein Feature, When Sub-Agent läuft, Then Fallback auf altes Verhalten ohne Fehler |
must |
| 6 | Given Eval-Harness läuft auf Goldstandard-19, When Baseline und Phase 2 verglichen, Then Phase 2 hat ≥ 25 Prozentpunkte bessere accuracy |
must |
| 7 | Given Phase 2 deployt, When Sub-Agent für irgendein Feature ohne Ontologie läuft, Then Verhalten unverändert (keine Regression) | must |
| 8 | Given Validator-Constraints aus Ontologie, When Constraint geändert wird, Then sowohl Prompt als auch Validator nutzen die neue Definition (Single Source of Truth) | must |
| 9 | Given Repair-Hint im Tool-Result, When als Tool-Description-Schema beschrieben, Then LLM versteht das Format ohne zusätzlichen Prompt-Text auf jedem Call | should |
| 10 | Given AgentTrace mit aggregierter Telemetrie, When Sub-Agent-Run analysiert wird, Then validationFailures und successAfterRepair (aus ToolCallLog-Aggregation) zeigen, ob Repair-Loop wirkt |
should |
Testplan
| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
|---|---|---|---|---|---|
| T1 | 1 | unit | ja | platform-core/tests/unit/services/test_queryValidator.py | DONE (24 tests) |
| T2 | 2 | unit | ja | platform-core/tests/unit/services/test_queryValidator.py | DONE |
| T3 | 3 | eval | standalone runner | platform-core/tests/eval/runTrusteeBenchmark.py | DONE (Phase 1 zeigt repair-deflection in q13: val-fail=2, accuracy 89.5% -> 94.7%) |
| T4 | 4 | unit | ja | platform-core/tests/unit/services/test_featureDataAgent_schema.py, test_trusteeOntology.py | DONE |
| T5 | 5 | unit | ja | platform-core/tests/unit/services/test_featureDataAgent_schema.py | DONE (test_skipsHintsForFeaturesWithoutHook, test_buildValidatorForFeature_unknownFeature_noOntology) |
| T6 | 6 | eval | standalone runner | platform-core/tests/eval/runTrusteeBenchmark.py | DONE -- Baseline 89.5% -> Phase 2 100% = +10.5 Pp (Ziel war ≥ 25 Pp; AC nur teilweise erfüllt -- ehrlich dokumentiert, siehe Diskussion unten) |
| T7 | 7 | unit + smoke | ja | platform-core/tests/unit/services/test_featureDataAgent_schema.py | DONE -- Features ohne Ontologie laufen unverändert |
| T8 | 8 | unit | ja | platform-core/tests/unit/services/test_trusteeOntology.py | DONE -- 16 tests, einer prüft Validator + Compiler über gleiche Constraint |
| T9 | 9 | manuell | nein | platform-core/modules/serviceCenter/services/serviceAgent/featureDataAgent.py:284-310 | DONE -- Tool-Description erklärt errorDetails-Format inline |
| T10 | 10 | unit | ja | platform-core/tests/unit/serviceAgent/test_agentTrace_repairCounters.py | DONE (8 tests) |
Links
- Forschungs-Input:
pamocreate/projects/poweron/admin-compliance-security/20-outputs/20260515-andreas-hein-learnings-fuer-porta.md(Punkte 3.1, 3.3) - Hein-Paper: Boosting Querying Accuracy with Ontology-Guided LLMs (ENTER 2025, Springer)
- Bestehender Code:
platform-core/modules/serviceCenter/services/serviceAgent/featureDataAgent.py,mainTrustee.py:676-754(_AGENT_DOMAIN_HINTS) - Bezug Wiki:
b-reference/platform-core/ai-agent.md(Abschnitt FeatureSubAgent)
Abschluss
b-reference/platform-core/ai-agent.mdaktualisiert (FeatureSubAgent-Abschnitt:ToolResult.errorDetails,QueryValidator, Ontologie-Hook, Eval-Harness, Repair-Telemetrie aufAgentTrace)b-reference/platform-core/features/trustee.mdneuer Abschnitt "Agent-Ontologie"wiki/d-guides/coding-conventions.mdMigrations-Pattern fürgetAgentOntology()- TOPICS.md geprüft (kein neuer Eintrag erforderlich -- bestehende KI-Agent-Sektion verweist auf die aktualisierte b-reference-Seite)
- Eval-Vergleich für Hein-Q&A:
local/notes/trustee-benchmark-20260515-161126.md(Summary-Tabelle) - Dieses Dokument →
c-work/4-done/nach Validierungs-Sign-off
Ergebnisse (Validation)
Benchmark-Run vom 2026-05-15 16:11 (19 Fragen × 3 Modi, 57 echte LLM-Calls):
| Mode | Accuracy | Pattern compliance | Validator rejects | Rounds | Cost (CHF) |
|---|---|---|---|---|---|
| Baseline (no validator) | 89.5 % | 89.5 % | 0 | 40 | 0.0841 |
| Phase 1 (validator on) | 94.7 % | 100 % | 2 | 41 | 0.0851 |
| Phase 2 (validator + ontology) | 100 % | 100 % | 0 | 39 | 0.0975 |
Beobachtungen:
- Phase 1 Repair-Loop wirkt aktiv (q13 "Summe aller Aufwandskonten"): Validator lehnt
aggregateTable(SUM, debitTotal)2× ab, der Agent lenkt eigenständig aufqueryTablemit Periode-Filter um -- ohne dass derselbe Tool-Call wiederholt wird (Deflection statt klassischer Repair). Das erklärtrepairAttempts=0trotz Erfolg: Der Agent korrigiert sich präemptiv über die strukturierteerrorDetails-Antwort. - Phase 2 Ontologie verhindert Halluzinationen präemptiv (q09 "Beratungsertrag"): Baseline und Phase 1 wählen fälschlicherweise
creditTotalstattclosingBalance. Die Ontologie-Beschreibung "debitTotal/creditTotal sind period TURNOVER, nicht Balance" lenkt den Agent korrekt zuclosingBalance-- kein Validator-Hit nötig. - Phase 2 ist effizienter (39 vs. 41 Runden) trotz längerem Ontologie-Prompt-Block: weniger Fehl-Versuche durch klare canonical patterns.
- Cost-Trade-off: Phase 2 ist 16 % teurer (0.0975 vs. 0.0841 CHF / 19 Fragen) für 10.5 Prozentpunkte mehr Accuracy. Klar gerechtfertigt.
- Side-Fix entdeckt:
aggregateTableexponierte denfilters-Parameter nicht im Tool-Schema -- LLM konnte keine gefilterten Aggregate machen. Behoben infeatureDataAgent._aggregateTable+ Schema; ohne diesen Fix wäre die Trustee-Suite verzerrt.
AC-6 Ehrlichkeit: Das Plan-Kriterium ≥ 25 Prozentpunkte Accuracy-Gain (Baseline → Phase 2) wurde nicht erreicht (real: +10.5 Pp). Grund: Die heutigen Trustee-Domain-Hints sind bereits hochwertig (Baseline schon bei 89.5 %), das Verbesserungs-Headroom war kleiner als angenommen. Die Forschungs-Vorlage hat von 16 % → 47 % (Hein 2025) gemessen -- bei einem deutlich schwächeren Baseline-Prompt. Die qualitative Aussage "Ontologie + Repair-Loop verhindern bekannte Halluzinations-Klassen" bleibt belegt (q09 ausschließlich durch Ontologie, q13 ausschließlich durch Repair-Loop).