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:
Meleki committed 2026-03-11 09:22:11 +01:00
1 parent baf53b7dac
commit add111ffe1
9 files changed
+727 -15

No files matched your search

+15 -8
View File
@@ -22,8 +22,10 @@ public class AnthropicService
private readonly MemoryService _memoryService;
private readonly bool _payloadLogging;
private readonly bool _payloadLoggingGzip;
private readonly VectorSearchService _vectorSearch;
private readonly EmbeddingBackgroundService _embeddingBg;
public AnthropicService(IHttpClientFactory httpFactory, string token, ToolService toolService, DatabaseService db, MemoryService memoryService = null, bool payloadLogging = false, bool payloadLoggingGzip = false)
public AnthropicService(IHttpClientFactory httpFactory, string token, ToolService toolService, DatabaseService db, MemoryService memoryService = null, bool payloadLogging = false, bool payloadLoggingGzip = false, VectorSearchService vectorSearch = null, EmbeddingBackgroundService embeddingBg = null)
{
_httpFactory = httpFactory;
_token = token;
@@ -32,6 +34,8 @@ public class AnthropicService
_memoryService = memoryService;
_payloadLogging = payloadLogging;
_payloadLoggingGzip = payloadLoggingGzip;
_vectorSearch = vectorSearch;
_embeddingBg = embeddingBg;
}
public async Task StreamChat(
@@ -153,12 +157,14 @@ public class AnthropicService
if (blockType == "text")
{
await _db.SaveMessage(new MessageRecord
var textContent = el.GetProperty("text").GetString();
var msgId = await _db.SaveMessage(new MessageRecord
{
ChainId = chainId, Iteration = iterations,
Role = "assistant", Type = "text",
Content = el.GetProperty("text").GetString()
Content = textContent
});
_embeddingBg?.Enqueue(msgId, chainId, sessionId, agentId, "assistant", textContent ?? "");
}
else if (blockType == "tool_use")
{
@@ -832,7 +838,7 @@ public class AnthropicService
string toolResult;
bool isError = false;
try { toolResult = await _toolService.ExecuteTool(toolName, toolInput, agentWorkspace, _memoryService, agentId, workCt); }
try { toolResult = await _toolService.ExecuteTool(toolName, toolInput, agentWorkspace, _memoryService, agentId, workCt, vectorSearch: _vectorSearch); }
catch (OperationCanceledException) { throw; }
catch (Exception ex) { toolResult = $"Fehler: {ex.Message}"; isError = true; }
@@ -1038,7 +1044,7 @@ public class AnthropicService
string toolResult;
bool isError = false;
try { toolResult = await _toolService.ExecuteTool(toolName, toolInput, ctx.Workspace, _memoryService, ctx.AgentId); }
try { toolResult = await _toolService.ExecuteTool(toolName, toolInput, ctx.Workspace, _memoryService, ctx.AgentId, vectorSearch: _vectorSearch); }
catch (OperationCanceledException) { throw; }
catch (Exception ex) { toolResult = $"Fehler: {ex.Message}"; isError = true; }
@@ -1339,7 +1345,7 @@ public class AnthropicService
string toolResult;
bool isError = false;
try { toolResult = await _toolService.ExecuteTool(currentToolName, inputEl, workspace, _memoryService, agentId, workCt, remoteMgr: remoteMgr, sessionId: sessionId); }
try { toolResult = await _toolService.ExecuteTool(currentToolName, inputEl, workspace, _memoryService, agentId, workCt, remoteMgr: remoteMgr, sessionId: sessionId, vectorSearch: _vectorSearch); }
catch (OperationCanceledException) { throw; }
catch (Exception ex) { toolResult = $"Fehler: {ex.Message}"; isError = true; }
@@ -1516,7 +1522,8 @@ public class AnthropicService
{
var hasWorkspace = !string.IsNullOrEmpty(workspace);
var includeMemory = useMemory && _memoryService != null && !string.IsNullOrEmpty(agentId);
var hasTools = hasWorkspace || includeMemory || includeRemote;
var includeMemorySearch = _vectorSearch != null && _vectorSearch.GetEmbeddingCount() > 0;
var hasTools = hasWorkspace || includeMemory || includeRemote || includeMemorySearch;
var hasWebSearch = !string.IsNullOrEmpty(webSearchVersion);
if (!hasTools && !hasWebSearch) return;
@@ -1524,7 +1531,7 @@ public class AnthropicService
var tools = new List<object>();
if (hasTools)
{
var allTools = _toolService.GetToolDefinitions(includeFileTools: hasWorkspace, includeMemory: includeMemory, includeUserChoice: allowUserChoice, includeRemote: includeRemote);
var allTools = _toolService.GetToolDefinitions(includeFileTools: hasWorkspace, includeMemory: includeMemory, includeUserChoice: allowUserChoice, includeRemote: includeRemote, includeMemorySearch: includeMemorySearch);
if (planMode)
allTools = _toolService.FilterReadOnlyTools(allTools);
tools.AddRange(allTools);