丁云鹏 1 день назад
Родитель
Сommit
42cf8444c2
3 измененных файлов с 275 добавлено и 2 удалено
  1. 1 2
      .gitignore
  2. 70 0
      examples/create/skills/search_person_tree_constants.md
  3. 204 0
      examples/create/tool/search_person_tree.py

+ 1 - 2
.gitignore

@@ -79,5 +79,4 @@ examples/plan/*
 knowhub/milvus_data/
 knowhub/milvus_data/
 
 
 # Vendor (non-submodule)
 # Vendor (non-submodule)
-vendor/browser-use/
-examples/
+vendor/browser-use/

+ 70 - 0
examples/create/skills/search_person_tree_constants.md

@@ -0,0 +1,70 @@
+---
+name: search_person_tree_constants
+description: 根据人设名称提取该人设树中的所有常量点
+---
+
+## 人设常量点检索
+
+你可以通过人设常量点检索工具 `search_person_tree_constants` 根据人设名称提取该人设树中的所有常量点。
+
+### 使用场景
+
+- 获取特定人设的所有核心特征点(常量点)
+- 快速了解人设在三个维度(形式、实质、意图)的固定属性
+- 作为选题推导的起始点集合
+- 分析人设的常量点分布和权重
+
+### 工具说明
+
+**search_person_tree_constants(persona_name)**
+
+参数:
+- `persona_name`: 人设名称,如 "家有大志"
+
+返回信息:
+- `人设名称`: 查询的人设名称
+- `常量点总数`: 三个维度的常量点总数
+- `形式常量点`: 形式维度的常量点列表,每个包含:
+  - `点名称`: 常量点的名称
+  - `维度`: "形式"
+  - `路径`: 点在树中的完整路径
+  - `权重`: 人设权重分数(_persona_weight_score)
+  - `帖子数量`: 该点关联的帖子数量(_post_count)
+- `实质常量点`: 实质维度的常量点列表(字段同上)
+- `意图常量点`: 意图维度的常量点列表(字段同上)
+- `统计`: 各维度的常量点数量统计
+
+### 常量点定义
+
+常量点是指人设树中同时满足以下条件的节点:
+- `_type` 字段值为 "ID"
+- `_is_constant` 字段值为 true
+
+这些点代表了该人设的核心特征和固定属性。
+
+### 使用示例
+
+```python
+# 获取"家有大志"人设的所有常量点
+result = await search_person_tree_constants("家有大志")
+
+# 返回结果包含三个维度的常量点
+# 可以直接用于后续的选题推导流程
+```
+
+### 注意事项
+
+- 人设名称必须与 `data/` 目录下的文件夹名称完全匹配
+- 工具会自动读取三个树文件:
+  - `形式_point_tree_how.json`
+  - `实质_point_tree_how.json`
+  - `意图_point_tree_how.json`
+- 如果某个维度的树文件不存在,会返回错误信息
+- 返回的常量点已按维度分组,便于后续处理
+
+### 与其他工具的配合
+
+1. 使用本工具获取人设的所有常量点
+2. 使用 `search_point_by_element_from_full_all_levels` 根据常量点的元素值在图数据库中查找关联点
+3. 使用 `search_point_by_path_from_full_all_levels` 获取特定点的完整关联边信息
+4. 组合使用完成选题推导流程

+ 204 - 0
examples/create/tool/search_person_tree.py

@@ -0,0 +1,204 @@
+"""
+人设树常量点检索工具 - 根据人设名称提取所有常量点
+
+用于 Agent 执行时自主提取人设树中的常量点。
+"""
+
+import json
+from pathlib import Path
+from typing import Any, Dict, List
+
+from agent.tools import tool, ToolResult
+
+# 人设数据基础路径
+PERSONA_DATA_BASE_PATH = Path(__file__).parent.parent / "data"
+
+
+def _extract_constant_points(tree_data: Dict[str, Any], dimension: str, path_prefix: str = "") -> List[Dict[str, Any]]:
+    """
+    递归提取树中所有常量点
+
+    Args:
+        tree_data: 树节点数据
+        dimension: 维度类型(形式/实质/意图)
+        path_prefix: 当前路径前缀
+
+    Returns:
+        常量点列表
+    """
+    constant_points = []
+
+    for node_name, node_data in tree_data.items():
+        # 跳过元数据字段
+        if node_name.startswith("_"):
+            continue
+
+        # 构建当前节点的路径
+        current_path = f"{path_prefix}>{node_name}" if path_prefix else node_name
+
+        # 检查是否为常量点:_type 为 "ID" 且 _is_constant 为 true
+        if isinstance(node_data, dict):
+            node_type = node_data.get("_type")
+            is_constant = node_data.get("_is_constant", False)
+
+            if node_type == "ID" and is_constant:
+                constant_points.append({
+                    "点名称": node_name,
+                    "维度": dimension,
+                    "路径": current_path,
+                    "权重": node_data.get("_persona_weight_score", 0),
+                    "帖子数量": node_data.get("_post_count", 0)
+                })
+
+            # 递归处理子节点
+            if "children" in node_data:
+                child_points = _extract_constant_points(
+                    node_data["children"],
+                    dimension,
+                    current_path
+                )
+                constant_points.extend(child_points)
+
+    return constant_points
+
+
+def _load_persona_tree(persona_name: str) -> Dict[str, List[Dict[str, Any]]]:
+    """
+    加载人设树数据并提取所有常量点
+
+    Args:
+        persona_name: 人设名称,如 "家有大志"
+
+    Returns:
+        包含三个维度常量点的字典
+    """
+    persona_dir = PERSONA_DATA_BASE_PATH / persona_name / "tree"
+
+    if not persona_dir.exists():
+        return {
+            "found": False,
+            "persona_name": persona_name,
+            "error": f"人设目录不存在: {persona_dir}"
+        }
+
+    tree_files = {
+        "形式": persona_dir / "形式_point_tree_how.json",
+        "实质": persona_dir / "实质_point_tree_how.json",
+        "意图": persona_dir / "意图_point_tree_how.json"
+    }
+
+    all_constant_points = {
+        "形式": [],
+        "实质": [],
+        "意图": []
+    }
+
+    missing_files = []
+
+    for dimension, tree_file in tree_files.items():
+        if not tree_file.exists():
+            missing_files.append(f"{dimension}_point_tree_how.json")
+            continue
+
+        try:
+            with open(tree_file, 'r', encoding='utf-8') as f:
+                tree_data = json.load(f)
+
+            # 提取该维度的所有常量点
+            constant_points = _extract_constant_points(tree_data, dimension)
+            all_constant_points[dimension] = constant_points
+
+        except Exception as e:
+            return {
+                "found": False,
+                "persona_name": persona_name,
+                "error": f"读取文件 {tree_file.name} 失败: {str(e)}"
+            }
+
+    if missing_files:
+        return {
+            "found": False,
+            "persona_name": persona_name,
+            "error": f"缺失文件: {', '.join(missing_files)}"
+        }
+
+    return {
+        "found": True,
+        "persona_name": persona_name,
+        "constant_points": all_constant_points
+    }
+
+
+@tool(
+    description="根据人设名称检索该人设树中的所有常量点(_is_constant=true的点)。",
+    display={
+        "zh": {
+            "name": "人设常量点检索",
+            "params": {
+                "persona_name": "人设名称(如:家有大志)",
+            },
+        },
+    },
+)
+async def search_person_tree_constants(persona_name: str) -> ToolResult:
+    """
+    根据人设名称检索该人设树中的所有常量点。
+
+    常量点是指人设树中 _type 为 "ID" 且 _is_constant 为 true 的节点。
+    这些点代表了该人设的核心特征和固定属性。
+
+    Args:
+        persona_name: 人设名称,如 "家有大志"
+
+    Returns:
+        ToolResult: 包含三个维度(形式、实质、意图)的常量点列表
+    """
+    if not persona_name:
+        return ToolResult(
+            title="人设常量点检索失败",
+            output="",
+            error="请提供人设名称",
+        )
+
+    try:
+        result = _load_persona_tree(persona_name)
+    except Exception as e:
+        return ToolResult(
+            title="人设常量点检索失败",
+            output="",
+            error=f"检索异常: {str(e)}",
+        )
+
+    if not result.get("found"):
+        return ToolResult(
+            title="人设常量点检索失败",
+            output="",
+            error=result.get("error", "未知错误"),
+        )
+
+    constant_points = result["constant_points"]
+
+    # 统计信息
+    total_count = sum(len(points) for points in constant_points.values())
+
+    # 格式化输出
+    output_data = {
+        "人设名称": persona_name,
+        "常量点总数": total_count,
+        "形式常量点": constant_points["形式"],
+        "实质常量点": constant_points["实质"],
+        "意图常量点": constant_points["意图"],
+        "统计": {
+            "形式点数": len(constant_points["形式"]),
+            "实质点数": len(constant_points["实质"]),
+            "意图点数": len(constant_points["意图"])
+        }
+    }
+
+    output = json.dumps(output_data, ensure_ascii=False, indent=2)
+
+    return ToolResult(
+        title=f"人设常量点检索 - {persona_name}",
+        output=output,
+        long_term_memory=f"检索到 {total_count} 个常量点(形式:{len(constant_points['形式'])},实质:{len(constant_points['实质'])},意图:{len(constant_points['意图'])})",
+    )