diff --git a/app/core/action_engine.py b/app/core/action_engine.py index 1d08078..a995d6a 100644 --- a/app/core/action_engine.py +++ b/app/core/action_engine.py @@ -695,6 +695,8 @@ def eval_open(c: dict, params: dict, caps: dict, room_amt: float): "pricing_state": c.get("pricing_state"), "pricing_text": c.get("pricing_text"), # 相关快讯 (2026-09-08 台账 050): 原值进账本与提议卡, 整句另送研判。只是上下文, 不进判定。 "news": c.get("news"), "news_text": c.get("news_text"), + # 行业催化 (2026-09-08 台账 051): 环节级, 原值进账本与提议卡; 产业逻辑不送研判。 + "industry_catalyst": c.get("industry_catalyst"), "industry_catalyst_text": c.get("industry_catalyst_text"), # ---- 仓位口径: 只进评审账本做判分锚。judge.py 送研判时会把这几项过滤掉, # 理由见那边的 OPEN_JUDGE_KEYS —— 决策系统本来就不管仓位, 别送过去带偏它。 "target_pct": target_pct, "target_amount": round(full_amt, 2), diff --git a/app/services/plan_feed.py b/app/services/plan_feed.py index 6d28e4b..445658c 100644 --- a/app/services/plan_feed.py +++ b/app/services/plan_feed.py @@ -189,6 +189,22 @@ def _news_or_none(v, limit=5): return {"count": count, "latest": _text_or_none(v.get("latest")), "items": items} +def _industry_catalyst_or_none(v, limit=3): + """行业催化 (2026-09-08 台账 051): 上游按环节标出的行业级催化, 只留最多 limit 条固定键。 + 产业逻辑不送研判 (judge.OPEN_JUDGE_KEYS 不收), 只进硬数字给人看与复盘。""" + if not isinstance(v, list) or not v: + return None + out = [] + for it in v: + if not isinstance(it, dict) or not it.get("title"): + continue + out.append({k: _text_or_none(it.get(k)) for k in + ("segment", "event_date", "title", "direction", "magnitude", "horizon", "confidence", "mechanism")}) + if len(out) >= limit: + break + return out or None + + def _events_or_none(v): """催化事件归一 (2026-09-08): 上游给 {latest, count, events:[{date, types, orgs, title, n_reports, compound}]}。 只收约定的键, 事件最多五条; 不是字典或没有事件一律 None。""" @@ -290,6 +306,9 @@ def _rows(raw, bucket: str) -> list: # (judge.OPEN_JUDGE_KEYS 收 news_text)。只是上下文, 不判正负, 不进任何判定。 "news": _news_or_none(it.get("news")), "news_text": _text_or_none(it.get("news_text")), + # 行业催化 (2026-09-08 台账 051): 环节级, 只给人看与复盘, 不送研判、不进判定。 + "industry_catalyst": _industry_catalyst_or_none(it.get("industry_catalyst")), + "industry_catalyst_text": _text_or_none(it.get("industry_catalyst_text")), }) return out @@ -485,7 +504,9 @@ def select_candidates(plan: dict, *, held=(), black=(), top_n: int = 30, tiers=N "valuation_text": r.get("valuation_text"), "events": r.get("events"), "events_text": r.get("events_text"), "pricing_state": r.get("pricing_state"), "pricing_text": r.get("pricing_text"), - "news": r.get("news"), "news_text": r.get("news_text")} + "news": r.get("news"), "news_text": r.get("news_text"), + "industry_catalyst": r.get("industry_catalyst"), + "industry_catalyst_text": r.get("industry_catalyst_text")} for r in passed[:n]] return {"date": plan.get("date"), "considered": len(pool), "eligible": len(passed), "items": items, "dropped": dropped, "st_unknown": st_unknown, diff --git a/app/web/static/index.html b/app/web/static/index.html index 089cd2c..eb0890d 100644 --- a/app/web/static/index.html +++ b/app/web/static/index.html @@ -1035,6 +1035,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}