+20
-25
@@ -55,7 +55,8 @@ func (e *Engine) synthesizeKnowledgeArticle(ctx context.Context, trigger string,
|
||||
// Previously accepted full-text evidence is already learned knowledge. It must
|
||||
// influence the create/update/merge decision, otherwise a later cycle could
|
||||
// skip before it ever sees the external facts it learned in an earlier cycle.
|
||||
planningResearch := uniqueResearchEvidence(append(filterUsableResearchEvidence(initialResearch), e.researchEvidenceForSources(sources)...))
|
||||
initialResearch = e.filterResearchEvidenceForThinking(filterUsableResearchEvidence(initialResearch), categoriesFromArticleSources(sources))
|
||||
planningResearch := uniqueResearchEvidence(append(initialResearch, e.researchEvidenceForSources(sources)...))
|
||||
e.Broker.Publish(model.Activity{Type: "article.plan.started", Source: "brain", Phase: "knowledge-planning", NodeIDs: nodeIDsFromArticleSources(sources), Message: fmt.Sprintf("%d Quellen werden auf einen echten Wissensmehrwert geprüft", len(sources)), Strength: .84, Metadata: map[string]any{"trigger": trigger, "productive_sources": productionCount, "ai_sources": aiCount, "production_ratio": productionRatio, "generation_depth": generationDepth, "model": e.Cfg.ChatModel, "learned_research_sources": len(planningResearch)}})
|
||||
|
||||
var plan model.ArticlePlanDecision
|
||||
@@ -85,7 +86,7 @@ func (e *Engine) synthesizeKnowledgeArticle(ctx context.Context, trigger string,
|
||||
return articleSynthesisOutcome{Skipped: true, Reason: "equivalent_staging_draft", Action: plan.Action}, nil
|
||||
}
|
||||
|
||||
newResearchResults := filterUsableResearchEvidence(initialResearch)
|
||||
newResearchResults := initialResearch
|
||||
if len(newResearchResults) > 0 {
|
||||
e.addResearchToSources(selected, newResearchResults)
|
||||
e.learnResearchEvidence(ctx, newResearchResults)
|
||||
@@ -184,13 +185,13 @@ func (e *Engine) selectArticleSources(seeds []model.Node) []articleSource {
|
||||
direct[edge.Source] += math.Max(.2, math.Max(edge.Confidence, edge.Weight))
|
||||
}
|
||||
}
|
||||
filters := e.thinkingCategories()
|
||||
filter := e.effectiveThinkingFilter()
|
||||
var production, ai []articleSource
|
||||
for _, node := range snapshot.Nodes {
|
||||
if node.Kind != "knowledge" && node.Kind != "ai-think" {
|
||||
continue
|
||||
}
|
||||
if !matchesCategories(node, filters) {
|
||||
if !filter.Matches(node) {
|
||||
continue
|
||||
}
|
||||
depth := nodeGenerationDepth(node)
|
||||
@@ -1093,7 +1094,7 @@ func (e *Engine) addRuntimeArticleNode(articleID string, sources []articleSource
|
||||
ID: nodeID, Kind: "ai-think", Label: draft.Title, Summary: clamp(strings.TrimSpace(draft.Text)+"\n\n"+formatArticleAnswer(draft), 1400),
|
||||
Status: "staging", Origin: "knowledge-staging", ExternalID: articleID, URI: "brain://article/" + articleID,
|
||||
Categories: unique(append([]string{"AI-THINK", "AI-Staging", "AI-Synthesis"}, draft.Categories...)), Keywords: unique(draft.Keywords), Weight: 1.45,
|
||||
Metadata: map[string]any{"subtype": "knowledge_synthesis", "action": plan.Action, "target_node_id": plan.TargetArticleID, "generation_depth": generationDepth, "confidence": draft.Confidence, "source_node_ids": nodeIDsFromArticleSources(sources), "productive_source_count": productionCount, "ai_source_count": aiCount, "production_ratio": productionRatio}, UpdatedAt: now,
|
||||
Metadata: map[string]any{"subtype": "knowledge_synthesis", "action": plan.Action, "target_node_id": plan.TargetArticleID, "generation_depth": generationDepth, "confidence": draft.Confidence, "source_node_ids": nodeIDsFromArticleSources(sources), "productive_source_count": productionCount, "ai_source_count": aiCount, "production_ratio": productionRatio, "source": "Neural Brain / " + e.Cfg.ChatModel + " (Knowledge Synthesis)"}, UpdatedAt: now,
|
||||
}
|
||||
e.Graph.UpsertNode(node)
|
||||
for _, source := range sources {
|
||||
@@ -1116,7 +1117,7 @@ func (e *Engine) learnRuntimeArticle(ctx context.Context, articleID string) {
|
||||
}
|
||||
nodeID := graph.ID("knowledge", articleID)
|
||||
node, ok := e.Graph.GetNode(nodeID)
|
||||
if !ok || !matchesCategories(node, e.learningCategories()) {
|
||||
if !ok || !e.effectiveLearningFilter().Matches(node) {
|
||||
return
|
||||
}
|
||||
text := embeddingText(node)
|
||||
@@ -1208,6 +1209,18 @@ func researchResultNode(id string, result model.ResearchResult, evidencePath, co
|
||||
return model.Node{ID: id, Kind: "external", Label: result.Title, Summary: clamp(content, 1800), Status: "research", Origin: "research", ExternalID: result.URL, URI: result.URL, Categories: unique(categories), Weight: weight, Metadata: metadata, UpdatedAt: time.Now().UTC()}
|
||||
}
|
||||
|
||||
func (e *Engine) filterResearchEvidenceForThinking(results []model.ResearchResult, categories []string) []model.ResearchResult {
|
||||
filter := e.effectiveThinkingFilter()
|
||||
out := make([]model.ResearchResult, 0, len(results))
|
||||
for _, result := range results {
|
||||
node := model.Node{Kind: "external", Origin: "research", URI: result.URL, ExternalID: result.URL, Categories: categories}
|
||||
if filter.Matches(node) {
|
||||
out = append(out, result)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func formatArticleAnswer(draft model.KnowledgeArticleDraft) string {
|
||||
var b strings.Builder
|
||||
b.WriteString(strings.TrimSpace(draft.Answer))
|
||||
@@ -1432,25 +1445,7 @@ func categoryAffinity(node model.Node, seeds []model.Node) float64 {
|
||||
}
|
||||
|
||||
func matchesCategories(node model.Node, filters []string) bool {
|
||||
if len(filters) == 0 {
|
||||
return true
|
||||
}
|
||||
wanted := map[string]bool{}
|
||||
for _, filter := range filters {
|
||||
wanted[strings.ToLower(strings.TrimSpace(filter))] = true
|
||||
}
|
||||
if wanted["*"] {
|
||||
return true
|
||||
}
|
||||
if len(node.Categories) == 0 {
|
||||
return wanted["__uncategorized__"]
|
||||
}
|
||||
for _, category := range node.Categories {
|
||||
if wanted[strings.ToLower(strings.TrimSpace(category))] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return (graph.NodeFilter{Categories: filters}).Matches(node)
|
||||
}
|
||||
|
||||
func metadataString(metadata map[string]any, key string) string {
|
||||
|
||||
Reference in New Issue
Block a user