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:
1 parent
a73a67f810
commit
3fba628bd2
11 files changed
+102
-26
No files matched your search
@@ -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();
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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)_
|
||||||
@@ -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._
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in new issue
Block a user