System-Prompt-Modal: strukturierte Blöcke via API statt Frontend-Parsing
- SystemPromptBuilder.BuildBlocks() liefert List<PromptBlock> mit Icon/Label/Content
- Neuer Endpoint GET /api/sessions/{id}/system-prompt-blocks (JSON-Array)
- Labels beschreiben präzise die Herkunft jedes Blocks (Agent, Session, System, Skills)
- Verhalten + Erinnerung nur noch mit Workspace + ToolChoice:any (Bugfix)
- Frontend: simples Block-Rendering statt komplexer Heuristik-Logik
- SP-Block Styling mit Label-Header und Content-Box
This commit is contained in:
1 parent
40e56acc30
commit
d42e5077c4
5 files changed
+148
-25
No files matched your search
@@ -4,35 +4,42 @@ using Gott.Models;
|
||||
|
||||
namespace Gott.Services;
|
||||
|
||||
public class PromptBlock
|
||||
{
|
||||
public string Icon { get; set; }
|
||||
public string Label { get; set; }
|
||||
public string Content { get; set; }
|
||||
}
|
||||
|
||||
public static class SystemPromptBuilder
|
||||
{
|
||||
public static string Build(AgentRecord agent, SessionRecord session, IEnumerable<MessageRecord> markedMessages = null, MemoryService memoryService = null, string agentMemoryDir = null, string dataDir = null, IEnumerable<string> skillSlugs = null, SkillService skillService = null)
|
||||
public static List<PromptBlock> BuildBlocks(AgentRecord agent, SessionRecord session, IEnumerable<MessageRecord> markedMessages = null, MemoryService memoryService = null, string agentMemoryDir = null, string dataDir = null, IEnumerable<string> skillSlugs = null, SkillService skillService = null)
|
||||
{
|
||||
var parts = new List<string>();
|
||||
var blocks = new List<PromptBlock>();
|
||||
|
||||
// IDENTITY.md - Wer bin ich (ersetzt "Du bist {Name}.")
|
||||
// IDENTITY.md
|
||||
var hasIdentity = false;
|
||||
if (agent.UseIdentity && memoryService != null && !string.IsNullOrWhiteSpace(agent.AgentId))
|
||||
{
|
||||
var identity = memoryService.ReadMemoryFile(agent.AgentId, "IDENTITY.md");
|
||||
if (!string.IsNullOrEmpty(identity))
|
||||
{
|
||||
parts.Add(identity);
|
||||
blocks.Add(new PromptBlock { Icon = "fa-id-badge", Label = "Agent · IDENTITY.md (UseIdentity)", Content = identity });
|
||||
hasIdentity = true;
|
||||
}
|
||||
}
|
||||
if (!hasIdentity && agent.UseIdentity)
|
||||
parts.Add($"Du bist {agent.Name}.");
|
||||
blocks.Add(new PromptBlock { Icon = "fa-id-badge", Label = "Agent · IDENTITY.md (UseIdentity, Fallback)", Content = $"Du bist {agent.Name}." });
|
||||
|
||||
// SOUL.md - Persönlichkeit + Regeln
|
||||
// SOUL.md
|
||||
if (agent.UseSoul && memoryService != null && !string.IsNullOrWhiteSpace(agent.AgentId))
|
||||
{
|
||||
var soul = memoryService.ReadMemoryFile(agent.AgentId, "SOUL.md");
|
||||
if (!string.IsNullOrEmpty(soul))
|
||||
parts.Add(soul);
|
||||
blocks.Add(new PromptBlock { Icon = "fa-heart", Label = "Agent · SOUL.md (UseSoul)", Content = soul });
|
||||
}
|
||||
|
||||
// USER.md - Global (aus data-Ordner)
|
||||
// USER.md
|
||||
if (agent.UseUserInfo && !string.IsNullOrWhiteSpace(dataDir))
|
||||
{
|
||||
var userFile = System.IO.Path.Combine(dataDir, "USER.md");
|
||||
@@ -42,7 +49,7 @@ public static class SystemPromptBuilder
|
||||
if (!string.IsNullOrWhiteSpace(userContent))
|
||||
{
|
||||
var absolutePath = System.IO.Path.GetFullPath(userFile);
|
||||
parts.Add($"{userContent}\n\n_Pfad: `{absolutePath}`_");
|
||||
blocks.Add(new PromptBlock { Icon = "fa-user", Label = "Agent · USER.md (UseUserInfo)", Content = $"{userContent}\n\n_Pfad: `{absolutePath}`_" });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,21 +60,31 @@ public static class SystemPromptBuilder
|
||||
{
|
||||
if (toolAny)
|
||||
{
|
||||
parts.Add(@$"## KRITISCH: Tool-Nutzung
|
||||
JEDE Datei-Operation und JEDER Shell-Befehl MUSS über einen Tool-Aufruf erfolgen. NIEMALS eine Aktion nur beschreiben oder behaupten - IMMER das Tool tatsächlich aufrufen.");
|
||||
blocks.Add(new PromptBlock
|
||||
{
|
||||
Icon = "fa-exclamation-triangle",
|
||||
Label = "System · Workspace + ToolChoice: any",
|
||||
Content = @$"## KRITISCH: Tool-Nutzung
|
||||
JEDE Datei-Operation und JEDER Shell-Befehl MUSS über einen Tool-Aufruf erfolgen. NIEMALS eine Aktion nur beschreiben oder behaupten - IMMER das Tool tatsächlich aufrufen."
|
||||
});
|
||||
}
|
||||
|
||||
parts.Add(@$"## Tools
|
||||
blocks.Add(new PromptBlock
|
||||
{
|
||||
Icon = "fa-wrench",
|
||||
Label = "Session · Workspace",
|
||||
Content = @$"## Tools
|
||||
Du hast Tools zur Verfügung (read_file, write_file, edit_file, list_files, delete_file, execute). Benutze sie um Dateien zu lesen, schreiben und bearbeiten.
|
||||
|
||||
Der Workspace-Pfad ist: `{session.Workspace}`
|
||||
Alle Dateipfade in Tool-Aufrufen müssen relativ zu diesem Workspace sein oder absolute Pfade verwenden, die mit diesem Pfad beginnen.
|
||||
|
||||
Wenn der User auf eine bestimmte Zeile in einer Datei verweist (z.B. `[Datei: foo.txt:11]`), zähle Leerzeilen immer mit. Verwende `cat -n` oder ein ähnliches Tool um Zeilennummern exakt zu bestimmen - NIEMALS Leerzeilen beim Zählen überspringen.");
|
||||
Wenn der User auf eine bestimmte Zeile in einer Datei verweist (z.B. `[Datei: foo.txt:11]`), zähle Leerzeilen immer mit. Verwende `cat -n` oder ein ähnliches Tool um Zeilennummern exakt zu bestimmen - NIEMALS Leerzeilen beim Zählen überspringen."
|
||||
});
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(session.Context))
|
||||
parts.Add($"## Kontext\n{session.Context}");
|
||||
blocks.Add(new PromptBlock { Icon = "fa-info-circle", Label = "Session · Kontext", Content = $"## Kontext\n{session.Context}" });
|
||||
|
||||
// Skills
|
||||
if (skillSlugs != null && skillService != null)
|
||||
@@ -87,13 +104,21 @@ Wenn der User auf eine bestimmte Zeile in einer Datei verweist (z.B. `[Datei: fo
|
||||
|
||||
if (skillParts.Count > 0)
|
||||
{
|
||||
var skillNames = new List<string>();
|
||||
foreach (var slug in skillSlugs)
|
||||
{
|
||||
var skill = skillService.GetSkill(slug);
|
||||
if (skill != null && skillService.GetSkillPrompt(slug) != null)
|
||||
skillNames.Add(skill.Name ?? slug);
|
||||
}
|
||||
var skillLabel = "Agent · Skills: " + string.Join(", ", skillNames);
|
||||
var skillSection = "## Skills\nDu hast folgende Skills zur Verfügung. Nutze sie über das `execute`-Tool wenn der User etwas anfragt, das ein Skill abdeckt.\n\n"
|
||||
+ string.Join("\n\n---\n\n", skillParts);
|
||||
parts.Add(skillSection);
|
||||
blocks.Add(new PromptBlock { Icon = "fa-puzzle-piece", Label = skillLabel, Content = skillSection });
|
||||
}
|
||||
}
|
||||
|
||||
// Memory-System
|
||||
// Memory
|
||||
if (agent.UseMemory && memoryService != null && !string.IsNullOrWhiteSpace(agent.AgentId))
|
||||
{
|
||||
var memoryLines = new List<string>
|
||||
@@ -119,10 +144,10 @@ Wenn der User auf eine bestimmte Zeile in einer Datei verweist (z.B. `[Datei: fo
|
||||
memoryLines.Add(mainMemory);
|
||||
}
|
||||
|
||||
parts.Add(string.Join("\n", memoryLines));
|
||||
blocks.Add(new PromptBlock { Icon = "fa-database", Label = "Agent · Memory (UseMemory)", Content = string.Join("\n", memoryLines) });
|
||||
}
|
||||
|
||||
if (toolAny)
|
||||
if (toolAny && !string.IsNullOrWhiteSpace(session.Workspace))
|
||||
{
|
||||
var verhaltensRegeln = new List<string>
|
||||
{
|
||||
@@ -142,10 +167,15 @@ Wenn der User auf eine bestimmte Zeile in einer Datei verweist (z.B. `[Datei: fo
|
||||
verhaltensRegeln.Add(@"- Git-Commits NIEMALS eigenständig erstellen. Commits werden NUR durchgeführt wenn der User es EXPLIZIT anfordert (z.B. ""erstelle einen Commit"", ""commit""). Auch nach abgeschlossenen Aufgaben NICHT automatisch committen.");
|
||||
}
|
||||
|
||||
parts.Add(string.Join("\n", verhaltensRegeln));
|
||||
blocks.Add(new PromptBlock { Icon = "fa-cog", Label = "Agent · Verhalten (Workspace + ToolChoice: any" + (agent.AutoCommit ? ", AutoCommit" : "") + ")", Content = string.Join("\n", verhaltensRegeln) });
|
||||
|
||||
parts.Add(@"## ERINNERUNG
|
||||
Diese Regel hat höchste Priorität und gilt für JEDE Antwort: Wenn eine Datei gelesen, geschrieben, bearbeitet, erstellt oder gelöscht werden soll, oder ein Befehl ausgeführt werden soll, MUSS der entsprechende Tool-Aufruf erfolgen. Ohne Tool-Aufruf ist NICHTS passiert. Beschreibe keine Änderungen - führe sie durch.");
|
||||
blocks.Add(new PromptBlock
|
||||
{
|
||||
Icon = "fa-exclamation-circle",
|
||||
Label = "System · Erinnerung (Workspace + ToolChoice: any)",
|
||||
Content = @"## ERINNERUNG
|
||||
Diese Regel hat höchste Priorität und gilt für JEDE Antwort: Wenn eine Datei gelesen, geschrieben, bearbeitet, erstellt oder gelöscht werden soll, oder ein Befehl ausgeführt werden soll, MUSS der entsprechende Tool-Aufruf erfolgen. Ohne Tool-Aufruf ist NICHTS passiert. Beschreibe keine Änderungen - führe sie durch."
|
||||
});
|
||||
}
|
||||
|
||||
var marked = markedMessages?.Where(m => !string.IsNullOrWhiteSpace(m.Content)).ToList();
|
||||
@@ -157,9 +187,15 @@ Diese Regel hat höchste Priorität und gilt für JEDE Antwort: Wenn eine Datei
|
||||
var role = m.Role == "user" ? "User" : "Assistant";
|
||||
lines.Add($"- [{role}]: {m.Content.Trim()}");
|
||||
}
|
||||
parts.Add(string.Join("\n", lines));
|
||||
blocks.Add(new PromptBlock { Icon = "fa-star", Label = "Markierte Nachrichten", Content = string.Join("\n", lines) });
|
||||
}
|
||||
|
||||
return string.Join("\n\n", parts);
|
||||
return blocks;
|
||||
}
|
||||
|
||||
public static string Build(AgentRecord agent, SessionRecord session, IEnumerable<MessageRecord> markedMessages = null, MemoryService memoryService = null, string agentMemoryDir = null, string dataDir = null, IEnumerable<string> skillSlugs = null, SkillService skillService = null)
|
||||
{
|
||||
var blocks = BuildBlocks(agent, session, markedMessages, memoryService, agentMemoryDir, dataDir, skillSlugs, skillService);
|
||||
return string.Join("\n\n", blocks.Select(b => b.Content));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user