Spensara Spensara Knowledge Base & Documentation Repository

Repository Docs & Knowledge Base

Consolidated Spensara documentation repository (722 Markdown & architecture documents in ../repository)
Built Word Documents (.docx): Spensara Overview (.docx) Capability Assessment (.docx) Brink POS Ingestion Analysis (.docx) The Borg Ingestion Architecture (.docx)

Implementation Specs (157)

Detailed engineering and feature implementation specifications.
implementation-specs/specs/AnomalyDetection/TASK2.1.md 6,161 bytes · Updated Oct 23, 2026 10:44 AM EDT
Raw File

TASK 2.1: Create AnomalyDetectorAgent Interface and Result Types

Summary

Define the interface for the anomaly detector agent and its result types. The agent is generic - it receives a MonitorSpec (with plain text instructions) and MCP tools (for database access), then uses LLM reasoning to detect anomalies.

Dependencies

  • TASK 1.1 (MonitorSpec entity)

Design Principles

  • Generic: The agent knows nothing about specific databases, tables, or KPIs. All domain knowledge comes from the MonitorSpec's plain text prompt.
  • MCP-driven: Database access is through MCP tools passed to the agent. The agent (LLM) formulates its own queries.
  • No typed models: No prep-time DTOs, no query builders, no domain-specific code. The LLM interprets raw query results.

Files to Create

1. backend/Kds.Ai.AnomalyDetector/Agents/IAnomalyDetectorAgent.cs

csharp
using Kds.Ai.Data.Entities;

namespace Kds.Ai.AnomalyDetector.Agents;

/// <summary>
/// Interface for the anomaly detection agent.
/// The agent receives a MonitorSpec describing what to monitor (in plain text)
/// and uses MCP tools to access data and detect anomalies.
/// </summary>
public interface IAnomalyDetectorAgent
{
    /// <summary>
    /// Run anomaly detection based on the provided monitor specification.
    /// The agent will use the MonitorSpec's SystemPrompt as instructions and
    /// formulate its own data queries via MCP tools.
    /// </summary>
    /// <param name="spec">The monitor specification with plain text instructions</param>
    /// <param name="analysisTime">The point in time to analyze (defaults to now)</param>
    /// <param name="ct">Cancellation token</param>
    /// <returns>Detection result with findings and optional alert recommendations</returns>
    Task<AnomalyDetectionResult> RunDetectionAsync(
        MonitorSpec spec,
        DateTimeOffset analysisTime,
        CancellationToken ct = default);
}

2. backend/Kds.Ai.AnomalyDetector/Agents/AnomalyDetectionResult.cs

csharp
using Kds.Ai.Data.Enums;

namespace Kds.Ai.AnomalyDetector.Agents;

/// <summary>
/// Result of an anomaly detection run.
/// Contains the structured output from the agent's analysis.
/// </summary>
public sealed record AnomalyDetectionResult
{
    /// <summary>
    /// Whether any anomaly was detected
    /// </summary>
    public required bool AnomalyDetected { get; init; }

    /// <summary>
    /// Recommended severity if anomaly detected
    /// </summary>
    public EventSeverity? Severity { get; init; }

    /// <summary>
    /// Brief summary of findings (for alert summary field)
    /// </summary>
    public string? Summary { get; init; }

    /// <summary>
    /// Detailed analysis in Markdown format (for alert content field)
    /// </summary>
    public string? DetailedAnalysis { get; init; }

    /// <summary>
    /// Individual anomaly findings
    /// </summary>
    public IReadOnlyList<AnomalyFinding> Findings { get; init; } = [];

    /// <summary>
    /// Full conversation history / reasoning trace (for debugging)
    /// </summary>
    public string? Reasoning { get; init; }

    /// <summary>
    /// Token usage during the detection run
    /// </summary>
    public int? TotalTokens { get; init; }

    /// <summary>
    /// Any errors or warnings encountered during detection
    /// </summary>
    public IReadOnlyList<string> Warnings { get; init; } = [];

    /// <summary>
    /// Create a result indicating no anomaly was found
    /// </summary>
    public static AnomalyDetectionResult NoAnomaly(string? reasoning = null) => new()
    {
        AnomalyDetected = false,
        Reasoning = reasoning
    };

    /// <summary>
    /// Create a result indicating detection failed
    /// </summary>
    public static AnomalyDetectionResult Failed(string error) => new()
    {
        AnomalyDetected = false,
        Warnings = [error]
    };
}

/// <summary>
/// A specific anomaly finding reported by the agent
/// </summary>
public sealed record AnomalyFinding
{
    /// <summary>
    /// Human-readable description of the anomaly
    /// </summary>
    public required string Description { get; init; }

    /// <summary>
    /// Severity of this specific finding
    /// </summary>
    public EventSeverity Severity { get; init; }

    /// <summary>
    /// The time period affected (as described by the agent)
    /// </summary>
    public string? AffectedPeriod { get; init; }

    /// <summary>
    /// Additional context or evidence as free-form text
    /// </summary>
    public string? Evidence { get; init; }
}

Design Decisions

No Factory Pattern

There is one generic agent, not multiple typed agents. The MonitorSpec's plain text prompt determines what the agent monitors. No MonitorType discriminator, no factory.

No Domain-Specific DTOs

The agent gets raw data from MCP tool calls (e.g., mysql_query returns result sets). The LLM interprets the data. No PrepTimeHourlyStats, OrderTypeBreakdown, etc.

Simplified Result

  • AnomalyFinding is intentionally simple - just description, severity, period, and evidence as text
  • No ZScore, DeviationPercent, Confidence etc. - these are for the LLM to reason about in its analysis, not for typed fields
  • The DetailedAnalysis Markdown field is where the agent puts its rich analysis

emit_conclusion Tool

The emit_conclusion tool (defined in TASK 2.2) is the only non-MCP tool we define. It gives the agent a structured way to report findings that map to AnomalyDetectionResult.

Acceptance Criteria

  • [ ] IAnomalyDetectorAgent interface created
  • [ ] AnomalyDetectionResult record created with all properties
  • [ ] AnomalyFinding record created
  • [ ] Static factory methods NoAnomaly() and Failed() work correctly
  • [ ] No domain-specific (prep-time, KDSDev) types or references
  • [ ] Solution builds without errors

Notes

  • The agent is generic - all domain knowledge comes from MonitorSpec.SystemPrompt
  • Database access is via MCP tools, not code-level services
  • The LLM decides what SQL to run, how to analyze results, and what constitutes an anomaly