Entwickler-Blog · 11.11.20

Rechnungs­erfassung mit künstlicher Intelligenz

Wir haben einen Machine-Learning-basierten Prototypen für die Rechnungserfassung zu einem MVP in Produktion ausgebaut. Thomas Herchenröder, Innovation Engineer bei MACH, beschreibt in diesem Artikel den Prozess und was wir daraus gelernt haben.

Im Jahr 2018 entwickelten mein Kollege André Gode und unser externer Berater Oliver Zeigermann einen Machine-Learning-basierten Prototypen für die Rechnungserfassung. Aufgrund existierender Rechnungsbelege wurde ein Modell trainiert und dann benutzt, um bei der Erstellung eines neuen Rechnungsbeleges anhand der bis dahin eingegebenen Daten wie dem Erfasser, dem Belegkonto und dem Partner, Vorschläge für das Gegenkonto zu machen. Die Verwaltung des Modells sowie die Erstellung der Vorhersagen wurden in einen Python-basierten Service implementiert, der dann aus dem entsprechenden MACHWeb Modul per REST angesprochen wurde.

Dieser "Gegenkonto Vorhersage (GKV) Service" sollte nun soweit produktionsreif gemacht werden, dass man damit eine Minimal Viable Product (MVP) Evaluierung in einer tatsächlichen Produktionsumgebung durchführen kann. Dieser Artikel beschäftigt sich schwerpunktmäßig mit diesem Prozess.

Der Prototyp

Der Prototyp wurde in typischer Labor Manier entwickelt: in isolierten Umgebungen, mit Testdaten und vielen interaktiven Tools wie Jupyter Notebooks und DBeaver, bei dem verschiedene andere Kolleg:innen zusätzlichen Input zu interessanten Einsatzszenarien und relevanten Daten lieferten. Das Ergebnis war ein kleiner Python Server, dem man zuvor generierte Modelle unterschob und der auf Anfrage Vorhersagen bezüglich des Gegenkontos machte.

Auf der anderen Seite wurden diese Anfragen von der Applikation "Rechnung bearbeiten" im Buchhaltungsmodul abgeschickt: Wenn ein Benutzer im Gegenkontofeld auf der Positionsseite eine Suche anstieß, bekam er zusätzlich zu den normalen Suchergebnissen auch die KI Vorschläge präsentiert.

zukunft.mach.de

Hintergrundinfos zu unserem KI-Ansatz

Wenn man heutzutage in den Medien von Künstlicher Intelligenz (KI) liest, geht es darum, in vorhandenen Informationen aus der Vergangenheit Strukturen und Muster zu finden, so dass ein System zukünftig Antworten auf scheinbar unbekannte Fragen geben kann.

Zu abstrakt? Hier gibt es konkrete Beispiele, die wir bei MACH im Kontext von ERP für die öffentliche Verwaltung gefunden haben.
Foto von Andre und Olli

Größere Integration

Um von diesem Stand zu einer Version zu gelangen, die sich in einer Produktionsumgebung betreiben lässt, mussten zunächst alle wichtigen Funktionen in den Service integriert werden. Insbesondere musste das Trainieren der Modelle vom MACHWeb aus anzustoßen sein, die Trainings-Daten von der aktuellen Datenbank kommen und via REST an den Service übergeben werden. Dieser musste die Modelle persistieren, ein Backup verwalten (damit man notfalls auf ein älteres Modell zurückgreifen kann) und in gewissen Abständen erneut trainiert werden, um neuere Belege zu berücksichtigen. Hierbei verzichteten wir auf die Automatisierung durch einen Scheduler, weil das den Scope unnötig für einen MVP ausgedehnt hätte. Stattdessen gab es einfach einen Button in der Web GUI, um das Training anzustoßen.

Eine weitere für uns wichtige funktionale Anforderung war, dass wir die Qualität der Voraussagen im Betrieb überprüfen können. Würden die Vorschlagswerte des Services den Konten entsprechen, die tatsächlich als Gegenkonto für diesen Beleg ausgewählt wurden? Wir brauchten also zumindest ein entsprechendes Logging, um später Auswertungen durchführen zu können, die diese Frage beantworten. Auch wurde klar, dass manche Benutzer:innen "ihre" Gegenkonten im Kopf haben und eine Suche gar nicht benötigen (das war natürlich nicht die Zielgruppe unseres Services), sodass wir die entsprechenden Daten auf jeden Fall nochmal beim Speichern des Beleges an den Service schickten. Damit konnten wir immer Vorschlagswerte und tatsächlich gewählte Gegenkonten miteinander vergleichen.

