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:
@@ -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
@@ -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 "-"
|
||||
|
||||
Reference in New Issue
Block a user