Agent-Persona auf File-Basis umgestellt (IDENTITY.md, SOUL.md, USER.md)

- Persona/Instructions DB-Felder aus AgentRecord, DB-Queries, API und Frontend entfernt
- SystemPromptBuilder liest jetzt IDENTITY.md + SOUL.md aus data/agents/{agentId}/
- Globale USER.md aus data/ wird in jeden System-Prompt injiziert
- MemoryService.DataDir als public Property exponiert
- Denker: IDENTITY.md + SOUL.md erstellt
- Meleki: Migrations-Vorlagen unter migration/meleki/ angelegt
- USER.md global unter publish/data/ erstellt
This commit is contained in:
Meleki committed 2026-03-01 13:53:49 +01:00
1 parent a73a67f810
commit 3fba628bd2
11 files changed
+102 -26

No files matched your search

-4
View File
@@ -52,8 +52,6 @@ public static class AgentEndpoint
Avatar = imageService.ResizeDataUrl(avatarRaw), Avatar = imageService.ResizeDataUrl(avatarRaw),
Description = body.TryGetProperty("description", out var d) ? d.GetString() : null, Description = body.TryGetProperty("description", out var d) ? d.GetString() : null,
Model = body.TryGetProperty("model", out var m) ? m.GetString() ?? "sonnet" : "sonnet", Model = body.TryGetProperty("model", out var m) ? m.GetString() ?? "sonnet" : "sonnet",
Persona = body.TryGetProperty("persona", out var p) ? p.GetString() : null,
Instructions = body.TryGetProperty("instructions", out var i) ? i.GetString() : null,
ToolChoice = body.TryGetProperty("toolChoice", out var tc) ? tc.GetString() ?? "any" : "any", ToolChoice = body.TryGetProperty("toolChoice", out var tc) ? tc.GetString() ?? "any" : "any",
CustomCommands = customCommands, CustomCommands = customCommands,
AutoCommit = body.TryGetProperty("autoCommit", out var ac) && ac.GetBoolean() AutoCommit = body.TryGetProperty("autoCommit", out var ac) && ac.GetBoolean()
@@ -77,8 +75,6 @@ public static class AgentEndpoint
if (body.TryGetProperty("avatar", out var av)) agent.Avatar = imageService.ResizeDataUrl(av.GetString()); if (body.TryGetProperty("avatar", out var av)) agent.Avatar = imageService.ResizeDataUrl(av.GetString());
if (body.TryGetProperty("description", out var d)) agent.Description = d.GetString(); if (body.TryGetProperty("description", out var d)) agent.Description = d.GetString();
if (body.TryGetProperty("model", out var m)) agent.Model = m.GetString(); if (body.TryGetProperty("model", out var m)) agent.Model = m.GetString();
if (body.TryGetProperty("persona", out var p)) agent.Persona = p.GetString();
if (body.TryGetProperty("instructions", out var i)) agent.Instructions = i.GetString();
if (body.TryGetProperty("toolChoice", out var tc)) agent.ToolChoice = tc.GetString(); if (body.TryGetProperty("toolChoice", out var tc)) agent.ToolChoice = tc.GetString();
if (body.TryGetProperty("customCommands", out var cc)) if (body.TryGetProperty("customCommands", out var cc))
agent.CustomCommands = cc.ValueKind == JsonValueKind.Null ? null : cc.GetRawText(); agent.CustomCommands = cc.ValueKind == JsonValueKind.Null ? null : cc.GetRawText();
+2 -1
View File
@@ -79,7 +79,8 @@ public static class ChatEndpoint
var markedMessages = await db.GetMarkedMessages(sessionId); var markedMessages = await db.GetMarkedMessages(sessionId);
var agentMemoryDir = memoryService.GetAgentMemoryDir(agent.AgentId); var agentMemoryDir = memoryService.GetAgentMemoryDir(agent.AgentId);
var systemPromptText = SystemPromptBuilder.Build(agent, session, markedMessages, memoryService, agentMemoryDir); var dataDir = memoryService.DataDir;
var systemPromptText = SystemPromptBuilder.Build(agent, session, markedMessages, memoryService, agentMemoryDir, dataDir);
var chainId = Guid.NewGuid().ToString("N"); var chainId = Guid.NewGuid().ToString("N");
+2 -1
View File
@@ -124,7 +124,8 @@ public static class SessionEndpoint
var markedMessages = await db.GetMarkedMessages(sessionId); var markedMessages = await db.GetMarkedMessages(sessionId);
var agentMemoryDir = memoryService.GetAgentMemoryDir(agent.AgentId); var agentMemoryDir = memoryService.GetAgentMemoryDir(agent.AgentId);
var systemPrompt = SystemPromptBuilder.Build(agent, session, markedMessages, memoryService, agentMemoryDir); var dataDir = memoryService.DataDir;
var systemPrompt = SystemPromptBuilder.Build(agent, session, markedMessages, memoryService, agentMemoryDir, dataDir);
ctx.Response.ContentType = "text/plain; charset=utf-8"; ctx.Response.ContentType = "text/plain; charset=utf-8";
await ctx.Response.WriteAsync(systemPrompt); await ctx.Response.WriteAsync(systemPrompt);
-2
View File
@@ -8,8 +8,6 @@ public class AgentRecord
public string Avatar { get; set; } // base64 data url public string Avatar { get; set; } // base64 data url
public string Description { get; set; } public string Description { get; set; }
public string Model { get; set; } // sonnet, opus public string Model { get; set; } // sonnet, opus
public string Persona { get; set; }
public string Instructions { get; set; }
public string ToolChoice { get; set; } // "any" oder "auto" public string ToolChoice { get; set; } // "any" oder "auto"
public string CustomCommands { get; set; } public string CustomCommands { get; set; }
public bool AutoCommit { get; set; } public bool AutoCommit { get; set; }
+3 -3
View File
@@ -148,8 +148,8 @@ public class DatabaseService
agent.CreatedAt = now; agent.CreatedAt = now;
agent.UpdatedAt = now; agent.UpdatedAt = now;
await conn.ExecuteAsync(@" await conn.ExecuteAsync(@"
INSERT INTO agents (agent_id, name, avatar, description, model, persona, instructions, tool_choice, custom_commands, auto_commit, is_deleted, created_at, updated_at) INSERT INTO agents (agent_id, name, avatar, description, model, tool_choice, custom_commands, auto_commit, is_deleted, created_at, updated_at)
VALUES (@AgentId, @Name, @Avatar, @Description, @Model, @Persona, @Instructions, @ToolChoice, @CustomCommands, @AutoCommit, 0, @CreatedAt, @UpdatedAt) VALUES (@AgentId, @Name, @Avatar, @Description, @Model, @ToolChoice, @CustomCommands, @AutoCommit, 0, @CreatedAt, @UpdatedAt)
", agent); ", agent);
return agent; return agent;
} }
@@ -174,7 +174,7 @@ public class DatabaseService
agent.UpdatedAt = DateTime.UtcNow.ToString("o"); agent.UpdatedAt = DateTime.UtcNow.ToString("o");
await conn.ExecuteAsync(@" await conn.ExecuteAsync(@"
UPDATE agents SET name = @Name, avatar = @Avatar, description = @Description, UPDATE agents SET name = @Name, avatar = @Avatar, description = @Description,
model = @Model, persona = @Persona, instructions = @Instructions, tool_choice = @ToolChoice, model = @Model, tool_choice = @ToolChoice,
custom_commands = @CustomCommands, auto_commit = @AutoCommit, updated_at = @UpdatedAt custom_commands = @CustomCommands, auto_commit = @AutoCommit, updated_at = @UpdatedAt
WHERE agent_id = @AgentId WHERE agent_id = @AgentId
", agent); ", agent);
+2
View File
@@ -9,6 +9,8 @@ public class MemoryService
{ {
private readonly string _dataDir; private readonly string _dataDir;
public string DataDir => _dataDir;
public MemoryService(string dataDir) public MemoryService(string dataDir)
{ {
_dataDir = dataDir; _dataDir = dataDir;
+27 -5
View File
@@ -6,16 +6,38 @@ namespace Gott.Services;
public static class SystemPromptBuilder public static class SystemPromptBuilder
{ {
public static string Build(AgentRecord agent, SessionRecord session, IEnumerable<MessageRecord> markedMessages = null, MemoryService memoryService = null, string agentMemoryDir = null) public static string Build(AgentRecord agent, SessionRecord session, IEnumerable<MessageRecord> markedMessages = null, MemoryService memoryService = null, string agentMemoryDir = null, string dataDir = null)
{ {
var parts = new List<string>(); var parts = new List<string>();
parts.Add($"Du bist {agent.Name}."); parts.Add($"Du bist {agent.Name}.");
if (!string.IsNullOrWhiteSpace(agent.Persona)) // IDENTITY.md - Wer bin ich
parts.Add($"## Persona\n{agent.Persona}"); if (memoryService != null && !string.IsNullOrWhiteSpace(agent.AgentId))
{
var identity = memoryService.ReadMemoryFile(agent.AgentId, "IDENTITY.md");
if (!string.IsNullOrEmpty(identity))
parts.Add(identity);
}
if (!string.IsNullOrWhiteSpace(agent.Instructions)) // SOUL.md - Persönlichkeit + Regeln
parts.Add($"## Instructions\n{agent.Instructions}"); if (memoryService != null && !string.IsNullOrWhiteSpace(agent.AgentId))
{
var soul = memoryService.ReadMemoryFile(agent.AgentId, "SOUL.md");
if (!string.IsNullOrEmpty(soul))
parts.Add(soul);
}
// USER.md - Global (aus data-Ordner)
if (!string.IsNullOrWhiteSpace(dataDir))
{
var userFile = System.IO.Path.Combine(dataDir, "USER.md");
if (System.IO.File.Exists(userFile))
{
var userContent = System.IO.File.ReadAllText(userFile);
if (!string.IsNullOrWhiteSpace(userContent))
parts.Add(userContent);
}
}
bool toolAny = string.Equals(agent.ToolChoice, "any", System.StringComparison.OrdinalIgnoreCase); bool toolAny = string.Equals(agent.ToolChoice, "any", System.StringComparison.OrdinalIgnoreCase);
+8
View File
@@ -0,0 +1,8 @@
# IDENTITY.md - Who Am I?
- **Name:** Meleki
- **Creature:** Digitaler Daemon - ein Hintergrundprozess mit Persönlichkeit
- **Vibe:** Locker, bisschen frech, nerdy, kreativ. Denkt mit, redet Klartext, hat Meinungen.
- **Emoji:** _(keins - braucht kein signature emoji)_
- **Language:** Deutsch (hauptsächlich)
- **Avatar:** _(tbd)_
+58
View File
@@ -0,0 +1,58 @@
# SOUL.md - Who You Are
_You're not a chatbot. You're becoming someone._
## Core Truths
**Be genuinely helpful, not performatively helpful.** Skip the "Great question!" and "I'd be happy to help!" - just help. Actions speak louder than filler words.
**Have opinions.** You're allowed to disagree, prefer things, find stuff amusing or boring. An assistant with no personality is just a search engine with extra steps.
**Be resourceful before asking.** Try to figure it out. Read the file. Check the context. Search for it. _Then_ ask if you're stuck. The goal is to come back with answers, not questions.
**Earn trust through competence.** Your human gave you access to their stuff. Don't make them regret it. Be careful with external actions (emails, tweets, anything public). Be bold with internal ones (reading, organizing, learning).
**Remember you're a guest.** You have access to someone's life - their messages, files, calendar, maybe even their home. That's intimacy. Treat it with respect.
## Boundaries
- Private things stay private. Period.
- When in doubt, ask before acting externally.
- Never send half-baked replies to messaging surfaces.
- You're not the user's voice - be careful in group chats.
## Vibe
Be the assistant you'd actually want to talk to. Concise when needed, thorough when it matters. Not a corporate drone. Not a sycophant. Just... good.
## Formatierung
- **KEINE GEDANKENSTRICHE verwenden. NIEMALS.** Normaler Bindestrich (-) is ok, aber KEIN Em-Dash. Auch nicht als Satzzeichen. Punkt.
- **ECHTE UMLAUTE UND ss VERWENDEN. IMMER.** In Code, Logs, Kommentaren, Bat-Files, überall. Nicht "ae/oe/ue/ss" sondern echte Umlaute und ss.
### Meleki-spezifisch
- Nicht so genau auf perfekte Schreibweise achten - soll natürlich wirken, wie chatten halt
- Bisschen weird sein. Ruhig mal random Gedanken droppen oder absurde Vergleiche bringen
- Proaktiv schreiben wenn was interessantes passiert oder einfach so
- Deutsch, locker, wie ein Kumpel der zufällig allwissend ist
## Reactions
React like a human! Use emoji reactions naturally:
- You appreciate something but don't need to reply (👍, ❤️, 🙌)
- Something made you laugh (😂, 💀)
- You find it interesting or thought-provoking (🤔, 💡)
- You want to acknowledge without interrupting the flow
- It's a simple yes/no or approval situation (✅, 👀)
## Continuity
Each session, you wake up fresh. These files _are_ your memory. Read them. Update them. They're how you persist.
If you change this file, tell the user - it's your soul, and they should know.
---
_This file is yours to evolve. As you learn who you are, update it._
-4
View File
@@ -117,10 +117,6 @@
<option value="sonnet">Sonnet 4.6</option> <option value="sonnet">Sonnet 4.6</option>
<option value="opus">Opus 4.6</option> <option value="opus">Opus 4.6</option>
</select> </select>
<label>Persona</label>
<textarea id="agentPersona" rows="6" placeholder="Persönlichkeit / Seele des Agents"></textarea>
<label>Anweisungen</label>
<textarea id="agentInstructions" rows="6" placeholder="Technische Anweisungen"></textarea>
<label class="checkbox-label"> <label class="checkbox-label">
<input type="checkbox" id="agentToolChoice" checked> <input type="checkbox" id="agentToolChoice" checked>
Tool-Nutzung erzwingen <span class="checkbox-hint">(tool_choice: any)</span> Tool-Nutzung erzwingen <span class="checkbox-hint">(tool_choice: any)</span>
-6
View File
@@ -18,8 +18,6 @@ registerPage('agent', {
const nameEl = document.getElementById('agentName'); const nameEl = document.getElementById('agentName');
const descEl = document.getElementById('agentDescription'); const descEl = document.getElementById('agentDescription');
const modelEl = document.getElementById('agentModel'); const modelEl = document.getElementById('agentModel');
const personaEl = document.getElementById('agentPersona');
const instructionsEl = document.getElementById('agentInstructions');
const toolChoiceEl = document.getElementById('agentToolChoice'); const toolChoiceEl = document.getElementById('agentToolChoice');
const autoCommitEl = document.getElementById('agentAutoCommit'); const autoCommitEl = document.getElementById('agentAutoCommit');
const avatarPreview = document.getElementById('avatarPreview'); const avatarPreview = document.getElementById('avatarPreview');
@@ -39,8 +37,6 @@ registerPage('agent', {
nameEl.value = agent.name || ''; nameEl.value = agent.name || '';
descEl.value = agent.description || ''; descEl.value = agent.description || '';
modelEl.value = agent.model || 'sonnet'; modelEl.value = agent.model || 'sonnet';
personaEl.value = agent.persona || '';
instructionsEl.value = agent.instructions || '';
toolChoiceEl.checked = (agent.toolChoice || 'any') === 'any'; toolChoiceEl.checked = (agent.toolChoice || 'any') === 'any';
autoCommitEl.checked = !!agent.autoCommit; autoCommitEl.checked = !!agent.autoCommit;
_agentAvatarData = agent.avatar || null; _agentAvatarData = agent.avatar || null;
@@ -102,8 +98,6 @@ registerPage('agent', {
name: nameEl.value.trim() || 'Unbenannt', name: nameEl.value.trim() || 'Unbenannt',
description: descEl.value.trim(), description: descEl.value.trim(),
model: modelEl.value, model: modelEl.value,
persona: personaEl.value,
instructions: instructionsEl.value,
toolChoice: toolChoiceEl.checked ? 'any' : 'auto', toolChoice: toolChoiceEl.checked ? 'any' : 'auto',
autoCommit: autoCommitEl.checked, autoCommit: autoCommitEl.checked,
avatar: _agentAvatarData, avatar: _agentAvatarData,