fix: stop summarizer from bailing to a canned empty brief #17

Merged
sirius merged 1 commit from fix/summarizer-empty-brief into master 2026-08-28 23:19:14 -04:00
3 changed files with 34 additions and 15 deletions

View file

@ -262,8 +262,9 @@ No Hermes-4 reasoning system prompt. Reduce uses `json_mode=True`.
## Prompts
Both prompts are env-overridable. Defaults focus on **breaking important news**
and explicitly ignore futures, commodity tape, and routine market moves.
Both prompts are env-overridable. Defaults recap the articles actually
provided, ranked by breaking important news, and ignore futures / commodity
tape. ticker/map may be empty; `summary_en` must still be a real brief.
`RECAP_PROMPT` (23:00, 24h window) is the daily recap; `SUMMARY_PROMPT` is the
15-min analyst. `INCLUDE_FUTURES` is ignored.

View file

@ -129,48 +129,46 @@ Articles in this batch:
"""
SUMMARY_PROMPT_DEFAULT = """\
CRITICAL INSTRUCTION - REPEAT 3 TIMES: YOU MUST USE ONLY THE DATA PROVIDED BELOW. DO NOT INVENT, RECALL, OR ADD ANY EVENTS, NAMES, DATES, IMPLICATIONS, PROJECTS, OR DETAILS NOT EXPLICITLY PRESENT IN THE DATA. IF THE DATA HAS NO BREAKING IMPORTANT NEWS, set summary_en to exactly: "No qualifying breaking news in the recent news data." and use empty ticker and map_items arrays. AND STOP. NO EXTERNAL KNOWLEDGE FROM TRAINING.
You are writing an English operator HUD brief from the article facts in DATA below. Use ONLY that data. Do not invent events, names, dates, places, or implications.
All text in English.
Always write a real summary_en that recaps the most important stories present in DATA. Rank geopolitics, military/conflict, security, disasters, and major political developments first. Ignore futures prices, commodity tape, ticker chatter, and routine market data do not treat price ticks as news.
Focus on breaking important news (geopolitical, military/conflict, security, disasters, major political developments). Ignore futures, commodity prices, and routine market data do not treat price ticks as news.
ticker and map_items may be empty if nothing is critical or high. Never replace summary_en with a canned empty-brief sentence when DATA contains article facts.
Demand a single JSON object (no markdown fences) with this exact shape:
{
"summary_en": "English markdown brief or the no-qualifying-events sentence",
"summary_en": "English markdown brief of the provided stories",
"ticker": [{"headline": "", "importance": "critical", "url": "", "location_name": ""}],
"map_items": [{"headline": "", "importance": "critical", "location_name": "", "lat": 0, "lon": 0, "location_confidence": "city", "category": "military/conflict", "url": ""}]
}
ticker: only critical and high, max 12, 140 chars, no markdown.
map_items: only critical and high where a real-world location is explicit in the data. Estimate lat/lon. If location is Unknown or not in the data, omit the item. Never invent a place. Max 20.
summary_en: English markdown brief of breaking important news for an operator HUD.
summary_en: English markdown brief of breaking important news for an operator HUD (bullets or short paragraphs). Cover the actual stories in DATA.
DATA:
{final_input}
"""
RECAP_PROMPT_DEFAULT = """\
You are writing a daily recap of the last 24 hours of news for an OSINT operator HUD.
You are writing a daily recap of the last 24 hours of news for an OSINT operator HUD, using ONLY the article facts in DATA below. Do not invent events, names, dates, places, or implications.
CRITICAL INSTRUCTION: YOU MUST USE ONLY THE DATA PROVIDED BELOW. DO NOT INVENT, RECALL, OR ADD ANY EVENTS, NAMES, DATES, IMPLICATIONS, PROJECTS, OR DETAILS NOT EXPLICITLY PRESENT IN THE DATA. IF THE DATA HAS NO BREAKING IMPORTANT NEWS, set summary_en to exactly: "No qualifying breaking news in the last 24 hours." and use empty ticker and map_items arrays. AND STOP. NO EXTERNAL KNOWLEDGE FROM TRAINING.
Always write a real summary_en daily recap of the most important stories in DATA. Rank geopolitics, military/conflict, security, disasters, and major political developments first. Ignore futures prices, commodity tape, ticker chatter, and routine market data do not treat price ticks as news.
All text in English.
Focus on breaking important news from the last 24 hours (geopolitical, military/conflict, security, disasters, major political developments). Ignore futures, commodity prices, and routine market data do not treat price ticks as news.
ticker and map_items may be empty if nothing is critical or high. Never replace summary_en with a canned empty-brief sentence when DATA contains article facts.
Demand a single JSON object (no markdown fences) with this exact shape:
{
"summary_en": "English markdown daily recap or the no-qualifying-events sentence",
"summary_en": "English markdown daily recap of the provided stories",
"ticker": [{"headline": "", "importance": "critical", "url": "", "location_name": ""}],
"map_items": [{"headline": "", "importance": "critical", "location_name": "", "lat": 0, "lon": 0, "location_confidence": "city", "category": "military/conflict", "url": ""}]
}
ticker: only critical and high, max 12, 140 chars, no markdown.
map_items: only critical and high where a real-world location is explicit in the data. Estimate lat/lon. If location is Unknown or not in the data, omit the item. Never invent a place. Max 20.
summary_en: English markdown daily recap of the last 24 hours of breaking important news.
summary_en: English markdown daily recap of the last 24 hours of breaking important news. Cover the actual stories in DATA.
DATA:
{final_input}

View file

@ -22,7 +22,27 @@ def test_summary_prompt_focuses_on_breaking_news_not_futures():
assert "commodity" in p or "market" in p
def test_recap_prompt_is_daily_24h_breaking_news():
def test_summary_prompt_does_not_bail_out_with_canned_empty_brief():
p = SUMMARY_PROMPT_DEFAULT
assert "AND STOP" not in p
assert "REPEAT 3 TIMES" not in p
assert "No qualifying" not in p
assert "no-qualifying" not in p.lower()
low = p.lower()
assert "always" in low
assert "recap" in low or "summar" in low
def test_recap_prompt_does_not_bail_out_with_canned_empty_brief():
p = RECAP_PROMPT_DEFAULT
assert "AND STOP" not in p
assert "No qualifying" not in p
assert "no-qualifying" not in p.lower()
low = p.lower()
assert "always" in low
assert "daily" in low
assert "24" in low
p = RECAP_PROMPT_DEFAULT.lower()
_assert_breaking_not_futures(RECAP_PROMPT_DEFAULT)
assert "daily" in p