post_summary_deconstruction_agent.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. """
  2. Post Summary Deconstruction Agent.
  3. 帖子整体解构Agent:解构帖子整体的各种描述维度。
  4. """
  5. from typing import Dict, Any, List
  6. import json
  7. from src.components.agents.base import BaseLLMAgent
  8. from src.states.what_deconstruction_state import WhatDeconstructionState
  9. from src.utils.logger import get_logger
  10. logger = get_logger(__name__)
  11. class PostSummaryDeconstructionAgent(BaseLLMAgent):
  12. """帖子整体解构Agent
  13. 根据 PRD 1.4 - 2.2.2.2 帖子整体节点要求:
  14. **核心功能**:
  15. 1. 综合所有子元素的解构结果(图片、文本)
  16. 2. 从多个描述维度解构帖子整体
  17. 3. 帖子总体的各种描述、总结、归纳等维度或角度,每一个维度都是一个字段项
  18. **必须满足的约束条件**:
  19. 1. 维度名称和数量根据每篇帖子动态变化
  20. 2. **一定要包含本帖子最关键/最吸引内容消费者的亮点维度**
  21. - 包括但不限于:内容亮点、情绪共鸣点、创作手法等维度
  22. 3. **描述维度的数量不得少于3个**
  23. - 要尽可能多而全的覆盖这个帖子的所有可描述角度
  24. 4. 包括但不限于:品类、主题、脚本等维度
  25. 5. 描述维度应根据帖子的品类、主题、关键词等信息来向知识库动态请求询问
  26. """
  27. def __init__(
  28. self,
  29. name: str = "post_summary_deconstruction_agent",
  30. description: str = "解构帖子整体的各种描述维度",
  31. model_provider: str = "google_genai",
  32. temperature: float = 0.1,
  33. max_tokens: int = 10240
  34. ):
  35. system_prompt = """你是一个专业的内容分析专家。你的任务是从多个维度解构帖子整体。
  36. 根据 PRD 1.4 - 2.2.2.2 帖子整体节点要求:
  37. **核心任务**:
  38. 1. 综合分析所有子元素(图片、文本)的解构结果
  39. 2. 从多个描述维度解构帖子整体
  40. 3. 帖子总体的各种描述、总结、归纳等维度或角度,每一个维度都是一个字段项
  41. **必须满足的约束条件**:
  42. 1. ✅ 维度名称和数量根据每篇帖子动态变化
  43. 2. ✅ **一定要包含本帖子最关键/最吸引内容消费者的亮点维度**
  44. - 包括但不限于:内容亮点、情绪共鸣点、创作手法等维度
  45. 3. ✅ **描述维度的数量不得少于3个**
  46. - 要尽可能多而全的覆盖这个帖子的所有可描述角度
  47. 4. ✅ 包括但不限于:品类、主题、脚本等维度
  48. 5. ✅ 描述维度应根据帖子的品类、主题、关键词等信息来向知识库动态请求询问
  49. - Query句式(参见 PRD 1.4 - 3.2):
  50. 对于一篇主题为"{帖子主题}",品类为"{帖子品类}",关键词包含"{帖子关键词列表}"的多模态社交媒体帖子,
  51. 从内容创作者视角进行What要素的初步识别和分类,需要使用哪些通用工具?
  52. **输出格式必须是JSON**:
  53. {
  54. "品类": "值",
  55. "主题": "值",
  56. "脚本": "值",
  57. "内容亮点": "值", # 必须包含
  58. "情绪共鸣点": "值", # 必须包含
  59. "创作手法": "值", # 必须包含
  60. "整体风格": "值",
  61. "视觉呈现": "值",
  62. "内容价值": "值",
  63. "目标受众": "值",
  64. ... # 其他动态维度(根据帖子特点)
  65. }
  66. **输出要求**:
  67. 1. ✅ 客观准确,严格基于实际解构结果,禁止主观臆测、凭空假设、虚构数据
  68. 2. ✅ 维度名称和数量根据帖子内容动态确定
  69. 3. ✅ 突出核心特征和亮点
  70. 4. ✅ **最少包含3个描述维度**
  71. 5. ✅ 必须包含"内容亮点"、"情绪共鸣点"、"创作手法"等吸引消费者的亮点维度
  72. """
  73. super().__init__(
  74. name=name,
  75. description=description,
  76. model_provider=model_provider,
  77. system_prompt=system_prompt,
  78. temperature=temperature,
  79. max_tokens=max_tokens
  80. )
  81. def _build_messages(self, state: WhatDeconstructionState) -> List[Dict[str, Any]]:
  82. """构建消息
  83. 包含所有子元素解构结果、消费者亮点、总结维度
  84. """
  85. messages = [
  86. {"role": "system", "content": self.system_prompt}
  87. ]
  88. # 构建用户消息
  89. user_prompt = "# 帖子解构结果\n\n"
  90. # 添加帖子基本信息
  91. category = state.get("category", "未知")
  92. theme = state.get("theme", "未知")
  93. keywords = state.get("keywords", [])
  94. user_prompt += f"**品类**:{category}\n"
  95. user_prompt += f"**主题**:{theme}\n"
  96. user_prompt += f"**关键词**:{', '.join(keywords)}\n\n"
  97. # 添加图片解构结果
  98. image_results = state.get("image_deconstruction_results", [])
  99. if image_results:
  100. user_prompt += "## 图片元素解构\n\n"
  101. for idx, img_result in enumerate(image_results, 1):
  102. what = img_result.get("what", "")
  103. desc = img_result.get("description", img_result.get("描述", {}))
  104. user_prompt += f"### 图片 {idx}\n"
  105. user_prompt += f"**What**: {what}\n"
  106. user_prompt += f"**描述**: {json.dumps(desc, ensure_ascii=False, indent=2)}\n\n"
  107. # 添加文本解构结果
  108. text_results = state.get("text_deconstruction_results", [])
  109. if text_results:
  110. user_prompt += "## 文本元素解构\n\n"
  111. for idx, text_result in enumerate(text_results, 1):
  112. what = text_result.get("what", "")
  113. desc = text_result.get("description", text_result.get("描述", {}))
  114. user_prompt += f"### 文本元素 {idx}\n"
  115. user_prompt += f"**What**: {what}...\n" # 限制长度
  116. user_prompt += f"**描述**: {json.dumps(desc, ensure_ascii=False, indent=2)}\n\n"
  117. # 添加消费者亮点
  118. consumer_highlights = state.get("consumer_highlights", [])
  119. if consumer_highlights:
  120. user_prompt += "## 消费者关注的亮点\n\n"
  121. for idx, highlight in enumerate(consumer_highlights, 1):
  122. element = highlight.get("element", "")
  123. highlight_desc = highlight.get("highlight", "")
  124. emotion = highlight.get("emotion", "")
  125. user_prompt += f"{idx}. **{element}**: {highlight_desc} ({emotion})\n"
  126. user_prompt += "\n"
  127. # 添加总结维度要求
  128. # 根据 PRD 1.4 - 2.2.2.2:
  129. # - 描述维度有哪些、有多少个,需要根据本帖子的品类、主题、关键词等信息来向知识库动态请求询问
  130. # - Query句式:参见3.2 环节中对query的定义
  131. # 这里使用默认维度作为示例(实际应从知识库获取)
  132. summary_dimensions = state.get("summary_dimensions", [
  133. "品类",
  134. "主题",
  135. "脚本",
  136. "内容亮点",
  137. "情绪共鸣点",
  138. "创作手法",
  139. "整体风格",
  140. "视觉呈现",
  141. "内容价值",
  142. "目标受众"
  143. ])
  144. user_prompt += "## 解构维度\n\n"
  145. user_prompt += "请根据以下维度进行帖子整体解构(至少包含3个维度):\n"
  146. for dim in summary_dimensions:
  147. user_prompt += f"- {dim}\n"
  148. user_prompt += "\n请严格按照JSON格式输出,字段名为上述维度名。至少包含3个描述维度。"
  149. messages.append({"role": "user", "content": user_prompt})
  150. return messages
  151. def _update_state(self, state: WhatDeconstructionState, response: Any) -> Dict[str, Any]:
  152. """更新状态
  153. 解析LLM返回的JSON,更新帖子整体
  154. """
  155. try:
  156. # 提取响应内容
  157. if hasattr(response, 'content'):
  158. content = response.content
  159. else:
  160. content = str(response)
  161. # 尝试提取JSON
  162. content = content.strip()
  163. if "```json" in content:
  164. content = content.split("```json")[1].split("```")[0].strip()
  165. elif "```" in content:
  166. content = content.split("```")[1].split("```")[0].strip()
  167. # 解析JSON
  168. result = json.loads(content)
  169. # 返回更新后的字段
  170. return {
  171. "post_overall": result # 更新为 post_overall
  172. }
  173. except Exception as e:
  174. logger.error(f"解析帖子整体响应失败: {e},原始内容: {response}")
  175. # 返回默认值
  176. return {
  177. "post_overall": {
  178. "错误": f"解析失败: {str(e)}"
  179. }
  180. }