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
};
}
}
}