Showing
1 changed file
with
280 additions
and
144 deletions
| 1 | """ | 1 | """ |
| 2 | -专为 AI Agent 设计的舆情搜索工具集 (Tavily) | 2 | +专为 AI Agent 设计的多模态搜索工具集 (Bocha) |
| 3 | 3 | ||
| 4 | -版本: 1.5 | 4 | +版本: 1.1 |
| 5 | 最后更新: 2025-08-22 | 5 | 最后更新: 2025-08-22 |
| 6 | 6 | ||
| 7 | -此脚本将复杂的Tavily搜索功能分解为一系列目标明确、参数极少的独立工具, | ||
| 8 | -专为AI Agent调用而设计。Agent只需根据任务意图选择合适的工具, | ||
| 9 | -无需理解复杂的参数组合。所有工具默认搜索“新闻”(topic='news')。 | 7 | +此脚本将复杂的 Bocha AI Search 功能分解为一系列目标明确、参数极少的独立工具, |
| 8 | +专为 AI Agent 调用而设计。Agent 只需根据任务意图(如常规搜索、查找结构化数据或时效性新闻) | ||
| 9 | +选择合适的工具,无需理解复杂的参数组合。 | ||
| 10 | 10 | ||
| 11 | -新特性: | ||
| 12 | -- 新增 `basic_search_news` 工具,用于执行标准、通用的新闻搜索。 | ||
| 13 | -- 每个搜索结果现在都包含 `published_date` (新闻发布日期)。 | 11 | +核心特性: |
| 12 | +- 强大多模态能力: 能同时返回网页、图片、AI总结、追问建议,以及丰富的“模态卡”结构化数据。 | ||
| 13 | +- 模态卡支持: 针对天气、股票、汇率、百科、医疗等特定查询,可直接返回结构化数据卡片,便于Agent直接解析和使用。 | ||
| 14 | 14 | ||
| 15 | 主要工具: | 15 | 主要工具: |
| 16 | -- basic_search_news: (新增) 执行标准、快速的通用新闻搜索。 | ||
| 17 | -- deep_search_news: 对主题进行最全面的深度分析。 | ||
| 18 | -- search_news_last_24_hours: 获取24小时内的最新动态。 | ||
| 19 | -- search_news_last_week: 获取过去一周的主要报道。 | ||
| 20 | -- search_images_for_news: 查找与新闻主题相关的图片。 | ||
| 21 | -- search_news_by_date: 在指定的历史日期范围内搜索。 | 16 | +- comprehensive_search: 执行全面搜索,返回网页、图片、AI总结及可能的模态卡。 |
| 17 | +- search_for_structured_data: 专门用于查询天气、股票、汇率等可触发“模态卡”的结构化信息。 | ||
| 18 | +- web_search_only: 执行纯网页搜索,不请求AI总结,速度更快。 | ||
| 19 | +- search_last_24_hours: 获取过去24小时内的最新信息。 | ||
| 20 | +- search_last_week: 获取过去一周内的主要报道。 | ||
| 22 | """ | 21 | """ |
| 23 | 22 | ||
| 24 | import os | 23 | import os |
| 25 | -from typing import List, Dict, Any, Optional | ||
| 26 | -from dataclasses import dataclass, field | 24 | +import json |
| 25 | +from typing import List, Dict, Any, Optional, Literal | ||
| 27 | 26 | ||
| 28 | -# 运行前请确保已安装Tavily库: pip install tavily-python | 27 | +# 运行前请确保已安装 requests 库: pip install requests |
| 29 | try: | 28 | try: |
| 30 | - from tavily import TavilyClient | 29 | + import requests |
| 31 | except ImportError: | 30 | except ImportError: |
| 32 | - raise ImportError("Tavily库未安装,请运行 `pip install tavily-python` 进行安装。") | 31 | + raise ImportError("requests 库未安装,请运行 `pip install requests` 进行安装。") |
| 33 | 32 | ||
| 34 | # --- 1. 数据结构定义 --- | 33 | # --- 1. 数据结构定义 --- |
| 34 | +from dataclasses import dataclass, field | ||
| 35 | 35 | ||
| 36 | @dataclass | 36 | @dataclass |
| 37 | -class SearchResult: | ||
| 38 | - """ | ||
| 39 | - 网页搜索结果数据类 | ||
| 40 | - 包含 published_date 属性来存储新闻发布日期 | ||
| 41 | - """ | ||
| 42 | - title: str | 37 | +class WebpageResult: |
| 38 | + """网页搜索结果""" | ||
| 39 | + name: str | ||
| 43 | url: str | 40 | url: str |
| 44 | - content: str | ||
| 45 | - score: Optional[float] = None | ||
| 46 | - raw_content: Optional[str] = None | ||
| 47 | - published_date: Optional[str] = None | 41 | + snippet: str |
| 42 | + display_url: Optional[str] = None | ||
| 43 | + date_last_crawled: Optional[str] = None | ||
| 48 | 44 | ||
| 49 | @dataclass | 45 | @dataclass |
| 50 | class ImageResult: | 46 | class ImageResult: |
| 51 | - """图片搜索结果数据类""" | ||
| 52 | - url: str | ||
| 53 | - description: Optional[str] = None | 47 | + """图片搜索结果""" |
| 48 | + name: str | ||
| 49 | + content_url: str | ||
| 50 | + host_page_url: Optional[str] = None | ||
| 51 | + thumbnail_url: Optional[str] = None | ||
| 52 | + width: Optional[int] = None | ||
| 53 | + height: Optional[int] = None | ||
| 54 | + | ||
| 55 | +@dataclass | ||
| 56 | +class ModalCardResult: | ||
| 57 | + """ | ||
| 58 | + 模态卡结构化数据结果 | ||
| 59 | + 这是 Bocha 搜索的核心特色,用于返回特定类型的结构化信息。 | ||
| 60 | + """ | ||
| 61 | + card_type: str # 例如: weather_china, stock, baike_pro, medical_common | ||
| 62 | + content: Dict[str, Any] # 解析后的JSON内容 | ||
| 54 | 63 | ||
| 55 | @dataclass | 64 | @dataclass |
| 56 | -class TavilyResponse: | ||
| 57 | - """封装Tavily API的完整返回结果,以便在工具间传递""" | 65 | +class BochaResponse: |
| 66 | + """封装 Bocha API 的完整返回结果,以便在工具间传递""" | ||
| 58 | query: str | 67 | query: str |
| 59 | - answer: Optional[str] = None | ||
| 60 | - results: List[SearchResult] = field(default_factory=list) | 68 | + conversation_id: Optional[str] = None |
| 69 | + answer: Optional[str] = None # AI生成的总结答案 | ||
| 70 | + follow_ups: List[str] = field(default_factory=list) # AI生成的追问 | ||
| 71 | + webpages: List[WebpageResult] = field(default_factory=list) | ||
| 61 | images: List[ImageResult] = field(default_factory=list) | 72 | images: List[ImageResult] = field(default_factory=list) |
| 62 | - response_time: Optional[float] = None | 73 | + modal_cards: List[ModalCardResult] = field(default_factory=list) |
| 63 | 74 | ||
| 64 | 75 | ||
| 65 | # --- 2. 核心客户端与专用工具集 --- | 76 | # --- 2. 核心客户端与专用工具集 --- |
| 66 | 77 | ||
| 67 | -class TavilyNewsAgency: | 78 | +class BochaMultimodalSearch: |
| 68 | """ | 79 | """ |
| 69 | - 一个包含多种专用新闻舆情搜索工具的客户端。 | 80 | + 一个包含多种专用多模态搜索工具的客户端。 |
| 70 | 每个公共方法都设计为供 AI Agent 独立调用的工具。 | 81 | 每个公共方法都设计为供 AI Agent 独立调用的工具。 |
| 71 | """ | 82 | """ |
| 83 | + | ||
| 84 | + BASE_URL = "https://api.bochaai.com/v1/ai-search" | ||
| 72 | 85 | ||
| 73 | def __init__(self, api_key: Optional[str] = None): | 86 | def __init__(self, api_key: Optional[str] = None): |
| 74 | """ | 87 | """ |
| 75 | 初始化客户端。 | 88 | 初始化客户端。 |
| 76 | Args: | 89 | Args: |
| 77 | - api_key: Tavily API密钥,若不提供则从环境变量 TAVILY_API_KEY 读取。 | 90 | + api_key: Bocha API密钥,若不提供则从环境变量 BOCHA_API_KEY 读取。 |
| 78 | """ | 91 | """ |
| 79 | if api_key is None: | 92 | if api_key is None: |
| 80 | - api_key = os.getenv("TAVILY_API_KEY") | 93 | + api_key = os.getenv("BOCHA_API_KEY") |
| 81 | if not api_key: | 94 | if not api_key: |
| 82 | - raise ValueError("Tavily API Key未找到!请设置TAVILY_API_KEY环境变量或在初始化时提供") | ||
| 83 | - self._client = TavilyClient(api_key=api_key) | ||
| 84 | - | ||
| 85 | - def _search_internal(self, **kwargs) -> TavilyResponse: | 95 | + raise ValueError("Bocha API Key未找到!请设置 BOCHA_API_KEY 环境变量或在初始化时提供") |
| 96 | + | ||
| 97 | + self._headers = { | ||
| 98 | + 'Authorization': f'Bearer {api_key}', | ||
| 99 | + 'Content-Type': 'application/json', | ||
| 100 | + 'Accept': '*/*' | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + def _parse_search_response(self, response_dict: Dict[str, Any], query: str) -> BochaResponse: | ||
| 104 | + """从API的原始字典响应中解析出结构化的BochaResponse对象""" | ||
| 105 | + | ||
| 106 | + final_response = BochaResponse(query=query) | ||
| 107 | + final_response.conversation_id = response_dict.get('conversation_id') | ||
| 108 | + | ||
| 109 | + messages = response_dict.get('messages', []) | ||
| 110 | + for msg in messages: | ||
| 111 | + role = msg.get('role') | ||
| 112 | + if role != 'assistant': | ||
| 113 | + continue | ||
| 114 | + | ||
| 115 | + msg_type = msg.get('type') | ||
| 116 | + content_type = msg.get('content_type') | ||
| 117 | + content_str = msg.get('content', '{}') | ||
| 118 | + | ||
| 119 | + try: | ||
| 120 | + content_data = json.loads(content_str) | ||
| 121 | + except json.JSONDecodeError: | ||
| 122 | + # 如果内容不是合法的JSON字符串(例如纯文本的answer),则直接使用 | ||
| 123 | + content_data = content_str | ||
| 124 | + | ||
| 125 | + if msg_type == 'answer' and content_type == 'text': | ||
| 126 | + final_response.answer = content_data | ||
| 127 | + | ||
| 128 | + elif msg_type == 'follow_up' and content_type == 'text': | ||
| 129 | + final_response.follow_ups.append(content_data) | ||
| 130 | + | ||
| 131 | + elif msg_type == 'source': | ||
| 132 | + if content_type == 'webpage': | ||
| 133 | + web_results = content_data.get('value', []) | ||
| 134 | + for item in web_results: | ||
| 135 | + final_response.webpages.append(WebpageResult( | ||
| 136 | + name=item.get('name'), | ||
| 137 | + url=item.get('url'), | ||
| 138 | + snippet=item.get('snippet'), | ||
| 139 | + display_url=item.get('displayUrl'), | ||
| 140 | + date_last_crawled=item.get('dateLastCrawled') | ||
| 141 | + )) | ||
| 142 | + elif content_type == 'image': | ||
| 143 | + final_response.images.append(ImageResult( | ||
| 144 | + name=content_data.get('name'), | ||
| 145 | + content_url=content_data.get('contentUrl'), | ||
| 146 | + host_page_url=content_data.get('hostPageUrl'), | ||
| 147 | + thumbnail_url=content_data.get('thumbnailUrl'), | ||
| 148 | + width=content_data.get('width'), | ||
| 149 | + height=content_data.get('height') | ||
| 150 | + )) | ||
| 151 | + # 所有其他 content_type 都视为模态卡 | ||
| 152 | + else: | ||
| 153 | + final_response.modal_cards.append(ModalCardResult( | ||
| 154 | + card_type=content_type, | ||
| 155 | + content=content_data | ||
| 156 | + )) | ||
| 157 | + | ||
| 158 | + return final_response | ||
| 159 | + | ||
| 160 | + | ||
| 161 | + def _search_internal(self, **kwargs) -> BochaResponse: | ||
| 86 | """内部通用的搜索执行器,所有工具最终都调用此方法""" | 162 | """内部通用的搜索执行器,所有工具最终都调用此方法""" |
| 163 | + query = kwargs.get("query", "Unknown Query") | ||
| 164 | + payload = { | ||
| 165 | + "stream": False, # Agent工具通常使用非流式以获取完整结果 | ||
| 166 | + } | ||
| 167 | + payload.update(kwargs) | ||
| 168 | + | ||
| 87 | try: | 169 | try: |
| 88 | - kwargs['topic'] = 'general' | ||
| 89 | - api_params = {k: v for k, v in kwargs.items() if v is not None} | ||
| 90 | - response_dict = self._client.search(**api_params) | ||
| 91 | - | ||
| 92 | - search_results = [ | ||
| 93 | - SearchResult( | ||
| 94 | - title=item.get('title'), | ||
| 95 | - url=item.get('url'), | ||
| 96 | - content=item.get('content'), | ||
| 97 | - score=item.get('score'), | ||
| 98 | - raw_content=item.get('raw_content'), | ||
| 99 | - published_date=item.get('published_date') | ||
| 100 | - ) for item in response_dict.get('results', []) | ||
| 101 | - ] | 170 | + response = requests.post(self.BASE_URL, headers=self._headers, json=payload, timeout=30) |
| 171 | + response.raise_for_status() # 如果HTTP状态码是4xx或5xx,则抛出异常 | ||
| 102 | 172 | ||
| 103 | - image_results = [ImageResult(url=item.get('url'), description=item.get('description')) for item in response_dict.get('images', [])] | 173 | + response_dict = response.json() |
| 174 | + if response_dict.get("code") != 200: | ||
| 175 | + print(f"API返回错误: {response_dict.get('msg', '未知错误')}") | ||
| 176 | + return BochaResponse(query=query) | ||
| 177 | + | ||
| 178 | + return self._parse_search_response(response_dict, query) | ||
| 104 | 179 | ||
| 105 | - return TavilyResponse( | ||
| 106 | - query=response_dict.get('query'), answer=response_dict.get('answer'), | ||
| 107 | - results=search_results, images=image_results, | ||
| 108 | - response_time=response_dict.get('response_time') | ||
| 109 | - ) | 180 | + except requests.exceptions.RequestException as e: |
| 181 | + print(f"搜索时发生网络错误: {str(e)}") | ||
| 182 | + return BochaResponse(query=query) | ||
| 110 | except Exception as e: | 183 | except Exception as e: |
| 111 | - print(f"搜索时发生错误: {str(e)}") | ||
| 112 | - return TavilyResponse(query=kwargs.get("query", "Unknown Query")) | 184 | + print(f"处理响应时发生未知错误: {str(e)}") |
| 185 | + return BochaResponse(query=query) | ||
| 113 | 186 | ||
| 114 | # --- Agent 可用的工具方法 --- | 187 | # --- Agent 可用的工具方法 --- |
| 115 | 188 | ||
| 116 | - def basic_search_news(self, query: str, max_results: int = 7) -> TavilyResponse: | 189 | + def comprehensive_search(self, query: str, max_results: int = 10) -> BochaResponse: |
| 117 | """ | 190 | """ |
| 118 | - 【工具】基础新闻搜索: 执行一次标准、快速的新闻搜索。 | ||
| 119 | - 这是最常用的通用搜索工具,适用于不确定需要何种特定搜索时。 | 191 | + 【工具】全面综合搜索: 执行一次标准的、包含所有信息类型的综合搜索。 |
| 192 | + 返回网页、图片、AI总结、追问建议和可能的模态卡。这是最常用的通用搜索工具。 | ||
| 120 | Agent可提供搜索查询(query)和可选的最大结果数(max_results)。 | 193 | Agent可提供搜索查询(query)和可选的最大结果数(max_results)。 |
| 121 | """ | 194 | """ |
| 122 | - print(f"--- TOOL: 基础新闻搜索 (query: {query}) ---") | 195 | + print(f"--- TOOL: 全面综合搜索 (query: {query}) ---") |
| 123 | return self._search_internal( | 196 | return self._search_internal( |
| 124 | query=query, | 197 | query=query, |
| 125 | - max_results=max_results, | ||
| 126 | - search_depth="basic", | ||
| 127 | - include_answer=False | 198 | + count=max_results, |
| 199 | + answer=True # 开启AI总结 | ||
| 128 | ) | 200 | ) |
| 129 | - | ||
| 130 | - def deep_search_news(self, query: str) -> TavilyResponse: | 201 | + |
| 202 | + def web_search_only(self, query: str, max_results: int = 15) -> BochaResponse: | ||
| 131 | """ | 203 | """ |
| 132 | - 【工具】深度新闻分析: 对一个主题进行最全面、最深入的搜索。 | ||
| 133 | - 返回AI生成的“高级”详细摘要答案和最多20条最相关的新闻结果。适用于需要全面了解某个事件背景的场景。 | ||
| 134 | - Agent只需提供搜索查询(query)。 | 204 | + 【工具】纯网页搜索: 只获取网页链接和摘要,不请求AI生成答案。 |
| 205 | + 适用于需要快速获取原始网页信息,而不需要AI额外分析的场景。速度更快,成本更低。 | ||
| 135 | """ | 206 | """ |
| 136 | - print(f"--- TOOL: 深度新闻分析 (query: {query}) ---") | 207 | + print(f"--- TOOL: 纯网页搜索 (query: {query}) ---") |
| 137 | return self._search_internal( | 208 | return self._search_internal( |
| 138 | - query=query, search_depth="advanced", max_results=20, include_answer="advanced" | 209 | + query=query, |
| 210 | + count=max_results, | ||
| 211 | + answer=False # 关闭AI总结 | ||
| 139 | ) | 212 | ) |
| 140 | 213 | ||
| 141 | - def search_news_last_24_hours(self, query: str) -> TavilyResponse: | 214 | + def search_for_structured_data(self, query: str) -> BochaResponse: |
| 142 | """ | 215 | """ |
| 143 | - 【工具】搜索24小时内新闻: 获取关于某个主题的最新动态。 | ||
| 144 | - 此工具专门查找过去24小时内发布的新闻。适用于追踪突发事件或最新进展。 | ||
| 145 | - Agent只需提供搜索查询(query)。 | 216 | + 【工具】结构化数据查询: 专门用于可能触发“模态卡”的查询。 |
| 217 | + 当Agent意图是查询天气、股票、汇率、百科定义、火车票、汽车参数等结构化信息时,应优先使用此工具。 | ||
| 218 | + 它会返回所有信息,但Agent应重点关注结果中的 `modal_cards` 部分。 | ||
| 146 | """ | 219 | """ |
| 147 | - print(f"--- TOOL: 搜索24小时内新闻 (query: {query}) ---") | ||
| 148 | - return self._search_internal(query=query, time_range='d', max_results=10) | ||
| 149 | - | ||
| 150 | - def search_news_last_week(self, query: str) -> TavilyResponse: | ||
| 151 | - """ | ||
| 152 | - 【工具】搜索本周新闻: 获取关于某个主题过去一周内的主要新闻报道。 | ||
| 153 | - 适用于进行周度舆情总结或回顾。 | ||
| 154 | - Agent只需提供搜索查询(query)。 | ||
| 155 | - """ | ||
| 156 | - print(f"--- TOOL: 搜索本周新闻 (query: {query}) ---") | ||
| 157 | - return self._search_internal(query=query, time_range='w', max_results=10) | 220 | + print(f"--- TOOL: 结构化数据查询 (query: {query}) ---") |
| 221 | + # 实现上与 comprehensive_search 相同,但通过命名和文档引导Agent的意图 | ||
| 222 | + return self._search_internal( | ||
| 223 | + query=query, | ||
| 224 | + count=5, # 结构化查询通常不需要太多网页结果 | ||
| 225 | + answer=True | ||
| 226 | + ) | ||
| 158 | 227 | ||
| 159 | - def search_images_for_news(self, query: str) -> TavilyResponse: | 228 | + def search_last_24_hours(self, query: str) -> BochaResponse: |
| 160 | """ | 229 | """ |
| 161 | - 【工具】查找新闻图片: 搜索与某个新闻主题相关的图片。 | ||
| 162 | - 此工具会返回图片链接及描述,适用于需要为报告或文章配图的场景。 | ||
| 163 | - Agent只需提供搜索查询(query)。 | 230 | + 【工具】搜索24小时内信息: 获取关于某个主题的最新动态。 |
| 231 | + 此工具专门查找过去24小时内发布的内容。适用于追踪突发事件或最新进展。 | ||
| 164 | """ | 232 | """ |
| 165 | - print(f"--- TOOL: 查找新闻图片 (query: {query}) ---") | ||
| 166 | - return self._search_internal( | ||
| 167 | - query=query, include_images=True, include_image_descriptions=True, max_results=5 | ||
| 168 | - ) | 233 | + print(f"--- TOOL: 搜索24小时内信息 (query: {query}) ---") |
| 234 | + return self._search_internal(query=query, freshness='oneDay', answer=True) | ||
| 169 | 235 | ||
| 170 | - def search_news_by_date(self, query: str, start_date: str, end_date: str) -> TavilyResponse: | 236 | + def search_last_week(self, query: str) -> BochaResponse: |
| 171 | """ | 237 | """ |
| 172 | - 【工具】按指定日期范围搜索新闻: 在一个明确的历史时间段内搜索新闻。 | ||
| 173 | - 这是唯一需要Agent提供详细时间参数的工具。适用于需要对特定历史事件进行分析的场景。 | ||
| 174 | - Agent需要提供查询(query)、开始日期(start_date)和结束日期(end_date),格式均为 'YYYY-MM-DD'。 | 238 | + 【工具】搜索本周信息: 获取关于某个主题过去一周内的主要报道。 |
| 239 | + 适用于进行周度舆情总结或回顾。 | ||
| 175 | """ | 240 | """ |
| 176 | - print(f"--- TOOL: 按指定日期范围搜索新闻 (query: {query}, from: {start_date}, to: {end_date}) ---") | ||
| 177 | - return self._search_internal( | ||
| 178 | - query=query, start_date=start_date, end_date=end_date, max_results=15 | ||
| 179 | - ) | 241 | + print(f"--- TOOL: 搜索本周信息 (query: {query}) ---") |
| 242 | + return self._search_internal(query=query, freshness='oneWeek', answer=True) | ||
| 180 | 243 | ||
| 181 | 244 | ||
| 182 | # --- 3. 测试与使用示例 --- | 245 | # --- 3. 测试与使用示例 --- |
| 183 | 246 | ||
| 184 | -def print_response_summary(response: TavilyResponse): | ||
| 185 | - """简化的打印函数,用于展示测试结果,现在会显示发布日期""" | 247 | +def print_response_summary(response: BochaResponse): |
| 248 | + """简化的打印函数,用于展示测试结果""" | ||
| 186 | if not response or not response.query: | 249 | if not response or not response.query: |
| 187 | print("未能获取有效响应。") | 250 | print("未能获取有效响应。") |
| 188 | return | 251 | return |
| 189 | 252 | ||
| 190 | - print(f"\n查询: '{response.query}' | 耗时: {response.response_time}s") | 253 | + print(f"\n查询: '{response.query}' | 会话ID: {response.conversation_id}") |
| 191 | if response.answer: | 254 | if response.answer: |
| 192 | - print(f"AI摘要: {response.answer[:120]}...") | ||
| 193 | - print(f"找到 {len(response.results)} 条网页, {len(response.images)} 张图片。") | ||
| 194 | - if response.results: | ||
| 195 | - first_result = response.results[0] | ||
| 196 | - date_info = f"(发布于: {first_result.published_date})" if first_result.published_date else "" | ||
| 197 | - print(f"第一条结果: {first_result.title} {date_info}") | 255 | + print(f"AI摘要: {response.answer[:150]}...") |
| 256 | + | ||
| 257 | + print(f"找到 {len(response.webpages)} 个网页, {len(response.images)} 张图片, {len(response.modal_cards)} 个模态卡。") | ||
| 258 | + | ||
| 259 | + if response.modal_cards: | ||
| 260 | + first_card = response.modal_cards[0] | ||
| 261 | + print(f"第一个模态卡类型: {first_card.card_type}") | ||
| 262 | + | ||
| 263 | + if response.webpages: | ||
| 264 | + first_result = response.webpages[0] | ||
| 265 | + print(f"第一条网页结果: {first_result.name}") | ||
| 266 | + | ||
| 267 | + if response.follow_ups: | ||
| 268 | + print(f"建议追问: {response.follow_ups}") | ||
| 269 | + | ||
| 198 | print("-" * 60) | 270 | print("-" * 60) |
| 199 | 271 | ||
| 200 | 272 | ||
| 201 | if __name__ == "__main__": | 273 | if __name__ == "__main__": |
| 202 | - # 在运行前,请确保您已设置 TAVILY_API_KEY 环境变量 | 274 | + # 在运行前,请确保您已设置 BOCHA_API_KEY 环境变量 |
| 203 | 275 | ||
| 204 | try: | 276 | try: |
| 205 | - # 初始化“新闻社”客户端,它内部包含了所有工具 | ||
| 206 | - agency = TavilyNewsAgency() | 277 | + # 初始化多模态搜索客户端,它内部包含了所有工具 |
| 278 | + search_client = BochaMultimodalSearch() | ||
| 207 | 279 | ||
| 208 | - # 场景1: Agent 进行一次常规、快速的搜索 | ||
| 209 | - response1 = agency.basic_search_news(query="奥运会最新赛况", max_results=5) | 280 | + # 场景1: Agent进行一次常规的、需要AI总结的综合搜索 |
| 281 | + response1 = search_client.comprehensive_search(query="人工智能对未来教育的影响") | ||
| 210 | print_response_summary(response1) | 282 | print_response_summary(response1) |
| 211 | 283 | ||
| 212 | - # 场景2: Agent 需要全面了解“全球芯片技术竞争”的背景 | ||
| 213 | - response2 = agency.deep_search_news(query="全球芯片技术竞争") | 284 | + # 场景2: Agent需要查询特定结构化信息 - 天气 |
| 285 | + response2 = search_client.search_for_structured_data(query="上海明天天气怎么样") | ||
| 214 | print_response_summary(response2) | 286 | print_response_summary(response2) |
| 287 | + # 深度解析第一个模态卡 | ||
| 288 | + if response2.modal_cards and response2.modal_cards[0].card_type == 'weather_china': | ||
| 289 | + print("天气模态卡详情:", json.dumps(response2.modal_cards[0].content, indent=2, ensure_ascii=False)) | ||
| 290 | + | ||
| 215 | 291 | ||
| 216 | - # 场景3: Agent 需要追踪“GTC大会”的最新消息 | ||
| 217 | - response3 = agency.search_news_last_24_hours(query="Nvidia GTC大会 最新发布") | 292 | + # 场景3: Agent需要查询特定结构化信息 - 股票 |
| 293 | + response3 = search_client.search_for_structured_data(query="东方财富股票") | ||
| 218 | print_response_summary(response3) | 294 | print_response_summary(response3) |
| 219 | - | ||
| 220 | - # 场景4: Agent 需要为一篇关于“自动驾驶”的周报查找素材 | ||
| 221 | - response4 = agency.search_news_last_week(query="自动驾驶商业化落地") | 295 | + |
| 296 | + # 场景4: Agent需要追踪某个事件的最新进展 | ||
| 297 | + response4 = search_client.search_last_24_hours(query="C929大飞机最新消息") | ||
| 222 | print_response_summary(response4) | 298 | print_response_summary(response4) |
| 223 | - | ||
| 224 | - # 场景5: Agent 需要查找“韦伯太空望远镜”的新闻图片 | ||
| 225 | - response5 = agency.search_images_for_news(query="韦伯太空望远镜最新发现") | ||
| 226 | - print_response_summary(response5) | ||
| 227 | 299 | ||
| 228 | - # 场景6: Agent 需要研究2025年第一季度关于“人工智能法规”的新闻 | ||
| 229 | - response6 = agency.search_news_by_date( | ||
| 230 | - query="人工智能法规", | ||
| 231 | - start_date="2025-01-01", | ||
| 232 | - end_date="2025-03-31" | ||
| 233 | - ) | 300 | + # 场景5: Agent只需要快速获取网页信息,不需要AI总结 |
| 301 | + response5 = search_client.web_search_only(query="Python dataclasses用法") | ||
| 302 | + print_response_summary(response5) | ||
| 303 | + | ||
| 304 | + # 场景6: Agent需要回顾一周内关于某项技术的新闻 | ||
| 305 | + response6 = search_client.search_last_week(query="量子计算商业化") | ||
| 234 | print_response_summary(response6) | 306 | print_response_summary(response6) |
| 307 | + | ||
| 308 | + '''下面是测试程序的输出: | ||
| 309 | + --- TOOL: 全面综合搜索 (query: 人工智能对未来教育的影响) --- | ||
| 310 | + | ||
| 311 | +查询: '人工智能对未来教育的影响' | 会话ID: bf43bfe4c7bb4f7b8a3945515d8ab69e | ||
| 312 | +AI摘要: 人工智能对未来教育有着多方面的影响。 | ||
| 313 | + | ||
| 314 | +从积极影响来看: | ||
| 315 | +- 在教学资源方面,人工智能有助于教育资源的均衡分配[引用:4]。例如通过人工智能云平台,可以实现优质资源的共享,这对于偏远地区来说意义重大,能让那里的学生也接触到优质的教育内 容,一定程度上缓解师资短缺的问题,因为AI驱动的智能教学助手或虚拟... | ||
| 316 | +找到 10 个网页, 1 张图片, 1 个模态卡。 | ||
| 317 | +第一个模态卡类型: video | ||
| 318 | +第一条网页结果: 人工智能如何影响教育变革 | ||
| 319 | +建议追问: [['人工智能将如何改变未来的教育模式?', '在未来教育中,人工智能会给教师带来哪些挑战?', '未来教育中,学生如何利用人工智能提升学习效果?']] | ||
| 320 | +------------------------------------------------------------ | ||
| 321 | +--- TOOL: 结构化数据查询 (query: 上海明天天气怎么样) --- | ||
| 322 | + | ||
| 323 | +查询: '上海明天天气怎么样' | 会话ID: e412aa1548cd43a295430e47a62adda2 | ||
| 324 | +AI摘要: 根据所给信息,无法确定上海明天的天气情况。 | ||
| 325 | + | ||
| 326 | +首先,所提供的信息都是关于2025年8月22日的天气状况,包括当天的气温、降水、风力、湿度以及高温预警等信息[引用:1][引用:2][引用:3][引用:5]。然而,这些信息没有涉及到明天(8月23 日)天气的预测内容。虽然提到了副热带高压一直到8月底高温都... | ||
| 327 | +找到 5 个网页, 1 张图片, 2 个模态卡。 | ||
| 328 | +第一个模态卡类型: video | ||
| 329 | +第一条网页结果: 今日冲击38!上海八月高温天数和夏季持续高温天数有望双双破纪录_天气_低压_气象站 | ||
| 330 | +建议追问: [['能告诉我上海明天的气温范围吗?', '上海明天会有降雨吗?', '上海明天的天气是晴天还是阴天呢?']] | ||
| 331 | +------------------------------------------------------------ | ||
| 332 | +--- TOOL: 结构化数据查询 (query: 东方财富股票) --- | ||
| 333 | + | ||
| 334 | +查询: '东方财富股票' | 会话ID: 584d62ed97834473b967127852e1eaa0 | ||
| 335 | +AI摘要: 仅根据提供的上下文,无法确切获取东方财富股票的相关信息。 | ||
| 336 | + | ||
| 337 | +从给出的这些数据来看,并没有直接表明与东方财富股票相关的特定数据。例如,没有东方财富股票的涨跌幅情况、成交量、市值等具体数据[引用:1][引用:3]。也没有涉及东方财富股票在研报 、评级方面的信息[引用:2]。同时,上下文里关于股票价格、成交... | ||
| 338 | +找到 5 个网页, 1 张图片, 2 个模态卡。 | ||
| 339 | +第一个模态卡类型: video | ||
| 340 | +第一条网页结果: 股票价格_分时成交_行情_走势图—东方财富网 | ||
| 341 | +建议追问: [['东方财富股票近期的走势如何?', '东方财富股票有哪些主要的投资亮点?', '东方财富股票的历史最高和最低股价是多少?']] | ||
| 342 | +------------------------------------------------------------ | ||
| 343 | +--- TOOL: 搜索24小时内信息 (query: C929大飞机最新消息) --- | ||
| 344 | + | ||
| 345 | +查询: 'C929大飞机最新消息' | 会话ID: 5904021dc29d497e938e04db18d7f2e2 | ||
| 346 | +AI摘要: 根据提供的上下文,没有关于C929大飞机的直接消息,无法确切给出C929大飞机的最新消息。 | ||
| 347 | + | ||
| 348 | +目前提供的上下文涵盖了众多航空领域相关事件,但多是围绕波音787、空客A380相关专家的人事变动、国产飞机“C909云端之旅”、科德数控的营收情况、俄制航空发动机供应相关以及其他非C929大飞机相关的内容。... | ||
| 349 | +找到 10 个网页, 1 张图片, 1 个模态卡。 | ||
| 350 | +第一个模态卡类型: video | ||
| 351 | +第一条网页结果: 放弃美国千万年薪,波音787顶尖专家回国,或可协助破解C929 | ||
| 352 | +建议追问: [['C929大飞机目前的研发进度如何?', '有没有关于C929大飞机预计首飞时间的消息?', 'C929大飞机在技术创新方面有哪些新进展?']] | ||
| 353 | +------------------------------------------------------------ | ||
| 354 | +--- TOOL: 纯网页搜索 (query: Python dataclasses用法) --- | ||
| 355 | + | ||
| 356 | +查询: 'Python dataclasses用法' | 会话ID: 74c742759d2e4b17b52d8b735ce24537 | ||
| 357 | +找到 15 个网页, 1 张图片, 1 个模态卡。 | ||
| 358 | +第一个模态卡类型: video | ||
| 359 | +第一条网页结果: 不可不知的dataclasses python小知识_python dataclasses-CSDN博客 | ||
| 360 | +------------------------------------------------------------ | ||
| 361 | +--- TOOL: 搜索本周信息 (query: 量子计算商业化) --- | ||
| 362 | + | ||
| 363 | +AI摘要: 量子计算商业化正在逐步推进。 | ||
| 364 | + | ||
| 365 | +量子计算商业化有着多方面的体现和推动因素。从国际上看,美国能源部橡树岭国家实验室选择IQM Radiance作为其首台本地部署的量子计算机,计划于2025年第三季度交付并集成至高性能计算系统中[引用:4];英国量子计算公司Oxford Ionics的全栈离子阱量子计算... | ||
| 366 | +找到 10 个网页, 1 张图片, 1 个模态卡。 | ||
| 367 | +第一个模态卡类型: video | ||
| 368 | +第一条网页结果: 量子计算商业潜力释放正酣,微美全息(WIMI.US)创新科技卡位“生态高地” | ||
| 369 | +建议追问: [['量子计算商业化目前有哪些成功的案例?', '哪些公司在推动量子计算商业化进程?', '量子计算商业化面临的主要挑战是什么?']] | ||
| 370 | +------------------------------------------------------------''' | ||
| 235 | 371 | ||
| 236 | except ValueError as e: | 372 | except ValueError as e: |
| 237 | print(f"初始化失败: {e}") | 373 | print(f"初始化失败: {e}") |
| 238 | - print("请确保 TAVILY_API_KEY 环境变量已正确设置。") | 374 | + print("请确保 BOCHA_API_KEY 环境变量已正确设置。") |
| 239 | except Exception as e: | 375 | except Exception as e: |
| 240 | print(f"测试过程中发生未知错误: {e}") | 376 | print(f"测试过程中发生未知错误: {e}") |
-
Please register or login to post a comment