Auto-Reminder alle 30 Messages + History-Limit 120
- SystemPromptBuilder.BuildReminder(): generiert automatisch Reminder-Text aus den aktiven Prompt-Blöcken (Identity, Workspace, Memory, etc.) - Alle ~30 Messages wird ein [System Reminder]-Paar in die History injiziert (nur nach assistant-Messages, um user/assistant-Alternierung zu wahren) - History auf maximal 120 Messages begrenzt (älteste werden entfernt) - ChatContext um PromptBlocks-Property erweitert - Alle Aufrufer (ChatEndpoint, TelegramService, CronjobService) übergeben promptBlocks an BuildHistoryFromDb
This commit is contained in:
1 parent
83945689a6
commit
47d6f44e07
6 files changed
+91
-6
No files matched your search
@@ -137,7 +137,7 @@ public static class ChatEndpoint
|
|||||||
{
|
{
|
||||||
// Preparer bekommt History MIT seinen eigenen vorherigen Messages (Tag "preparer"),
|
// Preparer bekommt History MIT seinen eigenen vorherigen Messages (Tag "preparer"),
|
||||||
// damit er den Kontext seiner Rückfragen kennt
|
// damit er den Kontext seiner Rückfragen kennt
|
||||||
var preparerHistory = await chatService.BuildHistoryFromDb(sessionId, includeTag: "preparer");
|
var preparerHistory = await chatService.BuildHistoryFromDb(sessionId, includeTag: "preparer", promptBlocks: ctx.PromptBlocks);
|
||||||
|
|
||||||
var preparerResult = await anthropic.RunPreparer(
|
var preparerResult = await anthropic.RunPreparer(
|
||||||
res, ctx.Preparer, preparerHistory, ctx.ChainId, ctx.Workspace, sessionId, cancellationToken);
|
res, ctx.Preparer, preparerHistory, ctx.ChainId, ctx.Workspace, sessionId, cancellationToken);
|
||||||
@@ -170,7 +170,7 @@ public static class ChatEndpoint
|
|||||||
}
|
}
|
||||||
|
|
||||||
// History für den Session-Agent: OHNE Sub-Agent-Messages (saubere History)
|
// History für den Session-Agent: OHNE Sub-Agent-Messages (saubere History)
|
||||||
var messages = await chatService.BuildHistoryFromDb(sessionId);
|
var messages = await chatService.BuildHistoryFromDb(sessionId, promptBlocks: ctx.PromptBlocks);
|
||||||
|
|
||||||
// Bei Choice-Response: toolChoice auf "auto" setzen, damit Claude frei antworten kann
|
// Bei Choice-Response: toolChoice auf "auto" setzen, damit Claude frei antworten kann
|
||||||
var effectiveToolChoice = isChoiceResponse ? "auto" : ctx.ToolChoice;
|
var effectiveToolChoice = isChoiceResponse ? "auto" : ctx.ToolChoice;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Gott.Services;
|
||||||
|
|
||||||
namespace Gott.Models;
|
namespace Gott.Models;
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ public class ChatContext
|
|||||||
public string AgentId { get; set; }
|
public string AgentId { get; set; }
|
||||||
public string SystemPromptText { get; set; }
|
public string SystemPromptText { get; set; }
|
||||||
public List<object> SystemPrompt { get; set; }
|
public List<object> SystemPrompt { get; set; }
|
||||||
|
public List<PromptBlock> PromptBlocks { get; set; }
|
||||||
public string ToolChoice { get; set; }
|
public string ToolChoice { get; set; }
|
||||||
public bool UseMemory { get; set; }
|
public bool UseMemory { get; set; }
|
||||||
public string WebSearchVersion { get; set; }
|
public string WebSearchVersion { get; set; }
|
||||||
|
|||||||
+51
-2
@@ -63,10 +63,11 @@ public class ChatService
|
|||||||
if (string.IsNullOrEmpty(workspace) && agentSkills.Any())
|
if (string.IsNullOrEmpty(workspace) && agentSkills.Any())
|
||||||
workspace = "/tmp";
|
workspace = "/tmp";
|
||||||
|
|
||||||
var systemPromptText = SystemPromptBuilder.Build(
|
var promptBlocks = SystemPromptBuilder.BuildBlocks(
|
||||||
agent, session, markedMessages, _memoryService, agentMemoryDir,
|
agent, session, markedMessages, _memoryService, agentMemoryDir,
|
||||||
agentSkills, _skillService, userPersona: user?.GetEffectivePersona(),
|
agentSkills, _skillService, userPersona: user?.GetEffectivePersona(),
|
||||||
resolvedWorkspace: workspace, planMode: planMode);
|
resolvedWorkspace: workspace, planMode: planMode);
|
||||||
|
var systemPromptText = string.Join("\n\n", promptBlocks.Select(b => b.Content));
|
||||||
|
|
||||||
var hasWorkspace = !string.IsNullOrEmpty(workspace);
|
var hasWorkspace = !string.IsNullOrEmpty(workspace);
|
||||||
var includeMemory = agent.UseMemory && !string.IsNullOrEmpty(agent.AgentId);
|
var includeMemory = agent.UseMemory && !string.IsNullOrEmpty(agent.AgentId);
|
||||||
@@ -258,6 +259,7 @@ Für welche Plattform soll die App entwickelt werden? Web, Mobile oder Desktop?
|
|||||||
AgentId = agent.AgentId,
|
AgentId = agent.AgentId,
|
||||||
SystemPromptText = systemPromptText,
|
SystemPromptText = systemPromptText,
|
||||||
SystemPrompt = systemPrompt,
|
SystemPrompt = systemPrompt,
|
||||||
|
PromptBlocks = promptBlocks,
|
||||||
ToolChoice = toolChoice,
|
ToolChoice = toolChoice,
|
||||||
UseMemory = agent.UseMemory,
|
UseMemory = agent.UseMemory,
|
||||||
WebSearchVersion = agent.WebSearchVersion,
|
WebSearchVersion = agent.WebSearchVersion,
|
||||||
@@ -300,7 +302,7 @@ Für welche Plattform soll die App entwickelt werden? Web, Mobile oder Desktop?
|
|||||||
/// includeTag: Wenn gesetzt, werden Messages mit diesem Tag NICHT ausgefiltert
|
/// includeTag: Wenn gesetzt, werden Messages mit diesem Tag NICHT ausgefiltert
|
||||||
/// (z.B. "preparer" damit der Preparer seine eigenen vorherigen Messages sieht).
|
/// (z.B. "preparer" damit der Preparer seine eigenen vorherigen Messages sieht).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<object>> BuildHistoryFromDb(string sessionId, string includeTag = null)
|
public async Task<List<object>> BuildHistoryFromDb(string sessionId, string includeTag = null, List<PromptBlock> promptBlocks = null)
|
||||||
{
|
{
|
||||||
var chainsList = (await _db.GetActiveChains(sessionId)).ToList();
|
var chainsList = (await _db.GetActiveChains(sessionId)).ToList();
|
||||||
// chronologisch sortieren (älteste zuerst)
|
// chronologisch sortieren (älteste zuerst)
|
||||||
@@ -476,6 +478,53 @@ Für welche Plattform soll die App entwickelt werden? Web, Mobile oder Desktop?
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// History auf maximal 120 Messages begrenzen (älteste entfernen)
|
||||||
|
const int maxHistory = 120;
|
||||||
|
if (history.Count > maxHistory)
|
||||||
|
history.RemoveRange(0, history.Count - maxHistory);
|
||||||
|
|
||||||
|
// Reminder einfügen: alle ~30 user/assistant-Paare ein Reminder-Paar einfügen
|
||||||
|
var reminder = SystemPromptBuilder.BuildReminder(promptBlocks);
|
||||||
|
if (!string.IsNullOrWhiteSpace(reminder) && history.Count > 30)
|
||||||
|
{
|
||||||
|
var reminderUser = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["role"] = "user",
|
||||||
|
["content"] = new List<object> { new { type = "text", text = "[System Reminder] " + reminder } }
|
||||||
|
};
|
||||||
|
var reminderAssistant = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["role"] = "assistant",
|
||||||
|
["content"] = new List<object> { new { type = "text", text = "Understood." } }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Alle 30 Messages ein Reminder-Paar einfügen (von hinten nach vorne, damit Indizes stimmen)
|
||||||
|
var insertPositions = new List<int>();
|
||||||
|
int messageCount = 0;
|
||||||
|
for (int i = 0; i < history.Count; i++)
|
||||||
|
{
|
||||||
|
messageCount++;
|
||||||
|
if (messageCount > 0 && messageCount % 30 == 0)
|
||||||
|
{
|
||||||
|
// Position nach dem aktuellen Element – aber nur einfügen wenn es eine
|
||||||
|
// assistant-Message ist (damit user/assistant-Alternierung nicht bricht)
|
||||||
|
if (history[i] is Dictionary<string, object> d &&
|
||||||
|
d.TryGetValue("role", out var r) && r.ToString() == "assistant")
|
||||||
|
{
|
||||||
|
insertPositions.Add(i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Von hinten nach vorne einfügen, damit Indizes nicht verschoben werden
|
||||||
|
for (int j = insertPositions.Count - 1; j >= 0; j--)
|
||||||
|
{
|
||||||
|
var pos = insertPositions[j];
|
||||||
|
history.Insert(pos, reminderAssistant);
|
||||||
|
history.Insert(pos, reminderUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return history;
|
return history;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@ public class CronjobService : BackgroundService
|
|||||||
throw new Exception("Session oder Agent nicht gefunden / nicht konfiguriert");
|
throw new Exception("Session oder Agent nicht gefunden / nicht konfiguriert");
|
||||||
|
|
||||||
// History aus DB laden
|
// History aus DB laden
|
||||||
var history = await _chatService.BuildHistoryFromDb(job.SessionId);
|
var history = await _chatService.BuildHistoryFromDb(job.SessionId, promptBlocks: ctx.PromptBlocks);
|
||||||
|
|
||||||
// User-Nachricht hinzufügen
|
// User-Nachricht hinzufügen
|
||||||
var userContent = new List<object> { new { type = "text", text = job.Prompt } };
|
var userContent = new List<object> { new { type = "text", text = job.Prompt } };
|
||||||
|
|||||||
@@ -232,4 +232,38 @@ Alle Dateipfade in Tool-Aufrufen müssen relativ zu diesem Workspace sein oder a
|
|||||||
var blocks = BuildBlocks(agent, session, markedMessages, memoryService, agentMemoryDir, skillSlugs, skillService, userPersona, resolvedWorkspace, planMode);
|
var blocks = BuildBlocks(agent, session, markedMessages, memoryService, agentMemoryDir, skillSlugs, skillService, userPersona, resolvedWorkspace, planMode);
|
||||||
return string.Join("\n\n", blocks.Select(b => b.Content));
|
return string.Join("\n\n", blocks.Select(b => b.Content));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generiert automatisch einen kompakten Reminder-Text basierend auf den aktiven Prompt-Blöcken.
|
||||||
|
/// Gibt null zurück wenn weniger als 2 Blöcke aktiv sind (kein Reminder nötig).
|
||||||
|
/// </summary>
|
||||||
|
public static string BuildReminder(List<PromptBlock> blocks)
|
||||||
|
{
|
||||||
|
if (blocks == null || blocks.Count < 2)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var parts = new List<string>();
|
||||||
|
|
||||||
|
foreach (var block in blocks)
|
||||||
|
{
|
||||||
|
var label = block.Label;
|
||||||
|
if (label.Contains("IDENTITY")) parts.Add("Identity");
|
||||||
|
else if (label.Contains("SOUL")) parts.Add("Soul");
|
||||||
|
else if (label.Contains("Anweisungen")) parts.Add("Anweisungen");
|
||||||
|
else if (label.Contains("Persona")) parts.Add("User-Info");
|
||||||
|
else if (label.Contains("Workspace")) parts.Add("Workspace");
|
||||||
|
else if (label.Contains("Rückfragen")) parts.Add("Rückfragen");
|
||||||
|
else if (label.Contains("Kontext")) parts.Add("Kontext");
|
||||||
|
else if (label.Contains("Skills")) parts.Add("Skills");
|
||||||
|
else if (label.Contains("Memory")) parts.Add("Memory");
|
||||||
|
else if (label.Contains("Git")) parts.Add("Git-Regeln");
|
||||||
|
else if (label.Contains("Plan-Modus")) parts.Add("Plan-Modus");
|
||||||
|
else if (label.Contains("Markierte")) parts.Add("Markierte Nachrichten");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts.Count == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return "Beachte weiterhin den System-Prompt (" + string.Join(", ", parts) + ").";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -172,7 +172,7 @@ public class TelegramService
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var history = await _chatService.BuildHistoryFromDb(session.SessionId);
|
var history = await _chatService.BuildHistoryFromDb(session.SessionId, promptBlocks: ctx.PromptBlocks);
|
||||||
history.Add(new Dictionary<string, object>
|
history.Add(new Dictionary<string, object>
|
||||||
{
|
{
|
||||||
["role"] = "user",
|
["role"] = "user",
|
||||||
|
|||||||
Reference in new issue
Block a user