read.markets/app/jobs
Giorgio Gilestro 45fa31bb2b ai: structured-output + reviewer agent for indicator summaries
Replaces the regex-based clean_summary / looks_like_leakage pipeline
that produced the 2026-05-29 valuation-read leak. Two layers of defence
in depth:

1. JSON-mode generation. The per-group and aggregate summary system
   prompts now require the model to emit a single object
   {"read": "..."}; response_format={"type":"json_object"} is passed
   through to the provider so the API enforces well-formed JSON. Prose
   outside the field is physically impossible. The "read" field is the
   only schema slot, so the model has nowhere to spill scratchpad
   into the envelope.

2. Reviewer agent. services/output_review.review_read() makes a second
   small LLM call that judges whether the candidate "read" string is
   publishable. It catches the residual failure mode — scratchpad
   INSIDE the field ("Let's see…", multi-question parentheticals,
   meta-commentary) — and returns a JSON verdict {"clean": bool,
   "reason": str}. Any failure (provider error, parse error, missing
   field) returns clean=false (fail-safe). Cost ~$0.0001/check; latency
   ~1-2 s in the hourly job, no user-facing latency.

The old regex scaffolding (_LEAK_PATTERNS, clean_summary,
looks_like_leakage, _TRAILING_QUOTE) is deleted entirely. It produced
false positives (chopped legitimate "The indicators are…" leaders) and
false negatives (never matched the chain-of-thought patterns the model
actually emits). The reviewer agent is strictly better on both.

On reviewer/parse rejection: don't persist a new IndicatorSummary; the
API's existing fallback to the previous good row continues to serve
the panel. Failures are logged as ind_summary.json_invalid /
ind_summary.reviewer_rejected so we can measure the rejection rate.

Reviewer cost is added to the row's recorded cost_usd so the monthly
budget cap covers the full pipeline.

Adds tests/test_output_review.py: 11 cases covering _extract_read
(JSON envelope handling — invalid JSON, missing field, wrong types,
empty values) and review_read (clean / unclean verdicts plus three
fail-safe paths for malformed reviewer responses).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 13:10:52 +02:00
..
__init__.py initial commit — cassandra v0.1 2026-05-15 21:56:10 +01:00
_helpers.py test+fix: make the suite run cleanly in the test container 2026-05-26 00:11:18 +02:00
_market_context.py backend: dedupe shared logic (indicator_summary_job, CHAT_REFERENCE_LINE, call_openrouter alias) 2026-05-27 19:30:11 +02:00
ai_log_job.py jobs: per-row savepoint + aggregate logging in translation fan-out 2026-05-28 12:37:06 +02:00
email_digest_job.py email: split digest renderer to digest_email.py 2026-05-27 21:33:06 +02:00
indicator_summary_job.py ai: structured-output + reviewer agent for indicator summaries 2026-05-29 13:10:52 +02:00
market_job.py phase G: data minimisation + passwordless auth + DeepSeek-first LLM 2026-05-18 14:16:57 +01:00
news_job.py news: auto-tag headlines + market-aware cadence + filter UI 2026-05-21 23:25:03 +01:00
rollup_job.py initial commit — cassandra v0.1 2026-05-15 21:56:10 +01:00
universe_flush_job.py phase G: data minimisation + passwordless auth + DeepSeek-first LLM 2026-05-18 14:16:57 +01:00