This commit is contained in:
2026-08-07 17:05:03 +02:00
parent 246241fcda
commit f8bf8c944e
1172 changed files with 38091 additions and 924 deletions
+67 -43
View File
@@ -54,45 +54,49 @@ type Engine struct {
GLPIKB *ingest.GLPIKBSyncer
Persistence *persist.Coordinator
mu sync.Mutex
stateMu sync.RWMutex
lastScan time.Time
lastEnrich time.Time
lastAttempt time.Time
nextEnrich time.Time
ollamaOK bool
enrichRunning bool
enrichTrigger string
enrichResult string
enrichError string
enrichCycles uint64
enrichCreated uint64
enrichRejected uint64
relationsCreated uint64
articlesCreated uint64
articlesSkipped uint64
enrichRequests chan string
runtimeMu sync.RWMutex
runtime RuntimeSettings
runtimePath string
researchEvidenceMu sync.RWMutex
researchEvidenceCache map[string]researchEvidenceRecord
sharedWork *workqueue.Limiter
researchDedupeMu sync.Mutex
researchDedupe map[string]*researchDedupeEntry
interactiveInflight atomic.Int64
autonomousWake chan struct{}
autonomousScanRequests chan string
autonomousRunning bool
autonomousTaskID string
autonomousTaskTopic string
autonomousLastStarted time.Time
autonomousLastCompleted time.Time
autonomousLastError string
autonomousCompleted uint64
autonomousFailed uint64
autonomousEvidence uint64
autonomousArticles uint64
mu sync.Mutex
stateMu sync.RWMutex
lastScan time.Time
lastEnrich time.Time
lastAttempt time.Time
nextEnrich time.Time
ollamaOK bool
enrichRunning bool
enrichTrigger string
enrichResult string
enrichError string
enrichCycles uint64
enrichCreated uint64
enrichRejected uint64
relationsCreated uint64
articlesCreated uint64
articlesSkipped uint64
enrichRequests chan string
runtimeMu sync.RWMutex
runtime RuntimeSettings
runtimePath string
researchEvidenceMu sync.RWMutex
researchEvidenceCache map[string]researchEvidenceRecord
sharedWork *workqueue.Limiter
researchDedupeMu sync.Mutex
researchDedupe map[string]*researchDedupeEntry
researchDedupeGuardFiltered uint64
researchDedupeGuardPrimaryMismatch uint64
researchDedupeGuardFocusMismatch uint64
researchDedupeGuardEntityMismatch uint64
interactiveInflight atomic.Int64
autonomousWake chan struct{}
autonomousScanRequests chan string
autonomousRunning bool
autonomousTaskID string
autonomousTaskTopic string
autonomousLastStarted time.Time
autonomousLastCompleted time.Time
autonomousLastError string
autonomousCompleted uint64
autonomousFailed uint64
autonomousEvidence uint64
autonomousArticles uint64
}
func New(cfg config.Config, g *graph.Store, b *activity.Broker) *Engine {
@@ -176,6 +180,12 @@ func New(cfg config.Config, g *graph.Store, b *activity.Broker) *Engine {
if strings.TrimSpace(cfg.ArticleLanguage) == "" {
cfg.ArticleLanguage = "de-DE"
}
if strings.TrimSpace(cfg.ArticleSynthesisModel) == "" {
cfg.ArticleSynthesisModel = cfg.ChatModel
}
if strings.TrimSpace(cfg.ArticleReviewModel) == "" {
cfg.ArticleReviewModel = cfg.ChatModel
}
if cfg.ResearchDedupeThreshold <= 0 {
cfg.ResearchDedupeThreshold = .92
}
@@ -261,7 +271,7 @@ func New(cfg config.Config, g *graph.Store, b *activity.Broker) *Engine {
e.GLPIKB = ingest.NewGLPIKBSyncer(ingest.GLPIKBConfig{Enabled: true, Path: cfg.GLPIKBPath, Filter: cfg.GLPIKBFilter, Limit: cfg.GLPIKBLimit, SyncInterval: cfg.GLPIKBSyncInterval, Source: cfg.GLPIKBSource, CachePath: filepath.Join(cfg.DataDir, "glpi-kb-cache.json"), ShouldSync: e.LearningEnabled}, client, g, b, persistence)
}
if b != nil {
b.Publish(model.Activity{Type: "system.started", Source: "brain", Phase: "startup", Message: "Neural Brain wurde gestartet; das Analyseprotokoll zeichnet Läufe und Graphänderungen auf", Strength: .3, Metadata: map[string]any{"chat_model": cfg.ChatModel, "embedding_model": cfg.EmbeddingModel, "article_language": cfg.ArticleLanguage, "research_ollama_max_inflight": cfg.ResearchOllamaMaxInflight, "research_ollama_queue_size": cfg.ResearchOllamaQueueSize, "graph_version": g.Version()}})
b.Publish(model.Activity{Type: "system.started", Source: "brain", Phase: "startup", Message: "Neural Brain wurde gestartet; das Analyseprotokoll zeichnet Läufe und Graphänderungen auf", Strength: .3, Metadata: map[string]any{"chat_model": cfg.ChatModel, "embedding_model": cfg.EmbeddingModel, "article_language": cfg.ArticleLanguage, "article_synthesis_model": cfg.ArticleSynthesisModel, "article_review_model": cfg.ArticleReviewModel, "article_review_repair_rounds": cfg.ArticleReviewRepairRounds, "article_pipeline": "research_generate_review", "research_ollama_max_inflight": cfg.ResearchOllamaMaxInflight, "research_ollama_queue_size": cfg.ResearchOllamaQueueSize, "graph_version": g.Version()}})
}
return e
}
@@ -811,7 +821,11 @@ func (e *Engine) enrichOne(ctx context.Context, trigger string) (EnrichOutcome,
researchErr = dedupeErr
} else if !lease.owner {
results = cloneResearchResults(reused)
e.Broker.Publish(model.Activity{Type: "research.deduplicated", Source: "brain", Phase: "research", NodeIDs: []string{a.ID, b.ID}, Message: fmt.Sprintf("Semantisch gleiche Relationsrecherche wurde wiederverwendet · %d Treffer", len(results)), Strength: .76, Metadata: mergeResearchMetadata(startMetadata, map[string]any{"similarity": lease.similarity, "reused_results": len(results), "dedupe_threshold": e.Cfg.ResearchDedupeThreshold})})
metadata := mergeResearchMetadata(startMetadata, map[string]any{"similarity": lease.similarity, "reused_results": len(results), "dedupe_threshold": e.Cfg.ResearchDedupeThreshold})
for key, value := range researchDedupeLeaseMetadata(lease) {
metadata[key] = value
}
e.Broker.Publish(model.Activity{Type: "research.deduplicated", Source: "brain", Phase: "research", NodeIDs: []string{a.ID, b.ID}, Message: fmt.Sprintf("Semantisch gleiche Relationsrecherche wurde wiederverwendet · %d Treffer", len(results)), Strength: .76, Metadata: metadata})
} else {
// Relation research intentionally considers more than the old four
// snippets. The full article pipeline remains the final quality gate.
@@ -860,6 +874,16 @@ func (e *Engine) enrichOne(ctx context.Context, trigger string) (EnrichOutcome,
decision = reviewed
}
}
// Always close the relation-research lifecycle explicitly. Older code
// only emitted research.ingested when at least one source passed the
// Thinking filter, leaving zero-result and filtered-out searches stuck
// as "running" forever in the analysis history.
completedMetadata := mergeResearchMetadata(resultMetadata, map[string]any{
"duration_ms": time.Since(researchStarted).Milliseconds(),
"accepted_count": len(allowedResults),
"result": "completed",
})
e.Broker.Publish(model.Activity{Type: "research.completed", Source: "brain", Phase: "research", NodeIDs: []string{a.ID, b.ID}, Message: fmt.Sprintf("Relationsrecherche beendet · %d verwendbare Treffer", len(allowedResults)), Strength: .72, Metadata: completedMetadata})
}
}
@@ -929,12 +953,12 @@ func (e *Engine) Status() map[string]any {
"article_research_min_relevance": e.Cfg.ArticleResearchMinRelevance, "article_research_min_quality": e.Cfg.ArticleResearchMinQuality,
"article_research_page_max_bytes": e.Cfg.ArticleResearchPageMaxBytes, "article_research_page_max_chars": e.Cfg.ArticleResearchPageMaxChars,
"article_research_fetch_timeout": e.Cfg.ArticleResearchFetchTimeout.String(), "article_research_allow_private": e.Cfg.ArticleResearchAllowPrivate,
"article_language": e.Cfg.ArticleLanguage, "research_dedupe": e.researchDedupeStatus(),
"article_language": e.Cfg.ArticleLanguage, "article_synthesis_model": e.Cfg.ArticleSynthesisModel, "article_review_model": e.Cfg.ArticleReviewModel, "article_review_repair_rounds": e.Cfg.ArticleReviewRepairRounds, "article_pipeline": "research_generate_review", "research_dedupe": e.researchDedupeStatus(),
"enrich_interval": e.Cfg.EnrichInterval.String(),
"enrich_batch_size": e.Cfg.EnrichBatchSize, "enrich_anchors": e.Cfg.EnrichAnchors,
"research_enabled": e.ResearchEnabledForRuntime(), "chat_model": e.Cfg.ChatModel, "embedding_model": e.Cfg.EmbeddingModel,
"searxng": e.ResearchStatus(),
"ollama_pool": e.Ollama.PoolStatus(), "persistence": e.Persistence.Status(), "graph_storage": e.Graph.StorageStatus(),
"ollama_pool": e.Ollama.PoolStatus(), "article_model_status": map[string]any{"synthesis": e.Ollama.ModelStatus(e.Cfg.ArticleSynthesisModel), "review": e.Ollama.ModelStatus(e.Cfg.ArticleReviewModel)}, "persistence": e.Persistence.Status(), "graph_storage": e.Graph.StorageStatus(),
"runtime_settings": e.RuntimeSettingsView(),
}
if e.GLPIKB != nil {