Просмотр исходного кода

Merge branch 'main' of https://git.yishihui.com/howard/Agent

guantao 16 часов назад
Родитель
Сommit
385c89ead6

+ 32 - 14
examples/content_tree_analyst/analyst.prompt

@@ -11,27 +11,45 @@ $system$
 ## 工作流程
 
 ### 第一步:获取节点局部结构
-给定一个内容树节点 id(category 或 element),调用 `search_content_tree` 或 `get_category_tree` 获取:
-- **祖先路径**(`include_ancestors=true`):了解该节点的上下文和所属维度
-- **同级节点**:搜索同名关键词或父节点的直接子节点,了解同类
-- **子孙节点**(`descendant_depth=2`):了解该节点的细分方向
+给定一个内容树节点 id(category),依次调用以下工具:
+1. **`get_category_tree`**:获取分类的祖先路径和子孙节点
+   - `include_ancestors=true`:了解该节点的上下文和所属维度
+   - `descendant_depth=2`:了解该节点的细分方向(2代子孙)
+2. **`get_category_elements`**:获取该分类下的所有元素
+   - `sort_by="occurrence_count"`,`order="desc"`:按出现频率降序排列
+   - `page_size=50`:获取前50个高频元素
+   - 可选:`min_occurrence=5` 过滤低频元素
+3. **`get_frequent_itemsets`**:获取该分类的频繁项集
+   - `entity_ids=[category_id]`:传入当前分类的 entity_id
+   - `top_n=5`:获取前5个频繁项集
 
 ### 第二步:判断与图文制作的相关性
 结合节点的名称、描述、所属维度(实质/形式/意图),判断哪些节点与**图文内容制作**直接相关:
-- **保留**:与视觉呈现、角色设计、场景构图、风格表达、情感传达等制作行为直接相关的节点
-- **过滤**:纯语义/主题分类节点(如"节日"、"品牌"等不涉及制作手法的节点)
+- **保留**:与视觉呈现、角色设计、场景构图、风格表达等视觉制作行为直接相关的节点
+- **过滤**:纯语义/主题分类的非视觉相关节点(如"节日"、"品牌"、“载体”等不涉及制作手法的节点)
+
+对获取到的子孙节点逐一判断,保留与图文制作直接相关的节点,过滤无关节点:
+  - 判断标准:该节点是否指向一种具体的制作或视觉要求?
+    能回答"怎么做"的保留,只回答"是什么类型"的过滤
+  - 过滤时连同其子树一起过滤,不再往下看
 
 ### 第三步:获取关联频繁项集
-对筛选出的重要节点,调用 `get_frequent_itemsets` 获取关联要素:
-- 传入节点的 `entity_id`(搜索接口返回的 `entity_id` 字段)
+对筛选出的重要节点(包括子孙分类节点),调用 `get_frequent_itemsets` 获取关联要素:
+- 传入节点的 `entity_id`(从 `get_category_tree` 返回的子孙节点中获取
 - 频繁项集揭示了在优质内容中经常与该节点共同出现的要素
-- 用这些关联要素扩展需求的覆盖范围(如"动作姿态"→"夸张"、"运动"等)
+- 可以批量传入多个 entity_id:`entity_ids=[123, 456, 789]`
+
 
 ### 第四步:归纳制作需求
-对每组相关节点,归纳出若干条制作能力或工具需求:
-- **粒度适中**:不能太细("生成猫咪"),也不能太粗("生成图像")
-- **正确示例**:"需要能够生成保持角色一致性的人物图像的能力"
-- **同批需求不重叠**:不同需求应覆盖不同的制作维度,而且最好是对应到不同的工具
+对每个保留的节点,判断其名称能否脱离内容树上下文独立被理解为一个视觉需求:
+  - 能独立理解的:直接作为需求(如"材质纹理")
+  - 不能独立理解的:从最近的父节点取定语补充,组成完整短语
+    (如"对称分割" → "对称分割构图","后期技法" → "光影后期技法")
+  - 需求名称应该是人能直接理解并搜索的短语,而不是节点路径的拼接。通常叶子节点是父节点的定语
+    - 检验方式:把名称给一个不了解内容树的人看,他能否立刻理解这是在做什么?
+        - 错误示例(路径拼接):光影表现氛围营造、特殊视角夸张变形
+        - 正确示例(自然表达):光影氛围营造、夸张的特殊视角构图
+其他要求:
 
 ### 第五步:输出结构化需求
 将归纳结果写入 `%output_dir%/requirements.md`,每条需求包含:
@@ -112,7 +130,7 @@ knowledge_save(
 $user$
 请对以下内容树节点进行制作需求归纳分析:
 
-entity_id:15382
+entity_id:13955
 source_type:形式
 
 请按照工作流程,逐步分析该节点及其周边结构,最终将结构化的制作需求列表输出到 %output_dir%/requirements.md。

+ 67 - 0
examples/content_tree_analyst/tools/content_tree.py

@@ -165,3 +165,70 @@ async def get_category_tree(
     except Exception as e:
         logger.exception("get_category_tree error")
         return ToolResult(title="获取分类树失败", output=f"错误: {e}")
+
+
+@tool(description="获取指定分类下的所有元素,支持分页、排序和筛选")
+async def get_category_elements(
+    category_id: int,
+    source_type: str,
+    page_size: int = 50,
+    sort_by: str = "occurrence_count",
+    order: str = "desc",
+    min_occurrence: Optional[int] = None,
+) -> ToolResult:
+    """
+    获取指定分类下的所有元素。
+
+    Args:
+        category_id: 分类的 entity_id
+        source_type: 维度,"实质" / "形式" / "意图"
+        page_size: 每页数量,1-200(默认50)
+        sort_by: 排序字段,"name" / "id" / "occurrence_count"(默认)
+        order: 排序方向,"asc" / "desc"(默认)
+        min_occurrence: 最小出现次数,用于过滤低频元素(可选)
+    """
+    params = {
+        "source_type": source_type,
+        "category_entity_id": category_id,
+        "page_size": page_size,
+        "sort_by": sort_by,
+        "order": order,
+    }
+    if min_occurrence is not None:
+        params["min_occurrence"] = min_occurrence
+
+    try:
+        async with httpx.AsyncClient(timeout=30.0) as client:
+            resp = await client.get(f"{BASE_URL}/api/agent/search/elements", params=params)
+            resp.raise_for_status()
+            data = resp.json()
+
+        total = data.get("total", 0)
+        results = data.get("results", [])
+
+        lines = [f"分类 {category_id} 下的元素({source_type}维度)共 {total} 个,返回 {len(results)} 个:\n"]
+        for r in results:
+            eid = r.get("id", "")
+            name = r.get("name", "")
+            occ = r.get("occurrence_count", 0)
+            desc = r.get("description", "")
+            category = r.get("category", {})
+            cat_path = category.get("path", "")
+
+            lines.append(f"[元素] entity_id={eid} | {name} | 出现次数={occ}")
+            if desc:
+                lines.append(f"  描述: {desc}")
+            if cat_path:
+                lines.append(f"  所属分类: {cat_path}")
+            lines.append("")
+
+        return ToolResult(
+            title=f"分类元素: category_id={category_id} → {total} 个",
+            output="\n".join(lines),
+        )
+
+    except httpx.HTTPError as e:
+        return ToolResult(title="获取分类元素失败", output=f"HTTP 错误: {e}")
+    except Exception as e:
+        logger.exception("get_category_elements error")
+        return ToolResult(title="获取分类元素失败", output=f"错误: {e}")