Supervisor S-2: OpenRouter-Agent, read-only Tool-Registry, Analyse-Chat
Der KI-Analyse-Agent (docs/konzepte/KONZEPT-Modul-Supervisor.md, Phase S-2): - SupervisorToolRegistry (transport-agnostisch, spaeter auch MCP-Light): 7 read-only-Tools - query_decisions (inkl. Rejects+ReasonCodes), query_order_events, query_trades, get_dossier (Markdown-Kette), read_logs (JSONL je Tag, CID-Filter), get_kpis (TradeAnalytics), get_architecture_context. Ausfuehrung fehlertolerant (Exception -> Fehlertext, wirft nie). KEIN Tool kann handeln/schreiben. - OpenRouterClient (IChatCompletionClient): OpenAI-kompatibles Chat-Completions-Schema inkl. Function-Calling; Request-Bau + Response-Parsing pur/testbar. API-Key GETRENNT vom Trading: env POLYTRADER_OPENROUTER_KEY oder gitignorierte openrouter.key (in .gitignore aufgenommen). - SupervisorAgent: Function-Calling-Loop (max 8 Iterationen), System-Prompt = Arbeitsanweisung + eingebettetes Architektur-Kontext-Dokument (Context/ArchitectureContext.md, EmbeddedResource, mit dem Code versioniert - beschreibt Entscheidungswege, ReasonCodes, Leiter-Mechanik, Eigenheiten). Tool-Aufrufe werden gesammelt und in der UI transparent angezeigt. - SupervisorMainForm: neuer Tab 'Analyse' (Chat, Modellwahl default openrouter/auto, Tool-Aufrufe live im Verlauf, Token-Zaehler) neben dem Dossiers-Tab. - Sicherheitskonzept: OpenRouter als bewusst freigegebener Egress dokumentiert (nur Tool-Ergebnisse, nie Secrets; Spend-Limit je Key empfohlen). Tests: +7 (Registry-Ausfuehrung/Fehler, Agent-Loop mit Tool-Rueckfluss, Iterationsgrenze, Request-Body/Response-Parsing, eingebetteter Kontext). Build 0 Fehler, 351 Tests gruen, --smoke-ui alle 5 Views gruen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
c75a958e36
commit
5d732277b2
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PolyTrader.Modules.Supervisor.Agent
|
||||
{
|
||||
/// <summary>Ergebnis einer Agenten-Anfrage inkl. transparenter Tool-Aufruf-Historie.</summary>
|
||||
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; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Der Analyse-Agent (S-2): Function-Calling-Loop gegen einen <see cref="IChatCompletionClient"/>
|
||||
/// mit der read-only <see cref="SupervisorToolRegistry"/>. System-Kontext = Arbeitsanweisung +
|
||||
/// Architektur-Dokument. Harte Iterationsgrenze gegen Endlosschleifen; jeder Tool-Aufruf wird
|
||||
/// festgehalten (Nachvollziehbarkeit in der UI).
|
||||
/// </summary>
|
||||
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();
|
||||
|
||||
/// <summary>
|
||||
/// Beantwortet eine Analyse-Frage. <paramref name="progress"/> meldet Tool-Aufrufe live an die UI.
|
||||
/// </summary>
|
||||
public async Task<AgentResult> AskAsync(string question, string? model = null,
|
||||
IProgress<string>? progress = null, CancellationToken ct = default)
|
||||
{
|
||||
var messages = new List<ChatMessage>
|
||||
{
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user