"""单 agent 聚合路径: 拼接全部切片(build_context)。 共享 session 贯穿所有切片(见 context_builder 模块 docstring 的性能说明)。 """ from __future__ import annotations from src.db.base import AsyncSessionLocal from src.llm.slices.common import MatchContext, header_text, load_match_header from src.llm.slices.form import form_slice from src.llm.slices.h2h import h2h_slice from src.llm.slices.home_away import home_away_slice from src.llm.slices.standings import standings_slice from src.llm.slices.stats import stats_slice async def build_context(match_id: int, *, form_last: int = 5, h2h_last: int = 5, backtest: bool = False, cutoff_at=None) -> MatchContext: """单 agent 路径的完整上下文: 拼接全部切片(before=cutoff,防未来信息)。 has_stats / has_standings 直接取切片显式声明的 has_data, 不再靠文案子串匹配(见审查报告 P2-1)。 P2-6: backtest=True 时 cutoff = match_date - 1天,确保只用赛前数据。 cutoff_at: 显式截止时间(优先于 backtest 自动计算)。 P1-1: 使用单个共享 session 贯穿所有切片查询,避免连接池耗尽。 """ async with AsyncSessionLocal() as db: header = await load_match_header(match_id, db=db) # 计算数据截止时间: 显式 > backtest 自动计算 > 默认(比赛时间) if cutoff_at is not None: cutoff = cutoff_at elif backtest and header.match_dt: from datetime import timedelta cutoff = header.match_dt - timedelta(days=1) else: cutoff = header.match_dt parts = [header_text(header), ""] form_res = await form_slice(header, limit=form_last, before=cutoff, db=db) parts.append(form_res.text) parts.append("") h2h_res = await h2h_slice(header, limit=h2h_last, before=cutoff, db=db) parts.append(h2h_res.text) parts.append("") stats_res = await stats_slice(header, before=cutoff, db=db) parts.append(stats_res.text) parts.append("") home_away_res = await home_away_slice(header, before=cutoff, db=db) parts.append(home_away_res.text) parts.append("") standings_res = await standings_slice(header, before=cutoff, db=db) parts.append(standings_res.text) return MatchContext( match_id=match_id, text="\n".join(parts), has_stats=form_res.has_data or stats_res.has_data, has_standings=standings_res.has_data, match_dt=header.match_dt, cutoff=cutoff, )