feat: 代码选定叙事轴并注入 Agent 开场约束

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-14 10:40:14 +08:00
parent 54f164cdbf
commit dcd0608b5d
4 changed files with 194 additions and 9 deletions

103
daily/narrative_axis.py Normal file
View File

@@ -0,0 +1,103 @@
"""叙事轴硬互斥:代码选定轴,注入 Agent Step1 并强制覆写。"""
from __future__ import annotations
import json
import logging
import random
from datetime import datetime, timedelta
from typing import Any
from daily.config import OUTPUT_DIR, narrative_axis_days
logger = logging.getLogger(__name__)
NARRATIVE_AXES: tuple[str, ...] = (
"政策监管",
"模型发布",
"工具链/Agent",
"芯片算力",
"开源生态",
"应用落地",
"安全/诉讼",
)
def pick_narrative_axis(
used: set[str],
*,
rng: random.Random | None = None,
) -> str:
"""从固定轴枚举中排除已用轴后随机选取;全用尽则回退全表。"""
available = [a for a in NARRATIVE_AXES if a not in used]
pool = available or list(NARRATIVE_AXES)
picker = rng or random.Random()
return picker.choice(pool)
def load_recent_axes(date_str: str, days: int | None = None) -> list[str]:
"""近 N 日 data.narrative_axis不含当日按时间从近到远"""
lookback = days if days is not None else narrative_axis_days()
try:
dt = datetime.strptime(date_str, "%Y-%m-%d")
except ValueError:
return []
axes: list[str] = []
for day_offset in range(1, lookback + 1):
prev = (dt - timedelta(days=day_offset)).strftime("%Y-%m-%d")
path = OUTPUT_DIR / f"{prev}.data.json"
if not path.exists():
continue
try:
payload = json.loads(path.read_text(encoding="utf-8"))
except (OSError, ValueError) as exc:
logger.warning("读取 narrative_axis %s 失败:%s", path, exc)
continue
data = payload.get("data") if isinstance(payload, dict) else None
if not isinstance(data, dict):
continue
axis = str(data.get("narrative_axis") or "").strip()
if axis:
axes.append(axis)
return axes
def load_recent_theme_summaries(date_str: str, days: int) -> list[str]:
"""近 N 日 theme/opening 摘要,供 Step1 软禁参考。"""
try:
dt = datetime.strptime(date_str, "%Y-%m-%d")
except ValueError:
return []
summaries: list[str] = []
for day_offset in range(1, days + 1):
prev = (dt - timedelta(days=day_offset)).strftime("%Y-%m-%d")
path = OUTPUT_DIR / f"{prev}.data.json"
if not path.exists():
continue
try:
payload = json.loads(path.read_text(encoding="utf-8"))
except (OSError, ValueError):
continue
data = payload.get("data") if isinstance(payload, dict) else None
if not isinstance(data, dict):
continue
theme = str(data.get("theme") or data.get("editorial_theme") or "").strip()
opening = ""
trends = data.get("trends") if isinstance(data.get("trends"), dict) else {}
if isinstance(trends, dict):
opening = str(trends.get("opening") or "").strip()
if not theme:
themes = trends.get("themes") or []
if themes and isinstance(themes[0], dict):
theme = str(themes[0].get("title") or "").strip()
bit = " · ".join(x for x in (prev, theme, opening[:40]) if x)
if bit:
summaries.append(bit)
return summaries
def enforce_narrative_axis(trends: dict[str, Any], axis: str) -> dict[str, Any]:
"""强制 trends['narrative_axis'] = axis。"""
out = dict(trends)
out["narrative_axis"] = axis
return out