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