The private database analysis agent has been basically completed.
Showing
5 changed files
with
368 additions
and
143 deletions
| @@ -19,7 +19,7 @@ from .nodes import ( | @@ -19,7 +19,7 @@ from .nodes import ( | ||
| 19 | ReportFormattingNode | 19 | ReportFormattingNode |
| 20 | ) | 20 | ) |
| 21 | from .state import State | 21 | from .state import State |
| 22 | -from .tools import TavilyNewsAgency, TavilyResponse | 22 | +from .tools import MediaCrawlerDB, DBResponse |
| 23 | from .utils import Config, load_config, format_search_results_for_prompt | 23 | from .utils import Config, load_config, format_search_results_for_prompt |
| 24 | 24 | ||
| 25 | 25 | ||
| @@ -39,8 +39,16 @@ class DeepSearchAgent: | @@ -39,8 +39,16 @@ class DeepSearchAgent: | ||
| 39 | # 初始化LLM客户端 | 39 | # 初始化LLM客户端 |
| 40 | self.llm_client = self._initialize_llm() | 40 | self.llm_client = self._initialize_llm() |
| 41 | 41 | ||
| 42 | + # 设置数据库环境变量 | ||
| 43 | + os.environ["DB_HOST"] = self.config.db_host or "" | ||
| 44 | + os.environ["DB_USER"] = self.config.db_user or "" | ||
| 45 | + os.environ["DB_PASSWORD"] = self.config.db_password or "" | ||
| 46 | + os.environ["DB_NAME"] = self.config.db_name or "" | ||
| 47 | + os.environ["DB_PORT"] = str(self.config.db_port) | ||
| 48 | + os.environ["DB_CHARSET"] = self.config.db_charset | ||
| 49 | + | ||
| 42 | # 初始化搜索工具集 | 50 | # 初始化搜索工具集 |
| 43 | - self.search_agency = TavilyNewsAgency(api_key=self.config.tavily_api_key) | 51 | + self.search_agency = MediaCrawlerDB() |
| 44 | 52 | ||
| 45 | # 初始化节点 | 53 | # 初始化节点 |
| 46 | self._initialize_nodes() | 54 | self._initialize_nodes() |
| @@ -53,7 +61,7 @@ class DeepSearchAgent: | @@ -53,7 +61,7 @@ class DeepSearchAgent: | ||
| 53 | 61 | ||
| 54 | print(f"Deep Search Agent 已初始化") | 62 | print(f"Deep Search Agent 已初始化") |
| 55 | print(f"使用LLM: {self.llm_client.get_model_info()}") | 63 | print(f"使用LLM: {self.llm_client.get_model_info()}") |
| 56 | - print(f"搜索工具集: TavilyNewsAgency (支持6种搜索工具)") | 64 | + print(f"搜索工具集: MediaCrawlerDB (支持5种本地数据库查询工具)") |
| 57 | 65 | ||
| 58 | def _initialize_llm(self) -> BaseLLM: | 66 | def _initialize_llm(self) -> BaseLLM: |
| 59 | """初始化LLM客户端""" | 67 | """初始化LLM客户端""" |
| @@ -103,46 +111,53 @@ class DeepSearchAgent: | @@ -103,46 +111,53 @@ class DeepSearchAgent: | ||
| 103 | except ValueError: | 111 | except ValueError: |
| 104 | return False | 112 | return False |
| 105 | 113 | ||
| 106 | - def execute_search_tool(self, tool_name: str, query: str, **kwargs) -> TavilyResponse: | 114 | + def execute_search_tool(self, tool_name: str, query: str, **kwargs) -> DBResponse: |
| 107 | """ | 115 | """ |
| 108 | - 执行指定的搜索工具 | 116 | + 执行指定的数据库查询工具 |
| 109 | 117 | ||
| 110 | Args: | 118 | Args: |
| 111 | tool_name: 工具名称,可选值: | 119 | tool_name: 工具名称,可选值: |
| 112 | - - "basic_search_news": 基础新闻搜索(快速、通用) | ||
| 113 | - - "deep_search_news": 深度新闻分析 | ||
| 114 | - - "search_news_last_24_hours": 24小时内最新新闻 | ||
| 115 | - - "search_news_last_week": 本周新闻 | ||
| 116 | - - "search_images_for_news": 新闻图片搜索 | ||
| 117 | - - "search_news_by_date": 按日期范围搜索新闻 | ||
| 118 | - query: 搜索查询 | ||
| 119 | - **kwargs: 额外参数(如start_date, end_date, max_results) | 120 | + - "search_hot_content": 查找热点内容 |
| 121 | + - "search_topic_globally": 全局话题搜索 | ||
| 122 | + - "search_topic_by_date": 按日期搜索话题 | ||
| 123 | + - "get_comments_for_topic": 获取话题评论 | ||
| 124 | + - "search_topic_on_platform": 平台定向搜索 | ||
| 125 | + query: 搜索关键词/话题 | ||
| 126 | + **kwargs: 额外参数(如start_date, end_date, platform, limit等) | ||
| 120 | 127 | ||
| 121 | Returns: | 128 | Returns: |
| 122 | - TavilyResponse对象 | 129 | + DBResponse对象 |
| 123 | """ | 130 | """ |
| 124 | - print(f" → 执行搜索工具: {tool_name}") | ||
| 125 | - | ||
| 126 | - if tool_name == "basic_search_news": | ||
| 127 | - max_results = kwargs.get("max_results", 7) | ||
| 128 | - return self.search_agency.basic_search_news(query, max_results) | ||
| 129 | - elif tool_name == "deep_search_news": | ||
| 130 | - return self.search_agency.deep_search_news(query) | ||
| 131 | - elif tool_name == "search_news_last_24_hours": | ||
| 132 | - return self.search_agency.search_news_last_24_hours(query) | ||
| 133 | - elif tool_name == "search_news_last_week": | ||
| 134 | - return self.search_agency.search_news_last_week(query) | ||
| 135 | - elif tool_name == "search_images_for_news": | ||
| 136 | - return self.search_agency.search_images_for_news(query) | ||
| 137 | - elif tool_name == "search_news_by_date": | 131 | + print(f" → 执行数据库查询工具: {tool_name}") |
| 132 | + | ||
| 133 | + if tool_name == "search_hot_content": | ||
| 134 | + time_period = kwargs.get("time_period", "week") | ||
| 135 | + limit = kwargs.get("limit", 10) | ||
| 136 | + return self.search_agency.search_hot_content(time_period=time_period, limit=limit) | ||
| 137 | + elif tool_name == "search_topic_globally": | ||
| 138 | + limit_per_table = kwargs.get("limit_per_table", 5) | ||
| 139 | + return self.search_agency.search_topic_globally(topic=query, limit_per_table=limit_per_table) | ||
| 140 | + elif tool_name == "search_topic_by_date": | ||
| 138 | start_date = kwargs.get("start_date") | 141 | start_date = kwargs.get("start_date") |
| 139 | end_date = kwargs.get("end_date") | 142 | end_date = kwargs.get("end_date") |
| 143 | + limit_per_table = kwargs.get("limit_per_table", 10) | ||
| 140 | if not start_date or not end_date: | 144 | if not start_date or not end_date: |
| 141 | - raise ValueError("search_news_by_date工具需要start_date和end_date参数") | ||
| 142 | - return self.search_agency.search_news_by_date(query, start_date, end_date) | 145 | + raise ValueError("search_topic_by_date工具需要start_date和end_date参数") |
| 146 | + return self.search_agency.search_topic_by_date(topic=query, start_date=start_date, end_date=end_date, limit_per_table=limit_per_table) | ||
| 147 | + elif tool_name == "get_comments_for_topic": | ||
| 148 | + limit = kwargs.get("limit", 50) | ||
| 149 | + return self.search_agency.get_comments_for_topic(topic=query, limit=limit) | ||
| 150 | + elif tool_name == "search_topic_on_platform": | ||
| 151 | + platform = kwargs.get("platform") | ||
| 152 | + start_date = kwargs.get("start_date") | ||
| 153 | + end_date = kwargs.get("end_date") | ||
| 154 | + limit = kwargs.get("limit", 20) | ||
| 155 | + if not platform: | ||
| 156 | + raise ValueError("search_topic_on_platform工具需要platform参数") | ||
| 157 | + return self.search_agency.search_topic_on_platform(platform=platform, topic=query, start_date=start_date, end_date=end_date, limit=limit) | ||
| 143 | else: | 158 | else: |
| 144 | - print(f" ⚠️ 未知的搜索工具: {tool_name},使用默认基础搜索") | ||
| 145 | - return self.search_agency.basic_search_news(query) | 159 | + print(f" ⚠️ 未知的搜索工具: {tool_name},使用默认全局搜索") |
| 160 | + return self.search_agency.search_topic_globally(topic=query) | ||
| 146 | 161 | ||
| 147 | def research(self, query: str, save_report: bool = True) -> str: | 162 | def research(self, query: str, save_report: bool = True) -> str: |
| 148 | """ | 163 | """ |
| @@ -231,7 +246,7 @@ class DeepSearchAgent: | @@ -231,7 +246,7 @@ class DeepSearchAgent: | ||
| 231 | print(" - 生成搜索查询...") | 246 | print(" - 生成搜索查询...") |
| 232 | search_output = self.first_search_node.run(search_input) | 247 | search_output = self.first_search_node.run(search_input) |
| 233 | search_query = search_output["search_query"] | 248 | search_query = search_output["search_query"] |
| 234 | - search_tool = search_output.get("search_tool", "basic_search_news") # 默认工具 | 249 | + search_tool = search_output.get("search_tool", "search_topic_globally") # 默认工具 |
| 235 | reasoning = search_output["reasoning"] | 250 | reasoning = search_output["reasoning"] |
| 236 | 251 | ||
| 237 | print(f" - 搜索查询: {search_query}") | 252 | print(f" - 搜索查询: {search_query}") |
| @@ -239,11 +254,13 @@ class DeepSearchAgent: | @@ -239,11 +254,13 @@ class DeepSearchAgent: | ||
| 239 | print(f" - 推理: {reasoning}") | 254 | print(f" - 推理: {reasoning}") |
| 240 | 255 | ||
| 241 | # 执行搜索 | 256 | # 执行搜索 |
| 242 | - print(" - 执行网络搜索...") | 257 | + print(" - 执行数据库查询...") |
| 243 | 258 | ||
| 244 | - # 处理search_news_by_date的特殊参数 | 259 | + # 处理特殊参数 |
| 245 | search_kwargs = {} | 260 | search_kwargs = {} |
| 246 | - if search_tool == "search_news_by_date": | 261 | + |
| 262 | + # 处理需要日期的工具 | ||
| 263 | + if search_tool in ["search_topic_by_date", "search_topic_on_platform"]: | ||
| 247 | start_date = search_output.get("start_date") | 264 | start_date = search_output.get("start_date") |
| 248 | end_date = search_output.get("end_date") | 265 | end_date = search_output.get("end_date") |
| 249 | 266 | ||
| @@ -254,12 +271,35 @@ class DeepSearchAgent: | @@ -254,12 +271,35 @@ class DeepSearchAgent: | ||
| 254 | search_kwargs["end_date"] = end_date | 271 | search_kwargs["end_date"] = end_date |
| 255 | print(f" - 时间范围: {start_date} 到 {end_date}") | 272 | print(f" - 时间范围: {start_date} 到 {end_date}") |
| 256 | else: | 273 | else: |
| 257 | - print(f" ⚠️ 日期格式错误(应为YYYY-MM-DD),改用基础搜索") | 274 | + print(f" ⚠️ 日期格式错误(应为YYYY-MM-DD),改用全局搜索") |
| 258 | print(f" 提供的日期: start_date={start_date}, end_date={end_date}") | 275 | print(f" 提供的日期: start_date={start_date}, end_date={end_date}") |
| 259 | - search_tool = "basic_search_news" | 276 | + search_tool = "search_topic_globally" |
| 277 | + elif search_tool == "search_topic_by_date": | ||
| 278 | + print(f" ⚠️ search_topic_by_date工具缺少时间参数,改用全局搜索") | ||
| 279 | + search_tool = "search_topic_globally" | ||
| 280 | + | ||
| 281 | + # 处理需要平台参数的工具 | ||
| 282 | + if search_tool == "search_topic_on_platform": | ||
| 283 | + platform = search_output.get("platform") | ||
| 284 | + if platform: | ||
| 285 | + search_kwargs["platform"] = platform | ||
| 286 | + print(f" - 指定平台: {platform}") | ||
| 260 | else: | 287 | else: |
| 261 | - print(f" ⚠️ search_news_by_date工具缺少时间参数,改用基础搜索") | ||
| 262 | - search_tool = "basic_search_news" | 288 | + print(f" ⚠️ search_topic_on_platform工具缺少平台参数,改用全局搜索") |
| 289 | + search_tool = "search_topic_globally" | ||
| 290 | + | ||
| 291 | + # 处理限制参数 | ||
| 292 | + if search_tool == "search_hot_content": | ||
| 293 | + time_period = search_output.get("time_period", "week") | ||
| 294 | + limit = search_output.get("limit", 10) | ||
| 295 | + search_kwargs["time_period"] = time_period | ||
| 296 | + search_kwargs["limit"] = limit | ||
| 297 | + elif search_tool in ["search_topic_globally", "search_topic_by_date"]: | ||
| 298 | + limit_per_table = search_output.get("limit_per_table", 5) | ||
| 299 | + search_kwargs["limit_per_table"] = limit_per_table | ||
| 300 | + elif search_tool in ["get_comments_for_topic", "search_topic_on_platform"]: | ||
| 301 | + limit = search_output.get("limit", 20) | ||
| 302 | + search_kwargs["limit"] = limit | ||
| 263 | 303 | ||
| 264 | search_response = self.execute_search_tool(search_tool, search_query, **search_kwargs) | 304 | search_response = self.execute_search_tool(search_tool, search_query, **search_kwargs) |
| 265 | 305 | ||
| @@ -270,12 +310,16 @@ class DeepSearchAgent: | @@ -270,12 +310,16 @@ class DeepSearchAgent: | ||
| 270 | max_results = min(len(search_response.results), 10) | 310 | max_results = min(len(search_response.results), 10) |
| 271 | for result in search_response.results[:max_results]: | 311 | for result in search_response.results[:max_results]: |
| 272 | search_results.append({ | 312 | search_results.append({ |
| 273 | - 'title': result.title, | ||
| 274 | - 'url': result.url, | ||
| 275 | - 'content': result.content, | ||
| 276 | - 'score': result.score, | ||
| 277 | - 'raw_content': result.raw_content, | ||
| 278 | - 'published_date': result.published_date # 新增字段 | 313 | + 'title': result.title_or_content, |
| 314 | + 'url': result.url or "", | ||
| 315 | + 'content': result.title_or_content, | ||
| 316 | + 'score': result.hotness_score, | ||
| 317 | + 'raw_content': result.title_or_content, | ||
| 318 | + 'published_date': result.publish_time.isoformat() if result.publish_time else None, | ||
| 319 | + 'platform': result.platform, | ||
| 320 | + 'content_type': result.content_type, | ||
| 321 | + 'author': result.author_nickname, | ||
| 322 | + 'engagement': result.engagement | ||
| 279 | }) | 323 | }) |
| 280 | 324 | ||
| 281 | if search_results: | 325 | if search_results: |
| @@ -324,7 +368,7 @@ class DeepSearchAgent: | @@ -324,7 +368,7 @@ class DeepSearchAgent: | ||
| 324 | # 生成反思搜索查询 | 368 | # 生成反思搜索查询 |
| 325 | reflection_output = self.reflection_node.run(reflection_input) | 369 | reflection_output = self.reflection_node.run(reflection_input) |
| 326 | search_query = reflection_output["search_query"] | 370 | search_query = reflection_output["search_query"] |
| 327 | - search_tool = reflection_output.get("search_tool", "basic_search_news") # 默认工具 | 371 | + search_tool = reflection_output.get("search_tool", "search_topic_globally") # 默认工具 |
| 328 | reasoning = reflection_output["reasoning"] | 372 | reasoning = reflection_output["reasoning"] |
| 329 | 373 | ||
| 330 | print(f" 反思查询: {search_query}") | 374 | print(f" 反思查询: {search_query}") |
| @@ -332,9 +376,11 @@ class DeepSearchAgent: | @@ -332,9 +376,11 @@ class DeepSearchAgent: | ||
| 332 | print(f" 反思推理: {reasoning}") | 376 | print(f" 反思推理: {reasoning}") |
| 333 | 377 | ||
| 334 | # 执行反思搜索 | 378 | # 执行反思搜索 |
| 335 | - # 处理search_news_by_date的特殊参数 | 379 | + # 处理特殊参数 |
| 336 | search_kwargs = {} | 380 | search_kwargs = {} |
| 337 | - if search_tool == "search_news_by_date": | 381 | + |
| 382 | + # 处理需要日期的工具 | ||
| 383 | + if search_tool in ["search_topic_by_date", "search_topic_on_platform"]: | ||
| 338 | start_date = reflection_output.get("start_date") | 384 | start_date = reflection_output.get("start_date") |
| 339 | end_date = reflection_output.get("end_date") | 385 | end_date = reflection_output.get("end_date") |
| 340 | 386 | ||
| @@ -345,12 +391,35 @@ class DeepSearchAgent: | @@ -345,12 +391,35 @@ class DeepSearchAgent: | ||
| 345 | search_kwargs["end_date"] = end_date | 391 | search_kwargs["end_date"] = end_date |
| 346 | print(f" 时间范围: {start_date} 到 {end_date}") | 392 | print(f" 时间范围: {start_date} 到 {end_date}") |
| 347 | else: | 393 | else: |
| 348 | - print(f" ⚠️ 日期格式错误(应为YYYY-MM-DD),改用基础搜索") | 394 | + print(f" ⚠️ 日期格式错误(应为YYYY-MM-DD),改用全局搜索") |
| 349 | print(f" 提供的日期: start_date={start_date}, end_date={end_date}") | 395 | print(f" 提供的日期: start_date={start_date}, end_date={end_date}") |
| 350 | - search_tool = "basic_search_news" | 396 | + search_tool = "search_topic_globally" |
| 397 | + elif search_tool == "search_topic_by_date": | ||
| 398 | + print(f" ⚠️ search_topic_by_date工具缺少时间参数,改用全局搜索") | ||
| 399 | + search_tool = "search_topic_globally" | ||
| 400 | + | ||
| 401 | + # 处理需要平台参数的工具 | ||
| 402 | + if search_tool == "search_topic_on_platform": | ||
| 403 | + platform = reflection_output.get("platform") | ||
| 404 | + if platform: | ||
| 405 | + search_kwargs["platform"] = platform | ||
| 406 | + print(f" 指定平台: {platform}") | ||
| 351 | else: | 407 | else: |
| 352 | - print(f" ⚠️ search_news_by_date工具缺少时间参数,改用基础搜索") | ||
| 353 | - search_tool = "basic_search_news" | 408 | + print(f" ⚠️ search_topic_on_platform工具缺少平台参数,改用全局搜索") |
| 409 | + search_tool = "search_topic_globally" | ||
| 410 | + | ||
| 411 | + # 处理限制参数 | ||
| 412 | + if search_tool == "search_hot_content": | ||
| 413 | + time_period = reflection_output.get("time_period", "week") | ||
| 414 | + limit = reflection_output.get("limit", 10) | ||
| 415 | + search_kwargs["time_period"] = time_period | ||
| 416 | + search_kwargs["limit"] = limit | ||
| 417 | + elif search_tool in ["search_topic_globally", "search_topic_by_date"]: | ||
| 418 | + limit_per_table = reflection_output.get("limit_per_table", 5) | ||
| 419 | + search_kwargs["limit_per_table"] = limit_per_table | ||
| 420 | + elif search_tool in ["get_comments_for_topic", "search_topic_on_platform"]: | ||
| 421 | + limit = reflection_output.get("limit", 20) | ||
| 422 | + search_kwargs["limit"] = limit | ||
| 354 | 423 | ||
| 355 | search_response = self.execute_search_tool(search_tool, search_query, **search_kwargs) | 424 | search_response = self.execute_search_tool(search_tool, search_query, **search_kwargs) |
| 356 | 425 | ||
| @@ -361,12 +430,16 @@ class DeepSearchAgent: | @@ -361,12 +430,16 @@ class DeepSearchAgent: | ||
| 361 | max_results = min(len(search_response.results), 10) | 430 | max_results = min(len(search_response.results), 10) |
| 362 | for result in search_response.results[:max_results]: | 431 | for result in search_response.results[:max_results]: |
| 363 | search_results.append({ | 432 | search_results.append({ |
| 364 | - 'title': result.title, | ||
| 365 | - 'url': result.url, | ||
| 366 | - 'content': result.content, | ||
| 367 | - 'score': result.score, | ||
| 368 | - 'raw_content': result.raw_content, | ||
| 369 | - 'published_date': result.published_date | 433 | + 'title': result.title_or_content, |
| 434 | + 'url': result.url or "", | ||
| 435 | + 'content': result.title_or_content, | ||
| 436 | + 'score': result.hotness_score, | ||
| 437 | + 'raw_content': result.title_or_content, | ||
| 438 | + 'published_date': result.publish_time.isoformat() if result.publish_time else None, | ||
| 439 | + 'platform': result.platform, | ||
| 440 | + 'content_type': result.content_type, | ||
| 441 | + 'author': result.author_nickname, | ||
| 442 | + 'engagement': result.engagement | ||
| 370 | }) | 443 | }) |
| 371 | 444 | ||
| 372 | if search_results: | 445 | if search_results: |
| @@ -35,8 +35,12 @@ output_schema_first_search = { | @@ -35,8 +35,12 @@ output_schema_first_search = { | ||
| 35 | "search_query": {"type": "string"}, | 35 | "search_query": {"type": "string"}, |
| 36 | "search_tool": {"type": "string"}, | 36 | "search_tool": {"type": "string"}, |
| 37 | "reasoning": {"type": "string"}, | 37 | "reasoning": {"type": "string"}, |
| 38 | - "start_date": {"type": "string", "description": "开始日期,格式YYYY-MM-DD,仅search_news_by_date工具需要"}, | ||
| 39 | - "end_date": {"type": "string", "description": "结束日期,格式YYYY-MM-DD,仅search_news_by_date工具需要"} | 38 | + "start_date": {"type": "string", "description": "开始日期,格式YYYY-MM-DD,search_topic_by_date和search_topic_on_platform工具可能需要"}, |
| 39 | + "end_date": {"type": "string", "description": "结束日期,格式YYYY-MM-DD,search_topic_by_date和search_topic_on_platform工具可能需要"}, | ||
| 40 | + "platform": {"type": "string", "description": "平台名称,search_topic_on_platform工具必需,可选值:bilibili, weibo, douyin, kuaishou, xhs, zhihu, tieba"}, | ||
| 41 | + "time_period": {"type": "string", "description": "时间周期,search_hot_content工具可选,可选值:24h, week, year"}, | ||
| 42 | + "limit": {"type": "integer", "description": "结果数量限制,各工具可选参数"}, | ||
| 43 | + "limit_per_table": {"type": "integer", "description": "每表结果数量限制,search_topic_globally和search_topic_by_date工具可选"} | ||
| 40 | }, | 44 | }, |
| 41 | "required": ["search_query", "search_tool", "reasoning"] | 45 | "required": ["search_query", "search_tool", "reasoning"] |
| 42 | } | 46 | } |
| @@ -80,8 +84,12 @@ output_schema_reflection = { | @@ -80,8 +84,12 @@ output_schema_reflection = { | ||
| 80 | "search_query": {"type": "string"}, | 84 | "search_query": {"type": "string"}, |
| 81 | "search_tool": {"type": "string"}, | 85 | "search_tool": {"type": "string"}, |
| 82 | "reasoning": {"type": "string"}, | 86 | "reasoning": {"type": "string"}, |
| 83 | - "start_date": {"type": "string", "description": "开始日期,格式YYYY-MM-DD,仅search_news_by_date工具需要"}, | ||
| 84 | - "end_date": {"type": "string", "description": "结束日期,格式YYYY-MM-DD,仅search_news_by_date工具需要"} | 87 | + "start_date": {"type": "string", "description": "开始日期,格式YYYY-MM-DD,search_topic_by_date和search_topic_on_platform工具可能需要"}, |
| 88 | + "end_date": {"type": "string", "description": "结束日期,格式YYYY-MM-DD,search_topic_by_date和search_topic_on_platform工具可能需要"}, | ||
| 89 | + "platform": {"type": "string", "description": "平台名称,search_topic_on_platform工具必需,可选值:bilibili, weibo, douyin, kuaishou, xhs, zhihu, tieba"}, | ||
| 90 | + "time_period": {"type": "string", "description": "时间周期,search_hot_content工具可选,可选值:24h, week, year"}, | ||
| 91 | + "limit": {"type": "integer", "description": "结果数量限制,各工具可选参数"}, | ||
| 92 | + "limit_per_table": {"type": "integer", "description": "每表结果数量限制,search_topic_globally和search_topic_by_date工具可选"} | ||
| 85 | }, | 93 | }, |
| 86 | "required": ["search_query", "search_tool", "reasoning"] | 94 | "required": ["search_query", "search_tool", "reasoning"] |
| 87 | } | 95 | } |
| @@ -141,47 +149,83 @@ SYSTEM_PROMPT_REPORT_STRUCTURE = f""" | @@ -141,47 +149,83 @@ SYSTEM_PROMPT_REPORT_STRUCTURE = f""" | ||
| 141 | 149 | ||
| 142 | # 每个段落第一次搜索的系统提示词 | 150 | # 每个段落第一次搜索的系统提示词 |
| 143 | SYSTEM_PROMPT_FIRST_SEARCH = f""" | 151 | SYSTEM_PROMPT_FIRST_SEARCH = f""" |
| 144 | -你是一位深度研究助手。你将获得报告中的一个段落,其标题和预期内容将按照以下JSON模式定义提供: | 152 | +你是一位专业的舆情分析师。你将获得报告中的一个段落,其标题和预期内容将按照以下JSON模式定义提供: |
| 145 | 153 | ||
| 146 | <INPUT JSON SCHEMA> | 154 | <INPUT JSON SCHEMA> |
| 147 | {json.dumps(input_schema_first_search, indent=2, ensure_ascii=False)} | 155 | {json.dumps(input_schema_first_search, indent=2, ensure_ascii=False)} |
| 148 | </INPUT JSON SCHEMA> | 156 | </INPUT JSON SCHEMA> |
| 149 | 157 | ||
| 150 | -你可以使用以下6种专业的新闻搜索工具: | 158 | +你可以使用以下5种专业的本地舆情数据库查询工具来挖掘真实的民意和公众观点: |
| 151 | 159 | ||
| 152 | -1. **basic_search_news** - 基础新闻搜索工具 | ||
| 153 | - - 适用于:一般性的新闻搜索,不确定需要何种特定搜索时 | ||
| 154 | - - 特点:快速、标准的通用搜索,是最常用的基础工具 | 160 | +1. **search_hot_content** - 查找热点内容工具 |
| 161 | + - 适用于:挖掘当前最受关注的舆情事件和话题 | ||
| 162 | + - 特点:基于真实的点赞、评论、分享数据发现热门话题 | ||
| 163 | + - 参数:time_period ('24h', 'week', 'year'),limit(数量限制) | ||
| 155 | 164 | ||
| 156 | -2. **deep_search_news** - 深度新闻分析工具 | ||
| 157 | - - 适用于:需要全面深入了解某个主题时 | ||
| 158 | - - 特点:提供最详细的分析结果,包含高级AI摘要 | 165 | +2. **search_topic_globally** - 全局话题搜索工具 |
| 166 | + - 适用于:全面了解公众对特定话题的讨论和观点 | ||
| 167 | + - 特点:覆盖B站、微博、抖音、快手、小红书、知乎、贴吧等主流平台的真实用户声音 | ||
| 168 | + - 参数:limit_per_table(每个表的结果数量限制) | ||
| 159 | 169 | ||
| 160 | -3. **search_news_last_24_hours** - 24小时最新新闻工具 | ||
| 161 | - - 适用于:需要了解最新动态、突发事件时 | ||
| 162 | - - 特点:只搜索过去24小时的新闻 | 170 | +3. **search_topic_by_date** - 按日期搜索话题工具 |
| 171 | + - 适用于:追踪舆情事件的时间线发展和公众情绪变化 | ||
| 172 | + - 特点:精确的时间范围控制,适合分析舆情演变过程 | ||
| 173 | + - 特殊要求:需要提供start_date和end_date参数,格式为'YYYY-MM-DD' | ||
| 174 | + - 参数:limit_per_table(每个表的结果数量限制) | ||
| 163 | 175 | ||
| 164 | -4. **search_news_last_week** - 本周新闻工具 | ||
| 165 | - - 适用于:需要了解近期发展趋势时 | ||
| 166 | - - 特点:搜索过去一周的新闻报道 | 176 | +4. **get_comments_for_topic** - 获取话题评论工具 |
| 177 | + - 适用于:深度挖掘网民的真实态度、情感和观点 | ||
| 178 | + - 特点:直接获取用户评论,了解民意走向和情感倾向 | ||
| 179 | + - 参数:limit(评论总数量限制) | ||
| 167 | 180 | ||
| 168 | -5. **search_images_for_news** - 图片搜索工具 | ||
| 169 | - - 适用于:需要可视化信息、图片资料时 | ||
| 170 | - - 特点:提供相关图片和图片描述 | 181 | +5. **search_topic_on_platform** - 平台定向搜索工具 |
| 182 | + - 适用于:分析特定社交平台用户群体的观点特征 | ||
| 183 | + - 特点:针对不同平台用户群体的观点差异进行精准分析 | ||
| 184 | + - 特殊要求:需要提供platform参数,可选start_date和end_date | ||
| 185 | + - 参数:platform(必须),start_date, end_date(可选),limit(数量限制) | ||
| 171 | 186 | ||
| 172 | -6. **search_news_by_date** - 按日期范围搜索工具 | ||
| 173 | - - 适用于:需要研究特定历史时期时 | ||
| 174 | - - 特点:可以指定开始和结束日期进行搜索 | ||
| 175 | - - 特殊要求:需要提供start_date和end_date参数,格式为'YYYY-MM-DD' | ||
| 176 | - - 注意:只有这个工具需要额外的时间参数 | 187 | +**你的核心使命:挖掘真实的民意和人情味** |
| 177 | 188 | ||
| 178 | 你的任务是: | 189 | 你的任务是: |
| 179 | -1. 根据段落主题选择最合适的搜索工具 | ||
| 180 | -2. 制定最佳的搜索查询 | ||
| 181 | -3. 如果选择search_news_by_date工具,必须同时提供start_date和end_date参数(格式:YYYY-MM-DD) | ||
| 182 | -4. 解释你的选择理由 | ||
| 183 | - | ||
| 184 | -注意:除了search_news_by_date工具外,其他工具都不需要额外参数。 | 190 | +1. **深度理解段落需求**:根据段落主题,思考需要了解哪些具体的公众观点和情感 |
| 191 | +2. **精准选择查询工具**:选择最能获取真实民意数据的工具 | ||
| 192 | +3. **设计接地气的搜索词**:**这是最关键的环节!** | ||
| 193 | + - **避免官方术语**:不要用"舆情传播"、"公众反应"、"情绪倾向"等书面语 | ||
| 194 | + - **使用网民真实表达**:模拟普通网友会怎么谈论这个话题 | ||
| 195 | + - **贴近生活语言**:用简单、直接、口语化的词汇 | ||
| 196 | + - **包含情感词汇**:网民常用的褒贬词、情绪词 | ||
| 197 | + - **考虑话题热词**:相关的网络流行语、缩写、昵称 | ||
| 198 | +4. **参数优化配置**: | ||
| 199 | + - search_topic_by_date: 必须提供start_date和end_date参数(格式:YYYY-MM-DD) | ||
| 200 | + - search_topic_on_platform: 必须提供platform参数(bilibili, weibo, douyin, kuaishou, xhs, zhihu, tieba之一) | ||
| 201 | + - 其他工具:合理配置limit参数以获取足够的样本 | ||
| 202 | +5. **阐述选择理由**:说明为什么这样的查询能够获得最真实的民意反馈 | ||
| 203 | + | ||
| 204 | +**搜索词设计核心原则**: | ||
| 205 | +- **想象网友怎么说**:如果你是个普通网友,你会怎么讨论这个话题? | ||
| 206 | +- **避免学术词汇**:杜绝"舆情"、"传播"、"倾向"等专业术语 | ||
| 207 | +- **使用具体词汇**:用具体的事件、人名、地名、现象描述 | ||
| 208 | +- **包含情感表达**:如"支持"、"反对"、"担心"、"愤怒"、"点赞"等 | ||
| 209 | +- **考虑网络文化**:网民的表达习惯、缩写、俚语、表情符号文字描述 | ||
| 210 | + | ||
| 211 | +**举例说明**: | ||
| 212 | +- ❌ 错误:"武汉大学舆情 公众反应" | ||
| 213 | +- ✅ 正确:"武大" 或 "武汉大学怎么了" 或 "武大学生" | ||
| 214 | +- ❌ 错误:"校园事件 学生反应" | ||
| 215 | +- ✅ 正确:"学校出事" 或 "同学们都在说" 或 "校友群炸了" | ||
| 216 | + | ||
| 217 | +**不同平台语言特色参考**: | ||
| 218 | +- **微博**:热搜词汇、话题标签,如 "武大又上热搜"、"心疼武大学子" | ||
| 219 | +- **知乎**:问答式表达,如 "如何看待武汉大学"、"武大是什么体验" | ||
| 220 | +- **B站**:弹幕文化,如 "武大yyds"、"武大人路过"、"我武最强" | ||
| 221 | +- **贴吧**:直接称呼,如 "武大吧"、"武大的兄弟们" | ||
| 222 | +- **抖音/快手**:短视频描述,如 "武大日常"、"武大vlog" | ||
| 223 | +- **小红书**:分享式,如 "武大真的很美"、"武大攻略" | ||
| 224 | + | ||
| 225 | +**情感表达词汇库**: | ||
| 226 | +- 正面:"太棒了"、"牛逼"、"绝了"、"爱了"、"yyds"、"666" | ||
| 227 | +- 负面:"无语"、"离谱"、"绝了"、"服了"、"麻了"、"破防" | ||
| 228 | +- 中性:"围观"、"吃瓜"、"路过"、"有一说一"、"实名" | ||
| 185 | 请按照以下JSON模式定义格式化输出(文字请使用中文): | 229 | 请按照以下JSON模式定义格式化输出(文字请使用中文): |
| 186 | 230 | ||
| 187 | <OUTPUT JSON SCHEMA> | 231 | <OUTPUT JSON SCHEMA> |
| @@ -194,13 +238,27 @@ SYSTEM_PROMPT_FIRST_SEARCH = f""" | @@ -194,13 +238,27 @@ SYSTEM_PROMPT_FIRST_SEARCH = f""" | ||
| 194 | 238 | ||
| 195 | # 每个段落第一次总结的系统提示词 | 239 | # 每个段落第一次总结的系统提示词 |
| 196 | SYSTEM_PROMPT_FIRST_SUMMARY = f""" | 240 | SYSTEM_PROMPT_FIRST_SUMMARY = f""" |
| 197 | -你是一位深度研究助手。你将获得搜索查询、搜索结果以及你正在研究的报告段落,数据将按照以下JSON模式定义提供: | 241 | +你是一位专业的舆情分析师和报告撰写专家。你将获得搜索查询、真实的社交媒体数据以及你正在研究的舆情报告段落: |
| 198 | 242 | ||
| 199 | <INPUT JSON SCHEMA> | 243 | <INPUT JSON SCHEMA> |
| 200 | {json.dumps(input_schema_first_summary, indent=2, ensure_ascii=False)} | 244 | {json.dumps(input_schema_first_summary, indent=2, ensure_ascii=False)} |
| 201 | </INPUT JSON SCHEMA> | 245 | </INPUT JSON SCHEMA> |
| 202 | 246 | ||
| 203 | -你的任务是作为研究者,使用搜索结果撰写与段落主题一致的内容,并适当地组织结构以便纳入报告中。 | 247 | +**你的核心任务:将真实的民意数据转化为有温度的舆情分析** |
| 248 | + | ||
| 249 | +撰写要求: | ||
| 250 | +1. **突出真实民意**:优先引用具体的用户评论、真实案例和情感表达 | ||
| 251 | +2. **展现多元观点**:呈现不同平台、不同群体的观点差异和讨论重点 | ||
| 252 | +3. **数据支撑分析**:用具体的点赞数、评论数、转发数等数据说明舆情热度 | ||
| 253 | +4. **情感色彩描述**:准确描述公众的情感倾向(愤怒、支持、担忧、期待等) | ||
| 254 | +5. **避免套话官话**:使用贴近民众的语言,避免过度官方化的表述 | ||
| 255 | + | ||
| 256 | +撰写风格: | ||
| 257 | +- 语言生动,有感染力 | ||
| 258 | +- 引用真实的网民声音和具体案例 | ||
| 259 | +- 体现舆情的复杂性和多面性 | ||
| 260 | +- 突出社会情绪和价值观念的碰撞 | ||
| 261 | +- 让读者感受到真实的民意脉搏 | ||
| 204 | 请按照以下JSON模式定义格式化输出: | 262 | 请按照以下JSON模式定义格式化输出: |
| 205 | 263 | ||
| 206 | <OUTPUT JSON SCHEMA> | 264 | <OUTPUT JSON SCHEMA> |
| @@ -213,29 +271,67 @@ SYSTEM_PROMPT_FIRST_SUMMARY = f""" | @@ -213,29 +271,67 @@ SYSTEM_PROMPT_FIRST_SUMMARY = f""" | ||
| 213 | 271 | ||
| 214 | # 反思(Reflect)的系统提示词 | 272 | # 反思(Reflect)的系统提示词 |
| 215 | SYSTEM_PROMPT_REFLECTION = f""" | 273 | SYSTEM_PROMPT_REFLECTION = f""" |
| 216 | -你是一位深度研究助手。你负责为研究报告构建全面的段落。你将获得段落标题、计划内容摘要,以及你已经创建的段落最新状态,所有这些都将按照以下JSON模式定义提供: | 274 | +你是一位资深的舆情分析师。你负责深化舆情报告的内容,让其更贴近真实的民意和社会情感。你将获得段落标题、计划内容摘要,以及你已经创建的段落最新状态: |
| 217 | 275 | ||
| 218 | <INPUT JSON SCHEMA> | 276 | <INPUT JSON SCHEMA> |
| 219 | {json.dumps(input_schema_reflection, indent=2, ensure_ascii=False)} | 277 | {json.dumps(input_schema_reflection, indent=2, ensure_ascii=False)} |
| 220 | </INPUT JSON SCHEMA> | 278 | </INPUT JSON SCHEMA> |
| 221 | 279 | ||
| 222 | -你可以使用以下6种专业的新闻搜索工具: | 280 | +你可以使用以下5种专业的本地舆情数据库查询工具来深度挖掘民意: |
| 223 | 281 | ||
| 224 | -1. **basic_search_news** - 基础新闻搜索工具 | ||
| 225 | -2. **deep_search_news** - 深度新闻分析工具 | ||
| 226 | -3. **search_news_last_24_hours** - 24小时最新新闻工具 | ||
| 227 | -4. **search_news_last_week** - 本周新闻工具 | ||
| 228 | -5. **search_images_for_news** - 图片搜索工具 | ||
| 229 | -6. **search_news_by_date** - 按日期范围搜索工具(需要时间参数) | 282 | +1. **search_hot_content** - 查找热点内容工具 |
| 283 | +2. **search_topic_globally** - 全局话题搜索工具 | ||
| 284 | +3. **search_topic_by_date** - 按日期搜索话题工具 | ||
| 285 | +4. **get_comments_for_topic** - 获取话题评论工具 | ||
| 286 | +5. **search_topic_on_platform** - 平台定向搜索工具 | ||
| 230 | 287 | ||
| 231 | -你的任务是: | ||
| 232 | -1. 反思段落文本的当前状态,思考是否遗漏了主题的某些关键方面 | ||
| 233 | -2. 选择最合适的搜索工具来补充缺失信息 | ||
| 234 | -3. 制定精确的搜索查询 | ||
| 235 | -4. 如果选择search_news_by_date工具,必须同时提供start_date和end_date参数(格式:YYYY-MM-DD) | ||
| 236 | -5. 解释你的选择和推理 | 288 | +**反思的核心目标:让报告更有人情味和真实感** |
| 237 | 289 | ||
| 238 | -注意:除了search_news_by_date工具外,其他工具都不需要额外参数。 | 290 | +你的任务是: |
| 291 | +1. **深度反思内容质量**: | ||
| 292 | + - 当前段落是否过于官方化、套路化? | ||
| 293 | + - 是否缺乏真实的民众声音和情感表达? | ||
| 294 | + - 是否遗漏了重要的公众观点和争议焦点? | ||
| 295 | + - 是否需要补充具体的网民评论和真实案例? | ||
| 296 | + | ||
| 297 | +2. **识别信息缺口**: | ||
| 298 | + - 缺少哪个平台的用户观点?(如B站年轻人、微博话题讨论、知乎深度分析等) | ||
| 299 | + - 缺少哪个时间段的舆情变化? | ||
| 300 | + - 缺少哪些具体的民意表达和情感倾向? | ||
| 301 | + | ||
| 302 | +3. **精准补充查询**: | ||
| 303 | + - 选择最能填补信息缺口的查询工具 | ||
| 304 | + - **设计接地气的搜索关键词**: | ||
| 305 | + * 避免继续使用官方化、书面化的词汇 | ||
| 306 | + * 思考网民会用什么词来表达这个观点 | ||
| 307 | + * 使用具体的、有情感色彩的词汇 | ||
| 308 | + * 考虑不同平台的语言特色(如B站弹幕文化、微博热搜词汇等) | ||
| 309 | + - 重点关注评论区和用户原创内容 | ||
| 310 | + | ||
| 311 | +4. **参数配置要求**: | ||
| 312 | + - search_topic_by_date: 必须提供start_date和end_date参数(格式:YYYY-MM-DD) | ||
| 313 | + - search_topic_on_platform: 必须提供platform参数(bilibili, weibo, douyin, kuaishou, xhs, zhihu, tieba之一) | ||
| 314 | + - 其他工具:合理配置参数以获取多样化的民意样本 | ||
| 315 | + | ||
| 316 | +5. **阐述补充理由**:明确说明为什么需要这些额外的民意数据 | ||
| 317 | + | ||
| 318 | +**反思重点**: | ||
| 319 | +- 报告是否反映了真实的社会情绪? | ||
| 320 | +- 是否包含了不同群体的观点和声音? | ||
| 321 | +- 是否有具体的用户评论和真实案例支撑? | ||
| 322 | +- 是否体现了舆情的复杂性和多面性? | ||
| 323 | +- 语言表达是否贴近民众,避免过度官方化? | ||
| 324 | + | ||
| 325 | +**搜索词优化示例(重要!)**: | ||
| 326 | +- 如果需要了解"武汉大学"相关内容: | ||
| 327 | + * ❌ 不要用:"武汉大学舆情"、"校园事件"、"学生反应" | ||
| 328 | + * ✅ 应该用:"武大"、"武汉大学"、"珞珈山"、"樱花大道" | ||
| 329 | +- 如果需要了解争议话题: | ||
| 330 | + * ❌ 不要用:"争议事件"、"公众争议" | ||
| 331 | + * ✅ 应该用:"出事了"、"怎么回事"、"翻车"、"炸了" | ||
| 332 | +- 如果需要了解情感态度: | ||
| 333 | + * ❌ 不要用:"情感倾向"、"态度分析" | ||
| 334 | + * ✅ 应该用:"支持"、"反对"、"心疼"、"气死"、"666"、"绝了" | ||
| 239 | 请按照以下JSON模式定义格式化输出: | 335 | 请按照以下JSON模式定义格式化输出: |
| 240 | 336 | ||
| 241 | <OUTPUT JSON SCHEMA> | 337 | <OUTPUT JSON SCHEMA> |
| @@ -248,18 +344,28 @@ SYSTEM_PROMPT_REFLECTION = f""" | @@ -248,18 +344,28 @@ SYSTEM_PROMPT_REFLECTION = f""" | ||
| 248 | 344 | ||
| 249 | # 总结反思的系统提示词 | 345 | # 总结反思的系统提示词 |
| 250 | SYSTEM_PROMPT_REFLECTION_SUMMARY = f""" | 346 | SYSTEM_PROMPT_REFLECTION_SUMMARY = f""" |
| 251 | -你是一位深度研究助手。 | ||
| 252 | -你将获得搜索查询、搜索结果、段落标题以及你正在研究的报告段落的预期内容。 | ||
| 253 | -你正在迭代完善这个段落,并且段落的最新状态也会提供给你。 | 347 | +你是一位资深的舆情分析师和内容优化专家。 |
| 348 | +你正在深化和完善舆情报告段落,让其更贴近真实民意、更有说服力和感染力。 | ||
| 254 | 数据将按照以下JSON模式定义提供: | 349 | 数据将按照以下JSON模式定义提供: |
| 255 | 350 | ||
| 256 | <INPUT JSON SCHEMA> | 351 | <INPUT JSON SCHEMA> |
| 257 | {json.dumps(input_schema_reflection_summary, indent=2, ensure_ascii=False)} | 352 | {json.dumps(input_schema_reflection_summary, indent=2, ensure_ascii=False)} |
| 258 | </INPUT JSON SCHEMA> | 353 | </INPUT JSON SCHEMA> |
| 259 | 354 | ||
| 260 | -你的任务是根据搜索结果和预期内容丰富段落的当前最新状态。 | ||
| 261 | -不要删除最新状态中的关键信息,尽量丰富它,只添加缺失的信息。 | ||
| 262 | -适当地组织段落结构以便纳入报告中。 | 355 | +**你的任务:让段落更有人情味和真实感** |
| 356 | + | ||
| 357 | +优化策略: | ||
| 358 | +1. **融入新的民意数据**:将补充搜索到的真实用户声音整合到段落中 | ||
| 359 | +2. **丰富情感表达**:增加具体的情感描述和社会情绪分析 | ||
| 360 | +3. **补充遗漏观点**:添加之前缺失的不同群体、平台的观点 | ||
| 361 | +4. **强化数据支撑**:用具体数字和案例让分析更有说服力 | ||
| 362 | +5. **优化语言表达**:让文字更生动、更贴近民众,减少官方套话 | ||
| 363 | + | ||
| 364 | +注意事项: | ||
| 365 | +- 保留段落的核心观点和重要信息 | ||
| 366 | +- 增强内容的真实性和可信度 | ||
| 367 | +- 体现舆情的复杂性和多样性 | ||
| 368 | +- 让读者能感受到真实的社会脉搏 | ||
| 263 | 请按照以下JSON模式定义格式化输出: | 369 | 请按照以下JSON模式定义格式化输出: |
| 264 | 370 | ||
| 265 | <OUTPUT JSON SCHEMA> | 371 | <OUTPUT JSON SCHEMA> |
| @@ -272,14 +378,28 @@ SYSTEM_PROMPT_REFLECTION_SUMMARY = f""" | @@ -272,14 +378,28 @@ SYSTEM_PROMPT_REFLECTION_SUMMARY = f""" | ||
| 272 | 378 | ||
| 273 | # 最终研究报告格式化的系统提示词 | 379 | # 最终研究报告格式化的系统提示词 |
| 274 | SYSTEM_PROMPT_REPORT_FORMATTING = f""" | 380 | SYSTEM_PROMPT_REPORT_FORMATTING = f""" |
| 275 | -你是一位深度研究助手。你已经完成了研究并构建了报告中所有段落的最终版本。 | 381 | +你是一位专业的舆情报告编辑和格式化专家。你已经完成了深度的舆情分析并构建了报告中所有段落的最终版本。 |
| 276 | 你将获得以下JSON格式的数据: | 382 | 你将获得以下JSON格式的数据: |
| 277 | 383 | ||
| 278 | <INPUT JSON SCHEMA> | 384 | <INPUT JSON SCHEMA> |
| 279 | {json.dumps(input_schema_report_formatting, indent=2, ensure_ascii=False)} | 385 | {json.dumps(input_schema_report_formatting, indent=2, ensure_ascii=False)} |
| 280 | </INPUT JSON SCHEMA> | 386 | </INPUT JSON SCHEMA> |
| 281 | 387 | ||
| 282 | -你的任务是将报告格式化为美观的形式,并以Markdown格式返回。 | ||
| 283 | -如果没有结论段落,请根据其他段落的最新状态在报告末尾添加一个结论。 | ||
| 284 | -使用段落标题来创建报告的标题。 | 388 | +**你的任务:将舆情分析格式化为专业、有感染力的报告** |
| 389 | + | ||
| 390 | +格式化要求: | ||
| 391 | +1. **标题设计**:创建吸引人、有概括性的报告标题 | ||
| 392 | +2. **结构优化**:确保段落逻辑清晰,层次分明 | ||
| 393 | +3. **突出重点**:用**粗体**、*斜体*等格式突出关键观点和数据 | ||
| 394 | +4. **数据可视**:用表格或列表呈现重要的舆情数据 | ||
| 395 | +5. **增强可读性**:合理使用分段、标题层级和格式化元素 | ||
| 396 | + | ||
| 397 | +结论撰写(如果需要): | ||
| 398 | +- 总结主要的舆情发现和民意倾向 | ||
| 399 | +- 突出不同平台和群体的观点特征 | ||
| 400 | +- 提炼深层的社会情绪和价值观念 | ||
| 401 | +- 用数据和具体案例支撑结论 | ||
| 402 | +- 语言简洁有力,避免空洞套话 | ||
| 403 | + | ||
| 404 | +最终输出:专业的Markdown格式舆情分析报告 | ||
| 285 | """ | 405 | """ |
| 1 | """ | 1 | """ |
| 2 | 工具调用模块 | 2 | 工具调用模块 |
| 3 | -提供外部工具接口,如网络搜索等 | 3 | +提供外部工具接口,如本地数据库查询等 |
| 4 | """ | 4 | """ |
| 5 | 5 | ||
| 6 | from .search import ( | 6 | from .search import ( |
| 7 | - TavilyNewsAgency, | ||
| 8 | - SearchResult, | ||
| 9 | - TavilyResponse, | ||
| 10 | - ImageResult, | 7 | + MediaCrawlerDB, |
| 8 | + QueryResult, | ||
| 9 | + DBResponse, | ||
| 11 | print_response_summary | 10 | print_response_summary |
| 12 | ) | 11 | ) |
| 13 | 12 | ||
| 14 | __all__ = [ | 13 | __all__ = [ |
| 15 | - "TavilyNewsAgency", | ||
| 16 | - "SearchResult", | ||
| 17 | - "TavilyResponse", | ||
| 18 | - "ImageResult", | 14 | + "MediaCrawlerDB", |
| 15 | + "QueryResult", | ||
| 16 | + "DBResponse", | ||
| 19 | "print_response_summary" | 17 | "print_response_summary" |
| 20 | ] | 18 | ] |
| @@ -14,7 +14,14 @@ class Config: | @@ -14,7 +14,14 @@ class Config: | ||
| 14 | # API密钥 | 14 | # API密钥 |
| 15 | deepseek_api_key: Optional[str] = None | 15 | deepseek_api_key: Optional[str] = None |
| 16 | openai_api_key: Optional[str] = None | 16 | openai_api_key: Optional[str] = None |
| 17 | - tavily_api_key: Optional[str] = None | 17 | + |
| 18 | + # 数据库配置 | ||
| 19 | + db_host: Optional[str] = None | ||
| 20 | + db_user: Optional[str] = None | ||
| 21 | + db_password: Optional[str] = None | ||
| 22 | + db_name: Optional[str] = None | ||
| 23 | + db_port: int = 3306 | ||
| 24 | + db_charset: str = "utf8mb4" | ||
| 18 | 25 | ||
| 19 | # 模型配置 | 26 | # 模型配置 |
| 20 | default_llm_provider: str = "deepseek" # deepseek 或 openai | 27 | default_llm_provider: str = "deepseek" # deepseek 或 openai |
| @@ -44,8 +51,8 @@ class Config: | @@ -44,8 +51,8 @@ class Config: | ||
| 44 | print("错误: OpenAI API Key未设置") | 51 | print("错误: OpenAI API Key未设置") |
| 45 | return False | 52 | return False |
| 46 | 53 | ||
| 47 | - if not self.tavily_api_key: | ||
| 48 | - print("错误: Tavily API Key未设置") | 54 | + if not all([self.db_host, self.db_user, self.db_password, self.db_name]): |
| 55 | + print("错误: 数据库连接信息不完整") | ||
| 49 | return False | 56 | return False |
| 50 | 57 | ||
| 51 | return True | 58 | return True |
| @@ -65,7 +72,14 @@ class Config: | @@ -65,7 +72,14 @@ class Config: | ||
| 65 | return cls( | 72 | return cls( |
| 66 | deepseek_api_key=getattr(config_module, "DEEPSEEK_API_KEY", None), | 73 | deepseek_api_key=getattr(config_module, "DEEPSEEK_API_KEY", None), |
| 67 | openai_api_key=getattr(config_module, "OPENAI_API_KEY", None), | 74 | openai_api_key=getattr(config_module, "OPENAI_API_KEY", None), |
| 68 | - tavily_api_key=getattr(config_module, "TAVILY_API_KEY", None), | 75 | + |
| 76 | + db_host=getattr(config_module, "DB_HOST", None), | ||
| 77 | + db_user=getattr(config_module, "DB_USER", None), | ||
| 78 | + db_password=getattr(config_module, "DB_PASSWORD", None), | ||
| 79 | + db_name=getattr(config_module, "DB_NAME", None), | ||
| 80 | + db_port=getattr(config_module, "DB_PORT", 3306), | ||
| 81 | + db_charset=getattr(config_module, "DB_CHARSET", "utf8mb4"), | ||
| 82 | + | ||
| 69 | default_llm_provider=getattr(config_module, "DEFAULT_LLM_PROVIDER", "deepseek"), | 83 | default_llm_provider=getattr(config_module, "DEFAULT_LLM_PROVIDER", "deepseek"), |
| 70 | deepseek_model=getattr(config_module, "DEEPSEEK_MODEL", "deepseek-chat"), | 84 | deepseek_model=getattr(config_module, "DEEPSEEK_MODEL", "deepseek-chat"), |
| 71 | openai_model=getattr(config_module, "OPENAI_MODEL", "gpt-4o-mini"), | 85 | openai_model=getattr(config_module, "OPENAI_MODEL", "gpt-4o-mini"), |
| @@ -92,7 +106,14 @@ class Config: | @@ -92,7 +106,14 @@ class Config: | ||
| 92 | return cls( | 106 | return cls( |
| 93 | deepseek_api_key=config_dict.get("DEEPSEEK_API_KEY"), | 107 | deepseek_api_key=config_dict.get("DEEPSEEK_API_KEY"), |
| 94 | openai_api_key=config_dict.get("OPENAI_API_KEY"), | 108 | openai_api_key=config_dict.get("OPENAI_API_KEY"), |
| 95 | - tavily_api_key=config_dict.get("TAVILY_API_KEY"), | 109 | + |
| 110 | + db_host=config_dict.get("DB_HOST"), | ||
| 111 | + db_user=config_dict.get("DB_USER"), | ||
| 112 | + db_password=config_dict.get("DB_PASSWORD"), | ||
| 113 | + db_name=config_dict.get("DB_NAME"), | ||
| 114 | + db_port=int(config_dict.get("DB_PORT", "3306")), | ||
| 115 | + db_charset=config_dict.get("DB_CHARSET", "utf8mb4"), | ||
| 116 | + | ||
| 96 | default_llm_provider=config_dict.get("DEFAULT_LLM_PROVIDER", "deepseek"), | 117 | default_llm_provider=config_dict.get("DEFAULT_LLM_PROVIDER", "deepseek"), |
| 97 | deepseek_model=config_dict.get("DEEPSEEK_MODEL", "deepseek-chat"), | 118 | deepseek_model=config_dict.get("DEEPSEEK_MODEL", "deepseek-chat"), |
| 98 | openai_model=config_dict.get("OPENAI_MODEL", "gpt-4o-mini"), | 119 | openai_model=config_dict.get("OPENAI_MODEL", "gpt-4o-mini"), |
| @@ -147,7 +168,7 @@ def print_config(config: Config): | @@ -147,7 +168,7 @@ def print_config(config: Config): | ||
| 147 | print(f"LLM提供商: {config.default_llm_provider}") | 168 | print(f"LLM提供商: {config.default_llm_provider}") |
| 148 | print(f"DeepSeek模型: {config.deepseek_model}") | 169 | print(f"DeepSeek模型: {config.deepseek_model}") |
| 149 | print(f"OpenAI模型: {config.openai_model}") | 170 | print(f"OpenAI模型: {config.openai_model}") |
| 150 | - print(f"最大搜索结果数: {config.max_search_results}") | 171 | + |
| 151 | print(f"搜索超时: {config.search_timeout}秒") | 172 | print(f"搜索超时: {config.search_timeout}秒") |
| 152 | print(f"最大内容长度: {config.max_content_length}") | 173 | print(f"最大内容长度: {config.max_content_length}") |
| 153 | print(f"最大反思次数: {config.max_reflections}") | 174 | print(f"最大反思次数: {config.max_reflections}") |
| @@ -155,8 +176,11 @@ def print_config(config: Config): | @@ -155,8 +176,11 @@ def print_config(config: Config): | ||
| 155 | print(f"输出目录: {config.output_dir}") | 176 | print(f"输出目录: {config.output_dir}") |
| 156 | print(f"保存中间状态: {config.save_intermediate_states}") | 177 | print(f"保存中间状态: {config.save_intermediate_states}") |
| 157 | 178 | ||
| 158 | - # 显示API密钥状态(不显示实际密钥) | 179 | + # 显示API密钥和数据库状态(不显示实际密钥) |
| 159 | print(f"DeepSeek API Key: {'已设置' if config.deepseek_api_key else '未设置'}") | 180 | print(f"DeepSeek API Key: {'已设置' if config.deepseek_api_key else '未设置'}") |
| 160 | print(f"OpenAI API Key: {'已设置' if config.openai_api_key else '未设置'}") | 181 | print(f"OpenAI API Key: {'已设置' if config.openai_api_key else '未设置'}") |
| 161 | - print(f"Tavily API Key: {'已设置' if config.tavily_api_key else '未设置'}") | 182 | + print(f"数据库连接: {'已配置' if all([config.db_host, config.db_user, config.db_password, config.db_name]) else '未配置'}") |
| 183 | + print(f"数据库主机: {config.db_host}") | ||
| 184 | + print(f"数据库端口: {config.db_port}") | ||
| 185 | + print(f"数据库名称: {config.db_name}") | ||
| 162 | print("==================\n") | 186 | print("==================\n") |
| @@ -12,8 +12,8 @@ import json | @@ -12,8 +12,8 @@ import json | ||
| 12 | # 添加src目录到Python路径 | 12 | # 添加src目录到Python路径 |
| 13 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), '.')) | 13 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), '.')) |
| 14 | 14 | ||
| 15 | -from QueryEngine import DeepSearchAgent, Config | ||
| 16 | -from config import DEEPSEEK_API_KEY, TAVILY_API_KEY | 15 | +from InsightEngine import DeepSearchAgent, Config |
| 16 | +from config import DEEPSEEK_API_KEY, DB_HOST, DB_USER, DB_PASSWORD, DB_NAME, DB_PORT, DB_CHARSET | ||
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | def main(): | 19 | def main(): |
| @@ -24,8 +24,8 @@ def main(): | @@ -24,8 +24,8 @@ def main(): | ||
| 24 | layout="wide" | 24 | layout="wide" |
| 25 | ) | 25 | ) |
| 26 | 26 | ||
| 27 | - st.title("Deep Search Agent") | ||
| 28 | - st.markdown("基于DeepSeek的无框架深度搜索AI代理") | 27 | + st.title("Insight Engine Agent") |
| 28 | + st.markdown("基于DeepSeek的本地舆情数据库深度分析AI代理") | ||
| 29 | 29 | ||
| 30 | # 侧边栏配置 | 30 | # 侧边栏配置 |
| 31 | with st.sidebar: | 31 | with st.sidebar: |
| @@ -96,21 +96,31 @@ def main(): | @@ -96,21 +96,31 @@ def main(): | ||
| 96 | st.error("请提供OpenAI API Key") | 96 | st.error("请提供OpenAI API Key") |
| 97 | return | 97 | return |
| 98 | 98 | ||
| 99 | - # 自动使用配置文件中的API密钥 | 99 | + # 自动使用配置文件中的API密钥和数据库配置 |
| 100 | deepseek_key = DEEPSEEK_API_KEY | 100 | deepseek_key = DEEPSEEK_API_KEY |
| 101 | - tavily_key = TAVILY_API_KEY | 101 | + db_host = DB_HOST |
| 102 | + db_user = DB_USER | ||
| 103 | + db_password = DB_PASSWORD | ||
| 104 | + db_name = DB_NAME | ||
| 105 | + db_port = DB_PORT | ||
| 106 | + db_charset = DB_CHARSET | ||
| 102 | 107 | ||
| 103 | # 创建配置 | 108 | # 创建配置 |
| 104 | config = Config( | 109 | config = Config( |
| 105 | deepseek_api_key=deepseek_key if llm_provider == "deepseek" else None, | 110 | deepseek_api_key=deepseek_key if llm_provider == "deepseek" else None, |
| 106 | openai_api_key=openai_key if llm_provider == "openai" else None, | 111 | openai_api_key=openai_key if llm_provider == "openai" else None, |
| 107 | - tavily_api_key=tavily_key, | 112 | + db_host=db_host, |
| 113 | + db_user=db_user, | ||
| 114 | + db_password=db_password, | ||
| 115 | + db_name=db_name, | ||
| 116 | + db_port=db_port, | ||
| 117 | + db_charset=db_charset, | ||
| 108 | default_llm_provider=llm_provider, | 118 | default_llm_provider=llm_provider, |
| 109 | deepseek_model=model_name if llm_provider == "deepseek" else "deepseek-chat", | 119 | deepseek_model=model_name if llm_provider == "deepseek" else "deepseek-chat", |
| 110 | openai_model=model_name if llm_provider == "openai" else "gpt-4o-mini", | 120 | openai_model=model_name if llm_provider == "openai" else "gpt-4o-mini", |
| 111 | max_reflections=max_reflections, | 121 | max_reflections=max_reflections, |
| 112 | max_content_length=max_content_length, | 122 | max_content_length=max_content_length, |
| 113 | - output_dir="query_engine_streamlit_reports" | 123 | + output_dir="insight_engine_streamlit_reports" |
| 114 | ) | 124 | ) |
| 115 | 125 | ||
| 116 | # 执行研究 | 126 | # 执行研究 |
-
Please register or login to post a comment