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

docs: add tool examples showing decorator usage

Examples include:
- Basic tool (minimal form)
- i18n display configuration
- Editable params for user adjustment
- Dangerous operations requiring confirmation
- Context parameter injection
- Sync vs async tools
- Complex return types

Reference: Resonote brain/tools.py and library/tools.py

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Howard 1 месяц назад
Родитель
Сommit
049139a9d2
2 измененных файлов с 450 добавлено и 0 удалено
  1. 3 0
      examples/__init__.py
  2. 447 0
      examples/tool_examples.py

+ 3 - 0
examples/__init__.py

@@ -0,0 +1,3 @@
+"""
+Examples 包 - 使用样例
+"""

+ 447 - 0
examples/tool_examples.py

@@ -0,0 +1,447 @@
+"""
+Tool Examples - 工具装饰器使用样例
+
+本文件展示 @tool 装饰器的各种用法,参考 Resonote 项目的实际工具实现。
+
+样例包括:
+1. 基础工具(最简形式)
+2. 带 i18n 展示信息的工具
+3. 带可编辑参数的工具
+4. 需要用户确认的危险操作
+5. 带 context 参数的工具
+6. 同步工具
+
+注意:
+- uid 参数会由框架自动注入,不需要用户传递
+- context 参数用于传递额外上下文(如当前阅读位置等)
+- 返回值会自动序列化为 JSON 字符串
+"""
+
+from typing import List, Dict, Any, Optional
+from reson_agent import tool
+
+
+# ============================================================
+# 1. 基础工具(最简形式)
+# ============================================================
+
+@tool()
+async def hello_world(name: str, uid: str = "") -> Dict[str, str]:
+    """
+    最简单的工具示例
+
+    Args:
+        name: 要问候的名字
+        uid: 用户ID(自动注入)
+
+    Returns:
+        包含问候语的字典
+    """
+    return {"greeting": f"Hello, {name}!"}
+
+
+# ============================================================
+# 2. 带 i18n 展示信息的工具
+# ============================================================
+
+@tool(
+    display={
+        "zh": {
+            "name": "搜索内容",
+            "params": {
+                "query": "搜索关键词",
+                "limit": "返回数量"
+            }
+        },
+        "en": {
+            "name": "Search Content",
+            "params": {
+                "query": "Search query",
+                "limit": "Number of results"
+            }
+        }
+    }
+)
+async def search_content(
+    query: str,
+    limit: int = 10,
+    uid: str = ""
+) -> List[Dict[str, Any]]:
+    """
+    搜索用户的内容
+
+    使用语义搜索查找相关内容。display 参数用于前端展示:
+    - 工具名称会根据用户语言显示为"搜索内容"或"Search Content"
+    - 参数名称也会相应翻译
+
+    Args:
+        query: 搜索查询文本
+        limit: 返回结果数量(默认10)
+        uid: 用户ID(自动注入)
+
+    Returns:
+        搜索结果列表,每个包含 id, title, content, score
+    """
+    # 实际实现中会调用向量搜索
+    # 这里只是示例
+    return [
+        {
+            "id": "doc_001",
+            "title": f"关于 {query} 的文档",
+            "content": f"这是与 {query} 相关的内容...",
+            "score": 0.95
+        }
+    ]
+
+
+# ============================================================
+# 3. 带可编辑参数的工具
+# ============================================================
+
+@tool(
+    editable_params=["query", "filters"],
+    display={
+        "zh": {
+            "name": "高级搜索",
+            "params": {
+                "query": "搜索关键词",
+                "filters": "过滤条件",
+                "sort_by": "排序方式"
+            }
+        },
+        "en": {
+            "name": "Advanced Search",
+            "params": {
+                "query": "Search query",
+                "filters": "Filters",
+                "sort_by": "Sort by"
+            }
+        }
+    }
+)
+async def advanced_search(
+    query: str,
+    filters: Optional[Dict[str, Any]] = None,
+    sort_by: str = "relevance",
+    limit: int = 20,
+    uid: str = ""
+) -> Dict[str, Any]:
+    """
+    高级搜索工具(允许用户编辑参数)
+
+    editable_params 指定哪些参数允许用户在 LLM 生成后编辑:
+    - LLM 会先生成 query 和 filters
+    - 用户可以在确认前修改这些参数
+    - 适用于搜索、创建等需要用户微调的场景
+
+    Args:
+        query: 搜索查询
+        filters: 过滤条件(如 {"type": "note", "date_range": "7d"})
+        sort_by: 排序方式(relevance/date/title)
+        limit: 返回数量
+        uid: 用户ID(自动注入)
+
+    Returns:
+        搜索结果和元数据
+    """
+    return {
+        "results": [
+            {"id": "1", "title": "Result 1", "score": 0.9},
+            {"id": "2", "title": "Result 2", "score": 0.8},
+        ],
+        "total": 42,
+        "query": query,
+        "filters_applied": filters or {},
+        "sort_by": sort_by
+    }
+
+
+# ============================================================
+# 4. 需要用户确认的危险操作
+# ============================================================
+
+@tool(
+    requires_confirmation=True,
+    display={
+        "zh": {
+            "name": "删除内容",
+            "params": {
+                "content_id": "内容ID",
+                "permanent": "永久删除"
+            }
+        },
+        "en": {
+            "name": "Delete Content",
+            "params": {
+                "content_id": "Content ID",
+                "permanent": "Permanent delete"
+            }
+        }
+    }
+)
+async def delete_content(
+    content_id: str,
+    permanent: bool = False,
+    uid: str = ""
+) -> Dict[str, Any]:
+    """
+    删除内容(需要用户确认)
+
+    requires_confirmation=True 表示这是一个危险操作:
+    - LLM 调用此工具时,不会立即执行
+    - 会先向用户展示操作详情,等待确认
+    - 用户确认后才会真正执行
+
+    适用场景:
+    - 删除操作
+    - 发送消息
+    - 修改重要设置
+    - 任何不可逆操作
+
+    Args:
+        content_id: 要删除的内容ID
+        permanent: 是否永久删除(False=移到回收站)
+        uid: 用户ID(自动注入)
+
+    Returns:
+        删除结果
+    """
+    # 实际实现中会执行删除
+    return {
+        "success": True,
+        "content_id": content_id,
+        "permanent": permanent,
+        "message": f"内容 {content_id} 已{'永久删除' if permanent else '移到回收站'}"
+    }
+
+
+# ============================================================
+# 5. 带 context 参数的工具
+# ============================================================
+
+@tool(
+    display={
+        "zh": {
+            "name": "获取相关推荐",
+            "params": {
+                "top_k": "推荐数量"
+            }
+        },
+        "en": {
+            "name": "Get Recommendations",
+            "params": {
+                "top_k": "Number of recommendations"
+            }
+        }
+    }
+)
+async def get_recommendations(
+    top_k: int = 5,
+    uid: str = "",
+    context: Optional[Dict[str, Any]] = None
+) -> List[Dict[str, Any]]:
+    """
+    获取相关推荐(使用 context 获取额外信息)
+
+    context 参数用于传递执行上下文,由框架自动注入:
+    - 当前阅读位置 (current_location)
+    - 当前会话 ID (session_id)
+    - 排除的内容 ID (exclude_ids)
+    - 等等...
+
+    框架会检查函数签名,如果有 context 参数就自动传入。
+
+    Args:
+        top_k: 返回推荐数量
+        uid: 用户ID(自动注入)
+        context: 执行上下文(自动注入)
+
+    Returns:
+        推荐列表
+    """
+    # 从 context 中提取信息
+    current_location = None
+    exclude_ids = []
+
+    if context:
+        current_location = context.get("current_location")
+        exclude_ids = context.get("exclude_ids", [])
+
+    # 实际实现中会根据 context 生成推荐
+    return [
+        {
+            "id": "rec_001",
+            "title": "推荐内容 1",
+            "reason": f"基于当前位置 {current_location}" if current_location else "基于您的兴趣"
+        },
+        {
+            "id": "rec_002",
+            "title": "推荐内容 2",
+            "reason": "热门内容"
+        }
+    ]
+
+
+# ============================================================
+# 6. 同步工具(非 async)
+# ============================================================
+
+@tool(
+    display={
+        "zh": {
+            "name": "格式化文本",
+            "params": {
+                "text": "原始文本",
+                "format_type": "格式类型"
+            }
+        },
+        "en": {
+            "name": "Format Text",
+            "params": {
+                "text": "Raw text",
+                "format_type": "Format type"
+            }
+        }
+    }
+)
+def format_text(
+    text: str,
+    format_type: str = "markdown",
+    uid: str = ""
+) -> str:
+    """
+    格式化文本(同步工具)
+
+    不需要 async 的工具可以定义为普通函数。
+    框架会自动检测并正确调用。
+
+    适用于:
+    - 纯计算操作
+    - 文本处理
+    - 不需要 I/O 的操作
+
+    Args:
+        text: 要格式化的文本
+        format_type: 格式类型(markdown/plain/html)
+        uid: 用户ID(自动注入)
+
+    Returns:
+        格式化后的文本
+    """
+    if format_type == "markdown":
+        return f"**{text}**"
+    elif format_type == "html":
+        return f"<p>{text}</p>"
+    else:
+        return text
+
+
+# ============================================================
+# 7. 复杂返回类型的工具
+# ============================================================
+
+@tool(
+    display={
+        "zh": {
+            "name": "分析内容",
+            "params": {
+                "content_id": "内容ID",
+                "analysis_types": "分析类型"
+            }
+        },
+        "en": {
+            "name": "Analyze Content",
+            "params": {
+                "content_id": "Content ID",
+                "analysis_types": "Analysis types"
+            }
+        }
+    }
+)
+async def analyze_content(
+    content_id: str,
+    analysis_types: Optional[List[str]] = None,
+    uid: str = ""
+) -> Dict[str, Any]:
+    """
+    分析内容(复杂返回类型)
+
+    展示如何返回复杂的嵌套结构。
+    返回值会自动序列化为 JSON。
+
+    Args:
+        content_id: 要分析的内容ID
+        analysis_types: 分析类型列表(sentiment/keywords/summary)
+        uid: 用户ID(自动注入)
+
+    Returns:
+        分析结果,包含多种分析数据
+    """
+    types = analysis_types or ["sentiment", "keywords"]
+
+    result = {
+        "content_id": content_id,
+        "analyses": {}
+    }
+
+    if "sentiment" in types:
+        result["analyses"]["sentiment"] = {
+            "score": 0.8,
+            "label": "positive",
+            "confidence": 0.92
+        }
+
+    if "keywords" in types:
+        result["analyses"]["keywords"] = [
+            {"word": "AI", "weight": 0.9},
+            {"word": "学习", "weight": 0.7},
+            {"word": "创新", "weight": 0.6}
+        ]
+
+    if "summary" in types:
+        result["analyses"]["summary"] = {
+            "short": "这是一篇关于AI学习的文章",
+            "long": "本文详细介绍了AI在学习领域的应用..."
+        }
+
+    return result
+
+
+# ============================================================
+# 使用示例
+# ============================================================
+
+if __name__ == "__main__":
+    import asyncio
+    from reson_agent import get_tool_registry
+
+    async def main():
+        # 获取全局注册表
+        registry = get_tool_registry()
+
+        # 查看已注册的工具
+        print("已注册的工具:")
+        for name in registry.get_tool_names():
+            print(f"  - {name}")
+
+        # 获取工具 Schema
+        schemas = registry.get_schemas(["search_content"])
+        print("\nsearch_content Schema:")
+        import json
+        print(json.dumps(schemas[0], indent=2, ensure_ascii=False))
+
+        # 执行工具
+        result = await registry.execute(
+            "search_content",
+            {"query": "人工智能", "limit": 5},
+            uid="user123"
+        )
+        print("\n执行结果:")
+        print(result)
+
+        # 获取 UI 元数据
+        ui_meta = registry.get_ui_metadata(locale="zh", tool_names=["advanced_search"])
+        print("\nUI 元数据 (中文):")
+        print(json.dumps(ui_meta, indent=2, ensure_ascii=False))
+
+    asyncio.run(main())