Harness gegen stillen Verlust gehaertet (#547-#551)
Fuenf Befunde mit einem gemeinsamen Fehlermodus: Keiner erzeugt eine Fehlermeldung. Der Lauf wird fertig, das Ergebnis sieht plausibel aus, und nur die Qualitaet ist schlechter. #547 Der Begleittext zum Werkzeugaufruf ging verloren. AssistantWithToolCalls baute eine neue Nachricht aus nur den Aufrufen; der Text, den das Modell daneben geschrieben hatte, fiel weg. Damit verlor das Modell zwischen zwei Schritten seinen eigenen Plan, und im Audit stand, dass ein Werkzeug lief, nicht warum. #548 finish_reason wurde nirgends gelesen. Eine an der Ausgabegrenze abgeschnittene Antwort galt als fertiges Ergebnis. Das trifft besonders Laeufe im Hintergrund, wo niemand zusieht. Neuer Status Truncated: Das Teilergebnis geht weiter, aber gekennzeichnet. #549 reasoning_details wurde verworfen. Bei Werkzeugnutzung muss der Denkverlauf unveraendert zurueck, sonst beginnt ein Thinking-Modell jedes Mal von vorn. Wird jetzt als rohes JSON durchgereicht - ohne Auslegung, weil Reihenfolge und Signatur daran haengen. #550 Die Anfrage verlangt jetzt einen Anbieter, der alle mitgeschickten Parameter beherrscht. Ohne das durfte ein Anbieter cache_control, reasoning oder sogar tools stillschweigend uebergehen - der Lauf lieferte dann eine gewoehnliche Textantwort, wo ein Werkzeugaufruf hingehoert haette. Dazu usage.include: der abgerechnete Betrag schlaegt die Eigenrechnung, die als Rueckfallebene bleibt. Denktiefe je Agent einstellbar, Vorgabe off. #551 Werkzeuge eines Zuges laufen gleichzeitig, sofern sie sich dafuer freigeben. Die Vorgabe von IsParallelSafe ist false, weil die falsche Antwort hier nicht auffaellt: Zwei schreibende Aufrufe auf derselben Datei scheitern nicht zuverlaessig, sondern gelegentlich. Freigegeben ist bisher nur WebFetch - es liest ausschliesslich. Die Reihenfolge der Ergebnisse folgt den Aufrufen, nicht der Fertigstellung; daran haengen die tool_call_id-Zuordnung und der Cache-Praefix. 27 neue Tests, 887 gesamt, keine Regression. Build ohne Warnungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -107,8 +107,60 @@ public sealed class AgentConfig
|
||||
[JsonPropertyName("maxToolResultChars")]
|
||||
public int MaxToolResultChars { get; set; } = 16_000;
|
||||
|
||||
/// <summary>
|
||||
/// Wie viele als nebenläufig gekennzeichnete Werkzeuge eines Zuges gleichzeitig
|
||||
/// laufen dürfen. 1 heißt: nacheinander, wie bisher.
|
||||
///
|
||||
/// Die Grenze ist keine Leistungsstellschraube, sondern eine Rücksichtnahme —
|
||||
/// zwanzig gleichzeitige Abrufe gegen dieselbe Gegenstelle sind aus deren Sicht
|
||||
/// nicht von einem Angriff zu unterscheiden.
|
||||
/// </summary>
|
||||
[JsonPropertyName("maxParallelToolCalls")]
|
||||
public int MaxParallelToolCalls { get; set; } = 4;
|
||||
|
||||
/// <summary>
|
||||
/// Denktiefe: "off" (Vorgabe), "minimal", "low", "medium", "high", "xhigh", "max"
|
||||
/// — oder eine Tokenzahl als Zeichenkette, etwa "4096".
|
||||
///
|
||||
/// Vorgabe ist "off", damit sich für bestehende Agenten nichts ändert: Denk-Tokens
|
||||
/// zählen als Ausgabe-Tokens und damit gegen <see cref="LoopGuardConfig.MaxResponseTokens"/>
|
||||
/// wie gegen das Budget.
|
||||
/// </summary>
|
||||
[JsonPropertyName("reasoningEffort")]
|
||||
public string ReasoningEffort { get; set; } = "off";
|
||||
|
||||
/// <summary>
|
||||
/// Ob das Gateway nur an Anbieter weiterreichen darf, die alle mitgeschickten
|
||||
/// Parameter beherrschen.
|
||||
///
|
||||
/// Vorgabe <c>true</c>. Ohne das darf ein Anbieter <c>cache_control</c>,
|
||||
/// <c>reasoning</c> oder sogar <c>tools</c> stillschweigend übergehen — der Lauf
|
||||
/// liefert dann eine gewöhnliche Textantwort, wo ein Werkzeugaufruf hingehört
|
||||
/// hätte, und nichts weist darauf hin. Lieber eine sichtbare Absage als ein
|
||||
/// unbemerkt entwerteter Lauf.
|
||||
/// </summary>
|
||||
[JsonPropertyName("requireProviderParameters")]
|
||||
public bool RequireProviderParameters { get; set; } = true;
|
||||
|
||||
[JsonPropertyName("budget")]
|
||||
public AgentBudget Budget { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Übersetzt <see cref="ReasoningEffort"/> in das Anfragefeld. Null heißt:
|
||||
/// nichts mitschicken, das Modell denkt wie voreingestellt.
|
||||
/// </summary>
|
||||
public Api.Models.ReasoningRequest? BuildReasoningRequest()
|
||||
{
|
||||
var value = (ReasoningEffort ?? "").Trim();
|
||||
|
||||
if (value.Length == 0 || value.Equals("off", StringComparison.OrdinalIgnoreCase))
|
||||
return null;
|
||||
|
||||
if (int.TryParse(value, out var tokens))
|
||||
return tokens > 0 ? new Api.Models.ReasoningRequest { MaxTokens = tokens } : null;
|
||||
|
||||
return new Api.Models.ReasoningRequest { Effort = value.ToLowerInvariant() };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user