TradingAgents-CN AKShare 新闻模块导入错误修复实战:Provider 层架构规范与 MongoDB limit 类型防御
【免费下载链接】TradingAgents-CN基于多智能体LLM的中文金融交易框架 - TradingAgents中文增强版项目地址: https://gitcode.com/GitHub_Trending/tr/TradingAgents-CN
导读
本文档完整复盘 TradingAgents-CN 中文金融交易框架中一次典型的"数据接口架构违规"故障:新闻获取模块因直接导入不存在的akshare_utils模块导致ModuleNotFoundError,同时 MongoDB 查询因浮点数参数触发limit must be an integer类型错误。文章以真实修复记录为骨架,结合 AKShareProvider 的源码实现,讲解"所有数据接口必须统一收敛到providers/目录、通过 Provider 层访问"的架构规范,并给出可直接复用的同步/异步新闻获取 API、字段映射方案与测试验证步骤。读完本文,你将掌握该框架新闻链路的正确接入方式、两类典型错误的根因与修复模式,以及一套可推广的 Provider 封装思路。
一、问题全景:一次分析流程中的两类连环报错
在股票分析流程(例如对600519发起分析)中,新闻获取模块曾同时出现两类错误:
ModuleNotFoundError: No module named 'tradingagents.dataflows.news.akshare_utils' TypeError: limit must be an integer, not <class 'float'>对应运行日志的表现为:
❌ [新闻分析] 东方财富新闻获取失败: No module named 'tradingagents.dataflows.news.akshare_utils' ❌ [统一新闻工具] 从数据库获取新闻失败: limit must be an integer, not <class 'float'> ⚠️ [统一新闻工具] 数据库中没有 600519 的新闻,尝试其他新闻源...第一类错误说明代码试图从tradingagents/dataflows/news/目录导入并不存在的akshare_utils.py模块;第二类错误则发生在 unified_news_tool.py 的 MongoDB 查询链路——max_news参数可能由配置或 LLM 以浮点数形式传入,而 MongoDB 的limit()方法严格要求整数。
两个错误叠加的直接后果是:A 股 / 港股的东方财富新闻彻底不可用,数据库缓存新闻也无法命中,新闻模块整体降级为"无新闻可用"。
二、架构规范:为什么必须通过 Provider 层访问数据接口
本次故障表面上是"文件不存在",本质是架构规范的违反。TradingAgents-CN 对数据访问有明确约束:
- ✅所有数据接口必须统一在
tradingagents/dataflows/providers/目录管理; - ✅数据源访问必须经过 Provider 层(如
AKShareProvider、TushareProvider),禁止业务模块直接import akshare; - ❌禁止在其他模块(news、agents、tools)中直接引入具体数据源 SDK。
目录结构上的约束如下:
tradingagents/ ├── dataflows/ │ ├── providers/ # ✅ 数据接口统一管理层 │ │ ├── china/ │ │ │ ├── akshare.py # AKShare 数据提供器 │ │ │ ├── tushare.py # Tushare 数据提供器 │ │ │ └── baostock.py # Baostock 数据提供器 │ │ ├── us/ │ │ └── ... │ └── news/ # 新闻聚合层 │ └── realtime_news.py # 通过 Provider 访问数据 ├── agents/ │ └── utils/ │ └── agent_utils.py # 通过 Provider 访问数据 └── tools/ └── unified_news_tool.py该规范的价值在源码中有直接体现:AKShareProvider 继承自 BaseStockDataProvider,后者定义了get_stock_basic_info、get_stock_quotes、get_historical_data等抽象接口以及is_available()可用性检查(base_provider.py)。业务层只面向 Provider 的稳定接口编程,底层数据源更换、接口升级、反爬策略调整都被隔离在 Provider 内部——这正是"统一管理、易于维护、可测试性、可扩展性"四重收益的来源。
三、根因分析:两处错误的具体位置
3.1 AKShare 导入错误
错误代码:
from .akshare_utils import get_stock_news_em根本原因:
tradingagents/dataflows/news/目录下不存在akshare_utils.py文件;- 代码试图导入一个从不存在的模块,且该导入绕过了 Provider 层。
影响范围(修复前共 4 处):
- realtime_news.py:3 处错误导入(A 股东方财富新闻、中文财经新闻、港股新闻三条分支);
- agent_utils.py:1 处错误导入(统一新闻工具分支)。
3.2 MongoDB limit 参数类型错误
错误代码:
cursor = collection.find(query).sort('publish_time', -1).limit(max_news)根本原因:
max_news可能以浮点数(如10.0)从配置或 LLM 调用链传入;- PyMongo 的
Cursor.limit()要求整数参数,浮点数直接触发TypeError。
影响范围:unified_news_tool.py 中从 MongoDBstock_news集合读取缓存新闻的分支。
四、解决方案与源码级实现
4.1 修复 1:在 AKShareProvider 中新增同步方法get_stock_news_sync
文件:tradingagents/dataflows/providers/china/akshare.py
新增同步版新闻获取方法,返回原始 DataFrame,供非异步上下文(如agent_utils.py中的同步工具函数)直接调用:
def get_stock_news_sync(self, symbol: str = None, limit: int = 10) -> Optional[pd.DataFrame]: """ 获取股票新闻(同步版本,返回原始 DataFrame) Args: symbol: 股票代码,为None时获取市场新闻 limit: 返回数量限制 Returns: 新闻 DataFrame 或 None """ if not self.is_available(): return None try: import akshare as ak if symbol: # 获取个股新闻 self.logger.debug(f"📰 获取AKShare个股新闻: {symbol}") # 标准化股票代码 symbol_6 = symbol.zfill(6) # 获取东方财富个股新闻 news_df = ak.stock_news_em(symbol=symbol_6) if news_df is not None and not news_df.empty: self.logger.info(f"✅ {symbol} AKShare新闻获取成功: {len(news_df)} 条") return news_df.head(limit) if limit else news_df else: self.logger.warning(f"⚠️ {symbol} 未获取到AKShare新闻数据") return None else: # 获取市场新闻 self.logger.debug("📰 获取AKShare市场新闻") news_df = ak.news_cctv() if news_df is not None and not news_df.empty: self.logger.info(f"✅ AKShare市场新闻获取成功: {len(news_df)} 条") return news_df.head(limit) if limit else news_df else: self.logger.warning("⚠️ 未获取到AKShare市场新闻数据") return None except Exception as e: self.logger.error(f"❌ AKShare新闻获取失败: {e}") return None从当前仓库源码看,该方法已进一步演化出重试与反爬防御机制:个股新闻调用ak.stock_news_em(symbol=symbol_6)时最多重试 3 次,重试间隔按指数退避(1s → 2s → 4s)增长,并专门捕获json.JSONDecodeError与KeyError('cmsArticleWebOld')(后者代表东方财富接口字段变更或反爬拦截,建议确认 AKShare 版本 ≥ 1.17.86)。此外,AKShareProvider.__init__阶段的_initialize_akshare()(akshare.py)会检测curl_cffi是否可用,并对eastmoney.com域名的requests.get做补丁包装:自动附加必要 headers、对东方财富请求施加至少 0.5 秒的请求间隔,从源头降低被反爬封禁的概率。这些细节是"Provider 内部隔离底层复杂性"的典型体现。
4.2 修复 2:更正 realtime_news.py 中的 3 处导入
文件:tradingagents/dataflows/news/realtime_news.py
修改位置 1(A 股东方财富新闻,原第 739-744 行):
# 修改前 try: logger.info(f"[新闻分析] 尝试导入 akshare_utils.get_stock_news_em") from .akshare_utils import get_stock_news_em logger.info(f"[新闻分析] 成功导入 get_stock_news_em 函数") # 修改后 try: logger.info(f"[新闻分析] 尝试通过 AKShare Provider 获取新闻") from tradingagents.dataflows.providers.china.akshare import AKShareProvider provider = AKShareProvider() logger.info(f"[新闻分析] 成功创建 AKShare Provider 实例")修改位置 2(调用东方财富 API,原第 751-756 行):
# 修改前 news_df = get_stock_news_em(clean_ticker, max_news=10) # 修改后 news_df = provider.get_stock_news_sync(symbol=clean_ticker, limit=10)修改位置 3(中文财经新闻,原第 312-331 行):
# 修改前 try: logger.info(f"[中文财经新闻] 尝试导入 AKShare 工具") from .akshare_utils import get_stock_news_em # ... news_df = get_stock_news_em(clean_ticker) # 修改后 try: logger.info(f"[中文财经新闻] 尝试通过 AKShare Provider 获取新闻") from tradingagents.dataflows.providers.china.akshare import AKShareProvider provider = AKShareProvider() # ... news_df = provider.get_stock_news_sync(symbol=clean_ticker)修改位置 4(港股新闻,原第 863-873 行):
# 修改前 try: from .akshare_utils import get_stock_news_em # ... news_df = get_stock_news_em(clean_ticker, max_news=10) # 修改后 try: from tradingagents.dataflows.providers.china.akshare import AKShareProvider provider = AKShareProvider() # ... news_df = provider.get_stock_news_sync(symbol=clean_ticker, limit=10)当前仓库代码已确认上述 3 处全部改为AKShareProvider方式:第 315/743/864 行完成导入并实例化,第 331/758/873 行统一调用provider.get_stock_news_sync(symbol=clean_ticker, limit=10)。
4.3 修复 3:更正 agent_utils.py 中的导入与字段映射
文件:tradingagents/agents/utils/agent_utils.py
修改前:
# 导入AKShare新闻获取函数 from tradingagents.dataflows.akshare_utils import get_stock_news_em # 获取东方财富新闻 news_df = get_stock_news_em(clean_ticker) if not news_df.empty: # 格式化东方财富新闻 em_news_items = [] for _, row in news_df.iterrows(): news_title = row.get('标题', '') news_time = row.get('时间', '') news_url = row.get('链接', '')修改后:
# 通过 AKShare Provider 获取新闻 from tradingagents.dataflows.providers.china.akshare import AKShareProvider provider = AKShareProvider() # 获取东方财富新闻 news_df = provider.get_stock_news_sync(symbol=clean_ticker) if news_df is not None and not news_df.empty: # 格式化东方财富新闻 em_news_items = [] for _, row in news_df.iterrows(): # AKShare 返回的字段名 news_title = row.get('新闻标题', '') or row.get('标题', '') news_time = row.get('发布时间', '') or row.get('时间', '') news_url = row.get('新闻链接', '') or row.get('链接', '')这一处修复同时解决了两个问题:导入路径合规化,以及字段名兼容——AKShare 不同版本 / 不同接口返回的列名存在差异(新闻标题与标题、发布时间与时间、新闻链接与链接),使用or回退链可保证新旧字段都能被正确解析。当前仓库 agent_utils.py 已按此实现。
4.4 修复 4:为 max_news 增加整数强制转换
文件:tradingagents/tools/unified_news_tool.py
修改前:
try: from tradingagents.dataflows.cache.app_adapter import get_mongodb_client from datetime import timedelta client = get_mongodb_client()修改后:
try: from tradingagents.dataflows.cache.app_adapter import get_mongodb_client from datetime import timedelta # 🔧 确保 max_news 是整数(防止传入浮点数) max_news = int(max_news) client = get_mongodb_client()当前仓库代码中max_news = int(max_news)位于第 109 行,紧接其后是 MongoDB 客户端获取与 30 天时间窗查询(datetime.now() - timedelta(days=30)),并在第 138 行执行collection.find(query).sort('publish_time', -1).limit(max_news)。值得注意的是,该函数还实现了多级查询回退:依次尝试symbol、原始stock_code、symbols字段在 30 天窗口内的查询,若均无结果再退化为不限时间的全量查询,保证缓存新闻的最大命中率。
五、修复效果验证
修复前日志
❌ [新闻分析] 东方财富新闻获取失败: No module named 'tradingagents.dataflows.news.akshare_utils' ❌ [统一新闻工具] 从数据库获取新闻失败: limit must be an integer, not <class 'float'> ⚠️ [统一新闻工具] 数据库中没有 600519 的新闻,尝试其他新闻源...修复后预期日志
✅ [新闻分析] 成功导入 akshare 模块 ✅ [新闻分析] 东方财富API调用成功,获取到 10 条新闻 ✅ [统一新闻工具] 从数据库获取新闻成功六、正确使用方式与数据格式规范
6.1 推荐:通过 Provider 访问
from tradingagents.dataflows.providers.china.akshare import AKShareProvider # 创建 Provider 实例 provider = AKShareProvider() # 获取个股新闻(同步版本,返回 DataFrame) news_df = provider.get_stock_news_sync(symbol="600519", limit=10) # 获取个股新闻(异步版本,返回结构化列表) news_list = await provider.get_stock_news(symbol="600519", limit=10)6.2 错误用法(禁止)
# ❌ 不要这样做!违反架构规范 import akshare as ak news_df = ak.stock_news_em(symbol="600519") # ❌ 不要这样做!模块不存在 from tradingagents.dataflows.akshare_utils import get_stock_news_em6.3 返回数据格式
同步版本get_stock_news_sync:
- 返回类型:
pd.DataFrame或None; - 关键字段:
新闻标题(标题)、新闻内容(正文)、发布时间、新闻来源(媒体)、新闻链接(原文 URL); - 兼容旧字段名:
标题、内容、时间、来源、链接。
异步版本get_stock_news(akshare.py):
- 返回类型:
List[Dict]或None; - 结构化字段:
symbol(股票代码)、title(标题)、content(正文)、summary(摘要)、url(链接)、source(来源,默认"东方财富")、author(作者)、publish_time(解析后的发布时间)、category(新闻分类)、sentiment(情绪)、sentiment_score(情绪分数)、keywords(关键词)、importance(重要性)、data_source(固定为"akshare")。
异步版本在字段提取上同样采用or回退链(如row.get('新闻标题', '') or row.get('标题', '')),并通过_parse_news_time支持 8 种时间格式解析(如%Y-%m-%d %H:%M:%S、%m-%d %H:%M等,仅含月日时自动补当年)。symbol=None时获取的是 CCTV 财经市场新闻(ak.news_cctv),source默认标记为"CCTV财经"。
6.4 参数说明
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
symbol | str | None | 股票代码(6 位数字,不带后缀)。A 股示例:"600519"(贵州茅台);港股示例:"00700"(腾讯控股)。为 None 时获取市场新闻 |
limit | int | 10 | 返回数量限制;传 0/None 时返回全部(return news_df.head(limit) if limit else news_df) |
七、测试建议
测试 1:验证 Provider 访问
from tradingagents.dataflows.providers.china.akshare import AKShareProvider # 创建 Provider 实例 provider = AKShareProvider() # 测试获取贵州茅台新闻 news_df = provider.get_stock_news_sync(symbol="600519", limit=10) if news_df is not None: print(f"✅ 获取到 {len(news_df)} 条新闻") print(news_df.head()) else: print("❌ 获取新闻失败")测试 2:验证新闻工具(浮点数防御)
from tradingagents.tools.unified_news_tool import UnifiedNewsAnalyzer # 创建分析器 analyzer = UnifiedNewsAnalyzer(toolkit) # 测试获取新闻(传入浮点数) news = analyzer.get_stock_news_unified("600519", max_news=10.0) print(news)该用例专门覆盖修复 4:max_news=10.0不再触发TypeError,因为函数入口已执行int(max_news)。
测试 3:完整分析流程回归
- 重启后端服务(使修复生效);
- 发起股票分析(如
600519); - 查看日志,确认出现:
✅ [新闻分析] 成功创建 AKShare Provider 实例 ✅ [新闻分析] 东方财富API调用成功 ✅ 600519 AKShare新闻获取成功: 10 条
八、修复总结与影响
| 问题 | 原因 | 解决方案 | 状态 |
|---|---|---|---|
| AKShare 导入错误 | 导入不存在的模块 | 通过AKShareProvider统一访问 | 已修复 |
| MongoDB limit 类型错误 | 传入浮点数参数 | 添加int()类型转换 | 已修复 |
| 架构规范违反 | 直接导入数据接口 | 遵循 Provider 层架构 | 已修复 |
修复文件清单:
- tradingagents/dataflows/providers/china/akshare.py:新增
get_stock_news_sync()同步方法,提供统一数据访问接口; - tradingagents/dataflows/news/realtime_news.py:修复 3 处 AKShare 导入错误,改用
AKShareProvider; - tradingagents/agents/utils/agent_utils.py:修复 1 处导入错误,改用 Provider 并修正字段名映射;
- tradingagents/tools/unified_news_tool.py:增加
max_news类型转换。
业务影响:
- 新闻获取功能恢复正常,A 股、港股新闻均可正常获取;
- MongoDB 缓存新闻查询不再报错;
- 全链路遵循 Provider 层架构规范,数据访问接口统一。
架构收益:统一管理(所有数据接口收敛于providers/)、易于维护(数据源变更只改 Provider)、可测试性(Provider 可独立单测)、可扩展性(新增数据源只需实现新 Provider)。
后续建议:重启后端服务以应用修复;回归测试新闻获取功能;持续监控日志确认修复生效;后续开发一律遵循 Provider 层架构规范,禁止业务模块直接引入akshare等底层 SDK。
九、延伸阅读
- 新闻分析使用指南:新闻分析功能的完整使用指引;
- 新闻分析系统架构:新闻分析系统的整体架构设计;
- 新闻情绪分析文档:新闻情绪分析模块的详细说明。
【免费下载链接】TradingAgents-CN基于多智能体LLM的中文金融交易框架 - TradingAgents中文增强版项目地址: https://gitcode.com/GitHub_Trending/tr/TradingAgents-CN
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考