feat: 集成MCP统一外部接口管理系统
All checks were successful
构建并部署 AI Agent 服务 / deploy (push) Successful in 5m38s

- 添加MCP Manager统一入口管理
- 实现Contact/Dictionary/News三个适配器
- 三层降级策略:MCP -> Database -> Mock
- 保持原有api_client向后兼容
- 添加完整文档和测试
This commit is contained in:
2026-05-03 12:36:12 +08:00
parent 3e9462a693
commit 9c53f58165
15 changed files with 1540 additions and 519 deletions

View File

@@ -1,72 +1,61 @@
"""
资讯子图API调用工具
News Analysis API Client
支持 async 和真实数据库缓存
资讯子图API调用工具使用MCP统一接口
"""
from typing import Dict, Any, Optional, List
import random
from datetime import datetime
from dataclasses import dataclass
from ...mcp.mcp_manager import mcp_manager
from ...mcp.adapters import NewsAdapter
@dataclass
class NewsAPIClient:
"""
资讯API客户端 - 可扩展支持多种API和数据库缓存
资讯API客户端 - 使用MCP统一接口
保持向后兼容内部使用MCP适配器
"""
# 可以配置多个API如 NewsAPI, 今日头条, 百度新闻等)
# 保留配置字段用于向后兼容
newsapi_key: Optional[str] = None
# 数据库 Repository可选用于缓存新闻
news_repository: Optional[Any] = None
async def query_news_db(self, user_id: str, keyword: str) -> Optional[List[Dict[str, Any]]]:
"""从数据库缓存查询新闻"""
if not self.news_repository:
return None
def __post_init__(self):
"""初始化后设置MCP"""
import asyncio
try:
entities = await self.news_repository.search_by_keywords(user_id, keyword)
if entities:
return [
{
"title": e.title,
"source": e.source,
"summary": e.content,
"keywords": e.keywords.split(",") if e.keywords else [],
"author": "",
"published_at": e.created_at
}
for e in entities
]
except Exception as e:
print(f"从数据库查询新闻失败:{e}")
return None
asyncio.create_task(self._init_mcp())
except RuntimeError:
pass
async def cache_news_db(self, user_id: str, news: Dict[str, Any]):
"""把新闻缓存到数据库"""
if not self.news_repository:
return
try:
from ...db.models import NewsEntity
entity = NewsEntity(
user_id=user_id,
title=news.get("title", ""),
content=news.get("summary", ""),
url=news.get("url", ""),
source=news.get("source", ""),
keywords=",".join(news.get("keywords", []))
async def _init_mcp(self):
"""初始化MCP系统"""
if not mcp_manager.get_adapter("news"):
mcp_manager.register_adapter(
NewsAdapter(news_repo=self.news_repository)
)
await self.news_repository.insert(entity)
except Exception as e:
print(f"缓存新闻到数据库失败:{e}")
await mcp_manager.initialize()
async def query_news(
self,
user_id: str = "default",
query: str = "",
use_cache: bool = True
) -> List[Dict[str, Any]]:
"""查询新闻(统一入口)"""
await self._init_mcp()
result = await mcp_manager.execute(
"news", "query_news",
user_id=user_id, query=query, use_cache=use_cache
)
if result.success:
return result.data
return self.query_news_mock(query)
def query_news_mock(self, query: str) -> List[Dict[str, Any]]:
"""
模拟查询资讯 - 目前用于演示
"""
# 模拟资讯数据库
"""模拟查询(保留用于向后兼容)"""
mock_news = [
{
"title": "OpenAI发布GPT-5智能再升级",
@@ -83,74 +72,44 @@ class NewsAPIClient:
"keywords": ["医疗", "大模型", "应用"],
"author": "Medical Team",
"published_at": datetime.now().isoformat()
},
{
"title": "2026年AI行业发展趋势报告",
"source": "Business Daily",
"summary": "最新行业报告显示AI行业将继续保持高速增长企业数字化转型加速...",
"keywords": ["趋势", "AI", "商业"],
"author": "Business Team",
"published_at": datetime.now().isoformat()
}
]
# 根据查询词简单过滤
results = []
query_lower = query.lower()
for news in mock_news:
if (query_lower in news["title"].lower() or
query_lower in news["summary"].lower() or
query_lower in news["summary"].lower() or
any(keyword.lower() in query_lower for keyword in news["keywords"])):
results.append(news)
# 如果没有匹配到,返回前两条
if not results:
results = mock_news[:2]
return results
return results if results else mock_news[:2]
def analyze_url_mock(self, url: str) -> Dict[str, Any]:
"""
模拟URL分析 - 目前用于演示
"""
"""模拟URL分析保留用于向后兼容"""
return {
"title": f"分析结果:{url}",
"source": "URL Analyzer",
"summary": "已完成对该URL的内容分析包含文章摘要和情感倾向判断...",
"keywords": ["News", "Analysis", url.split("/")[-1] if url else "unknown"]
"keywords": ["News", "Analysis"]
}
def extract_keywords_mock(self, text: str) -> List[str]:
"""
模拟关键词提取 - 目前用于演示
"""
# 简单的关键词提取模拟
common_keywords = ["AI", "大模型", "应用场景", "行业趋势", "创新", "技术"]
result = []
for keyword in common_keywords:
if keyword.lower() in text.lower():
result.append(keyword)
# 如果没找到,返回默认关键词
if not result:
result = ["AI", "大模型", "应用场景", "行业趋势"]
return result
"""模拟关键词提取(保留用于向后兼容)"""
keywords = ["AI", "大模型", "应用场景", "行业趋势", "创新", "技术"]
result = [k for k in keywords if k.lower() in text.lower()]
return result if result else keywords[:4]
def generate_report_mock(self, query: str) -> str:
"""
模拟报告生成 - 目前用于演示
"""
report = f"""═══════════════════════════════════════════
"""模拟报告生成(保留用于向后兼容)"""
return f"""═══════════════════════════════════════════
📊 资讯分析报告
═══════════════════════════════════════════
主题:{query}
📋 摘要:
这是一份关于 {query} 的资讯分析综合报告,包含最新行业动态和趋势分析
这是关于 {query} 的资讯分析综合报告。
🔍 主要发现:
1. AI技术持续快速发展
@@ -161,36 +120,10 @@ class NewsAPIClient:
- AI
- 大模型
- 数字化转型
- 创新
═══════════════════════════════════════════
💡 建议:继续关注行业动态,把握发展机遇!
"""
return report
# ========== 统一入口(优先查缓存) ==========
async def query_news(self, user_id: str = "default", query: str = "", use_cache: bool = True) -> List[Dict[str, Any]]:
"""查询新闻(统一入口,优先查数据库缓存)"""
# 1. 先查数据库缓存
if use_cache:
cached = await self.query_news_db(user_id, query)
if cached:
return cached
# 2. 查第三方 API暂未实现
# api_result = await self.query_news_api(query)
# if api_result:
# for news in api_result:
# await self.cache_news_db(user_id, news)
# return api_result
# 3. 用模拟数据(兜底)
mock_result = self.query_news_mock(query)
if use_cache:
for news in mock_result:
await self.cache_news_db(user_id, news)
return mock_result
# 单例实例(模拟模式,保持向后兼容)
# 全局单例(保持向后兼容)
news_api = NewsAPIClient()