Review-Agent: nur Text-Konversation übergeben + Doku aktualisiert

BuildReviewMessages() komplett umgebaut:
- Tool-Calls und Tool-Results werden nicht mehr übergeben
- Nur noch die reine Text-Konversation (user/assistant) wird extrahiert
- Aufeinanderfolgende Messages gleicher Rolle werden zusammengeführt
- Korrekte user/assistant-Abwechslung sichergestellt
- Fallback wenn nur Tool-Calls ohne Text vorhanden
- Unnötigen Serialisierungs-Roundtrip für Rollen-Prüfung entfernt

Erwartete Token-Ersparnis: 50-100k+ Input-Tokens pro Review-Runde
bei typischen Worker-Sessions mit vielen write_file/edit_file-Calls.

ARCHITECTURE.md:
- Inhaltsverzeichnis-Nummerierung korrigiert (22-28 statt doppelt 20-21)
- ServerSettingsEndpoint.cs in Projektstruktur ergänzt
- Tabellenbeschreibungen vervollständigt (users, agents, sessions, chains,
  messages, cronjobs) mit allen fehlenden Feldern
- ChainRecord/MessageRecord/SessionWithStats Felder aktualisiert
- AutoCommit mention-Beschreibung korrigiert

ARCHITECTURE_QUICK.md:
- ToolService 12 → 13 Tools korrigiert
- Tabellenbeschreibungen analog zur Hauptdoku aktualisiert
This commit is contained in:
Meleki committed 2026-03-08 00:50:29 +01:00
1 parent 87bde0cbaf
commit f3b6491643
3 files changed
+114 -51

No files matched your search

