# What解构业务技术设计文档 v2.0 > **文档目标**: 为开发者提供清晰、可执行的技术实现指导 > > **设计原则**: 每个决策都有明确的PRD依据和技术理由 --- ## 目录 1. [PRD目标解析](#1-prd目标解析) 2. [功能需求拆解](#2-功能需求拆解) 3. [工作流 vs 组件职责划分](#3-工作流-vs-组件职责划分) 4. [工作流设计](#4-工作流设计) 5. [组件设计](#5-组件设计) 6. [数据结构设计](#6-数据结构设计) 7. [实现路线图](#7-实现路线图) --- ## 1. PRD目标解析 ### 1.1 核心目标是什么? **PRD原文** (1. 需求目标): > 针对给定的小红书多模态内容(图文、视频、音频),从消费者视角进行充分必要的What要素逆向解构,通过层级化递归深入分析,识别和提取内容中所有构成成分"what"。 **核心目标拆解**: | 维度 | 具体含义 | PRD依据 | |------|---------|---------| | **输入对象** | 小红书多模态内容(图+文+视频+音频) | 2.1.1节 | | **视角** | 从消费者视角分析 | 1. 需求目标 | | **方法** | 层级化递归深入分析 | 1. 需求目标、2.2.3节 | | **产出** | What要素树(JSON结构) | 2.2.1节、2.2.2节 | | **价值** | 通过聚合分析提取爆款特征和趋势 | 1. 需求目标(核心价值) | **目标提炼**: > **构建一个递归解构系统,将多模态帖子内容自上而下、从整体到局部地拆解为树状的"What元素"结构,每个元素包含动态描述维度(由知识库提供),并关联原始素材。** ### 1.2 关键约束条件 **PRD依据** (2.2.3节、4节): 1. **递归深度**: 最多10层 2. **停止条件**: - 已是不可再分的最小单元 - 达到10层深度 3. **解构原则**: - 由宏观到微观、由整体到局部 - 子节点不重不漏(充分必要) 4. **知识驱动**: 描述维度、分割判断都由知识库动态提供 5. **客观性**: 严格基于帖子内容,禁止臆测 --- ## 2. 功能需求拆解 ### 2.1 核心功能清单 基于PRD 3.1节"总体解构流程"和3.3节"对单一节点的递归解构",提炼出以下核心功能: | 功能ID | 功能名称 | 功能描述 | PRD依据 | 实现方式 | |--------|---------|---------|---------|---------| | **F1** | 帖子初理解 | 分析帖子整体,提取品类、主题、关键词 | 3.1节步骤1 | Agent组件 | | **F2** | 知识需求生成 | 根据品类/主题/关键词生成解构所需的query | 3.2节 | 工作流逻辑 | | **F3** | 知识检索 | 调用知识库获取描述维度、工具推荐、分割判断 | 3.2节、3.3.2节 | Tool组件 | | **F4** | 评论理解 | 分析评论,提取消费者关注的亮点 | 3.1节步骤4 | Agent组件 | | **F5** | 图片递归解构 | 递归解构图片元素,直到不可再分 | 3.1节步骤6-7、3.3节 | Agent组件 | | **F6** | 图片分割 | 将图片分割为多个独立的视觉元素 | 3.3.1节、3.5节 | Tool组件 | | **F7** | 文本递归解构 | 递归解构文本元素(标题/正文/标签) | 3.1节步骤8-11、3.3节 | Agent组件 | | **F8** | 文本切分 | 根据知识库指导切分文本 | 3.5节 | Function组件 | | **F9** | 帖子整体解构 | 从点线面体维度总结帖子 | 3.1节步骤13、3.2节 | Agent组件 | | **F10** | 结果汇总 | 将所有解构结果组装为树状JSON | 2.2.2节 | Function组件 | ### 2.2 功能依赖关系 ``` F1(帖子初理解) ↓ F2(知识需求生成) → F3(知识检索) ↓ F4(评论理解) ↓ [F5(图片递归解构) + F6(图片分割)] 并行 [F7(文本递归解构) + F8(文本切分)] ↓ F9(帖子整体解构) ↓ F10(结果汇总) ``` **关键观察**: - F5和F7内部都会多次调用F3(知识检索) - F6和F8是F5、F7的支撑工具 - F2是一个轻量级逻辑,不需要独立组件 --- ## 3. 工作流 vs 组件职责划分 ### 3.1 划分原则 **CLAUDE.md原则**: > - **工作流**:负责编排组件执行顺序和流程逻辑 > - **组件**:实现具体业务功能 ### 3.2 职责划分表 | 功能 | 实现方式 | 决策依据 | |------|---------|---------| | **F1 帖子初理解** | **Agent组件** | 需要多模态理解+语义推理(不确定性任务) | | **F2 知识需求生成** | **工作流逻辑** | 简单的字符串拼接,不需要独立组件 | | **F3 知识检索** | **Tool组件** | 需要被Agent调用 + 确定性API调用 | | **F4 评论理解** | **Agent组件** | 需要情感分析+语义理解(不确定性任务) | | **F5 图片递归解构** | **Agent组件** | 需要LLM视觉理解+ReAct模式调用工具 | | **F6 图片分割** | **Tool组件** | 需要被Agent调用 + 确定性模型调用 | | **F7 文本递归解构** | **Agent组件** | 需要LLM语义理解+ReAct模式调用工具 | | **F8 文本切分** | **Function组件** | 确定性字符串处理 + 工作流直接调用 | | **F9 帖子整体解构** | **Agent组件** | 需要综合分析+抽象提炼(不确定性任务) | | **F10 结果汇总** | **Function组件** | 确定性数据结构转换 + 工作流直接调用 | | **递归控制** | **工作流逻辑** | 编排递归流程,管理状态和深度 | | **并行执行** | **工作流逻辑** | 编排图片和文本并行解构 | **总结**: - **1个主工作流**: WhatDeconstructionWorkflow - **5个Agent组件**: F1, F4, F5, F7, F9 - **2个Tool组件**: F3, F6 - **2个Function组件**: F8, F10 --- ## 4. 工作流设计 ### 4.1 工作流核心职责 **WhatDeconstructionWorkflow** 的核心职责: 1. **编排执行顺序**: 按PRD 3.1节流程图编排各组件 2. **管理状态传递**: 维护 WhatDeconstructionState,在各节点间传递数据 3. **控制递归深度**: 确保不超过10层 4. **并行执行控制**: 图片和文本解构并行执行 5. **知识query生成**: 根据中间结果动态生成知识库query ### 4.2 工作流流程图 **PRD依据**: 3.1节"总体解构流程" ```mermaid graph TD START([开始]) --> N1[节点1: 帖子初理解
PostUnderstandingAgent] N1 --> N2[节点2: 评论理解
CommentAnalysisAgent] N2 --> PARALLEL{并行分支} PARALLEL --> N3[节点3: 图片递归解构
RecursiveImageDeconstructionAgent] PARALLEL --> N4[节点4: 文本递归解构
RecursiveTextDeconstructionAgent] N3 --> N5[节点5: 帖子整体解构
PostSummaryDeconstructionAgent] N4 --> N5 N5 --> N6[节点6: 结果汇总
ResultAggregationFunction] N6 --> END([结束]) style N3 fill:#e1f5ff style N4 fill:#e1f5ff style PARALLEL fill:#fff3cd ``` **流程说明**: | 节点 | 输入 | 输出 | PRD依据 | |------|------|------|---------| | N1 | 帖子多模态内容 | category, theme, keywords | 3.1节步骤1 | | N2 | 评论列表 | consumer_highlights | 3.1节步骤4 | | N3 | images列表 | image_deconstruction_results | 3.1节步骤6-7 | | N4 | text对象 | text_deconstruction_results | 3.1节步骤8-11 | | N5 | 所有子元素解构结果 | post_summary | 3.1节步骤13 | | N6 | 所有解构结果 | final_result (JSON树) | 2.2.2节 | ### 4.3 工作流核心代码框架 ```python from langgraph.graph import StateGraph, END from typing import TypedDict, List, Dict, Any # 1. 定义状态 class WhatDeconstructionState(TypedDict): """工作流状态定义""" # 输入 images: List[str] text: Dict[str, Any] # {title, body, hashtags} comments: List[Dict[str, str]] # 中间状态 category: str theme: str keywords: List[str] consumer_highlights: List[Dict] # 解构结果 image_deconstruction_results: List[Dict] text_deconstruction_results: List[Dict] post_summary: Dict[str, Any] # 最终输出 final_result: Dict[str, Any] # 2. 工作流类 class WhatDeconstructionWorkflow: """What解构主工作流""" def __init__(self, llm, knowledge_retrieval_tool, image_segment_tool): # 初始化所有组件 self.post_understanding_agent = PostUnderstandingAgent(llm) self.comment_analysis_agent = CommentAnalysisAgent(llm) self.image_agent = RecursiveImageDeconstructionAgent( llm, knowledge_retrieval_tool, image_segment_tool, max_depth=10 ) self.text_agent = RecursiveTextDeconstructionAgent( llm, knowledge_retrieval_tool, max_depth=10 ) self.summary_agent = PostSummaryDeconstructionAgent(llm, knowledge_retrieval_tool) self.aggregation_func = ResultAggregationFunction() # 构建图 self.graph = self._build_graph() def _build_graph(self) -> StateGraph: """构建LangGraph工作流""" workflow = StateGraph(WhatDeconstructionState) # 添加节点 workflow.add_node("post_understanding", self._node_post_understanding) workflow.add_node("comment_analysis", self._node_comment_analysis) workflow.add_node("image_deconstruction", self._node_image_deconstruction) workflow.add_node("text_deconstruction", self._node_text_deconstruction) workflow.add_node("post_summary", self._node_post_summary) workflow.add_node("result_aggregation", self._node_result_aggregation) # 定义边(流程) workflow.set_entry_point("post_understanding") workflow.add_edge("post_understanding", "comment_analysis") # 并行分支:评论理解后,图片和文本并行解构 workflow.add_edge("comment_analysis", "image_deconstruction") workflow.add_edge("comment_analysis", "text_deconstruction") # 汇聚:图片和文本都完成后,执行帖子整体解构 workflow.add_edge("image_deconstruction", "post_summary") workflow.add_edge("text_deconstruction", "post_summary") workflow.add_edge("post_summary", "result_aggregation") workflow.add_edge("result_aggregation", END) return workflow.compile() # 3. 节点实现 async def _node_post_understanding(self, state: WhatDeconstructionState): """节点1: 帖子初理解""" result = await self.post_understanding_agent.ainvoke({ "images": state["images"], "text": state["text"] }) return { "category": result["category"], "theme": result["theme"], "keywords": result["keywords"] } async def _node_comment_analysis(self, state: WhatDeconstructionState): """节点2: 评论理解""" result = await self.comment_analysis_agent.ainvoke({ "comments": state["comments"], "post_content": state["text"] }) return {"consumer_highlights": result["highlights"]} async def _node_image_deconstruction(self, state: WhatDeconstructionState): """节点3: 图片递归解构(处理所有图片)""" results = [] for idx, image_path in enumerate(state["images"]): result = await self.image_agent.ainvoke({ "image_path": image_path, "node_id": f"img_{idx+1}", "depth": 0, "category": state["category"], "theme": state["theme"] }) results.append(result) return {"image_deconstruction_results": results} async def _node_text_deconstruction(self, state: WhatDeconstructionState): """节点4: 文本递归解构(标题+正文+标签)""" results = [] text_data = state["text"] # 解构标题 if text_data.get("title"): title_result = await self.text_agent.ainvoke({ "text": text_data["title"], "text_type": "title", "node_id": "title", "depth": 0 }) results.append(title_result) # 解构正文 if text_data.get("body"): body_result = await self.text_agent.ainvoke({ "text": text_data["body"], "text_type": "body", "node_id": "body", "depth": 0 }) results.append(body_result) # 解构话题标签 if text_data.get("hashtags"): hashtags_result = await self.text_agent.ainvoke({ "text": " ".join(text_data["hashtags"]), "text_type": "hashtags", "node_id": "hashtags", "depth": 0 }) results.append(hashtags_result) return {"text_deconstruction_results": results} async def _node_post_summary(self, state: WhatDeconstructionState): """节点5: 帖子整体解构""" result = await self.summary_agent.ainvoke({ "category": state["category"], "theme": state["theme"], "keywords": state["keywords"], "image_results": state["image_deconstruction_results"], "text_results": state["text_deconstruction_results"], "consumer_highlights": state["consumer_highlights"] }) return {"post_summary": result["summary"]} async def _node_result_aggregation(self, state: WhatDeconstructionState): """节点6: 结果汇总""" final_result = self.aggregation_func.invoke({ "post_summary": state["post_summary"], "image_results": state["image_deconstruction_results"], "text_results": state["text_deconstruction_results"] }) return {"final_result": final_result} # 4. 入口方法 async def ainvoke(self, input_data: Dict) -> Dict: """执行工作流""" initial_state = WhatDeconstructionState( images=input_data["multimedia_content"]["images"], text=input_data["multimedia_content"]["text"], comments=input_data["comments"] ) result = await self.graph.ainvoke(initial_state) return result["final_result"] ``` **关键设计点**: 1. **并行执行**: `image_deconstruction` 和 `text_deconstruction` 从 `comment_analysis` 同时出发 2. **状态传递**: 通过 `WhatDeconstructionState` 在节点间传递数据 3. **递归封装**: 图片/文本的递归逻辑封装在各自的Agent内部 4. **知识库调用**: 在Agent内部调用 KnowledgeRetrievalTool --- ## 5. 组件设计 ### 5.1 Agent组件总览 **CLAUDE.md原则**: > **Agent组件特征**: 不确定性任务 + LLM智能能力 + 推理/多模态理解/语义分析 | Agent | 任务类型 | LLM能力需求 | PRD依据 | |-------|---------|------------|---------| | PostUnderstandingAgent | 多模态理解 | 图文融合理解 | 3.1节步骤1 | | CommentAnalysisAgent | 语义理解 | 情感分析、关键点提取 | 3.1节步骤4、2.1.4节 | | RecursiveImageDeconstructionAgent | 视觉理解+推理 | 图片理解、分割判断 | 3.3节、3.3.1节 | | RecursiveTextDeconstructionAgent | 语义理解+推理 | 文本分析、切分判断 | 3.3节 | | PostSummaryDeconstructionAgent | 综合分析 | 抽象提炼、多维度总结 | 3.2节 | --- ### 5.2 Agent组件详细设计 #### 5.2.1 PostUnderstandingAgent(帖子初理解) **组件类型**: Agent (继承 BaseLLMAgent) **选择依据**: - **PRD依据**: 3.1节步骤1 "帖子初理解,输出品类、主题、关键词" - **CLAUDE.md依据**: 需要多模态理解(图+文),属于不确定性任务 **核心职责**: 1. 综合分析图片和文本内容 2. 推理提取品类(如"时尚美妆"、"美食"等) 3. 总结主题(如"夏日穿搭") 4. 提取关键词(如"OOTD"、"小白裙") **关键点**: | 关键点 | 实现方式 | 依据 | |--------|---------|------| | **多模态输入** | 将图片和文本一起输入给LLM | LLM具备多模态理解能力 | | **结构化输出** | 使用JSON格式约束LLM输出 | 需要提取固定字段 | | **品类识别准确性** | 在prompt中提供常见品类列表 | 提升品类识别准确性 | **代码框架**: ```python from agents.base import BaseLLMAgent from typing import List, Dict, Any class PostUnderstandingAgent(BaseLLMAgent): """帖子初理解Agent PRD依据: 3.1节步骤1 功能: 分析帖子多模态内容,提取品类、主题、关键词 """ async def ainvoke(self, state: Dict[str, Any]) -> Dict[str, Any]: """ 执行帖子理解 Args: state: { "images": List[str], # 图片路径列表 "text": { # 文本内容 "title": str, "body": str, "hashtags": List[str] } } Returns: { "category": str, # 品类 "theme": str, # 主题 "keywords": List[str] # 关键词 } """ # 1. 构建多模态prompt prompt = self._build_prompt(state["images"], state["text"]) # 2. 调用LLM response = await self.llm.ainvoke(prompt) # 3. 解析结果 result = self._parse_response(response) return result def _build_prompt(self, images: List[str], text: Dict) -> str: """构建prompt 关键点: - 多模态输入(图片+文本) - 明确要求输出JSON格式 - 提供品类参考列表 """ return f""" 你是一个小红书内容分析专家。请分析以下帖子的多模态内容,提取品类、主题和关键词。 【文本内容】 标题: {text.get('title', '')} 正文: {text.get('body', '')} 话题标签: {', '.join(text.get('hashtags', []))} 【图片】 {self._format_images(images)} 【常见品类参考】 时尚美妆、美食、旅游、家居、母婴、运动健身、数码科技等 请以JSON格式输出: {{ "category": "品类", "theme": "主题", "keywords": ["关键词1", "关键词2", ...] }} """ def _parse_response(self, response: str) -> Dict: """解析LLM响应,提取JSON""" import json # 提取JSON部分 json_str = self._extract_json(response) return json.loads(json_str) ``` --- #### 5.2.2 CommentAnalysisAgent(评论理解) **组件类型**: Agent (继承 BaseLLMAgent) **选择依据**: - **PRD依据**: 2.1.4节 "评论信息用于提取内容消费者关注的亮点" - **CLAUDE.md依据**: 需要情感分析和语义理解,属于不确定性任务 **核心职责**: 1. 分析评论区文字内容 2. 识别消费者关注的亮点(对应帖子的哪些元素) 3. 提取高频关注点和情绪共鸣点 **关键点**: | 关键点 | 实现方式 | 依据 | |--------|---------|------| | **亮点映射** | 将评论中的关注点映射到帖子具体元素 | PRD 2.1.4节要求 | | **情感分析** | 识别评论的情感倾向(共鸣、兴趣、疑问等) | 理解消费者反应 | | **高频提取** | 聚合多条评论的共同关注点 | 识别关键吸引点 | **代码框架**: ```python class CommentAnalysisAgent(BaseLLMAgent): """评论理解Agent PRD依据: 2.1.4节 功能: 分析评论,提取消费者关注的亮点 """ async def ainvoke(self, state: Dict[str, Any]) -> Dict[str, Any]: """ 执行评论分析 Args: state: { "comments": List[Dict], # [{user, content}, ...] "post_content": Dict # 帖子内容(用于映射) } Returns: { "highlights": List[Dict] # 消费者关注的亮点列表 # [ # { # "element": "封面图中的小白裙", # "reason": "多条评论询问购买链接", # "emotion": "兴趣、购买意愿" # }, # ... # ] } """ prompt = self._build_prompt(state["comments"], state["post_content"]) response = await self.llm.ainvoke(prompt) return {"highlights": self._parse_highlights(response)} def _build_prompt(self, comments: List[Dict], post_content: Dict) -> str: """构建prompt 关键点: - 提供帖子内容作为参考(用于映射) - 要求识别具体元素 - 要求分析情感和关注原因 """ comments_text = "\n".join([ f"- {c['content']}" for c in comments ]) return f""" 你是一个内容分析专家。请分析评论区内容,提取消费者关注的亮点。 【帖子内容】 标题: {post_content.get('title', '')} 正文: {post_content.get('body', '')} 【评论区】 {comments_text} 请分析: 1. 评论中提到了帖子的哪些具体元素(封面、图片、标题、正文中的哪部分) 2. 消费者对这些元素的关注原因(为什么吸引他们) 3. 评论的情感倾向(共鸣、兴趣、疑问、赞美等) 以JSON格式输出亮点列表: {{ "highlights": [ {{ "element": "具体元素描述", "reason": "关注原因", "emotion": "情感倾向" }}, ... ] }} """ ``` --- #### 5.2.3 RecursiveImageDeconstructionAgent(图片递归解构) **组件类型**: Agent (继承 BaseReactAgent) **选择依据**: - **PRD依据**: 3.3节 "对单一节点的递归解构"、3.3.1节 "判断图片元素是否要继续分割拆解" - **CLAUDE.md依据**: 需要LLM视觉理解 + 动态调用工具(ReAct模式) **核心职责**: 1. 粗理解图片,生成what字段 2. 调用知识库获取描述维度 3. 细致理解图片,填充描述值 4. 判断是否需要分割(调用知识库) 5. 如需分割,调用图片分割工具 6. 递归处理子元素(最多10层) **关键点**: | 关键点 | 实现方式 | PRD依据 | |--------|---------|---------| | **ReAct模式** | 思考→行动→观察循环 | 需要多次调用工具 | | **递归控制** | 深度参数 + 停止条件判断 | 2.2.3节(最多10层) | | **知识驱动** | 描述维度和分割判断都由知识库提供 | 3.3.2节 | | **分割优先判断** | 优先判断是否为多子图拼接 | 3.3.1节 | | **what字段生成** | 对图片的文本描述 | 2.2.2节 | | **判断不依赖分割** | 判断是否拆解基于完整原图+文字描述,不依赖分割结果 | PRD 3.3.1节原则 | **递归流程图**: ```mermaid graph TD START([接收图片节点]) --> DEPTH{检查深度
是否≥10层?} DEPTH -->|是| STOP([返回结果
不再递归]) DEPTH -->|否| ROUGH[粗理解图片
基于完整原图+节点描述
生成what字段] ROUGH --> QUERY1[生成query:
获取描述维度] QUERY1 --> KB1[调用知识库] KB1 --> DETAIL[细致理解图片
基于完整原图+节点描述+维度
填充描述维度值] DETAIL --> QUERY2[生成query:
是否需要分割?] QUERY2 --> KB2[调用知识库
基于完整原图+what+描述] KB2 --> DECISION{知识库判断:
是否分割?} DECISION -->|否| STOP DECISION -->|是| IDENTIFY[识别子元素
基于完整原图
生成子元素描述] IDENTIFY --> SEGMENT[调用图片分割工具
传入完整原图
获得子图片列表] SEGMENT --> RECURSE[遍历子图片
递归调用自身
传递完整原图] RECURSE --> STOP style ROUGH fill:#e1f5ff style DETAIL fill:#e1f5ff style KB1 fill:#fff3cd style KB2 fill:#fff3cd style SEGMENT fill:#d4edda ``` **代码框架**: ```python from agents.base import BaseReactAgent from tools.base import BaseTool from typing import List, Dict, Any class RecursiveImageDeconstructionAgent(BaseReactAgent): """图片递归解构Agent PRD依据: 3.3节、3.3.1节 功能: 递归解构图片元素,最多10层 """ def __init__( self, llm, knowledge_retrieval_tool: BaseTool, image_segment_tool: BaseTool, max_depth: int = 10 ): super().__init__(llm, tools=[knowledge_retrieval_tool, image_segment_tool]) self.knowledge_tool = knowledge_retrieval_tool self.segment_tool = image_segment_tool self.max_depth = max_depth async def ainvoke(self, state: Dict[str, Any]) -> Dict[str, Any]: """ 递归解构图片 Args: state: { "original_image_path": str, # **完整原图路径**(PRD 3.3.1:始终传递未分割的完整图) "current_image_path": str, # 当前节点对应的图片路径(首次调用时=original_image_path) "node_description": str, # 当前节点的文字描述(首次为空,递归时为子元素描述) "node_id": str, # 节点ID(如"1", "1_1") "depth": int, # 当前递归深度 "category": str, # 帖子品类(用于生成query) "theme": str # 帖子主题(用于生成query) } Returns: { "id": str, "what": str, "description": Dict, "children": List[Dict], # 子元素(递归结构) "importance_weight": float } """ # 步骤1: 检查递归深度(PRD 2.2.3节) if state["depth"] >= self.max_depth: return self._build_leaf_node(state) # 步骤2: 粗理解当前节点,生成what字段(PRD 3.3节流程步骤B0) # **关键**: 基于完整原图 + 节点描述进行理解(PRD 3.3.1原则) what_value = await self._rough_understanding( original_image=state["original_image_path"], current_image=state["current_image_path"], node_description=state.get("node_description", "") ) # 步骤3: 调用知识库获取描述维度(PRD 3.3.2节) description_query = self._generate_description_query(what_value) description_dimensions = await self.knowledge_tool.ainvoke({ "query": description_query, "query_type": "description" }) # 步骤4: 根据描述维度细致理解当前节点(PRD 3.3节流程步骤C) # **关键**: 基于完整原图 + 节点描述 + 描述维度进行理解(PRD 3.3.1原则) description_values = await self._detailed_understanding( original_image=state["original_image_path"], current_image=state["current_image_path"], node_description=state.get("node_description", ""), description_dimensions=description_dimensions ) # 步骤5: 判断是否需要分割(PRD 3.3节流程步骤D0) # **关键**: 判断分割的输入是完整原图 + what + 描述,不依赖分割结果(PRD 3.3.1原则) split_query = self._generate_split_query(what_value, description_values) split_decision = await self.knowledge_tool.ainvoke({ "query": split_query, "query_type": "split_decision" }) # 构建当前节点结果 result = { "id": state["node_id"], "what": what_value, "description": description_values, "children": [], "importance_weight": 0.5 # TODO: 计算权重逻辑 } # 步骤6: 如需分割,执行分割并递归(PRD 3.3节流程步骤E1-E2) if split_decision.get("should_split", False): # 识别子元素(基于完整原图 + 当前节点描述) sub_elements = await self._identify_sub_elements( original_image=state["original_image_path"], current_node_what=what_value, current_node_description=state.get("node_description", "") ) # 调用分割工具(输入:完整原图 + 子元素描述列表) sub_images = await self.segment_tool.ainvoke({ "image_path": state["original_image_path"], # **传入完整原图** "elements_description": sub_elements }) # 递归处理每个子元素 children = [] for idx, (sub_img_path, sub_element_desc) in enumerate(zip(sub_images, sub_elements)): child_state = { "original_image_path": state["original_image_path"], # **传递完整原图** "current_image_path": sub_img_path, # 分割后的子图片 "node_description": sub_element_desc, # 子元素的文字描述 "node_id": f"{state['node_id']}_{idx+1}", "depth": state["depth"] + 1, "category": state["category"], "theme": state["theme"] } child_result = await self.ainvoke(child_state) children.append(child_result) result["children"] = children return result async def _rough_understanding( self, original_image: str, current_image: str, node_description: str ) -> str: """粗理解当前节点,生成what字段 PRD依据: 3.3节步骤B0, 3.3.1节原则 关键点: 基于完整原图 + 节点描述进行理解 Args: original_image: 完整原图路径(提供上下文) current_image: 当前节点对应的图片路径 node_description: 当前节点的文字描述(首次为空,递归时为子元素描述) """ # 构建prompt:如果有节点描述,说明是递归调用,需要结合原图理解特定部分 if node_description: prompt = f""" 请基于完整原图的上下文,描述指定的视觉元素。 【完整原图】: {original_image} 【当前关注的元素描述】: {node_description} 【当前元素图片】: {current_image} 请用一句话描述这个视觉元素的核心内容。 要求: 简洁准确,聚焦于该元素本身。 """ else: # 首次调用,直接理解整图 prompt = f""" 请用一句话描述这张图片的核心内容和视觉元素。 要求: 简洁准确,涵盖主要视觉要素。 图片: {original_image} """ response = await self.llm.ainvoke(prompt) return response.strip() def _generate_description_query(self, what_value: str) -> str: """生成描述维度query PRD依据: 3.3.2节、2.2.2节 Query句式: PRD 3.3.2节定义 """ return f'刻画描述"{what_value}"核心特征的角度和维度有哪些?请尽可能不重不漏列举全。' def _generate_split_query(self, what_value: str, description: Dict) -> str: """生成分割判断query PRD依据: 3.3节步骤D0 """ return f'该节点"{what_value}"是否需要继续拆解分割?' async def _detailed_understanding( self, original_image: str, current_image: str, node_description: str, dimensions: Dict ) -> Dict: """根据描述维度细致理解当前节点 PRD依据: 3.3节步骤C, 3.3.1节原则 关键点: 基于完整原图 + 节点描述 + 描述维度进行理解 Args: original_image: 完整原图路径(提供上下文) current_image: 当前节点对应的图片路径 node_description: 当前节点的文字描述 dimensions: 知识库返回的描述维度 """ # 构建prompt:结合原图上下文和节点描述 if node_description: prompt = f""" 请基于完整原图的上下文,分析指定视觉元素的特征。 【完整原图】: {original_image} 【当前关注的元素描述】: {node_description} 【当前元素图片】: {current_image} 请从以下维度分析这个视觉元素: {self._format_dimensions(dimensions)} 以JSON格式输出各维度的值。 """ else: # 首次调用,分析整图 prompt = f""" 请从以下维度分析图片: {self._format_dimensions(dimensions)} 图片: {original_image} 以JSON格式输出各维度的值。 """ response = await self.llm.ainvoke(prompt) return self._parse_description(response) async def _identify_sub_elements( self, original_image: str, current_node_what: str, current_node_description: str ) -> List[str]: """识别当前节点中的子元素 PRD依据: 3.3.1节 关键点: 基于完整原图 + 当前节点描述识别子元素(不依赖分割结果) Args: original_image: 完整原图路径(提供上下文) current_node_what: 当前节点的what字段值 current_node_description: 当前节点的文字描述 """ # 构建prompt:基于原图和当前节点描述识别子元素 if current_node_description: prompt = f""" 请基于完整原图的上下文,识别指定视觉元素中包含的子元素。 【完整原图】: {original_image} 【当前关注的元素】: {current_node_what} 【当前元素的详细描述】: {current_node_description} 判断步骤: 1. 该元素是否为多张子图拼接?(如4宫格、9宫格) 2. 如果不是,该元素包含哪些独立的视觉对象?(如人物、商品、背景元素) 请列出所有子元素的文字描述,每个元素一行。 """ else: # 首次调用,分析整图 prompt = f""" 分析图片"{current_node_what}",识别其中包含的子视觉元素。 【图片】: {original_image} 判断步骤: 1. 是否为多张子图拼接?(如4宫格、9宫格) 2. 如果不是,包含哪些独立的视觉对象?(如人物、商品、背景元素) 请列出所有子元素的文字描述,每个元素一行。 """ response = await self.llm.ainvoke(prompt) return [line.strip() for line in response.strip().split('\n') if line.strip()] ``` --- #### 5.2.4 RecursiveTextDeconstructionAgent(文本递归解构) **组件类型**: Agent (继承 BaseReactAgent) **选择依据**: - **PRD依据**: 3.3节 "对单一节点的递归解构" - **CLAUDE.md依据**: 需要LLM语义理解 + 动态调用工具 **核心职责**: 1. 提取文本原文作为what字段(不可改写) 2. 调用知识库获取描述维度 3. 分析文本,填充描述值 4. 判断是否需要切分 5. 如需切分,调用文本切分函数 6. 递归处理子文本 **关键点**: | 关键点 | 实现方式 | PRD依据 | |--------|---------|---------| | **what字段是原文** | 直接使用文本原文,不可总结改写 | 2.2.2节 | | **充分必要原则** | 子文本合并后必须等于父文本 | 3.4节 | | **递归控制** | 深度限制 + 最小语义单元判断 | 2.2.3节 | **代码框架**: ```python class RecursiveTextDeconstructionAgent(BaseReactAgent): """文本递归解构Agent PRD依据: 3.3节 功能: 递归解构文本元素(标题/正文/标签) """ def __init__( self, llm, knowledge_retrieval_tool: BaseTool, max_depth: int = 10 ): super().__init__(llm, tools=[knowledge_retrieval_tool]) self.knowledge_tool = knowledge_retrieval_tool self.max_depth = max_depth async def ainvoke(self, state: Dict[str, Any]) -> Dict[str, Any]: """ 递归解构文本 Args: state: { "text": str, # 当前文本内容 "text_type": str, # 文本类型(title/body/hashtags) "node_id": str, # 节点ID "depth": int # 当前递归深度 } Returns: { "id": str, "what": str, # 文本原文 "description": Dict, "children": List[Dict] } """ # 步骤1: 检查递归深度 if state["depth"] >= self.max_depth: return self._build_leaf_node(state) # 步骤2: what字段直接使用原文(PRD 2.2.2节) what_value = state["text"] # 步骤3: 调用知识库获取描述维度 description_query = self._generate_description_query( what_value, state["text_type"] ) description_dimensions = await self.knowledge_tool.ainvoke({ "query": description_query, "query_type": "description" }) # 步骤4: 根据描述维度分析文本 description_values = await self._analyze_text( what_value, description_dimensions, state["text_type"] ) # 步骤5: 判断是否需要切分 split_query = self._generate_split_query(what_value) split_decision = await self.knowledge_tool.ainvoke({ "query": split_query, "query_type": "split_decision" }) result = { "id": state["node_id"], "what": what_value, # 原文,不可改写 "description": description_values, "children": [] } # 步骤6: 如需切分,执行切分并递归 if split_decision.get("should_split", False): # 获取切分规则 split_rules = split_decision.get("split_rules", {}) # 执行文本切分(调用Function组件) sub_texts = self._split_text(what_value, split_rules) # 递归处理子文本 children = [] for idx, sub_text in enumerate(sub_texts): child_state = { "text": sub_text, "text_type": state["text_type"], "node_id": f"{state['node_id']}_{idx+1}", "depth": state["depth"] + 1 } child_result = await self.ainvoke(child_state) children.append(child_result) result["children"] = children return result def _split_text(self, text: str, split_rules: Dict) -> List[str]: """切分文本 PRD依据: 3.5节 关键点: 保证子文本合并后等于父文本(充分必要) """ strategy = split_rules.get("strategy", "sentence") if strategy == "sentence": # 按句子切分 import re sentences = re.split(r'[。!?\n]', text) return [s.strip() for s in sentences if s.strip()] elif strategy == "paragraph": # 按段落切分 paragraphs = text.split('\n\n') return [p.strip() for p in paragraphs if p.strip()] else: return [text] ``` --- #### 5.2.5 PostSummaryDeconstructionAgent(帖子整体解构) **组件类型**: Agent (继承 BaseLLMAgent) **选择依据**: - **PRD依据**: 3.2节 "帖子整体解构环节" - **CLAUDE.md依据**: 需要综合分析和抽象提炼能力 **核心职责**: 1. 调用知识库获取总结维度 2. 综合所有子元素解构结果 3. 从点线面体维度总结帖子 **关键点**: | 关键点 | 实现方式 | PRD依据 | |--------|---------|---------| | **动态总结维度** | 由知识库根据品类/主题/关键词提供 | 2.2.2节、3.2节 | | **综合分析** | 整合图片、文本、评论的解构结果 | 3.1节步骤13 | | **维度数量** | 不少于3个维度 | 2.2.2节 | **代码框架**: ```python class PostSummaryDeconstructionAgent(BaseLLMAgent): """帖子整体解构Agent PRD依据: 3.2节 功能: 从点线面体维度总结帖子 """ def __init__(self, llm, knowledge_retrieval_tool: BaseTool): super().__init__(llm) self.knowledge_tool = knowledge_retrieval_tool async def ainvoke(self, state: Dict[str, Any]) -> Dict[str, Any]: """ 执行帖子整体解构 Args: state: { "category": str, "theme": str, "keywords": List[str], "image_results": List[Dict], "text_results": List[Dict], "consumer_highlights": List[Dict] } Returns: { "summary": Dict # 动态维度的总结 } """ # 步骤1: 生成总结维度query(PRD 3.2节) summary_query = self._generate_summary_query( state["category"], state["theme"], state["keywords"] ) # 步骤2: 调用知识库获取总结维度 summary_dimensions = await self.knowledge_tool.ainvoke({ "query": summary_query, "query_type": "description" }) # 步骤3: 根据总结维度分析帖子 prompt = self._build_summary_prompt( summary_dimensions, state["image_results"], state["text_results"], state["consumer_highlights"] ) response = await self.llm.ainvoke(prompt) summary = self._parse_summary(response) return {"summary": summary} def _generate_summary_query( self, category: str, theme: str, keywords: List[str] ) -> str: """生成总结维度query PRD依据: 3.2节 Query句式: PRD 3.2节定义 """ keywords_str = ', '.join(keywords) return f'对于一篇主题为"{theme}",品类为"{category}",关键词包含"{keywords_str}"的多模态社交媒体帖子,从内容创作者视角进行What要素的初步识别和分类,需要使用哪些通用工具?' ``` --- ### 5.3 Tool组件详细设计 **CLAUDE.md原则**: > **Tool组件特征**: 确定性任务 + 可被Agent调用 #### 5.3.1 KnowledgeRetrievalTool(知识检索工具) **组件类型**: Tool (继承 BaseTool) **选择依据**: - **PRD依据**: 3.2节、3.3.2节 "对单一节点解构如何获取知识" - **CLAUDE.md依据**: 确定性API调用 + 需要被Agent调用 **核心职责**: 1. 接收query和query类型 2. 调用外部知识库API 3. 根据query类型解析返回结果 **关键点**: | 关键点 | 实现方式 | PRD依据 | |--------|---------|---------| | **支持多种query类型** | description/tool/split_decision | 3.3.2节 | | **降级策略** | 知识库不可用时使用LLM默认知识 | 容错处理 | | **结果格式化** | 转换为Agent可用的结构化数据 | 提升可用性 | **代码框架**: ```python from tools.base import BaseTool from typing import Dict, Any class KnowledgeRetrievalTool(BaseTool): """知识检索工具 PRD依据: 3.2节、3.3.2节 功能: 根据query检索解构所需的知识 """ name: str = "knowledge_retrieval" description: str = "根据query检索What解构所需的知识(描述维度、工具推荐、分割判断等)" def __init__(self, knowledge_base_url: str, fallback_llm=None): super().__init__() self.knowledge_base_url = knowledge_base_url self.fallback_llm = fallback_llm # 降级LLM async def _arun( self, query: str, query_type: str = "description" ) -> Dict[str, Any]: """ 调用知识库检索 Args: query: 查询语句(PRD 3.3.2节定义的句式) query_type: 查询类型 - description: 获取描述维度 - tool: 获取工具推荐 - split_decision: 获取分割判断 Returns: 根据query_type返回不同格式的结果 """ try: # 调用知识库API response = await self._call_knowledge_base(query) # 根据query类型解析结果 if query_type == "description": return self._parse_description_dimensions(response) elif query_type == "tool": return self._parse_tool_recommendations(response) elif query_type == "split_decision": return self._parse_split_decision(response) return response except Exception as e: # 降级策略: 使用LLM默认知识 if self.fallback_llm: return await self._fallback_with_llm(query, query_type) raise e def _parse_description_dimensions(self, response) -> Dict[str, Any]: """解析描述维度 返回格式: { "dimensions": ["维度1", "维度2", "维度3", ...], "count": 3 } """ dimensions = response.get("dimensions", []) return { "dimensions": dimensions, "count": len(dimensions) } def _parse_split_decision(self, response) -> Dict[str, Any]: """解析分割判断 返回格式: { "should_split": True/False, "split_rules": { # 如果should_split=True "strategy": "sentence/paragraph/grid", "params": {...} } } """ return { "should_split": response.get("should_split", False), "split_rules": response.get("split_rules", {}) } ``` **复用决策**: - **现有组件**: `tools/knowledge_retrieval_tools.py` - **决策**: **复用并修改** - **修改点**: 增强支持多种query_type,增加降级策略 --- #### 5.3.2 ImageSegmentTool(图片分割工具) **组件类型**: Tool (继承 BaseTool) **选择依据**: - **PRD依据**: 3.3.1节 "判断图片元素是否要继续分割拆解"、3.5节 - **CLAUDE.md依据**: 确定性模型调用 + 需要被Agent调用 **核心职责**: 1. 接收图片路径和子元素描述 2. 调用分割模型(SAM/GroundingDINO) 3. 保存分割后的子图片 4. 返回子图片路径列表 **关键点**: | 关键点 | 实现方式 | PRD依据 | |--------|---------|---------| | **支持多种分割模式** | grid(子图拼接)/ object(视觉元素) | 3.3.1节 | | **分割质量验证** | 验证mask是否覆盖元素 | 保证准确性 | | **文件管理** | 统一命名规范和存储路径 | 2.1.1节 | **代码框架**: ```python from tools.base import BaseTool from typing import List class ImageSegmentTool(BaseTool): """图片分割工具 PRD依据: 3.3.1节、3.5节 功能: 根据文字描述将图片分割为多个独立的视觉元素 """ name: str = "image_segment" description: str = "根据文字描述将图片分割为多个独立的视觉元素" def __init__(self, segment_model, output_dir: str): super().__init__() self.segment_model = segment_model self.output_dir = output_dir async def _arun( self, image_path: str, elements_description: List[str], segment_type: str = "object" ) -> List[str]: """ 分割图片 Args: image_path: 原始图片路径 elements_description: 子元素文字描述列表 segment_type: 分割类型 - object: 视觉元素分割(抠图) - grid: 子图拼接拆解(简单切割) Returns: 分割后的子图片路径列表 """ # 1. 加载图片 image = self._load_image(image_path) # 2. 根据分割类型选择策略 if segment_type == "grid": # 拼接图拆解(简单切割) sub_images = self._grid_split(image, len(elements_description)) else: # 视觉元素分割(调用分割模型) sub_images = await self._segment_objects(image, elements_description) # 3. 保存子图片 saved_paths = [] for idx, sub_img in enumerate(sub_images): save_path = f"{self.output_dir}/{self._generate_filename(image_path, idx)}" self._save_image(sub_img, save_path) saved_paths.append(save_path) return saved_paths def _grid_split(self, image, num_elements: int): """拼接图拆解 PRD依据: 3.3.1节(优先判断是否为多子图拼接) 支持: 2x2, 3x3, 1xN等常见拼接模式 """ # TODO: 根据num_elements推测拼接模式 pass async def _segment_objects(self, image, descriptions: List[str]): """视觉元素分割 调用SAM/GroundingDINO等模型 """ masks = await self.segment_model.segment(image, descriptions) return self._extract_objects(image, masks) ``` **复用决策**: - **现有组件**: `tools/segment_tools.py` - **决策**: **直接复用** - **理由**: 已实现图片分割功能,完全符合PRD需求 --- ### 5.4 Function组件详细设计 **CLAUDE.md原则**: > **Function组件特征**: 确定性任务 + 不需要被Agent调用 #### 5.4.1 TextSplitFunction(文本切分函数) **组件类型**: Function (继承 BaseFunction) **选择依据**: - **PRD依据**: 3.5节 "文本切分工具" - **CLAUDE.md依据**: 确定性字符串处理 + 工作流直接调用 **核心职责**: 1. 接收文本和切分规则 2. 按规则切分文本 3. 保证不重不漏(子文本合并=父文本) **关键点**: | 关键点 | 实现方式 | PRD依据 | |--------|---------|---------| | **充分必要原则** | 验证子文本合并后等于父文本 | 3.4节 | | **多种切分策略** | sentence/paragraph/semantic | 3.5节 | | **保留原文** | 不修改文本内容 | 2.2.2节 | **代码框架**: ```python from functions.base import BaseFunction from typing import List, Dict class TextSplitFunction(BaseFunction): """文本切分函数 PRD依据: 3.5节 功能: 根据切分规则切分文本 """ def invoke(self, text: str, split_rules: Dict) -> List[str]: """ 切分文本 Args: text: 待切分文本 split_rules: 切分规则 { "strategy": "sentence/paragraph/semantic", "params": {...} } Returns: 切分后的子文本列表 """ strategy = split_rules.get("strategy", "sentence") if strategy == "sentence": sub_texts = self._split_by_sentence(text) elif strategy == "paragraph": sub_texts = self._split_by_paragraph(text) elif strategy == "semantic": sub_texts = self._split_by_semantic(text, split_rules.get("params")) else: sub_texts = [text] # 验证充分必要原则(PRD 3.4节) self._validate_completeness(text, sub_texts) return sub_texts def _split_by_sentence(self, text: str) -> List[str]: """按句子切分""" import re sentences = re.split(r'[。!?\n]', text) return [s.strip() for s in sentences if s.strip()] def _validate_completeness(self, parent_text: str, child_texts: List[str]): """验证子文本合并后是否等于父文本 PRD依据: 3.4节 "充分必要原则" """ merged = ''.join(child_texts) if merged.replace(' ', '') != parent_text.replace(' ', ''): raise ValueError("子文本合并后不等于父文本,违反充分必要原则") ``` **复用决策**: - **现有组件**: 无 - **决策**: **完全新增** - **理由**: 项目中不存在文本切分功能 --- #### 5.4.2 ResultAggregationFunction(结果汇总函数) **组件类型**: Function (继承 BaseFunction) **选择依据**: - **PRD依据**: 2.2.2节 "输出格式定义" - **CLAUDE.md依据**: 确定性数据结构转换 **核心职责**: 1. 接收所有解构结果 2. 组装为树状JSON结构 3. 生成节点ID 4. 验证JSON格式 **关键点**: | 关键点 | 实现方式 | PRD依据 | |--------|---------|---------| | **递归构建树** | 递归函数处理children | 2.2.2节 | | **ID生成规则** | "1", "1_1", "1_1_2"格式 | 2.2.2节 | | **格式验证** | 验证所有必需字段存在 | 2.2.2节 | **代码框架**: ```python from functions.base import BaseFunction from typing import Dict, List class ResultAggregationFunction(BaseFunction): """结果汇总函数 PRD依据: 2.2.2节 功能: 将所有解构结果组装为树状JSON """ def invoke(self, state: Dict) -> Dict: """ 汇总所有解构结果 Args: state: { "post_summary": Dict, "image_results": List[Dict], "text_results": List[Dict] } Returns: 最终的树状JSON结构(PRD 2.2.2节格式) """ # 1. 提取各部分结果 post_summary = state["post_summary"] image_results = state["image_results"] text_results = state["text_results"] # 2. 构建元素列表 elements = [] # 添加图片元素 for idx, img_result in enumerate(image_results): element = self._build_element_tree( id=str(idx + 1), result=img_result ) elements.append(element) # 添加文本元素 text_id_start = len(image_results) + 1 for idx, text_result in enumerate(text_results): element = self._build_element_tree( id=str(text_id_start + idx), result=text_result ) elements.append(element) # 3. 组装最终JSON final_result = { "帖子总结": post_summary, "帖子包含元素": elements } # 4. 验证格式 self._validate_result(final_result) return final_result def _build_element_tree(self, id: str, result: Dict) -> Dict: """递归构建元素树 PRD依据: 2.2.2节 """ element = { "id": id, "what": result["what"], "描述": result["description"], "子节点元素关系": result.get("relationships", []), "元素重要性权重": result.get("importance_weight", 0.5), "子节点元素": [] } # 递归处理children if "children" in result and result["children"]: for idx, child in enumerate(result["children"]): child_element = self._build_element_tree( id=f"{id}_{idx+1}", result=child ) element["子节点元素"].append(child_element) return element def _validate_result(self, result: Dict): """验证JSON格式 PRD依据: 2.2.2节 检查: 必需字段、维度数量≥3等 """ # 验证帖子总结 assert "帖子总结" in result, "缺少'帖子总结'字段" assert len(result["帖子总结"]) >= 3, "帖子总结维度不足3个" # 验证帖子包含元素 assert "帖子包含元素" in result, "缺少'帖子包含元素'字段" # 递归验证每个元素节点 for element in result["帖子包含元素"]: self._validate_element(element) def _validate_element(self, element: Dict): """递归验证元素节点""" required_fields = ["id", "what", "描述", "子节点元素关系", "元素重要性权重", "子节点元素"] for field in required_fields: assert field in element, f"元素节点缺少'{field}'字段" # 验证描述维度数量 assert len(element["描述"]) >= 3, f"元素{element['id']}的描述维度不足3个" # 递归验证子节点 for child in element["子节点元素"]: self._validate_element(child) ``` **复用决策**: - **现有组件**: `functions/result_aggregation.py` - **决策**: **复用并修改** - **理由**: 可复用结果聚合逻辑,需新增树状结构构建 --- ## 6. 数据结构设计 ### 6.1 WhatDeconstructionState(工作流状态) **PRD依据**: 2.1节(输入)、2.2节(输出) ```python from typing import TypedDict, List, Dict, Any, Optional class WhatDeconstructionState(TypedDict): """What解构工作流状态 PRD依据: 2.1节、2.2节 """ # ========== 输入数据(PRD 2.1节) ========== images: List[str] # 图片路径列表(1-9张) text: Dict[str, Any] # {title, body, hashtags} comments: List[Dict[str, str]] # [{user, content}, ...] creator_info: Optional[Dict] # 创作者信息(可选) # ========== 中间状态 ========== category: str # 品类(F1输出) theme: str # 主题(F1输出) keywords: List[str] # 关键词(F1输出) consumer_highlights: List[Dict] # 消费者亮点(F4输出) # ========== 解构结果 ========== image_deconstruction_results: List[Dict] # 图片解构结果(F5输出) text_deconstruction_results: List[Dict] # 文本解构结果(F7输出) post_summary: Dict[str, Any] # 帖子总结(F9输出) # ========== 最终输出(PRD 2.2.2节) ========== final_result: Dict[str, Any] # 树状JSON结果 ``` ### 6.2 ElementNode(元素节点结构) **PRD依据**: 2.2.2节 "输出格式定义" ```python class ElementNode(TypedDict): """元素节点结构(递归树的节点) PRD依据: 2.2.2节 """ id: str # 节点ID(如"1", "1_1", "1_2_3") what: str # 元素概述(文本原文 or 图片描述) 描述: Dict[str, Any] # 动态描述维度(≥3个) 子节点元素关系: List[str] # 子节点之间的关系描述 元素重要性权重: float # 0-1之间 子节点元素: List['ElementNode'] # 递归的子节点列表 ``` ### 6.3 FinalOutput(最终输出结构) **PRD依据**: 2.2.2节 ```python class FinalOutput(TypedDict): """最终输出的JSON结构 PRD依据: 2.2.2节 """ 帖子总结: Dict[str, Any] # 动态总结维度(≥3个) 帖子包含元素: List[ElementNode] # 元素树的根节点列表 ``` --- ## 7. 实现路线图 ### Phase 1: 基础框架搭建 **目标**: 建立工作流骨架和基础组件 | 任务 | 组件 | 优先级 | 依赖 | |------|------|--------|------| | 1.1 定义数据结构 | WhatDeconstructionState | P0 | 无 | | 1.2 实现PostUnderstandingAgent | Agent | P0 | 1.1 | | 1.3 修改KnowledgeRetrievalTool | Tool | P0 | 无 | | 1.4 搭建工作流骨架 | Workflow | P0 | 1.1, 1.2, 1.3 | | 1.5 端到端测试(简化版) | - | P0 | 1.4 | **验收标准**: - 工作流能够运行 - PostUnderstandingAgent能够输出品类/主题/关键词 - 知识库工具能够返回模拟数据 ### Phase 2: 核心解构能力 **目标**: 实现图片和文本的递归解构 | 任务 | 组件 | 优先级 | 依赖 | |------|------|--------|------| | 2.1 实现RecursiveImageDeconstructionAgent | Agent | P0 | Phase 1 | | 2.2 复用ImageSegmentTool | Tool | P0 | 2.1 | | 2.3 实现RecursiveTextDeconstructionAgent | Agent | P0 | Phase 1 | | 2.4 实现TextSplitFunction | Function | P0 | 2.3 | | 2.5 集成测试(单个图片+单个文本) | - | P0 | 2.1-2.4 | **验收标准**: - 能够递归解构单张图片(至少2层) - 能够递归解构单段文本(至少2层) - 递归深度控制生效(不超过10层) ### Phase 3: 增强功能 **目标**: 完成评论理解、帖子总结、结果汇总 | 任务 | 组件 | 优先级 | 依赖 | |------|------|--------|------| | 3.1 实现CommentAnalysisAgent | Agent | P1 | Phase 2 | | 3.2 实现PostSummaryDeconstructionAgent | Agent | P1 | Phase 2 | | 3.3 修改ResultAggregationFunction | Function | P0 | Phase 2 | | 3.4 完整端到端测试 | - | P0 | 3.1-3.3 | **验收标准**: - 能够分析评论并提取亮点 - 能够生成帖子整体总结 - 能够输出符合PRD 2.2.2节格式的完整JSON ### Phase 4: 完善优化 **目标**: 优化性能、容错、测试覆盖 | 任务 | 优先级 | |------|--------| | 4.1 递归深度优化(提前停止) | P2 | | 4.2 错误处理和降级策略 | P1 | | 4.3 并行执行优化 | P2 | | 4.4 单元测试覆盖 | P1 | | 4.5 性能基准测试 | P2 | **验收标准**: - 单元测试覆盖率 > 80% - 错误情况下有合理降级 - 性能满足预期(待定义) --- ## 8. 决策依据总结表 ### 8.1 工作流设计决策 | 决策点 | 选择 | PRD依据 | CLAUDE.md依据 | 技术理由 | |--------|------|---------|---------------|---------| | 工作流框架 | LangGraph | - | 要求使用LangGraph | 支持状态管理、节点编排 | | 并行执行 | 图片和文本并行 | 3.1节流程图 | 工作流负责编排 | 两者无依赖,可并行提升效率 | | 递归深度 | 最多10层 | 2.2.3节 | - | 防止无限递归,控制成本 | | 知识query生成 | 工作流逻辑 | 3.2节 | 轻量级逻辑不需组件 | 简单字符串拼接 | ### 8.2 组件类型选择决策 | 组件 | 类型 | PRD依据 | CLAUDE.md依据 | 选择理由 | |------|------|---------|---------------|---------| | PostUnderstandingAgent | Agent | 3.1步骤1 | 多模态理解(不确定性) | 需要LLM理解图文 | | CommentAnalysisAgent | Agent | 2.1.4节 | 语义理解(不确定性) | 需要LLM情感分析 | | RecursiveImageDeconstructionAgent | Agent (ReAct) | 3.3节 | 视觉理解+工具调用 | 需要多次调用知识库和分割工具 | | RecursiveTextDeconstructionAgent | Agent (ReAct) | 3.3节 | 语义理解+工具调用 | 需要多次调用知识库 | | PostSummaryDeconstructionAgent | Agent | 3.2节 | 综合分析(不确定性) | 需要LLM抽象提炼 | | KnowledgeRetrievalTool | Tool | 3.3.2节 | Agent需调用 | 确定性API调用 | | ImageSegmentTool | Tool | 3.3.1节 | Agent需调用 | 确定性模型调用 | | TextSplitFunction | Function | 3.5节 | 确定性+工作流调用 | 简单字符串处理 | | ResultAggregationFunction | Function | 2.2.2节 | 确定性+工作流调用 | 数据结构转换 | ### 8.3 复用决策 | 组件 | 决策 | 理由 | 修改点 | |------|------|------|--------| | knowledge_retrieval_tools.py | 复用并修改 | 已有基础查询功能 | 增强query类型支持、降级策略 | | segment_tools.py | 直接复用 | 功能完全匹配 | 无 | | result_aggregation.py | 复用并修改 | 可复用聚合逻辑 | 新增树状结构构建 | | TextSplitFunction | 完全新增 | 项目中无此功能 | - | | 所有Agent | 完全新增 | PRD业务逻辑全新 | - | --- ## 附录: Query模板速查表 **PRD依据**: 3.2节、3.3.2节 | Query类型 | 句式模板 | 使用场景 | |-----------|---------|---------| | 帖子整体总结维度 | 对于一篇主题为"{主题}",品类为"{品类}",关键词包含"{关键词}"的多模态社交媒体帖子,从内容创作者视角进行What要素的初步识别和分类,需要使用哪些通用工具? | PostSummaryDeconstructionAgent | | 元素描述维度 | 刻画描述"{元素的what字段值}"核心特征的角度和维度有哪些?请尽可能不重不漏列举全。 | 所有递归解构Agent | | 分割判断 | 该节点"{元素的what字段值}"是否需要继续拆解分割? | 所有递归解构Agent | | 图片整体分割工具 | 对一张描述为"{图片描述}"的图片,从内容创作者视角进行视觉元素分割,需要使用哪些图片分割/抠图工具? | RecursiveImageDeconstructionAgent | | 文本段落分割工具 | 对一段描述为"{文本摘要}"的文本段落,从内容创作者视角进行结构化分割拆解,需要使用什么文本切分工具或文本结构化大模型? | RecursiveTextDeconstructionAgent | --- **文档版本**: v2.0 **最后更新**: 2025-10-21 **适用PRD版本**: v1.3