From a94650a5be629e5f4f0788228a02fff0c74afbc8 Mon Sep 17 00:00:00 2001
From: TevinClaw <510129976@qq.com>
Date: Sat, 14 Mar 2026 12:26:27 +0800
Subject: [PATCH] 添加次 agent lifehelper 基本配置

---
 workspace/skills/tavily-search/scripts/tavily_search.py |  178 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 178 insertions(+), 0 deletions(-)

diff --git a/workspace/skills/tavily-search/scripts/tavily_search.py b/workspace/skills/tavily-search/scripts/tavily_search.py
new file mode 100755
index 0000000..fc78222
--- /dev/null
+++ b/workspace/skills/tavily-search/scripts/tavily_search.py
@@ -0,0 +1,178 @@
+#!/usr/bin/env python3
+"""
+Tavily AI Search API Client
+Usage: python tavily_search.py "your search query" [--max-results 5] [--depth basic|advanced]
+"""
+
+import os
+import sys
+import json
+import argparse
+from pathlib import Path
+from typing import List, Dict, Any, Optional
+
+
+def get_tavily_key_from_env_file() -> Optional[str]:
+    """Read Tavily API key from ~/.openclaw/.env file.
+    
+    Expected format: {"env": {"TAVILY_API_KEY": "your_key"}}
+    """
+    env_file = Path.home() / ".openclaw" / ".env"
+    if not env_file.exists():
+        return None
+    
+    try:
+        with open(env_file, 'r', encoding='utf-8') as f:
+            env_data = json.load(f)
+        # Support nested structure: {"env": {"TAVILY_API_KEY": "..."}}
+        if "env" in env_data:
+            return env_data["env"].get("TAVILY_API_KEY")
+        # Fallback to flat structure: {"TAVILY_API_KEY": "..."}
+        return env_data.get("TAVILY_API_KEY")
+    except (json.JSONDecodeError, IOError):
+        return None
+
+
+def get_api_key(api_key: Optional[str] = None) -> str:
+    """Get Tavily API key from various sources."""
+    # Priority: parameter > env var > .env file
+    if api_key:
+        return api_key
+    
+    env_key = os.environ.get("TAVILY_API_KEY")
+    if env_key:
+        return env_key
+    
+    env_file_key = get_tavily_key_from_env_file()
+    if env_file_key:
+        return env_file_key
+    
+    raise ValueError(
+        "Tavily API key required. Set via one of:\n"
+        "  1. TAVILY_API_KEY environment variable\n"
+        "  2. ~/.openclaw/.env file (JSON format: {\"env\": {\"TAVILY_API_KEY\": \"...\"}})\n"
+        "  3. Pass as api_key parameter\n"
+        "\nGet your API key at: https://tavily.com"
+    )
+
+
+def tavily_search(
+    query: str,
+    max_results: int = 5,
+    search_depth: str = "basic",
+    include_answer: bool = False,
+    include_images: bool = False,
+    api_key: Optional[str] = None
+) -> Dict[str, Any]:
+    """
+    Search using Tavily AI Search API.
+    
+    Args:
+        query: Search query string
+        max_results: Number of results to return (1-20)
+        search_depth: "basic" or "advanced"
+        include_answer: Include AI-generated answer
+        include_images: Include image URLs
+        api_key: Tavily API key (optional, auto-detected from env/config)
+    
+    Returns:
+        Dictionary containing search results
+    """
+    api_key = get_api_key(api_key)
+    
+    try:
+        import requests
+    except ImportError:
+        raise ImportError("requests package required. Install with: pip install requests")
+    
+    url = "https://api.tavily.com/search"
+    
+    payload = {
+        "api_key": api_key,
+        "query": query,
+        "max_results": min(max(max_results, 1), 20),
+        "search_depth": search_depth,
+        "include_answer": include_answer,
+        "include_images": include_images,
+    }
+    
+    response = requests.post(url, json=payload, timeout=30)
+    response.raise_for_status()
+    
+    return response.json()
+
+
+def format_results(results: Dict[str, Any]) -> str:
+    """Format search results for display."""
+    output = []
+    
+    if "answer" in results and results["answer"]:
+        output.append("=" * 60)
+        output.append("AI ANSWER")
+        output.append("=" * 60)
+        output.append(results["answer"])
+        output.append("")
+    
+    output.append("=" * 60)
+    output.append("SEARCH RESULTS")
+    output.append("=" * 60)
+    output.append(f"Query: {results.get('query', 'N/A')}")
+    output.append("")
+    
+    for i, result in enumerate(results.get("results", []), 1):
+        output.append(f"{i}. {result.get('title', 'No title')}")
+        output.append(f"   URL: {result.get('url', 'N/A')}")
+        
+        if result.get('published_date'):
+            output.append(f"   Published: {result['published_date']}")
+        
+        if result.get('score'):
+            output.append(f"   Relevance: {result['score']:.2f}")
+        
+        content = result.get('content', '')
+        if content:
+            # Truncate long content
+            if len(content) > 300:
+                content = content[:297] + "..."
+            output.append(f"   {content}")
+        
+        output.append("")
+    
+    return "\n".join(output)
+
+
+def main():
+    parser = argparse.ArgumentParser(description="Tavily AI Search")
+    parser.add_argument("query", help="Search query")
+    parser.add_argument("--max-results", type=int, default=5, help="Number of results (1-20)")
+    parser.add_argument("--depth", choices=["basic", "advanced"], default="basic", help="Search depth")
+    parser.add_argument("--answer", action="store_true", help="Include AI-generated answer")
+    parser.add_argument("--images", action="store_true", help="Include images")
+    parser.add_argument("--json", action="store_true", help="Output raw JSON")
+    
+    args = parser.parse_args()
+    
+    try:
+        results = tavily_search(
+            query=args.query,
+            max_results=args.max_results,
+            search_depth=args.depth,
+            include_answer=args.answer,
+            include_images=args.images
+        )
+        
+        if args.json:
+            print(json.dumps(results, indent=2, ensure_ascii=False))
+        else:
+            print(format_results(results))
+    
+    except ValueError as e:
+        print(f"Error: {e}", file=sys.stderr)
+        sys.exit(1)
+    except Exception as e:
+        print(f"Search failed: {e}", file=sys.stderr)
+        sys.exit(1)
+
+
+if __name__ == "__main__":
+    main()

--
Gitblit v1.9.1