From fd8cc4a32e3f4f1b24addde686b6f771d02ff1ae Mon Sep 17 00:00:00 2001 From: rcourtman Date: Wed, 10 Dec 2025 23:03:01 +0000 Subject: [PATCH] feat(ai): Add per-resource notes to patrol context Knowledge store notes are now included in the patrol LLM prompt. When users save notes about resources (e.g., 'This VM intentionally runs hot'), the patrol AI will see these notes and avoid flagging documented behavior as issues. Changes: - Added knowledge store reference to PatrolService - Added SetKnowledgeStore() method to configure the store - Enhanced buildPatrolPrompt() to include knowledge context - Connected knowledge store to patrol in service.go SetStateProvider() This complements the dismissed findings context to give the LLM a complete picture of what the user considers normal/expected behavior. --- internal/ai/patrol.go | 39 ++++++++++++++++++++++++++++++++++++--- internal/ai/service.go | 4 ++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/internal/ai/patrol.go b/internal/ai/patrol.go index 7cd5109..1ff0af6 100644 --- a/internal/ai/patrol.go +++ b/internal/ai/patrol.go @@ -10,6 +10,7 @@ import ( "sync" "time" + "github.com/rcourtman/pulse-go-rewrite/internal/ai/knowledge" "github.com/rcourtman/pulse-go-rewrite/internal/models" "github.com/rs/zerolog/log" ) @@ -206,6 +207,7 @@ type PatrolService struct { thresholdProvider ThresholdProvider config PatrolConfig findings *FindingsStore + knowledgeStore *knowledge.Store // For per-resource notes in patrol context // Cached thresholds (recalculated when thresholdProvider changes) thresholds PatrolThresholds @@ -320,6 +322,13 @@ func (p *PatrolService) SetRunHistoryPersistence(persistence PatrolHistoryPersis return nil } +// SetKnowledgeStore sets the knowledge store for including per-resource notes in patrol context +func (p *PatrolService) SetKnowledgeStore(store *knowledge.Store) { + p.mu.Lock() + defer p.mu.Unlock() + p.knowledgeStore = store +} + // GetConfig returns the current patrol configuration func (p *PatrolService) GetConfig() PatrolConfig { p.mu.RLock() @@ -1632,6 +1641,15 @@ func (p *PatrolService) buildPatrolPrompt(summary string, deep bool) string { // Get user feedback context (dismissed/snoozed findings) feedbackContext := p.findings.GetDismissedForContext() + // Get resource notes from knowledge store (per-resource user notes) + var knowledgeContext string + p.mu.RLock() + knowledgeStore := p.knowledgeStore + p.mu.RUnlock() + if knowledgeStore != nil { + knowledgeContext = knowledgeStore.FormatAllForContext() + } + basePrompt := fmt.Sprintf(`Please perform a comprehensive analysis of the following infrastructure and identify any issues, potential problems, or optimization opportunities. %s @@ -1645,16 +1663,31 @@ Analyze the above and report any findings using the structured format. Focus on: If everything looks healthy, say so briefly.`, summary) - // If there's user feedback, append instructions + var contextAdditions strings.Builder + + // Append knowledge context (user notes about resources) + if knowledgeContext != "" { + contextAdditions.WriteString("\n\n") + contextAdditions.WriteString(knowledgeContext) + contextAdditions.WriteString("\nIMPORTANT: Consider the user's saved notes above when analyzing. If a user has noted that a resource behaves a certain way (e.g., 'runs hot for transcoding'), do not flag it as an issue.\n") + } + + // Append user feedback context (dismissed/snoozed findings) if feedbackContext != "" { - return basePrompt + "\n\n" + feedbackContext + ` + contextAdditions.WriteString("\n\n") + contextAdditions.WriteString(feedbackContext) + contextAdditions.WriteString(` IMPORTANT: Respect the user's feedback above. Do NOT re-raise findings that are: - Permanently suppressed - the user has explicitly said to never mention these again - Dismissed as "not_an_issue" or "expected_behavior" - the user knows about these - Currently snoozed - only re-raise if the severity has significantly worsened -Only report NEW issues or issues where the severity has clearly escalated.` +Only report NEW issues or issues where the severity has clearly escalated.`) + } + + if contextAdditions.Len() > 0 { + return basePrompt + contextAdditions.String() } return basePrompt diff --git a/internal/ai/service.go b/internal/ai/service.go index 4c8510b..2865829 100644 --- a/internal/ai/service.go +++ b/internal/ai/service.go @@ -75,6 +75,10 @@ func (s *Service) SetStateProvider(sp StateProvider) { // Initialize patrol service if not already done if s.patrolService == nil && sp != nil { s.patrolService = NewPatrolService(s, sp) + // Connect knowledge store to patrol for per-resource notes in context + if s.knowledgeStore != nil { + s.patrolService.SetKnowledgeStore(s.knowledgeStore) + } } // Initialize alert-triggered analyzer if not already done