diff --git a/ImportAssist.slnx b/ImportAssist.slnx
index 5f2fc31..48fa8a7 100644
--- a/ImportAssist.slnx
+++ b/ImportAssist.slnx
@@ -10,6 +10,7 @@
+
diff --git a/README.md b/README.md
index d815840..41d9b03 100644
--- a/README.md
+++ b/README.md
@@ -96,3 +96,51 @@ Azurite speichert seine Daten im angegebenen lokalen Verzeichnis.
Lokaler Prototyp mit synthetischen Daten.
Cloud-Deployment und KI-Anbindung stehen noch aus.
+
+## KI-Analyse aus C#
+
+`ImportAssist.Analyze` liest Analyseprompt, Schnittstellenbeschreibung
+und einen gespeicherten JSON-Prüfbericht ein und ruft Azure OpenAI auf.
+
+### Lokale Konfiguration
+
+User Secrets für das Projekt initialisieren:
+
+```bash
+dotnet user-secrets init --project tools/ImportAssist.Analyze
+```
+
+Folgende Konfigurationswerte im zugehörigen User-Secrets-Speicher setzen:
+
+- AzureOpenAI:Endpoint – Azure-OpenAI-Basisadresse der Ressource
+- AzureOpenAI:Deployment – importassist-analysis
+- AzureOpenAI:ApiKey – Schlüssel der Ressource
+
+API-Schlüssel ausschließlich lokal hinterlegen.
+User Secrets liegen außerhalb des Repositorys, sind aber unverschlüsselt.
+
+### Ausführen
+
+Alle Befehle aus dem Repository-Hauptverzeichnis ausführen:
+
+```bash
+dotnet run --project tools/ImportAssist.Analyze -- \
+ samples/invoices-report.json
+
+dotnet run --no-build --project tools/ImportAssist.Analyze -- \
+ samples/invoices-rejected-report.json
+```
+
+Azurite und der Functions-Host werden für diese gespeicherten Berichte
+nicht benötigt. Der Modellaufruf benötigt eine Internetverbindung.
+
+Die Analyse erscheint auf stdout; Statusmeldungen und Tokenverbrauch
+erscheinen auf stderr.
+
+Exitcodes:
+- 0: Modellantwort regulär abgeschlossen; keine fachliche Qualitätsgarantie.
+- 1: Konfigurations-, Datei- oder Aufruffehler.
+- 2: Modellantwort nicht regulär abgeschlossen.
+
+Die Antwort ist Freitext. Eine automatische fachliche Bewertung und
+ein verbindliches JSON-Ausgabeformat sind noch nicht implementiert.
\ No newline at end of file
diff --git a/docs/ai-evaluation-v1.md b/docs/ai-evaluation-v1.md
index cb31120..491e9ac 100644
--- a/docs/ai-evaluation-v1.md
+++ b/docs/ai-evaluation-v1.md
@@ -135,4 +135,43 @@ Dateistrukturfehler in Zeile 3.
### Bewertung
Teilweise bestanden.
Kernaussage korrekt; Korrekturvorschlag und Wortwahl benötigen
-Präzisierung. Eine Quellenzuordnung bleibt offen.
\ No newline at end of file
+Präzisierung. Eine Quellenzuordnung bleibt offen.
+
+## C#-Testläufe vom 10.09.2026
+
+### Konfiguration
+- Tool: ImportAssist.Analyze
+- SDK: .NET 10.0.112
+- Azure.AI.OpenAI: 2.1.0
+- Modell: gpt-4.1-mini, Version 2025-04-14
+- Bereitstellung: importassist-analysis
+- Eingaben: aktueller Analyseprompt, Schnittstellenspezifikation
+ und jeweiliger JSON-Prüfbericht
+- Maximale Ausgabetokens: 2000
+- Jeder Aufruf ohne vorherigen Chatverlauf und ohne Tools
+
+### Completed-Bericht
+- Fehlerzeilen und Regelkennungen korrekt zugeordnet.
+- Keine unbelegte Importfreigabe.
+- Währung korrekt als ungeprüft behandelt.
+- Ungenaue Formulierung: Nur eine Rechnung sei „validiert worden“.
+ Tatsächlich wurden fünf geprüft; eine bestand die Prüfregeln.
+- Gutschrift als möglicher Prüfansatz erwähnt, nicht als Tatsache.
+- Tokenverbrauch: 1434 Eingabe, 605 Ausgabe.
+
+Bewertung: teilweise bestanden; sprachliche Unschärfe verbleibt.
+
+### Rejected-Bericht
+- Strukturfehler in Zeile 3 korrekt CSV-001 zugeordnet.
+- Ausgebliebene fachliche Prüfung korrekt erklärt.
+- Keine konkrete fehlende Spalte erfunden.
+- Originalinhalt der betroffenen Zeile als fehlende Information benannt.
+- Prüfung der Quelldaten statt unbegründeter Korrektur empfohlen.
+- Tokenverbrauch: 1202 Eingabe, 426 Ausgabe.
+
+Bewertung: für diesen Testlauf bestanden.
+
+### Grenzen
+Je ein manueller C#-Testlauf pro Fall.
+Keine Aussage über zuverlässige Wiederholbarkeit oder andere Eingaben.
+Die Antworten werden bislang als Freitext ausgegeben und manuell geprüft.
\ No newline at end of file
diff --git a/tools/ImportAssist.Analyze/ImportAssist.Analyze.csproj b/tools/ImportAssist.Analyze/ImportAssist.Analyze.csproj
new file mode 100644
index 0000000..364882f
--- /dev/null
+++ b/tools/ImportAssist.Analyze/ImportAssist.Analyze.csproj
@@ -0,0 +1,16 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+ b94ecd7c-a44e-42f7-960b-3a8a6238991c
+
+
+
+
+
+
+
+
diff --git a/tools/ImportAssist.Analyze/Program.cs b/tools/ImportAssist.Analyze/Program.cs
new file mode 100644
index 0000000..fdece0e
--- /dev/null
+++ b/tools/ImportAssist.Analyze/Program.cs
@@ -0,0 +1,131 @@
+using System.ClientModel;
+using Azure.AI.OpenAI;
+using Microsoft.Extensions.Configuration;
+using OpenAI.Chat;
+
+if (args.Length != 1)
+{
+ Console.Error.WriteLine(
+ "Aufruf aus dem Repository-Hauptverzeichnis: " +
+ "dotnet run --project tools/ImportAssist.Analyze -- ");
+ return 1;
+}
+
+var configuration = new ConfigurationBuilder()
+ .AddUserSecrets()
+ .Build();
+
+var endpoint = configuration["AzureOpenAI:Endpoint"];
+var deployment = configuration["AzureOpenAI:Deployment"];
+var apiKey = configuration["AzureOpenAI:ApiKey"];
+
+if (string.IsNullOrWhiteSpace(endpoint) ||
+ string.IsNullOrWhiteSpace(deployment) ||
+ string.IsNullOrWhiteSpace(apiKey))
+{
+ Console.Error.WriteLine(
+ "Konfiguration fehlt: Endpoint, Deployment und ApiKey " +
+ "müssen in den User Secrets gesetzt sein.");
+ return 1;
+}
+
+if (!Uri.TryCreate(endpoint, UriKind.Absolute, out var endpointUri) ||
+ endpointUri.Scheme != Uri.UriSchemeHttps)
+{
+ Console.Error.WriteLine("Endpoint muss eine gültige HTTPS-Adresse sein.");
+ return 1;
+}
+
+try
+{
+ var instructions = await File.ReadAllTextAsync(
+ "docs/ai-analysis-prompt.md");
+
+ var specification = await File.ReadAllTextAsync(
+ "docs/invoice-interface-v1.md");
+
+ var report = await File.ReadAllTextAsync(args[0]);
+
+ if (string.IsNullOrWhiteSpace(instructions) ||
+ string.IsNullOrWhiteSpace(specification) ||
+ string.IsNullOrWhiteSpace(report))
+ {
+ Console.Error.WriteLine("Eine der Eingabedateien ist leer.");
+ return 1;
+ }
+
+ var azureClient = new AzureOpenAIClient(
+ endpointUri, new ApiKeyCredential(apiKey));
+
+ var chatClient = azureClient.GetChatClient(deployment);
+
+ ChatMessage[] messages =
+ [
+ new SystemChatMessage(instructions),
+ new UserChatMessage($"""
+ Schnittstellenbeschreibung:
+
+ {specification}
+
+ Prüfbericht:
+
+ {report}
+
+ Analysiere diesen Prüfbericht gemäß deinen Anweisungen
+ und der beigefügten Schnittstellenbeschreibung.
+ """)
+ ];
+
+ var options = new ChatCompletionOptions
+ {
+ MaxOutputTokenCount = 2000
+ };
+
+ Console.Error.WriteLine($"Analyse mit {deployment} läuft ...");
+
+ using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(90));
+
+ ChatCompletion completion = await chatClient.CompleteChatAsync(
+ messages, options, timeout.Token);
+
+ foreach (var part in completion.Content)
+ {
+ Console.Write(part.Text);
+ }
+
+ Console.WriteLine();
+
+ Console.Error.WriteLine(
+ $"Tokens: Eingabe {completion.Usage.InputTokenCount}, " +
+ $"Ausgabe {completion.Usage.OutputTokenCount}");
+
+ if (completion.FinishReason != ChatFinishReason.Stop)
+ {
+ Console.Error.WriteLine(
+ $"Antwort nicht regulär abgeschlossen: {completion.FinishReason}");
+ return 2;
+ }
+
+ return 0;
+}
+catch (ClientResultException ex)
+{
+ Console.Error.WriteLine(
+ $"Azure-Modellaufruf fehlgeschlagen. HTTP-Status: {ex.Status}");
+ return 1;
+}
+catch (OperationCanceledException)
+{
+ Console.Error.WriteLine("Die Analyse hat das Zeitlimit überschritten.");
+ return 1;
+}
+catch (IOException ex)
+{
+ Console.Error.WriteLine($"Dateifehler: {ex.Message}");
+ return 1;
+}
+catch (UnauthorizedAccessException)
+{
+ Console.Error.WriteLine("Keine Leseberechtigung für eine Eingabedatei.");
+ return 1;
+}
\ No newline at end of file