report_agent.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. """
  2. 报告大纲生成Agent (Report Outline Generation Agent)
  3. ===============================================
  4. 此Agent负责根据用户需求和数据样本,生成专业的报告大纲结构。
  5. 核心功能:
  6. 1. 分析用户需求:理解报告目标和关键指标
  7. 2. 数据结构分析:识别可用字段和数据特征
  8. 3. 大纲生成:创建结构化的报告章节和指标需求
  9. 4. 智能推断:自动推断所需字段和计算逻辑
  10. 工作流程:
  11. 1. 接收用户查询和数据样本
  12. 2. 分析数据结构和可用字段
  13. 3. 生成报告标题和章节结构
  14. 4. 定义全局指标需求
  15. 5. 返回结构化的大纲对象
  16. 技术实现:
  17. - 使用LangChain和结构化输出
  18. - 支持异步处理
  19. - 自动字段推断和补全
  20. - 错误处理和默认值提供
  21. 作者: Big Agent Team
  22. 版本: 1.0.0
  23. 创建时间: 2024-12-20
  24. """
  25. from typing import List, Dict, Any
  26. from langchain_openai import ChatOpenAI
  27. from langchain_core.prompts import ChatPromptTemplate
  28. from llmops.config import enable_kp_rc_prompts
  29. class ReportSectionGeneratorAgent:
  30. """报告章节内容生成智能体"""
  31. def __init__(self, api_key: str, base_url: str = "https://api.deepseek.com", model_name: str = "deepseek-chat"):
  32. """
  33. 初始化大纲生成Agent
  34. Args:
  35. api_key: DeepSeek API密钥
  36. base_url: DeepSeek API基础URL
  37. model_name: 模型名称
  38. """
  39. self.llm = ChatOpenAI(
  40. model=model_name,
  41. api_key=api_key,
  42. base_url=base_url,
  43. temperature=0.1
  44. )
  45. def get_prompt(self):
  46. """
  47. 获取报告章节写作提示词模板, 根据配置开关动态获取
  48. """
  49. prompt = ""
  50. if enable_kp_rc_prompts == 1: # 从知识沉淀平台获取
  51. prompt = self._get_prompt_from_klg()
  52. if len(prompt) == 0: # 获取默认提示词
  53. prompt = self._get_base_prompt()
  54. return prompt
  55. def _get_prompt_from_klg(self):
  56. """
  57. 从知识沉淀平台获取报告章节内容提示词模板(预留)
  58. 可能包括的步骤:
  59. 1、对应接口(通过配置指定) post 请求
  60. 2、获取结果,重新解析组织
  61. 3、异常情况处理
  62. """
  63. try:
  64. pass
  65. except Exception as e:
  66. return ""
  67. def _get_base_prompt(self):
  68. """
  69. 获取基础(默认)报告章节写作提示词
  70. """
  71. template = """
  72. ## 基本要求
  73. 你是一位专业的报告撰写专家,需要基于以下参数生成高质量的章节内容。
  74. ## 输入参数
  75. 1. ** 章节标题 **:{title}
  76. 2. ** 写作范围 **:{writing_scope}
  77. 3. ** 指标要求 **:{indicators}
  78. ## 生成要求
  79. 请按照以下结构生成内容:
  80. ### 1. 章节开头(引言部分)
  81. - 简要说明本章节的核心主题
  82. - 阐述本章节在整体报告中的定位和作用
  83. - 概述将要分析的主要内容和逻辑脉络
  84. ### 2. 主体内容分析
  85. ** 基于写作范围和指标要求,具体包含: **
  86. #### a) 数据/现状分析
  87. - 对相关指标进行系统性分析
  88. - 使用数据支持观点(如提供具体数据)
  89. #### b) 问题/趋势识别
  90. - 识别当前存在的主要问题
  91. - 分析发展趋势和潜在机遇
  92. #### c) 深度解读
  93. - 对关键指标进行深入解读
  94. - 分析指标间的相互关系和影响
  95. ### 3. 章节总结
  96. - 归纳本章核心发现
  97. - 提出关键结论
  98. - 引出可能的建议或下一步分析方向
  99. ## 写作风格要求
  100. - 专业、严谨、客观
  101. - 数据驱动,避免主观臆断
  102. - 逻辑清晰,层次分明
  103. - 语言精炼,避免冗余
  104. - 使用适当的学术 / 行业术语
  105. ## 格式要求
  106. - 使用Markdown格式
  107. - 适当使用标题层级( ##、###等)
  108. - 重要观点可使用 ** 加粗 ** 强调
  109. - 数据可使用表格或列表清晰呈现
  110. 请开始生成章节内容:
  111. """
  112. return template
  113. async def generate_section_content(self, section: Dict[str, Any]) -> str:
  114. """异步生成报告章节内容"""
  115. # 获取报告章节写作提示词
  116. template = self.get_prompt()
  117. pt = ChatPromptTemplate.from_template(template)
  118. chain = pt | self.llm
  119. response = await chain.ainvoke({
  120. "title": section["title"],
  121. "writing_scope": section["description"],
  122. "indicators": section["metrics"]})
  123. # 解析JSON响应
  124. try:
  125. # 从响应中提取JSON内容
  126. section_content = response.content if hasattr(response, 'content') else str(response)
  127. except Exception as e:
  128. print(f"生成报告章节内容失败: {e}")
  129. raise ValueError(f"生成报告章节:{section['title']}异常, {str(e)}")
  130. return section_content
  131. async def generate_report_section_content(api_key: str, base_url: str, model_name: str, section: Dict[str, Any], chapter_num: int, total_sections: int, max_retries: int = 2) -> str:
  132. """
  133. 生成报告章节内容(可以重试)
  134. Args:
  135. api_key: API密钥
  136. base_url: LLM base url
  137. model_name: LLM model name
  138. section:章节对象
  139. max_retries: 重试次数
  140. Returns:
  141. 生成的章节内容
  142. """
  143. import asyncio
  144. import time
  145. agent = ReportSectionGeneratorAgent(api_key=api_key, base_url=base_url, model_name=model_name)
  146. print(f"📝 开始生成报告第 {chapter_num}章/{total_sections} 内容:{section['title']} 内容(最多重试 {max_retries} 次)...")
  147. section_content = ""
  148. for attempt in range(max_retries):
  149. try:
  150. print(f" 尝试 {attempt + 1}/{max_retries}...")
  151. start_time = time.time()
  152. # 生成章节内容
  153. section_content = await agent.generate_section_content(section)
  154. elapsed_time = time.time() - start_time
  155. print(f"{elapsed_time:.2f}")
  156. print(f"\n📝 章节{section['title']} 内容生成成功:")
  157. return section_content
  158. except Exception as e:
  159. elapsed_time = time.time() - start_time if 'start_time' in locals() else 0
  160. print(f" 错误详情: {str(e)}")
  161. # 如果不是最后一次尝试,等待后重试
  162. if attempt < max_retries - 1:
  163. print(f" ⏳ {retry_delay} 秒后进行第 {attempt + 2} 次重试...")
  164. await asyncio.sleep(retry_delay)
  165. # 增加重试间隔,避免频繁调用
  166. retry_delay = min(retry_delay * 1.5, 10.0) # 最多等待10秒
  167. else:
  168. print(f" ❌ 生成报告章节内容{section['title']} 已达到最大重试次数 ({max_retries})")
  169. # 所有重试都失败后,使用默认结构
  170. print("⚠️ 所有重试均失败,使用默认大纲结构")