The truth is in the logs

Die Auswertung der Logfiles beantwortete die Frage, ob die Vorschlagswerte des Service den Konten entsprechen, die tatsächlich als Gegenkonto für diesen Beleg ausgewählt wurden.

In Richtung Produktion

Als MVP Produktionsumgebung, in der wir unseren Ansatz erproben wollten, wurde unsere eigene Buchhaltung erkoren. Die Idee war, den Code in das nächste Release von MACHWeb zu bringen, woraus er dann (durch entsprechende Lizenzen geschützt) intern ausgerollt werden würde. Daraus wurde aber leider nichts. Zu spät kamen wir ins Gespräch mit unseren Architekten, die grundsätzliche Umbauten an unserem Code verlangten, was in der Zeit bis zum Code Freeze für das Release nicht mehr zu schaffen war. Besonders das SQL, mit dem die Daten für das Training der Modelle aus der Datenbank extrahiert wurden, sollte überarbeitet werden. Darüber hinaus gab es die Anforderung, dass bestimmte Requests an den GKV Server asynchron erfolgen, damit zum Beispiel die Reaktionszeit beim Speichern eines Rechnungsbeleges nicht leidet. Ziel müsse es sein, die Anbindung von MACHWeb an den GKV Service möglichst lose zu koppeln, am besten mit dem Verschicken von Nachrichten über ein Publish/Subscribe Muster.

Diese architektonischen Umbauten waren in der Kürze der Zeit nicht mehr zu schaffen. Was also tun? Nach einer Besprechung mit allen Beteiligten wurde folgender Beschluss gefasst: Wir stellen der internen Produktion ein MACHWeb zur Verfügung, das auf dem offiziellen Release basiert und darüber hinaus unsere Erweiterungen für die Anbindung des GKV Services enthält. Eine Custom Version sozusagen.

Damit waren wir aus den strengen Anforderungen des offiziellen Production Streams heraus. Trotzdem implementierten wir weiter Anpassungen, die für den Betrieb notwendig oder sinnvoll waren (wie robusteres Fehlerverhalten oder die asynchrone Anbindung an bestimmten Stellen), aber erst eimal mit den einfacheren Mitteln, die Java schon mitbringt ("Executors").

Den GKV Server selbst packten wir mit Python Mitteln in ein self-contained Executable, das wir sowohl für Windows als für Linux bauen konnten. Damit konnte der Service auf einen beliebigen Zielrechner kopiert und dort mit ein paar Kommandoptionen gestartet werden.

Testphase

Unsere Fachadministration nahm dann unsere erweiterte Variante von MACHWeb auf einem internen Testsystem in Betrieb, so dass auch Kolleg:innen aus der Fachabteilung die neuen Features, inklusive der Gegenkonto Vorhersage, ausprobieren konnten. Einerseits lief dieses Testsystem mit einem GKV Service, den wir auf einer Entwicklungsmaschine hosteten, andererseits lief der Service auch auf einer Testmaschine. Den Service bei uns selbst zu hosten hatte den Vorteil, dass wir jederzeit seine Funktion am laufenden System überwachen und auch Änderungen direkt einspielen konnten. Das waren ideale, direkte und enge Feedback Loops für die Entwicklung.

Von den Nutzer:innen bekamen wir auf der einen Seite erstes positives Feedback, zum anderen wurde aber auch schnell klar, dass die Trainingsdaten (und damit die Vorschlagswerte) noch verbessert werden mussten. Das taten wir beispielsweise durch eine verbesserte Selektion der Trainingsdaten. Sehr wertvoll war hier das Feedback von Kolleg:innen, die viel tiefer mit der Fachlichkeit vertraut waren als wir.

MVP in Produktion

