Auto-Context: Semantische Kontext-Injektion, Memory-File-Embeddings, Agent-Toggle

- Automatische Embedding-basierte Kontext-Injektion vor jeder API-Anfrage
  (Top 5 Chat-Ergebnisse + Top 3 Memory-File-Treffer als recalled_context)
- Memory-Dateien (.md) werden embedded (Backfill + Live nach memory_write)
- Neue Agent-Option 'useAutoContext' (Checkbox, Default: aktiviert)
- VectorSearchService: SearchMemoryFilesAsync, EmbedMemoryFileAsync, Chunking
- EmbeddingBackgroundService: Memory-File-Backfill + EnqueueMemoryFile
- ToolService: memory_write löst Re-Embedding aus
- embeddingBg Parameter an ExecuteTool durchgereicht
This commit is contained in:
Meleki committed 2026-03-11 09:45:50 +01:00
1 parent add111ffe1
commit 148d1459a3
11 files changed
+305 -12

No files matched your search

+3
View File
@@ -64,6 +64,7 @@ public static class AgentEndpoint
a.UseIdentity,
a.UseUserInfo,
a.UseMemory,
a.UseAutoContext,
a.UseWebSearch,
a.WebSearchVersion,
a.AllowUserChoice,
@@ -119,6 +120,7 @@ public static class AgentEndpoint
UseIdentity = !body.TryGetProperty("useIdentity", out var ui) || ui.GetBoolean(),
UseUserInfo = !body.TryGetProperty("useUserInfo", out var uu) || uu.GetBoolean(),
UseMemory = !body.TryGetProperty("useMemory", out var um) || um.GetBoolean(),
UseAutoContext = !body.TryGetProperty("useAutoContext", out var uac) || uac.GetBoolean(),
UseWebSearch = body.TryGetProperty("webSearchVersion", out var wsv) && !string.IsNullOrEmpty(wsv.GetString()),
WebSearchVersion = body.TryGetProperty("webSearchVersion", out var wsv2) ? wsv2.GetString() ?? "" : "",
AllowUserChoice = !body.TryGetProperty("allowUserChoice", out var auc) || auc.GetBoolean(),
@@ -160,6 +162,7 @@ public static class AgentEndpoint
if (body.TryGetProperty("useIdentity", out var ui)) agent.UseIdentity = ui.GetBoolean();
if (body.TryGetProperty("useUserInfo", out var uu)) agent.UseUserInfo = uu.GetBoolean();
if (body.TryGetProperty("useMemory", out var um)) agent.UseMemory = um.GetBoolean();
if (body.TryGetProperty("useAutoContext", out var uac)) agent.UseAutoContext = uac.GetBoolean();
if (body.TryGetProperty("webSearchVersion", out var wsv))
{
agent.WebSearchVersion = wsv.GetString() ?? "";
+1 -1
View File
@@ -208,7 +208,7 @@ public static class ChatEndpoint
try
{
await anthropic.StreamChat(res, ctx.ModelId, ctx.SystemPrompt, messages, ctx.ChainId, ctx.Workspace, sessionId, agentId: ctx.AgentId, useMemory: ctx.UseMemory, webSearchVersion: ctx.WebSearchVersion, maxIterations: ctx.MaxIterations, toolChoice: effectiveToolChoice, review: ctx.Review, finalizer: ctx.Finalizer, allowUserChoice: ctx.Agent?.AllowUserChoice ?? true, planMode: ctx.PlanMode, workCt: workCt, hardCt: hardCt, sseCt: sseCt, remoteMgr: remoteMgr);
await anthropic.StreamChat(res, ctx.ModelId, ctx.SystemPrompt, messages, ctx.ChainId, ctx.Workspace, sessionId, agentId: ctx.AgentId, useMemory: ctx.UseMemory, webSearchVersion: ctx.WebSearchVersion, maxIterations: ctx.MaxIterations, toolChoice: effectiveToolChoice, review: ctx.Review, finalizer: ctx.Finalizer, allowUserChoice: ctx.Agent?.AllowUserChoice ?? true, planMode: ctx.PlanMode, useAutoContext: ctx.Agent?.UseAutoContext ?? true, workCt: workCt, hardCt: hardCt, sseCt: sseCt, remoteMgr: remoteMgr);
}
finally
{
+1
View File
@@ -16,6 +16,7 @@ public class AgentRecord
public bool UseIdentity { get; set; } = true;
public bool UseUserInfo { get; set; } = true;
public bool UseMemory { get; set; } = true;
public bool UseAutoContext { get; set; } = true;
public bool UseWebSearch { get; set; }
public string WebSearchVersion { get; set; } = "";
public bool AllowUserChoice { get; set; } = true;
+73 -3
View File
@@ -55,6 +55,7 @@ public class AnthropicService
SubAgentContext finalizer = null,
bool allowUserChoice = true,
bool planMode = false,
bool useAutoContext = true,
CancellationToken workCt = default,
CancellationToken hardCt = default,
CancellationToken sseCt = default,
@@ -76,6 +77,46 @@ public class AnthropicService
try
{
// ── Automatische Kontext-Injektion via Embedding-Suche ──
if (useAutoContext && _vectorSearch != null && systemPrompt != null && messages.Count > 0)
{
try
{
// Letzte User-Message als Query extrahieren
var lastUserText = ExtractLastUserText(messages);
if (!string.IsNullOrWhiteSpace(lastUserText))
{
var contextResults = await _vectorSearch.SearchAsync(lastUserText, agentId, topN: 5, minScore: 0.45f);
// Nur Ergebnisse aus anderen Sessions (nicht aktuelle Chain) und nicht zu kurze
contextResults.RemoveAll(r => r.ChainId == chainId || (r.TextPreview?.Length ?? 0) < 20);
// Memory-Dateien durchsuchen
var memoryResults = await _vectorSearch.SearchMemoryFilesAsync(lastUserText, agentId, topN: 3, minScore: 0.45f);
if (contextResults.Count > 0 || memoryResults.Count > 0)
{
var ctxSb = new StringBuilder();
ctxSb.AppendLine("\n<recalled_context>");
ctxSb.AppendLine("Die folgende Information wurde automatisch abgerufen und könnte relevant sein:");
foreach (var r in contextResults)
{
var date = DateTime.TryParse(r.CreatedAt, out var dt) ? dt.ToString("yyyy-MM-dd HH:mm") : r.CreatedAt;
ctxSb.AppendLine($"- [Gespräch, {r.Role}, {date}]: {r.TextPreview}");
}
foreach (var r in memoryResults)
{
ctxSb.AppendLine($"- [Memory, {r.FilePath}]: {r.TextPreview}");
}
ctxSb.AppendLine("</recalled_context>");
systemPrompt.Add(new { type = "text", text = ctxSb.ToString() });
}
}
}
catch (Exception ex)
{
Console.WriteLine($"[AutoContext] Error: {ex.Message}");
}
}
while (iterations < maxIterations)
{
// Hard-Stop: Sofortiger Abbruch aus while-Loop (Doppel-Klick Stop)
@@ -838,7 +879,7 @@ public class AnthropicService
string toolResult;
bool isError = false;
try { toolResult = await _toolService.ExecuteTool(toolName, toolInput, agentWorkspace, _memoryService, agentId, workCt, vectorSearch: _vectorSearch); }
try { toolResult = await _toolService.ExecuteTool(toolName, toolInput, agentWorkspace, _memoryService, agentId, workCt, vectorSearch: _vectorSearch, embeddingBg: _embeddingBg); }
catch (OperationCanceledException) { throw; }
catch (Exception ex) { toolResult = $"Fehler: {ex.Message}"; isError = true; }
@@ -1044,7 +1085,7 @@ public class AnthropicService
string toolResult;
bool isError = false;
try { toolResult = await _toolService.ExecuteTool(toolName, toolInput, ctx.Workspace, _memoryService, ctx.AgentId, vectorSearch: _vectorSearch); }
try { toolResult = await _toolService.ExecuteTool(toolName, toolInput, ctx.Workspace, _memoryService, ctx.AgentId, vectorSearch: _vectorSearch, embeddingBg: _embeddingBg); }
catch (OperationCanceledException) { throw; }
catch (Exception ex) { toolResult = $"Fehler: {ex.Message}"; isError = true; }
@@ -1345,7 +1386,7 @@ public class AnthropicService
string toolResult;
bool isError = false;
try { toolResult = await _toolService.ExecuteTool(currentToolName, inputEl, workspace, _memoryService, agentId, workCt, remoteMgr: remoteMgr, sessionId: sessionId, vectorSearch: _vectorSearch); }
try { toolResult = await _toolService.ExecuteTool(currentToolName, inputEl, workspace, _memoryService, agentId, workCt, remoteMgr: remoteMgr, sessionId: sessionId, vectorSearch: _vectorSearch, embeddingBg: _embeddingBg); }
catch (OperationCanceledException) { throw; }
catch (Exception ex) { toolResult = $"Fehler: {ex.Message}"; isError = true; }
@@ -1468,6 +1509,35 @@ public class AnthropicService
: result;
}
/// <summary>
/// Extrahiert den Text der letzten User-Message aus der Message-Liste.
/// </summary>
private static string ExtractLastUserText(List<object> messages)
{
for (int i = messages.Count - 1; i >= 0; i--)
{
var json = JsonSerializer.Serialize(messages[i]);
var el = JsonSerializer.Deserialize<JsonElement>(json);
if (!el.TryGetProperty("role", out var role) || role.GetString() != "user") continue;
if (!el.TryGetProperty("content", out var content)) continue;
if (content.ValueKind == JsonValueKind.String)
return content.GetString();
if (content.ValueKind == JsonValueKind.Array)
{
foreach (var block in content.EnumerateArray())
{
var type = block.TryGetProperty("type", out var t) ? t.GetString() : "";
if (type == "text" && block.TryGetProperty("text", out var txt))
return txt.GetString();
}
}
break;
}
return null;
}
/// <summary>
/// Extrahiert Web-Search-Sources aus einem web_search_tool_result-Block.
/// </summary>
+4 -3
View File
@@ -149,6 +149,7 @@ public class DatabaseService
TryAddColumn(conn, "agents", "use_identity", "INTEGER NOT NULL DEFAULT 1");
TryAddColumn(conn, "agents", "use_user_info", "INTEGER NOT NULL DEFAULT 1");
TryAddColumn(conn, "agents", "use_memory", "INTEGER NOT NULL DEFAULT 1");
TryAddColumn(conn, "agents", "use_auto_context", "INTEGER NOT NULL DEFAULT 1");
TryAddColumn(conn, "messages", "deleted_on", "TEXT");
TryAddColumn(conn, "agents", "use_web_search", "INTEGER NOT NULL DEFAULT 0");
@@ -472,8 +473,8 @@ public class DatabaseService
agent.CreatedAt = now;
agent.UpdatedAt = now;
await conn.ExecuteAsync(@"
INSERT INTO agents (agent_id, user_id, name, avatar, avatar_small, description, model, tool_choice, custom_commands, auto_commit, use_soul, use_identity, use_user_info, use_memory, use_web_search, web_search_version, allow_user_choice, task_instruction, avatar_scale, avatar_offset_x, avatar_offset_y, is_deleted, created_at, updated_at)
VALUES (@AgentId, @UserId, @Name, @Avatar, @AvatarSmall, @Description, @Model, @ToolChoice, @CustomCommands, @AutoCommit, @UseSoul, @UseIdentity, @UseUserInfo, @UseMemory, @UseWebSearch, @WebSearchVersion, @AllowUserChoice, @TaskInstruction, @AvatarScale, @AvatarOffsetX, @AvatarOffsetY, 0, @CreatedAt, @UpdatedAt)
INSERT INTO agents (agent_id, user_id, name, avatar, avatar_small, description, model, tool_choice, custom_commands, auto_commit, use_soul, use_identity, use_user_info, use_memory, use_auto_context, use_web_search, web_search_version, allow_user_choice, task_instruction, avatar_scale, avatar_offset_x, avatar_offset_y, is_deleted, created_at, updated_at)
VALUES (@AgentId, @UserId, @Name, @Avatar, @AvatarSmall, @Description, @Model, @ToolChoice, @CustomCommands, @AutoCommit, @UseSoul, @UseIdentity, @UseUserInfo, @UseMemory, @UseAutoContext, @UseWebSearch, @WebSearchVersion, @AllowUserChoice, @TaskInstruction, @AvatarScale, @AvatarOffsetX, @AvatarOffsetY, 0, @CreatedAt, @UpdatedAt)
", agent);
return agent;
}
@@ -507,7 +508,7 @@ public class DatabaseService
UPDATE agents SET name = @Name, avatar = @Avatar, avatar_small = @AvatarSmall, description = @Description,
model = @Model, tool_choice = @ToolChoice,
custom_commands = @CustomCommands, auto_commit = @AutoCommit,
use_soul = @UseSoul, use_identity = @UseIdentity, use_user_info = @UseUserInfo, use_memory = @UseMemory, use_web_search = @UseWebSearch, web_search_version = @WebSearchVersion,
use_soul = @UseSoul, use_identity = @UseIdentity, use_user_info = @UseUserInfo, use_memory = @UseMemory, use_auto_context = @UseAutoContext, use_web_search = @UseWebSearch, web_search_version = @WebSearchVersion,
allow_user_choice = @AllowUserChoice, task_instruction = @TaskInstruction,
avatar_scale = @AvatarScale, avatar_offset_x = @AvatarOffsetX, avatar_offset_y = @AvatarOffsetY,
is_public = @IsPublic, updated_at = @UpdatedAt
+66 -3
View File
@@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
@@ -21,12 +22,14 @@ public class EmbeddingBackgroundService : BackgroundService
readonly string _dbConnectionString;
readonly ConcurrentQueue<EmbeddingJob> _queue = new();
readonly SemaphoreSlim _signal = new(0);
readonly string _dataPath;
public EmbeddingBackgroundService(VectorSearchService vectorSearch, EmbeddingService embedding, string dbPath)
{
_vectorSearch = vectorSearch;
_embedding = embedding;
_dbConnectionString = $"Data Source={dbPath}";
_dataPath = Path.GetDirectoryName(dbPath);
}
/// <summary>Enqueue eine neue Message zum Embedden.</summary>
@@ -50,6 +53,9 @@ public class EmbeddingBackgroundService : BackgroundService
// Backfill bestehender Messages
await BackfillAsync(ct);
// Memory-Dateien embedden
await BackfillMemoryFilesAsync(ct);
// Live-Embedding neuer Messages
Console.WriteLine("[EmbeddingBG] Waiting for new messages...");
while (!ct.IsCancellationRequested)
@@ -62,12 +68,21 @@ public class EmbeddingBackgroundService : BackgroundService
if (ct.IsCancellationRequested) break;
try
{
await _vectorSearch.EmbedMessageAsync(job.MessageId, job.ChainId, job.SessionId,
job.AgentId, job.Role, job.Text);
if (job.Role.StartsWith("memory:"))
{
// Memory-File-Embedding
var filePath = job.Role.Substring("memory:".Length);
await _vectorSearch.EmbedMemoryFileAsync(job.AgentId, filePath, job.Text);
}
else
{
await _vectorSearch.EmbedMessageAsync(job.MessageId, job.ChainId, job.SessionId,
job.AgentId, job.Role, job.Text);
}
}
catch (Exception ex)
{
Console.WriteLine($"[EmbeddingBG] Error embedding message {job.MessageId}: {ex.Message}");
Console.WriteLine($"[EmbeddingBG] Error embedding job: {ex.Message}");
}
}
}
@@ -133,6 +148,54 @@ public class EmbeddingBackgroundService : BackgroundService
Console.WriteLine($"[EmbeddingBG] Backfill complete: {embedded} new embeddings, {skipped} skipped, {total} total messages.");
}
async Task BackfillMemoryFilesAsync(CancellationToken ct)
{
Console.WriteLine("[EmbeddingBG] Starting memory file backfill...");
var agentsDir = Path.Combine(_dataPath, "agents");
if (!Directory.Exists(agentsDir))
{
Console.WriteLine("[EmbeddingBG] No agents directory found, skipping memory backfill.");
return;
}
int total = 0;
foreach (var agentDir in Directory.GetDirectories(agentsDir))
{
if (ct.IsCancellationRequested) break;
var agentId = Path.GetFileName(agentDir);
// Alle .md Dateien im Agent-Verzeichnis (rekursiv)
var mdFiles = Directory.GetFiles(agentDir, "*.md", SearchOption.AllDirectories);
foreach (var mdFile in mdFiles)
{
if (ct.IsCancellationRequested) break;
try
{
var text = await File.ReadAllTextAsync(mdFile, ct);
if (string.IsNullOrWhiteSpace(text) || text.Length < 10) continue;
var relativePath = Path.GetRelativePath(agentDir, mdFile);
await _vectorSearch.EmbedMemoryFileAsync(agentId, relativePath, text);
total++;
}
catch (Exception ex)
{
Console.WriteLine($"[EmbeddingBG] Error embedding memory file {mdFile}: {ex.Message}");
}
}
}
Console.WriteLine($"[EmbeddingBG] Memory file backfill complete: {total} files embedded.");
}
/// <summary>Embeddet eine einzelne Memory-Datei (nach memory_write).</summary>
public void EnqueueMemoryFile(string agentId, string filePath, string text)
{
_queue.Enqueue(new EmbeddingJob(0, "", "", agentId, "memory:" + filePath, text));
_signal.Release();
}
record EmbeddingJob(long MessageId, string ChainId, string SessionId, string AgentId, string Role, string Text);
class BackfillRow
+8 -1
View File
@@ -354,7 +354,7 @@ public class ToolService
};
}
public async Task<string> ExecuteTool(string name, JsonElement input, string workspace, MemoryService memoryService = null, string agentId = null, CancellationToken cancellationToken = default, RemoteConnectionManager remoteMgr = null, string sessionId = null, VectorSearchService vectorSearch = null)
public async Task<string> ExecuteTool(string name, JsonElement input, string workspace, MemoryService memoryService = null, string agentId = null, CancellationToken cancellationToken = default, RemoteConnectionManager remoteMgr = null, string sessionId = null, VectorSearchService vectorSearch = null, EmbeddingBackgroundService embeddingBg = null)
{
switch (name)
{
@@ -628,6 +628,13 @@ public class ToolService
memoryService.AppendMemoryFile(agentId, path, content);
else
memoryService.WriteMemoryFile(agentId, path, content);
// Memory-Datei nach Schreiben embedden
if (embeddingBg != null)
{
var fullText = memoryService.ReadMemoryFile(agentId, path);
if (!string.IsNullOrEmpty(fullText))
embeddingBg.EnqueueMemoryFile(agentId, path, fullText);
}
return $"Memory-Datei {(append ? "erweitert" : "geschrieben")}: {path} ({content.Length} Zeichen)";
}
case "memory_list":
+141
View File
@@ -1,4 +1,5 @@
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@@ -49,6 +50,17 @@ public class VectorSearchService
CREATE INDEX IF NOT EXISTS idx_emb_session ON message_embeddings(session_id);
CREATE INDEX IF NOT EXISTS idx_emb_agent ON message_embeddings(agent_id);
CREATE INDEX IF NOT EXISTS idx_emb_message ON message_embeddings(message_id);
CREATE TABLE IF NOT EXISTS memory_file_embeddings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
agent_id TEXT NOT NULL,
file_path TEXT NOT NULL,
chunk_index INTEGER NOT NULL,
text_preview TEXT,
vector BLOB NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_memfile_agent ON memory_file_embeddings(agent_id);
");
Console.WriteLine("[VectorSearch] Schema initialized.");
}
@@ -188,6 +200,135 @@ public class VectorSearchService
return new HashSet<long>(ids);
}
/// <summary>
/// Generiert und speichert ein Embedding für eine Memory-Datei (oder aktualisiert es).
/// </summary>
public async Task EmbedMemoryFileAsync(string agentId, string filePath, string text)
{
if (!_embedding.IsAvailable || string.IsNullOrWhiteSpace(text)) return;
if (text.Length < 10) return;
// Chunks erstellen: Memory-Dateien können lang sein → in 500-Zeichen-Chunks aufteilen
var chunks = ChunkText(text, 500);
using var conn = CreateConnection();
// Alte Embeddings für diese Datei löschen (bei Update)
await conn.ExecuteAsync(
"DELETE FROM memory_file_embeddings WHERE agent_id = @agentId AND file_path = @filePath",
new { agentId, filePath });
for (int i = 0; i < chunks.Count; i++)
{
float[] vector;
await _embeddingLock.WaitAsync();
try { vector = _embedding.GenerateEmbedding(chunks[i]); }
finally { _embeddingLock.Release(); }
if (vector.Length == 0) continue;
var preview = chunks[i].Length > 200 ? chunks[i].Substring(0, 200) : chunks[i];
await conn.ExecuteAsync(@"
INSERT INTO memory_file_embeddings
(agent_id, file_path, chunk_index, text_preview, vector, updated_at)
VALUES
(@agentId, @filePath, @chunkIndex, @preview, @vector, @updatedAt)",
new
{
agentId,
filePath,
chunkIndex = i,
preview,
vector = EmbeddingService.ToBytes(vector),
updatedAt = DateTime.UtcNow.ToString("o")
});
}
}
/// <summary>
/// Sucht über Memory-Dateien eines Agents.
/// </summary>
public async Task<List<MemorySearchResult>> SearchMemoryFilesAsync(string query, string agentId, int topN = 5, float minScore = 0.4f)
{
if (!_embedding.IsAvailable || string.IsNullOrEmpty(agentId)) return new List<MemorySearchResult>();
float[] queryVec;
await _embeddingLock.WaitAsync();
try { queryVec = _embedding.GenerateEmbedding(query); }
finally { _embeddingLock.Release(); }
if (queryVec.Length == 0) return new List<MemorySearchResult>();
using var conn = CreateConnection();
var rows = await conn.QueryAsync<MemoryFileRow>(
"SELECT agent_id, file_path, chunk_index, text_preview, vector, updated_at FROM memory_file_embeddings WHERE agent_id = @agentId",
new { agentId });
var results = new List<MemorySearchResult>();
foreach (var row in rows)
{
var vec = EmbeddingService.FromBytes(row.Vector);
var score = EmbeddingService.CosineSimilarity(queryVec, vec);
if (score >= minScore)
{
results.Add(new MemorySearchResult
{
AgentId = row.AgentId,
FilePath = row.FilePath,
ChunkIndex = row.ChunkIndex,
TextPreview = row.TextPreview,
Score = score,
UpdatedAt = row.UpdatedAt
});
}
}
return results.OrderByDescending(r => r.Score).Take(topN).ToList();
}
static List<string> ChunkText(string text, int chunkSize)
{
var chunks = new List<string>();
if (string.IsNullOrEmpty(text)) return chunks;
// Versuche an Absätzen zu trennen
var paragraphs = text.Split(new[] { "\n\n", "\r\n\r\n" }, StringSplitOptions.RemoveEmptyEntries);
var current = new StringBuilder();
foreach (var para in paragraphs)
{
if (current.Length + para.Length > chunkSize && current.Length > 0)
{
chunks.Add(current.ToString().Trim());
current.Clear();
}
if (current.Length > 0) current.AppendLine();
current.Append(para);
}
if (current.Length > 0)
chunks.Add(current.ToString().Trim());
return chunks;
}
public class MemoryFileRow
{
public string AgentId { get; set; }
public string FilePath { get; set; }
public int ChunkIndex { get; set; }
public string TextPreview { get; set; }
public byte[] Vector { get; set; }
public string UpdatedAt { get; set; }
}
public class MemorySearchResult
{
public string AgentId { get; set; }
public string FilePath { get; set; }
public int ChunkIndex { get; set; }
public string TextPreview { get; set; }
public float Score { get; set; }
public string UpdatedAt { get; set; }
}
public class EmbeddingRow
{
public long MessageId { get; set; }
+1 -1
View File
@@ -42,7 +42,7 @@
- [x] Anstelle von `[Bild: ...]`-Platzhalter direkt das gesendete Bild anzeigen
## Ideas / Backlog
- [ ] Memory: Embedding-Modell für semantische Suche über Memory-Dateien (Vektor-basiert + BM25 Hybrid Search)
- [x] Memory: Embedding-Modell für semantische Suche über Memory-Dateien (Vektor-basiert + BM25 Hybrid Search)
- [x] ~~Claude (Sub-)Agents~~ – Nicht umsetzbar: Claude Code Sub-Agents sind ein CLI-Feature, nicht über die Messages API verfügbar. Eigenes Sub-Agent-System (Preparer/Review/Finalizer) deckt den Use-Case bereits ab.
## Features aus Devy übernehmen
+4
View File
@@ -227,6 +227,10 @@
<input type="checkbox" id="agentUseMemory">
Zugriff auf Erinnerungen <span class="checkbox-hint">(Memory-Tools verfügbar)</span>
</label>
<label class="checkbox-label">
<input type="checkbox" id="agentUseAutoContext" checked>
Automatischer Kontext <span class="checkbox-hint">(Relevante Erinnerungen werden automatisch abgerufen)</span>
</label>
<label>Web-Suche</label>
<select id="agentWebSearchVersion">
<option value="">Deaktiviert</option>
+3
View File
@@ -24,6 +24,7 @@ registerPage('agent', {
const useIdentityEl = document.getElementById('agentUseIdentity');
const useUserInfoEl = document.getElementById('agentUseUserInfo');
const useMemoryEl = document.getElementById('agentUseMemory');
const useAutoContextEl = document.getElementById('agentUseAutoContext');
const webSearchVersionEl = document.getElementById('agentWebSearchVersion');
const avatarPreview = document.getElementById('avatarPreview');
const saveBtn = document.getElementById('agentSaveBtn');
@@ -64,6 +65,7 @@ registerPage('agent', {
useIdentityEl.checked = agent.useIdentity !== false;
useUserInfoEl.checked = agent.useUserInfo !== false;
useMemoryEl.checked = agent.useMemory !== false;
useAutoContextEl.checked = agent.useAutoContext !== false;
webSearchVersionEl.value = agent.webSearchVersion || '';
isPublicEl.checked = agent.isPublic || false;
deleteBtn.hidden = false;
@@ -105,6 +107,7 @@ registerPage('agent', {
useIdentity: useIdentityEl.checked,
useUserInfo: useUserInfoEl.checked,
useMemory: useMemoryEl.checked,
useAutoContext: useAutoContextEl.checked,
webSearchVersion: webSearchVersionEl.value,
avatar: _agentAvatarEditor.getImage(),
avatarScale: _agentAvatarEditor.getScale(),