feat: 日报事件多新闻源入库(news_event.sources 列)

- ExtractedEvent 增加 sources 字段(主源居首去重,旧产物兜底 [source_id])
- extract_event(_async) 增加 sources 参数;run_event_extraction 读取
  deduped uniques 的 sources 透传
- reporter _load_events_from_dir 读取 sources → EventRow.sources
- news_event 建表加 sources TEXT 列;init_schema 幂等迁移旧表(捕获
  Duplicate column);save_report INSERT 写 JSON 多源
- 新增测试:ExtractedEvent/EventRow.sources 兜底与保序、_load_events_from_dir 多源
This commit is contained in:
2026-08-12 12:56:04 +08:00
parent 182f788857
commit 794ce6c853
10 changed files with 129 additions and 7 deletions
+6
View File
@@ -188,10 +188,12 @@ def extract_event(
*,
template: PromptTemplate | None = None,
max_attempts: int | None = None,
sources: list[str] | None = None,
) -> ExtractedEvent:
"""同步抽取单篇文章的事件(带重试)。
max_attempts 为 None 时使用 config.max_attempts(来自 YAML/环境变量配置)。
sources 为该新闻全部来源(来自去重层多源记录);None 时兜底 [article.source_id]。
"""
tpl = template or PromptTemplate()
prompt = tpl.render(article)
@@ -208,6 +210,7 @@ def extract_event(
url_hash=article.url_hash,
title=article.title,
publish_time=article.publish_time,
sources=sources or [article.source_id],
event=event,
provider=config.provider,
model=config.model,
@@ -246,11 +249,13 @@ async def extract_event_async(
*,
template: PromptTemplate | None = None,
max_attempts: int | None = None,
sources: list[str] | None = None,
semaphore: asyncio.Semaphore | None = None,
) -> ExtractedEvent:
"""异步抽取(批处理用),与同步版逻辑等价。
max_attempts 为 None 时使用 config.max_attempts。
sources 为该新闻全部来源;None 时兜底 [article.source_id]。
"""
tpl = template or PromptTemplate()
prompt = tpl.render(article)
@@ -268,6 +273,7 @@ async def extract_event_async(
url_hash=article.url_hash,
title=article.title,
publish_time=article.publish_time,
sources=sources or [article.source_id],
event=event,
provider=config.provider,
model=config.model,
+20 -1
View File
@@ -130,7 +130,12 @@ class EventExtraction(BaseModel):
class ExtractedEvent(BaseModel):
"""落盘格式:文章元数据 + LLM 抽取结果 + 调用元信息。"""
"""落盘格式:文章元数据 + LLM 抽取结果 + 调用元信息。
sources: 该唯一新闻的全部来源(主源 source_id 居首)。
来自去重层多源记录(M3 sources.json / uniques JSON 的 sources 字段),
旧产物无此字段时兜底为 [source_id]。
"""
# ---- 来源标识 ----
source_id: str
@@ -138,6 +143,10 @@ class ExtractedEvent(BaseModel):
url_hash: str
title: str
publish_time: datetime | None = None
sources: list[str] = Field(
default_factory=list,
description="全部来源(主源居首,去重保序);旧产物无字段时兜底为 [source_id]",
)
# ---- 抽取结果 ----
event: EventExtraction
@@ -150,6 +159,16 @@ class ExtractedEvent(BaseModel):
prompt_tokens: int | None = None
completion_tokens: int | None = None
@model_validator(mode="after")
def _ensure_sources(self) -> Self:
"""保证 sources 非空、去重且以主源 source_id 开头。"""
seen: list[str] = []
for s in [self.source_id, *self.sources]:
if s and s not in seen:
seen.append(s)
self.sources = seen
return self
def short_summary(self) -> str:
ev = self.event
codes = ",".join(ev.stock_codes) or "-"