Damit waren die Deliverables, also das "Was", klar. Die nächste Frage war die der Bereitstellung. Wir übergaben das GKV Executable, das MACHWeb .ear File und eine handvoll .cds Dateien für einen zusätzlichen Systemparameter an unsere IT, die sie dann auf die Rechner der internen Produktion spielten. Doch wie sollten Updates während der MVP Phase bereitgestellt werden? Es gab noch keinen etablierten Prozess, um interne MVPs durchzuführen.

Zum einen hatten wir uns verpflichtet, Bugfixes und Programmverbesserungen, die in der offiziellen Version gemacht wurden, zu übernehmen und auf Anfrage der internen Produktion bereitzustellen. Das bedeutete in jedem Fall, den Source Code aus dem Production Branch zu übernehmen, etwaige Probleme bei der Übernahme aufzulösen, die Konsistenz und Funktionalität des resultierenden Codes sicherzustellen und ein neues Artefakt zu erstellen, das alle Fixes plus unsere KI Erweiterungen enthielt. Das resultiernde MACHWeb .ear File konnten wir dann wieder der IT übergeben.

Eine weitere Komplexität war die Pflege eines neuen Systemparameters, den wir mit der KI Funktionalität eingeführt hatten. Sollte eine Rekonfiguration mit einem neueren MACH C/S auf der Produktivdatenbank nötig werden, würde unser Systemparameter gelöscht werden. Wir hätten dann jedes Mal Patches für die .cds Dateien erstellen müssen, die entweder sofort bei der Rekonfiguration benutzt würden oder in einer zweiten Rekonfiguration nur dafür da waren, unseren Systemparameter wiederherzustellen.

All das wurde von der Administrationsseite als zu manuell und daher zu wenig robust und zuverlässig angesehen. Um Abhilfe zu schaffen entschlossen wir uns, den Systemparameter im Production Branch der Software einzuführen, wodurch er automatisch mit dem normalen MACH C/S ausgeliefert wurde und die Notwendigkeit für angepasste .cds Dateien entfiel. Darüber hinaus wurde eine Teamproduktion für uns eingerichtet, die unsere Artefakte automatisch auf unsrer CI baute und im Artifact Store ablegte. So konnte unsere Produktion auf Artefakte zurückgreifen, die automatisch und in einer standardisierten Umgebung gebaut wurden.

Das Ende

Am Schluss der MVP Phase wurde allerdings deutlich, dass die Zuverlässigkeit der Vorschläge für das Gegenkonto bei der Rechnungserfassung nicht gut genug waren, um nützlich zu sein – insbesondere für gelegentliche Nutzer:innen. Die verschiedenen Gegenkonten, die in Belegen genutzt wurden, waren sehr unterschiedlich verteilt. So gab es ein paar Gegenkonten, die sehr häufig auftraten (zum Beispiel für Reisebelege) und andere, die nur selten genutzt wurden. Diese ungleiche Mächtigkeit der Kategorien im Modell führte dazu, dass häufig genutzte Gegenkonten auch überproportional häufig vorgeschlagen wurden, auch wenn das inhaltlich keinen Sinn machte (zum Beispiel beim Erfassen einer Rechnung für Büromaterial). Auch verschiedene Versuche die Parameter des Modells anzupassen, führten zu keiner signifikanten Verbesserung. Damit war klar, dass dieser Ansatz so für uns nicht brauchbar war.

Das war einerseits natürlich eine Enttäuschung für alle Beteiligten. Andererseits hat sich damit der MVP Ansatz bestätigt und wir konnten diese Ergebnisse zu einem möglichst frühen Zeitpunkt im Produktzyklus sammeln und entsprechend darauf reagieren.

Wir haben aus diesem Experiment viel gelernt und werden weiter daran arbeiten.

MACH Karriere

Komm ins Team

und mach mit uns die öffentliche Verwaltung digital
Du willst deine Superkräfte für eine gute Sache mobilisieren? Dann finde bei uns deine Aufgabe!
Entwickler-Blog
#Fachwissen #Methode

Delphi to Java - Ein Sourcecode Übersetzer

Kann man alten Code automatisch in eine aktuelle Programmiersprache übersetzen? Unser Innovation Engineer Thomas Herchenröder berichtet im Blog über seine Erfahrungen mit dem Delphi to Java Sourcecode Übersetzer.