feat: Semantische Suche über Chat-History (memory_search)
- Lokales Embedding-Model (all-MiniLM-L6-v2, ONNX, CPU) - EmbeddingService: WordPiece-Tokenizer + Mean Pooling (384 Dim) - VectorSearchService: SQLite-basierte Vektor-Speicherung + Cosine Similarity - EmbeddingBackgroundService: Backfill bestehender Messages + Live-Queue - Neues Tool memory_search: Semantische Suche über alle Sessions (inkl. archivierte) - ~9000 Messages embedded, Suche über komplette Chat-History
This commit is contained in:
1 parent
baf53b7dac
commit
add111ffe1
9 files changed
+727
-15
No files matched your search
+47
-3
@@ -33,7 +33,7 @@ public class ToolService
|
||||
private static readonly HashSet<string> ReadOnlyToolNames = new HashSet<string>
|
||||
{
|
||||
"read_file", "list_files", "glob", "grep", "execute", "todo_write",
|
||||
"memory_read", "memory_write", "memory_list",
|
||||
"memory_read", "memory_write", "memory_list", "memory_search",
|
||||
"remote_execute", AskUserChoiceToolName
|
||||
};
|
||||
|
||||
@@ -54,7 +54,7 @@ public class ToolService
|
||||
return filtered.ToArray();
|
||||
}
|
||||
|
||||
public object[] GetToolDefinitions(bool includeFileTools = false, bool includeMemory = false, bool includeUserChoice = true, bool includeRemote = false)
|
||||
public object[] GetToolDefinitions(bool includeFileTools = false, bool includeMemory = false, bool includeUserChoice = true, bool includeRemote = false, bool includeMemorySearch = false)
|
||||
{
|
||||
var allTools = new List<object>();
|
||||
if (includeUserChoice)
|
||||
@@ -63,6 +63,8 @@ public class ToolService
|
||||
allTools.AddRange(GetFileTools());
|
||||
if (includeMemory)
|
||||
allTools.AddRange(GetMemoryToolDefinitions());
|
||||
if (includeMemorySearch)
|
||||
allTools.Add(GetMemorySearchTool());
|
||||
if (includeRemote)
|
||||
allTools.Add(GetRemoteExecuteTool());
|
||||
return allTools.ToArray();
|
||||
@@ -264,6 +266,25 @@ public class ToolService
|
||||
};
|
||||
}
|
||||
|
||||
private object GetMemorySearchTool()
|
||||
{
|
||||
return new
|
||||
{
|
||||
name = "memory_search",
|
||||
description = "Semantische Suche über alle vergangenen Chat-Nachrichten (inklusive archivierter Sessions). Findet relevante Informationen aus früheren Gesprächen anhand von Bedeutung, nicht exakten Wörtern. Nutze dies wenn du Kontext aus vergangenen Interaktionen brauchst.",
|
||||
input_schema = new
|
||||
{
|
||||
type = "object",
|
||||
properties = new Dictionary<string, object>
|
||||
{
|
||||
["query"] = new { type = "string", description = "Suchanfrage – beschreibe wonach du suchst (z.B. 'Druckerproblem letzte Woche', 'Server-Migration Konfiguration')" },
|
||||
["top_n"] = new { type = "integer", description = "Anzahl der Ergebnisse (Default: 10, Max: 30)" }
|
||||
},
|
||||
required = new[] { "query" }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private object GetRemoteExecuteTool()
|
||||
{
|
||||
return new
|
||||
@@ -333,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)
|
||||
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)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
@@ -617,6 +638,29 @@ public class ToolService
|
||||
var files = memoryService.ListMemoryFiles(agentId, subPath);
|
||||
return files.Count == 0 ? "(keine Memory-Dateien vorhanden)" : string.Join("\n", files);
|
||||
}
|
||||
case "memory_search":
|
||||
{
|
||||
if (vectorSearch == null)
|
||||
return "Fehler: Semantische Suche nicht verfügbar";
|
||||
var query = input.GetProperty("query").GetString() ?? "";
|
||||
var topN = input.TryGetProperty("top_n", out var tn) ? tn.GetInt32() : 10;
|
||||
if (topN > 30) topN = 30;
|
||||
if (topN < 1) topN = 1;
|
||||
var results = await vectorSearch.SearchAsync(query, agentId, topN);
|
||||
if (results.Count == 0)
|
||||
return "Keine relevanten Ergebnisse gefunden.";
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"Gefunden: {results.Count} relevante Einträge\n");
|
||||
for (int i = 0; i < results.Count; i++)
|
||||
{
|
||||
var r = results[i];
|
||||
var date = DateTime.TryParse(r.CreatedAt, out var dt) ? dt.ToString("yyyy-MM-dd HH:mm") : r.CreatedAt;
|
||||
sb.AppendLine($"[{i + 1}] Score: {r.Score:F2} | {r.Role} | {date} | Session: {r.SessionId}");
|
||||
sb.AppendLine(r.TextPreview);
|
||||
sb.AppendLine();
|
||||
}
|
||||
return sb.ToString().TrimEnd();
|
||||
}
|
||||
case "remote_execute":
|
||||
{
|
||||
if (remoteMgr == null || string.IsNullOrEmpty(sessionId))
|
||||
|
||||
Reference in new issue
Block a user