From Generator to Classifier

This was the single largest architectural rebuild in the project. The original Signal Collector called an LLM to invent 2–4 new signals per run, instructed to continue whatever trend the client's history already showed. This created two compounding failures: circular scoring (the same model wrote the sentiment labels it later graded itself on) and a doom loop (a declining client's fiction could never reverse, since each run manufactured more decline).

Agent 1 now does zero generation. It reads real events from sentinel_raw_events where processing_status = 'ready' — already summarized by Agent 0 — and makes one classification call per event.


What It Actually Does

  1. Fetches up to 20 ready raw events for the client, oldest first
  2. For each, calls Groq (llama-3.1-8b-instant) with the event's clean summary and any verbatim key quotes
  3. Classifies: signal_type, sentiment, is_concerning, a plain-English content summary, and a rationale citing the evidence
  4. Writes the result to sentinel_signals — the same table structure every downstream agent already consumed
  5. Marks the source raw event processed, linking it to the signal it produced

Grounding Rules Enforced in the Prompt


Error Handling

If classification fails — a Groq error or malformed JSON — the event is marked failed with the error recorded, and the pipeline stops before Health Scoring runs. This was a deliberate fix: the original design let a failed collection step flow silently into a scorer working against empty or corrupted context, producing a garbage score with no visible failure. Now a failed Agent 1 run means no score is written at all — the dashboard shows nothing changed, not something wrong.