- Gerbil.CharacterTraits (List<string>, JSON text column, opaque labels) + CharacterNote (string?). On GerbilDto + Input; round-trips on GET/POST/PUT. Additive migration. - SaleAdAnimal gains Traits (German labels) + CharacterNote; prompt builder emits "Charakter: …" + "Charakter-Notiz: …" lines for the AI Verkaufstext. - Tests: traits/note round-trip (POST->GET via SQLite host), empty-default, prompt inclusion.
112 lines
5.7 KiB
C#
112 lines
5.7 KiB
C#
using System.Globalization;
|
||
using System.Text;
|
||
|
||
namespace GerbilManagerWebAPI.SaleAd
|
||
{
|
||
/// <summary>
|
||
/// FEAT-12a: assembles the German prompt for the sale-ad (Abgabe-Inserat)
|
||
/// generation. Style and rules follow the breeder's real Jimdo listings
|
||
/// (analysed in hive/agents/god/FEAT12-jimdo-publish-research.md):
|
||
/// group-based listings, status line, bold emotive tagline, per-animal
|
||
/// Farbschlag + "geboren am DD.MM.YYYY" + personality prose, NO PRICES.
|
||
/// </summary>
|
||
public static class SaleAdPromptBuilder
|
||
{
|
||
/// <summary>
|
||
/// Two few-shot examples derived from the documented listing template.
|
||
/// NOTE: the research file documents the format and one real tagline;
|
||
/// these examples are synthesized to that template. Swap in verbatim
|
||
/// listings from kleine-chaoten.jimdofree.com when available.
|
||
/// </summary>
|
||
private const string ExampleListing1 = """
|
||
Status: FREI
|
||
|
||
„Großer und kleiner Bruder Dynamik“ – Männliches Rennmaus-Duo sucht ein liebevolles Zuhause
|
||
|
||
Balu – CP-Agouti, geboren am 12.03.2024
|
||
Balu ist der ruhige Pol des Duos: Er beobachtet erst in aller Seelenruhe und buddelt sich dann zielstrebig durch jedes Einstreu-Projekt. Aus der Hand nimmt er Leckerlis schon ganz vorsichtig.
|
||
|
||
Benny – Schwarz Schecke, geboren am 12.03.2024
|
||
Benny ist der Entdecker: kein Röhrchen bleibt unerforscht, kein Häuschen unbewohnt. Mit seinem Bruder kuschelt er sich abends ins Nest — getrennt werden die beiden deshalb nicht.
|
||
|
||
Die zwei werden nur gemeinsam in ein rennmausgerechtes Zuhause abgegeben.
|
||
""";
|
||
|
||
private const string ExampleListing2 = """
|
||
Status: LOCKER RESERVIERT Anna
|
||
|
||
„Zwei Schwestern, ein Herz und ganz viel Neugier“ – Weibliches Duo sucht seine Menschen
|
||
|
||
Frieda – Gold, geboren am 28.06.2024
|
||
Frieda ist die Mutige der beiden und steht beim Öffnen des Geheges sofort am Glas. Sie liebt Kolbenhirse und nimmt sie dir behutsam aus den Fingern.
|
||
|
||
Fine – Agouti, geboren am 28.06.2024
|
||
Fine ist etwas zurückhaltender, taut aber neben ihrer Schwester schnell auf. Beim abendlichen Buddeln sind die zwei ein unschlagbares Team.
|
||
|
||
Die Schwestern ziehen selbstverständlich nur zusammen um.
|
||
""";
|
||
|
||
/// <summary>System prompt: role, style description, hard rules, few-shot examples.</summary>
|
||
public static string BuildSystemPrompt() => $"""
|
||
Du schreibst Abgabe-Inserate für Mongolische Rennmäuse im Stil der „Zucht der kleinen Chaoten“.
|
||
|
||
Stil und Aufbau (verbindlich):
|
||
- Erste Zeile: die Status-Zeile, exakt wie vorgegeben (z. B. „Status: FREI“).
|
||
- Danach eine fette, emotionale Überschrift (Tagline) in Anführungszeichen mit kurzem Untertitel, die den Charakter der Gruppe einfängt.
|
||
- Danach pro Tier ein Absatz: Name – Farbschlag, geboren am TT.MM.JJJJ, gefolgt von warmherziger Persönlichkeits-Prosa auf Basis der mitgelieferten Notizen.
|
||
- Abschluss: ein Satz, dass die Tiere nur gemeinsam in ein artgerechtes Zuhause abgegeben werden.
|
||
|
||
Harte Regeln:
|
||
- NIEMALS Preise oder Schutzgebühren nennen.
|
||
- KEINE Fakten erfinden: Verwende ausschließlich die mitgelieferten Daten (Namen, Farbschläge, Geburtsdaten, Notizen). Fehlt eine Angabe, lässt du sie weg.
|
||
- Sprache: Deutsch, warm und liebevoll, aber nicht kitschig-übertrieben.
|
||
- Gib NUR den Inserat-Text aus — keine Erklärungen, keine Markdown-Code-Blöcke.
|
||
|
||
Beispiel 1:
|
||
{ExampleListing1}
|
||
|
||
Beispiel 2:
|
||
{ExampleListing2}
|
||
""";
|
||
|
||
/// <summary>User prompt: the actual group data + the user's free-text hints.</summary>
|
||
public static string BuildUserPrompt(SaleAdRequest request)
|
||
{
|
||
var sb = new StringBuilder();
|
||
sb.AppendLine("Erstelle ein Abgabe-Inserat für folgende Gruppe:");
|
||
sb.AppendLine();
|
||
sb.AppendLine($"Status-Zeile: {request.StatusLine}");
|
||
sb.AppendLine();
|
||
sb.AppendLine("Tiere:");
|
||
foreach (var animal in request.Animals)
|
||
{
|
||
sb.Append($"- Name: {animal.Name}");
|
||
if (!string.IsNullOrWhiteSpace(animal.Farbschlag))
|
||
sb.Append($" | Farbschlag: {animal.Farbschlag}");
|
||
if (!string.IsNullOrWhiteSpace(animal.DateOfBirth))
|
||
sb.Append($" | geboren am {FormatGermanDate(animal.DateOfBirth)}");
|
||
if (!string.IsNullOrWhiteSpace(animal.Notes))
|
||
sb.Append($" | Notizen: {animal.Notes}");
|
||
if (animal.Traits is { Count: > 0 })
|
||
sb.Append($" | Charakter: {string.Join(", ", animal.Traits)}");
|
||
if (!string.IsNullOrWhiteSpace(animal.CharacterNote))
|
||
sb.Append($" | Charakter-Notiz: {animal.CharacterNote}");
|
||
sb.AppendLine();
|
||
}
|
||
if (!string.IsNullOrWhiteSpace(request.Hints))
|
||
{
|
||
sb.AppendLine();
|
||
sb.AppendLine($"Wünsche/Hinweise: {request.Hints}");
|
||
}
|
||
return sb.ToString();
|
||
}
|
||
|
||
/// <summary>ISO "YYYY-MM-DD" -> "TT.MM.JJJJ"; anything else passes through verbatim.</summary>
|
||
internal static string FormatGermanDate(string isoDate) =>
|
||
DateOnly.TryParseExact(isoDate, "yyyy-MM-dd", CultureInfo.InvariantCulture,
|
||
DateTimeStyles.None, out var date)
|
||
? date.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture)
|
||
: isoDate;
|
||
}
|
||
}
|