+29 -18
View File
@@ -33,13 +33,13 @@
19. [Interaktive Rückfragen (ask_user_choice)](#interaktive-rückfragen-ask_user_choice)
20. [Plan-Modus](#plan-modus)
21. [Nachrichten teilen (Shared Messages)](#nachrichten-teilen-shared-messages)
20. [Workspace & File-Explorer](#workspace--file-explorer)
21. [Avatar-System](#avatar-system)
22. [LaTeX/Math-Rendering](#latexmath-rendering)
23. [Windows Desktop App](#windows-desktop-app)
24. [Deployment](#deployment)
25. [Konfiguration](#konfiguration)
26. [Datenbank-Migrationen](#datenbank-migrationen)
22. [Workspace & File-Explorer](#workspace--file-explorer)
23. [Avatar-System](#avatar-system)
24. [LaTeX/Math-Rendering](#latexmath-rendering)
25. [Windows Desktop App](#windows-desktop-app)
26. [Deployment](#deployment)
27. [Konfiguration](#konfiguration)
28. [Datenbank-Migrationen](#datenbank-migrationen)
---
@@ -143,6 +143,7 @@ gott/
│ ├── SkillEndpoint.cs # List, Agent-Skill-Mapping, Output-Files
│ ├── SharedEndpoint.cs # Nachrichten teilen (öffentliche Links)
│ ├── AdminEndpoint.cs # Admin: User-CRUD, Impersonate (Admin-only)
│ ├── ServerSettingsEndpoint.cs # Admin: Server-Settings lesen/schreiben (Admin-only)
│ ├── ProfileEndpoint.cs # Profil, Persona, Passwort
│ ├── SettingsEndpoint.cs # User-Settings (JSON blob)
│ ├── BackupEndpoint.cs # Backup als tar.gz Download
@@ -277,16 +278,16 @@ Message (1) ─── (*) SharedMessage
| Tabelle | Beschreibung |
|-------------------|-----------------------------------------------------|
| `users` | Benutzer (username, password_hash, persona, settings, default_path) |
| `users` | Benutzer (username, password_hash, display_name, is_admin, persona, settings, default_path) |
| `tokens` | Auth-Tokens (token → user_id) |
| `agents` | AI-Agenten (model, soul, identity, memory, tools, avatar_scale, avatar_offset_x/y, allow_user_choice, task_instruction, avatar_small) |
| `sessions` | Chat-Sessions (workspace, context, telegram-config, avatar_scale, avatar_offset_x/y, telegram_allowed_chat_ids, preparer_agent_id, review_agent_id, max_review_iterations, finalizer_agent_id, image_small) |
| `chains` | Request-Chains (model, tokens, iterations, timing) |
| `messages` | Einzelne Nachrichten (text, tool_use, tool_result, web_search_result) |
| `agents` | AI-Agenten (model, tool_choice, custom_commands, auto_commit, use_soul/identity/user_info/memory, use_web_search, web_search_version, allow_user_choice, task_instruction, avatar_scale/offset, avatar_small) |
| `sessions` | Chat-Sessions (description, image, image_small, workspace, context, custom_commands, telegram-config, telegram_allowed_chat_ids, avatar_scale/offset, preparer_agent_id, review_agent_id, max_review_iterations, finalizer_agent_id) |
| `chains` | Request-Chains (model, tokens, iterations, timing, tool_choice, tool_names, stream_error_info, plan_mode, archive_id) |
| `messages` | Einzelne Nachrichten (text, tool_use, tool_result, error, web_search_result), Tag für Sub-Agent-Markierung |
| `attachments` | Datei-Anhänge (base64, image/document) |
| `archives` | Archivierte Chain-Gruppen |
| `agent_skills` | Agent ↔ Skill Zuordnung |
| `cronjobs` | Geplante Tasks (cron expression, prompt) |
| `cronjobs` | Geplante Tasks (cron expression, prompt, notify_session_id, is_enabled, last_run, next_run) |
| `shared_messages` | Geteilte Nachrichten (Share-Token, Ablaufdatum) |
| `telegram_links` | Telegram Chat ↔ Session Verknüpfung |
@@ -295,26 +296,36 @@ Message (1) ─── (*) SharedMessage
**ChainRecord** – Repräsentiert einen kompletten Request-Response-Zyklus:
- `ChainId` – Eindeutige ID
- `SessionId` – Zugehörige Session
- `AgentId` – Zugehöriger Agent
- `Model` – Verwendetes Claude-Modell (z.B. `claude-sonnet-4-6`)
- `SystemPrompt` – Der vollständige System-Prompt (gespeichert für Nachvollziehbarkeit)
- `InputTokens`, `OutputTokens`, `CacheCreationTokens`, `CacheReadTokens` – Token-Verbrauch
- `Iterations` – Anzahl der Tool-Loop-Durchläufe
- `StopReason` – `end_turn`, `tool_use`, `max_iterations`, `error`, `client_disconnected`, `awaiting_user_choice`, `preparer_query`
- `ToolChoice` – Verwendete Tool-Choice-Strategie
- `ToolNames` – Komma-separierte Liste der verwendeten Tools
- `ArchiveId` – Zugehöriges Archiv (wenn archiviert)
- `StreamErrorInfo` – Fehlerdetails bei Stream-Abbrüchen
- `PlanMode` – Ob die Chain im Plan-Modus erstellt wurde (informativ)
- `DurationMs` – Gesamtdauer der Chain
**MessageRecord** – Eine einzelne Nachricht innerhalb einer Chain:
- `Role` – `user`, `assistant`, `system`
- `Role` – `user`, `assistant`
- `Type` – `text`, `tool_use`, `tool_result`, `error`, `web_search_result`
- `Content` – Textinhalt oder JSON (bei tool_use: Input-JSON)
- `ToolName`, `ToolCallId` – Bei Tool-Aufrufen
- `IsMarked` – Als wichtig markierte Nachricht (wird in System-Prompt aufgenommen)
- `IsError` – Ob die Nachricht ein Fehler ist
- `Iteration` – Welche Tool-Loop-Iteration
- `AttachmentCount` – Anzahl angehängter Dateien
- `AttachmentNames` – JSON-Array mit Dateinamen
- `Tag` – Sub-Agent-Markierung (`null` = normal, `"preparer"/"review"/"finalizer"` = Sub-Agent-Message, wird aus API-History gefiltert)
- `IsMarked` – Als wichtig markierte Nachricht (wird in System-Prompt aufgenommen)
- `DeletedOn` – Soft-Delete Zeitstempel
**SessionWithStats** – Erweitertes Session-Objekt für Listen-Endpunkt:
- Enthält alle `SessionRecord`-Felder + `MessageCount`, `ArchiveCount`
- `GitStatus` – Inline Git-Status-Summary (`Add`, `Edit`, `Delete`, `Outgoing`) oder `null` wenn kein Workspace
- Enthält alle `SessionRecord`-Felder + `MessageCount`, `ArchiveMessageCount`, `TotalMessageCount`
- `TelegramConfigured`, `TelegramLinked` – Telegram-Status
- `GitStatus` – Inline Git-Status-Summary (`Add`, `Edit`, `Delete`, `Outgoing`) oder `null` wenn kein Workspace (wird im Endpoint hinzugefügt, nicht Teil des Records)
- `Image` wird durch `ImageSmall` ersetzt (Thumbnail für Dashboard-Performance)
**GitStatusSummary** – Kompakte Git-Status-Info:
@@ -647,7 +658,7 @@ Ein **Agent** definiert die Persönlichkeit und Fähigkeiten der AI:
| `Model` | `sonnet`, `sonnet-4-5`, `sonnet-4-6`, `opus`, `opus-4-5`, `opus-4-6`, `haiku`, `haiku-4-5` |
| `ToolChoice` | `any` oder `auto` |
| `CustomCommands` | JSON-Array mit Slash-Commands (Label + Prompt) |
| `AutoCommit` | `""` (nichts), `"mention"` (verbieten), `"enforce"` (erzwingen), `"forbid"` (strikt verboten) |
| `AutoCommit` | `""` (nichts), `"mention"` (anmerken – nur auf User-Request), `"enforce"` (erzwingen – nach jeder Änderung), `"forbid"` (strikt verboten) |
| `UseSoul` | SOUL.md in System-Prompt einbinden |
| `UseIdentity` | IDENTITY.md in System-Prompt einbinden |
| `UseUserInfo` | User-Persona in System-Prompt einbinden |
+5 -5
View File
@@ -63,11 +63,11 @@ User 1──* Agent 1──* Session 1──* Chain 1──* Message 1──* At
| Tabelle | Zweck |
|---------|-------|
| `users` | Login, Persona, Settings (JSON), DefaultPath |
| `users` | Login, DisplayName, IsAdmin, Persona, Settings (JSON), DefaultPath |
| `tokens` | Auth-Tokens → User |
| `agents` | AI-Agenten (Model, Tools, Config, AvatarScale, AvatarOffsetX/Y, AvatarSmall) |
| `sessions` | Chat-Sessions (Workspace, Context, Telegram, AvatarScale, AvatarOffsetX/Y, AllowedChatIds, PreparerAgentId, ReviewAgentId, MaxReviewIterations, FinalizerAgentId, ImageSmall) |
| `chains` | Ein Request-Response-Zyklus (Tokens, Timing, Iterations, PlanMode) |
| `agents` | AI-Agenten (Model, ToolChoice, CustomCommands, AutoCommit, UseSoul/Identity/UserInfo/Memory, WebSearch, AllowUserChoice, TaskInstruction, AvatarScale/Offset, AvatarSmall) |
| `sessions` | Chat-Sessions (Description, Image, ImageSmall, Workspace, Context, CustomCommands, Telegram, AvatarScale/Offset, AllowedChatIds, PreparerAgentId, ReviewAgentId, MaxReviewIterations, FinalizerAgentId) |
| `chains` | Ein Request-Response-Zyklus (Tokens, Timing, Iterations, PlanMode, ToolChoice, ToolNames, StreamErrorInfo, ArchiveId) |
| `messages` | Einzelne Nachrichten (text / tool_use / tool_result / error / web_search_result), Tag für Sub-Agent-Markierung |
| `attachments` | Base64-Anhänge (Bilder, Dokumente) |
| `archives` | Archivierte Chain-Gruppen |
@@ -110,7 +110,7 @@ User 1──* Agent 1──* Session 1──* Chain 1──* Message 1──* At
| **AuthService** | Token-Auth, Passwort-Hashing (SHA256) |
| **ChatService** | Chat-Kontext aufbauen, History aus DB laden |
| **AnthropicService** | Claude API: SSE Streaming + Non-Streaming |
| **ToolService** | Tool-Definitionen + Ausführung (12 Tools) |
| **ToolService** | Tool-Definitionen + Ausführung (13 Tools) |
| **WorkspaceHelper** | Workspace-Pfad-Auflösung (relativ → absolut), Path-Traversal-Schutz (static) |
| **SystemPromptBuilder** | System-Prompt aus modularen Blöcken zusammenbauen (static) |
| **MemoryService** | Agent-Gedächtnis auf Dateisystem |
+80 -28
View File
@@ -524,10 +524,13 @@ public class AnthropicService
/// </summary>
private List<object> BuildReviewMessages(List<object> workerMessages)
{
// Worker-Konversation zusammenfassen
var summary = new StringBuilder();
summary.AppendLine("Hier ist die bisherige Konversation zwischen User und Worker-Agent. Bitte überprüfe ob die Aufgabe korrekt erledigt wurde:\n");
summary.AppendLine("---");
// Nur die Text-Konversation extrahieren – Tool-Calls und Tool-Results weglassen.
// Der Review/Finalizer-Agent hat eigene Tools und kann bei Bedarf selbst Dateien lesen.
// Aufeinanderfolgende Messages gleicher Rolle werden zusammengeführt,
// da die Anthropic API abwechselnde user/assistant-Rollen erfordert.
var messages = new List<object>();
string lastRole = null;
var currentParts = new List<string>();
foreach (var msg in workerMessages)
{
@@ -537,11 +540,15 @@ public class AnthropicService
if (!el.TryGetProperty("role", out var role)) continue;
var roleStr = role.GetString() ?? "";
var textParts = new List<string>();
if (el.TryGetProperty("content", out var content))
{
if (content.ValueKind == JsonValueKind.String)
{
summary.AppendLine($"\n**{roleStr}:** {content.GetString()}");
var s = content.GetString() ?? "";
if (!string.IsNullOrWhiteSpace(s))
textParts.Add(s);
}
else if (content.ValueKind == JsonValueKind.Array)
{
@@ -551,37 +558,82 @@ public class AnthropicService
if (blockType == "text")
{
var text = block.TryGetProperty("text", out var t) ? t.GetString() ?? "" : "";
summary.AppendLine($"\n**{roleStr}:** {text}");
}
else if (blockType == "tool_use")
{
var toolName = block.TryGetProperty("name", out var tn) ? tn.GetString() ?? "" : "";
var toolInput = block.TryGetProperty("input", out var ti) ? ti.ToString() : "{}";
summary.AppendLine($"\n**{roleStr} [Tool: {toolName}]:** {toolInput}");
}
else if (blockType == "tool_result")
{
var resultContent = block.TryGetProperty("content", out var rc) ? rc.ToString() : "";
// Tool-Results kürzen für Review-Kontext
if (resultContent.Length > 2000)
resultContent = resultContent[..2000] + "\n[... gekürzt]";
summary.AppendLine($"\n**{roleStr} [Tool-Result]:** {resultContent}");
if (!string.IsNullOrWhiteSpace(text))
textParts.Add(text);
}
}
}
}
}
summary.AppendLine("\n---");
summary.AppendLine("\nBitte überprüfe ob die Aufgabe korrekt erledigt wurde. Du kannst dazu auch Tools verwenden (z.B. Dateien lesen). Antworte mit exakt [APPROVED] wenn alles korrekt ist, oder beschreibe was falsch ist und korrigiert werden muss.");
return new List<object>
if (textParts.Count == 0) continue;
if (roleStr == lastRole)
{
// Gleiche Rolle wie vorher → zusammenführen
currentParts.AddRange(textParts);
}
else
{
// Neue Rolle → vorherige Message abschließen
if (lastRole != null && currentParts.Count > 0)
{
messages.Add(new Dictionary<string, object>
{
["role"] = lastRole,
["content"] = new List<object> { new { type = "text", text = string.Join("\n\n", currentParts) } }
});
}
lastRole = roleStr;
currentParts = new List<string>(textParts);
}
}
// Letzte angesammelte Message abschließen
if (lastRole != null && currentParts.Count > 0)
{
new Dictionary<string, object>
messages.Add(new Dictionary<string, object>
{
["role"] = lastRole,
["content"] = new List<object> { new { type = "text", text = string.Join("\n\n", currentParts) } }
});
}
// Anthropic erfordert: erste Message = user, letzte Message = user,
// und abwechselnde Rollen (durch die Zusammenführung oben bereits sichergestellt).
if (messages.Count > 0)
{
var first = (Dictionary<string, object>)messages[0];
if ((string)first["role"] == "assistant")
{
messages.Insert(0, new Dictionary<string, object>
{
["role"] = "user",
["content"] = new List<object> { new { type = "text", text = "Hier ist die bisherige Konversation. Bitte überprüfe den aktuellen Zustand." } }
});
}
var last = (Dictionary<string, object>)messages[messages.Count - 1];
if ((string)last["role"] == "assistant")
{
messages.Add(new Dictionary<string, object>
{
["role"] = "user",
["content"] = new List<object> { new { type = "text", text = "Bitte überprüfe ob die Aufgabe korrekt erledigt wurde." } }
});
}
}
// Falls keine Text-Messages vorhanden, Fallback mit Hinweis
if (messages.Count == 0)
{
messages.Add(new Dictionary<string, object>
{
["role"] = "user",
["content"] = new List<object> { new { type = "text", text = summary.ToString() } }
}
};
["content"] = new List<object> { new { type = "text", text = "Die bisherige Konversation enthielt nur Tool-Aufrufe ohne Text. Bitte überprüfe den aktuellen Zustand des Workspaces mit deinen Tools." } }
});
}
return messages;
}
/// <summary>