""" AgentOrchestrator — Agent 编排器。 管理所有 Agent 的生命周期、执行顺序、结果传递。 """ from datetime import datetime from agents.research_agent import ResearchAgent from agents.selection_agent import SelectionAgent from agents.risk_agent import RiskAgent from agents.report_agent import ReportAgent class AgentOrchestrator: """ Agent 编排器。 用法: orch = AgentOrchestrator(dm=dm, fe=engine_fe, bt=engine_bt, ...) orch.setup() # 注册所有 Agent orch.run_daily() # 执行每日流程 """ def __init__(self, **engines): self.engines = engines self.agents: dict = {} self._last_results: dict = {} def setup(self): """注册所有 Agent。""" self.agents["research"] = ResearchAgent(**self.engines) self.agents["selection"] = SelectionAgent(**self.engines) self.agents["risk"] = RiskAgent(**self.engines) self.agents["report"] = ReportAgent(**self.engines) print(f"[Orchestrator] 已注册 {len(self.agents)} 个 Agent: {list(self.agents)}") # ── 每日流程 ────────────────────────────────────────── def run_daily(self, date: str | None = None) -> dict: """ 每日任务流: 1. 同步数据 2. 风险评估 3. 股票打分 4. 生成日报 """ date = date or datetime.now().strftime("%Y%m%d") print(f"\n{'='*60}") print(f"[Orchestrator] 每日流程 — {date}") print(f"{'='*60}") results = {"date": date} # Step 1: 增量同步已缓存股票的最新行情 print("\n[Step 1/4] 同步行情...") dm = self.engines.get("dm") sent = self.engines.get("sent") from database.dao import get_latest_trade_date scope_stocks = [] if sent: scope_stocks = sent.get_scope_stocks() if not scope_stocks and dm: scope_stocks = list(dm.get_stock_list().index[:100]) print(" 范围: {} 只股票".format(len(scope_stocks))) # 分类:已缓存(增量更新),未缓存(统计跳过) cached = [c for c in scope_stocks if get_latest_trade_date(c)] uncached = len(scope_stocks) - len(cached) print(" 已缓存: {} 只 (增量更新), 未缓存: {} 只 (跳过,需首次批量预热)".format(len(cached), uncached)) synced = 0 for i, ts_code in enumerate(cached): try: n = dm.sync_daily(ts_code) synced += n except Exception: continue if (i + 1) % 100 == 0: print(" [同步] 进度: {}/{}".format(i + 1, len(cached))) print(" 同步完成: {} 条新数据 (已缓存{}/全量{})".format(synced, len(cached), len(scope_stocks))) if uncached > 0: print(" 提示: {} 只股票未缓存,运行 'agent_cli.py warmup' 首次批量预热".format(uncached)) # Step 2: 风险评估 print("\n[Step 2/4] 风险评估...") risk = self.agents["risk"].execute() results["risk"] = risk print(f" 风险: {risk['risk_level']}, 仓位: {risk['target_exposure']:.0%}") # Step 3: 选股打分 print("\n[Step 3/4] 股票打分...") selection = self.agents["selection"].execute(date=date, top_n=15) results["selection"] = selection top = selection.get("top_picks", []) if top: print(" Top 5: {}".format(", ".join(p['ts_code'] for p in top[:5]))) # Step 4: 情绪因子 print("\n[Step 4/5] 情绪因子...") sentiment_df = None sent_eng = self.engines.get("sent") if sent_eng: try: # 用范围内第一只有缓存的股票计算情绪因子 ref_code = cached[0] if cached else "000001.SZ" sentiment_df = sent_eng.compute(ref_code, max_news=30) if sentiment_df is not None and not sentiment_df.empty: valid = sentiment_df.dropna(how="all") print(" {}: {} 个因子, {} 个有效交易日".format(ref_code, sentiment_df.shape[1], len(valid))) else: print(" (无有效情绪数据)") except Exception as e: print(" [SKIP] 情绪因子计算失败: {}".format(e)) else: print(" (SentimentEngine 未配置)") results["sentiment"] = sentiment_df # Step 5: 生成日报 print("\n[Step 5/5] 生成日报...") report = self.agents["report"].execute( date=date, selection_result=selection, risk_result=risk, sentiment_result=sentiment_df, ) results["report"] = report print(f" 日报: {report['report_path']}") self._last_results = results print(f"\n{'='*60}") print(f"[Orchestrator] 每日流程完成") print(f"{'='*60}\n") return results # ── 研究流程(每周一次) ──────────────────────────────── def run_research_cycle(self, ts_codes: list[str] | None = None) -> dict: """ 研究周期: 1. 因子发现与评估 2. 更新 IC 权重 """ print(f"\n{'='*60}") print(f"[Orchestrator] 研究周期") print(f"{'='*60}") print("\n[Step 1/2] 因子发现...") research = self.agents["research"].execute(ts_codes=ts_codes) results = {"research": research} top = research.get("top_factors", []) if top: print(f" Top 5 因子:") for f in top[:5]: print(f" {f['name']:20s} IC={f['ic_mean']:+.4f} ICIR={f['icir']:.3f}") return results # ── 便捷方法 ────────────────────────────────────────── def picks(self, date: str | None = None, top_n: int = 15) -> dict: """快速选股。""" return self.agents["selection"].execute(date=date, top_n=top_n) def risk_check(self) -> dict: """快速风险评估。""" return self.agents["risk"].execute() def generate_report(self, date: str | None = None) -> dict: """快速生成日报。""" date = date or datetime.now().strftime("%Y%m%d") # 尝试拉取最新数据(Tushare 优先,几秒即可完成) dm = self.engines.get("dm") if dm: try: dm.sync_daily("000001.SZ") except Exception: pass # 查 DB 最新交易日 data_freshness = None try: from database.dao import get_latest_trade_date data_freshness = get_latest_trade_date("000001.SZ") except Exception: pass sel = self.picks(date) risk = self.risk_check() # 尝试取情绪因子 sentiment_df = None sent_eng = self.engines.get("sent") if sent_eng: try: sentiment_df = sent_eng.compute("000001.SZ", max_news=30) except Exception: pass return self.agents["report"].execute( date=date, selection_result=sel, risk_result=risk, sentiment_result=sentiment_df, data_freshness=data_freshness, ) @property def last_results(self) -> dict: return self._last_results