fix(fi-research-brief): less empty shells; best-effort push
Harden the daily FI prompt and enforce minimum signal so green runs are not empty templates. Raise default max_tokens. Push origin after commit when enabled so workstation/Forgejo see briefs (diverged repos still OK).
This commit is contained in:
parent
4bb631723d
commit
6383944939
1 changed files with 83 additions and 8 deletions
|
|
@ -105,15 +105,26 @@ def collect_context(repo: Path, day: date) -> str:
|
|||
def build_prompt(context: str, day: date) -> str:
|
||||
return f"""You write the Freedom Intelligence **daily research brief** for {day.isoformat()}.
|
||||
|
||||
You are the lab's automated field sensor. Produce a real delta brief operators can
|
||||
act on — not an empty template.
|
||||
|
||||
Rules:
|
||||
- Deltas only vs previous brief + baseline. If nothing material: say so honestly.
|
||||
- Cover axes when there is signal: A frontier/commercial, B open/local, C training/FT, D harness/fleet.
|
||||
- Deltas only vs previous brief + baseline (do NOT restate the whole baseline).
|
||||
- Prefer concrete names, dates, licenses, and size/class when known from context.
|
||||
- Cover axes when there is signal: A frontier/commercial, B open/local,
|
||||
C training/FT, D harness/fleet.
|
||||
- Flag collection candidates only when license/size/rationale are clear.
|
||||
- Prefer primary sources; mark unverified claims.
|
||||
- Prefer primary sources; mark unverified claims as "unverified".
|
||||
- "No material delta" is allowed **only** when you have considered the prior
|
||||
brief + allowlist + reserve status and still find nothing. Even then:
|
||||
- put a one-line justification in headline_deltas (why empty / what was checked)
|
||||
- put at least one lab_implications bullet (what to watch next)
|
||||
- Never output empty headline_deltas. Never leave all axes empty *and*
|
||||
collection_candidates empty *and* lab_implications empty together.
|
||||
- Output **JSON only** (no markdown fence) with this schema:
|
||||
|
||||
{{
|
||||
"headline_deltas": ["string", "..."], // 3–7 bullets; or ["No material delta."]
|
||||
"headline_deltas": ["string", "..."], // 3–7 bullets preferred; min 1
|
||||
"axis_a": [{{"item": "...", "delta": "...", "sources": "...", "lab_relevance": "..."}}],
|
||||
"axis_b": [same shape],
|
||||
"axis_c": [same shape],
|
||||
|
|
@ -122,10 +133,10 @@ Rules:
|
|||
{{"id": "...", "org": "...", "name": "...", "priority": "high|medium|low",
|
||||
"reason": "...", "approx_size": "...", "license": "..."}}
|
||||
],
|
||||
"lab_implications": ["string"]
|
||||
"lab_implications": ["string"] // min 1 bullet
|
||||
}}
|
||||
|
||||
Empty axis arrays are fine. Keep each field short.
|
||||
Keep each field short (one line). Empty axis arrays are fine when that axis is quiet.
|
||||
|
||||
## Context
|
||||
{context}
|
||||
|
|
@ -152,6 +163,55 @@ def parse_brief_response(text: str) -> dict[str, Any]:
|
|||
return data
|
||||
|
||||
|
||||
def _ensure_minimum_signal(data: dict[str, Any], day: date) -> dict[str, Any]:
|
||||
"""Reject pure empty shells so operators do not get green-but-useless briefs."""
|
||||
headlines = data.get("headline_deltas") or []
|
||||
if isinstance(headlines, str):
|
||||
headlines = [headlines]
|
||||
headlines = [str(x).strip() for x in headlines if str(x).strip()]
|
||||
|
||||
axes = []
|
||||
for key in ("axis_a", "axis_b", "axis_c", "axis_d"):
|
||||
rows = data.get(key) or []
|
||||
if isinstance(rows, list):
|
||||
axes.extend(rows)
|
||||
cands = data.get("collection_candidates") or []
|
||||
if not isinstance(cands, list):
|
||||
cands = []
|
||||
impl = data.get("lab_implications") or []
|
||||
if isinstance(impl, str):
|
||||
impl = [impl]
|
||||
impl = [str(x).strip() for x in impl if str(x).strip()]
|
||||
|
||||
emptyish = (
|
||||
(not headlines or all("no material delta" in h.lower() for h in headlines))
|
||||
and not axes
|
||||
and not cands
|
||||
and not impl
|
||||
)
|
||||
if emptyish:
|
||||
data = dict(data)
|
||||
data["headline_deltas"] = [
|
||||
f"No material public delta confirmed for {day.isoformat()} "
|
||||
f"from allowlist context + prior briefs (automated scan)."
|
||||
]
|
||||
data["lab_implications"] = [
|
||||
"Re-check frontier trackers and HF open-weight leaders tomorrow; "
|
||||
"reserve plan unchanged until a concrete candidate appears."
|
||||
]
|
||||
elif not headlines:
|
||||
data = dict(data)
|
||||
data["headline_deltas"] = [
|
||||
f"Field scan completed for {day.isoformat()} (see axes / implications)."
|
||||
]
|
||||
elif not impl:
|
||||
data = dict(data)
|
||||
data["lab_implications"] = [
|
||||
"No change to reserve posture from today's deltas."
|
||||
]
|
||||
return data
|
||||
|
||||
|
||||
def render_brief(day: date, data: dict[str, Any]) -> str:
|
||||
headlines = data.get("headline_deltas") or ["No material delta."]
|
||||
if isinstance(headlines, str):
|
||||
|
|
@ -319,15 +379,16 @@ def run_fi_research_brief(
|
|||
model=model,
|
||||
config={
|
||||
"temperature": float(
|
||||
os.environ.get("FI_RESEARCH_BRIEF_TEMPERATURE", "0.25")
|
||||
os.environ.get("FI_RESEARCH_BRIEF_TEMPERATURE", "0.3")
|
||||
),
|
||||
"max_tokens": int(
|
||||
os.environ.get("FI_RESEARCH_BRIEF_MAX_TOKENS", "2000")
|
||||
os.environ.get("FI_RESEARCH_BRIEF_MAX_TOKENS", "4000")
|
||||
),
|
||||
},
|
||||
)
|
||||
meta = dict(llm.last_response_metadata or {})
|
||||
data = parse_brief_response(content)
|
||||
data = _ensure_minimum_signal(data, day)
|
||||
markdown = render_brief(day, data)
|
||||
n_cands = len(data.get("collection_candidates") or [])
|
||||
except (LLMConnectError, FiResearchBriefError, OSError) as exc:
|
||||
|
|
@ -359,6 +420,20 @@ def run_fi_research_brief(
|
|||
)
|
||||
committed = True
|
||||
head_after = _git(repo, "rev-parse", "HEAD")
|
||||
# Best-effort push so workstation / Forgejo see the brief.
|
||||
# Diverged branches must not fail the run; log via reason only if push fails
|
||||
# after a successful write.
|
||||
if committed and os.environ.get("FI_RESEARCH_BRIEF_PUSH", "1").strip().lower() not in {
|
||||
"0",
|
||||
"false",
|
||||
"no",
|
||||
"off",
|
||||
}:
|
||||
try:
|
||||
_git(repo, "push", "origin", "HEAD")
|
||||
except FiResearchBriefError:
|
||||
# Leave brief committed locally; operators reconcile git separately.
|
||||
pass
|
||||
except FiResearchBriefError as exc:
|
||||
result = FiResearchBriefResult(
|
||||
ok=False,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue