using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace PolyTrader.Modules.Supervisor.Agent { /// Ergebnis einer Agenten-Anfrage inkl. transparenter Tool-Aufruf-Historie. public sealed class AgentResult { public string Answer { get; init; } = string.Empty; public List<(string Tool, string Arguments, string Result)> ToolInvocations { get; init; } = new(); public int PromptTokens { get; init; } public int CompletionTokens { get; init; } } /// /// Der Analyse-Agent (S-2): Function-Calling-Loop gegen einen /// mit der read-only . System-Kontext = Arbeitsanweisung + /// Architektur-Dokument. Harte Iterationsgrenze gegen Endlosschleifen; jeder Tool-Aufruf wird /// festgehalten (Nachvollziehbarkeit in der UI). /// public sealed class SupervisorAgent { public const int MaxIterations = 8; public const string DefaultModel = "openrouter/auto"; private readonly IChatCompletionClient _chat; private readonly SupervisorToolRegistry _tools; public SupervisorAgent(IChatCompletionClient chat, SupervisorToolRegistry tools) { _chat = chat; _tools = tools; } private static string SystemPrompt() => "Du bist der Supervisor von PolyTrader: ein Analyse-Agent für automatisierten Polymarket-Handel. " + "Du bist strikt read-only – du kannst und darfst nicht handeln. Nutze die Tools, um Entscheidungsjournal, " + "Order-Events, Trades und Logs abzufragen, BEVOR du Schlüsse ziehst. Zitiere konkrete Daten " + "(SignalIds, Zeiten, Preise, ReasonCodes). Antworte auf Deutsch, präzise und mit klarer Schlussfolgerung.\n\n" + "=== ARCHITEKTUR-KONTEXT ===\n" + ArchitectureContext.Load(); /// /// Beantwortet eine Analyse-Frage. meldet Tool-Aufrufe live an die UI. /// public async Task AskAsync(string question, string? model = null, IProgress? progress = null, CancellationToken ct = default) { var messages = new List { ChatMessage.System(SystemPrompt()), ChatMessage.User(question) }; var invocations = new List<(string, string, string)>(); int promptTokens = 0, completionTokens = 0; string usedModel = string.IsNullOrWhiteSpace(model) ? DefaultModel : model.Trim(); for (int i = 0; i < MaxIterations; i++) { ct.ThrowIfCancellationRequested(); var response = await _chat.CompleteAsync(usedModel, messages, _tools.Tools, ct); promptTokens += response.PromptTokens; completionTokens += response.CompletionTokens; if (response.ToolCalls.Count == 0) { return new AgentResult { Answer = response.Content ?? "(keine Antwort)", ToolInvocations = invocations, PromptTokens = promptTokens, CompletionTokens = completionTokens }; } messages.Add(ChatMessage.Assistant(response.Content, response.ToolCalls)); foreach (var call in response.ToolCalls) { progress?.Report($"🔧 {call.Name}({call.ArgumentsJson})"); string result = _tools.Execute(call.Name, call.ArgumentsJson); invocations.Add((call.Name, call.ArgumentsJson, result)); messages.Add(ChatMessage.ToolResult(call.Id, result)); } } return new AgentResult { Answer = "Abbruch: maximale Tool-Iterationen erreicht (Frage ggf. eingrenzen).", ToolInvocations = invocations, PromptTokens = promptTokens, CompletionTokens = completionTokens }; } } }