From cc7aa1899d8963bd25d9cbf0d6d09239d8b53ade Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Tue, 28 Apr 2026 11:58:53 +0200
Subject: [PATCH] fixes ai agents parameter flow
---
b-reference/gateway/ai-agent.md | 8 +++++---
c-work/_CHANGELOG.md | 14 +++++++++++++-
2 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/b-reference/gateway/ai-agent.md b/b-reference/gateway/ai-agent.md
index cf37dab..3780a28 100644
--- a/b-reference/gateway/ai-agent.md
+++ b/b-reference/gateway/ai-agent.md
@@ -1,6 +1,6 @@
-
-
+
+
# AI Agent & Knowledge Store
@@ -288,7 +288,9 @@ Siehe [`b-reference/teams-bot/architecture.md`](../teams-bot/architecture.md) f
| `gateway/modules/serviceCenter/services/serviceAgent/toolRegistry.py` | Registrierung, Dispatch, `readOnly`, Function-Calling-Format |
| `gateway/modules/serviceCenter/services/serviceAgent/conversationManager.py` | Kontextfenster, Summarization, Systemprompt |
| `gateway/modules/serviceCenter/services/serviceAgent/datamodelAgent.py` | `AgentConfig`, Events, Trace-Modelle |
-| `gateway/modules/serviceCenter/services/serviceAgent/actionToolAdapter.py` | Workflow-Actions → Agent-Tools |
+| `gateway/modules/serviceCenter/services/serviceAgent/actionToolAdapter.py` | Workflow-Actions → Agent-Tools (Schema-Generierung; Param-Validierung erfolgt zentral im `ActionExecutor`) |
+| `gateway/modules/workflows/processing/shared/parameterValidation.py` | Universelle Action-Parameter-Validierung + Coercion (Required-Enforcement, Ref-Schema → id-String, Primitive-Coercion); aufgerufen aus `ActionExecutor.executeAction` für **alle** Aufrufpfade (Agent, Workflow-Graph, REST) |
+| `gateway/modules/connectors/connectorDbPostgre.py` | DB-Connector; Read-Methoden raisen `DatabaseQueryError` bei echten Query-Fehlern (Postgres-Adapt, UndefinedTable/Column, OperationalError, …); Empty Result Sets bleiben `[]`/`None` |
| `gateway/modules/serviceCenter/services/serviceAgent/toolboxRegistry.py` | Toolbox-Definitionen, `requestToolbox` Meta-Tool-Schema |
| `gateway/modules/serviceCenter/services/serviceAgent/workflowTools.py` | Workflow-Editing-Tools (readWorkflowGraph, addNode, ...) |
| `gateway/modules/serviceCenter/services/serviceAgent/sandboxExecutor.py` | `executeCode`-Sandbox |
diff --git a/c-work/_CHANGELOG.md b/c-work/_CHANGELOG.md
index 7fcc83c..99ab652 100644
--- a/c-work/_CHANGELOG.md
+++ b/c-work/_CHANGELOG.md
@@ -1,5 +1,5 @@
-
+
# Changelog (c-work)
@@ -12,6 +12,18 @@ type: `feat` `fix` `refactor` `docs` `test` `chore` `build` · scope: `gateway
Skip: reine Refactors, Formatting, Lint, Dep-Bumps, Test-only, Wiki-Tippfehler.
+## 2026-04-28
+
+- 2026-04-28 | refactor | gateway | **Cleanup der zwei tieferliegenden Defensive-Programming-Schichten, die den Trustee-Bug (vorheriger Eintrag) ueberhaupt erst durchgelassen haben.**
+ - **(1) DB-Connector: fail-loud statt swallow.** `connectorDbPostgre.getRecord/getRecordset/getRecordsetPaginated/getDistinctColumnValues/_loadTable/semanticSearch` haben bisher jede Exception per `except Exception → log → return []` (bzw. `None`/leeres Pagination-Resultat) verschluckt. Folge: jeder echte DB-Fehler (Postgres-Adapt, UndefinedTable, UndefinedColumn, OperationalError, etc.) wurde fuer den Caller ununterscheidbar von "0 Rows" -- darauf basierten misleading downstream Errors wie "No active accounting configuration found". Neu: typisierte Exception `DatabaseQueryError(table, message, original)` plus zentrales `_rollbackQuietly(connection)` (Postgres setzt die Connection in Error-State nach jedem fehlgeschlagenen Statement). Empty Result Sets liefern weiterhin `[]`/`None`/`{items: [], totalItems: 0, totalPages: 0}` (= Normalpfad ueber `cursor.fetchall()/fetchone()`), aber jede Exception innerhalb des Query-Pfads wird hochgereicht. Tests: `tests/unit/connectors/test_connectorDbPostgre_failLoud.py` (9 Tests).
+ - **(2) Action-Parameter: zentrale Validierung statt impliziter Kontrakt.** Workflow-Actions haben `parameters: Dict[str, Any]` ohne Schema-Enforcement bekommen; die Aktionsimplementationen mussten ad-hoc `isinstance`-Branches haben oder mit Postgres-Errors abstuerzen (siehe Trustee-Bug). Neues Modul `gateway/modules/workflows/processing/shared/parameterValidation.py` mit `InvalidActionParameterError(ValueError)` + `validateAndCoerceParameters(actionDef, parameters)`. Zentral aufgerufen in `ActionExecutor.executeAction` -- gilt fuer alle Aufrufpfade (Agent, Workflow-Graph, REST). Logik:
+ 1. Required-Parameter erzwungen (typisierter Fehler statt opaque downstream).
+ 2. Ref-Schemas (`FeatureInstanceRef`/`ConnectionRef`/...) -- Dict mit `id` wird auf String-UUID kollabiert; Pass-Through fuer bereits-Strings; Dict ohne `id` raisst kontrolliert.
+ 3. Primitive-Coercion (`bool`/`int`/`float`) aus haeufigen String-Formen ("true"/"12"/"3.14"). Unbekannte Extra-Keys (`parentOperationId`, `expectedDocumentFormats`, ...) bleiben unangetastet. Die in der vorherigen Iteration in `actionToolAdapter` eingebaute Ref-Normalisierung wurde komplett entfernt (Single Source of Truth in `parameterValidation`). Tests: `tests/unit/workflows/test_parameterValidation.py` (19 Tests).
+ - Gesamttest-Suite: 512 passed (vorher 503, plus 9 neue DB-Tests; +19 neue Validation-Tests; -7 obsolete Adapter-Tests, da Logik umgezogen).
+
+- 2026-04-28 | fix | gateway | Agent-Tool-Calls auf Workflow-Actions mit `*Ref`-Parametern (z.B. `trustee_refreshAccountingData(featureInstanceId=...)`) brachen mit irrefuehrendem "No active accounting configuration found" ab. Root cause: das Tool-Schema (Phase-3 Typed Action Architecture) exponiert `FeatureInstanceRef`/`ConnectionRef` absichtlich als typisiertes Objekt mit `id`+Diskriminator (`featureCode`/`authority`), damit der LLM bei mehreren Instanzen die richtige picken kann -- aber die Action-Implementierungen verwenden den Wert direkt als String-UUID in `recordFilter={"featureInstanceId": , ...}`. Der LLM uebergibt korrekt das Dict `{id, featureCode, label, mandateId}`, Postgres-Adapter scheitert mit `can't adapt type 'dict'`, der DB-Connector swallowed den Fehler (soft-fail mit `[]`), und die Action interpretiert das leere Resultset als "Konfiguration fehlt". Initial-Fix in `actionToolAdapter` (Ref-Dict -> id-String). **Diese Adapter-Normalisierung wurde im Folgeschritt zur zentralen `parameterValidation` migriert (siehe naechster Eintrag).**
+
## 2026-04-27
- 2026-04-27 | fix | gateway | Trustee-Subagent gab fuer "Banksaldo per 31.12.2025" CHF 11'861'162.50 zurueck statt der echten 48'507.41 (Konto 1020) und identifizierte 5400 (Materialaufwand) + 3434 (Erloeskorrekturen) als "Bankkonten". Root cause war doppelt: (1) `closingBalance` ist bereits ein Saldo pro Periode -- der Agent rief `aggregateTable(SUM, closingBalance, GROUP BY accountNumber)` ohne periodYear/periodMonth-Filter und summierte 7 Jahre x 13 Perioden auf (~90x echter Saldo); (2) er hatte kein Wissen darueber, dass Schweizer KMU-Bankkonten dem Praefix `102x` folgen und `periodMonth=0` das Jahres-Total bedeutet. Fix in zwei Teilen: (a) generische Regel in `featureDataAgent._buildSchemaContext` System-Prompt -- "NEVER apply SUM/AVG to columns that already represent a balance, closing/opening total or aggregate" mit konkreten Beispielen `closingBalance/openingBalance/debitTotal/creditTotal`; (b) Pro-Feature-Hook `getAgentDomainHints() -> str` in `mainXxx.py`: wenn die Funktion existiert, wird ihr Rueckgabetext ans Ende des Subagent-Prompts angehaengt. Trustee liefert jetzt einen kompakten Domain-Guide mit KMU-Kontoplan-Praefixen (1xxx/2xxx/3xxx/4xxx, 100x/102x), Periodenkonvention (`periodMonth=0` = Jahr, 1-12 = Monat), drei kanonischen Query-Patterns (Banksaldo, Konto-Saldo, Buchungen-im-Monat) und einer Anti-Pattern-Liste (kein SUM auf closingBalance, debitTotal/creditTotal sind keine Salden). Loader in `_loadFeatureDomainHints` nutzt `loadFeatureMainModules()` und ist tolerant gegen fehlende Hooks/Module. Unit-Tests in `tests/unit/services/test_featureDataAgent_schema.py` (4 neue: Generische Regel, Trustee-Hints angehaengt, kein Hints-Block fuer Features ohne Hook, Anti-Pattern-Erwaehnungen)