init: Search Hub - 统一多搜索引擎聚合服务

This commit is contained in:
2026-05-09 18:46:05 +08:00
commit 81d726179c
27 changed files with 3179 additions and 0 deletions

129
config.py Normal file
View File

@@ -0,0 +1,129 @@
"""从 Hermes 配置文件中读取各搜索源所需密钥"""
import os
import re
import yaml
HERMES_CONFIG = '/root/.hermes/config.yaml'
HERMES_ENV = '/root/.hermes/.env'
SEARCH_HUB_CONFIG = '/root/agentspace/projects/search-hub/config.yaml'
def load_config():
"""加载所有搜索源配置,返回 dict"""
# 从 hermes config 读取
with open(HERMES_CONFIG, 'r') as f:
config = yaml.safe_load(f)
# 读取 .env
env_vars = {}
if os.path.exists(HERMES_ENV):
with open(HERMES_ENV, 'r') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#'):
if '=' in line:
k, v = line.split('=', 1)
env_vars[k.strip()] = v.strip()
def resolve_api_key(raw_key):
if not raw_key:
return None
m = re.match(r'\$\{(\w+)\}', str(raw_key))
if m:
var_name = m.group(1)
return env_vars.get(var_name) or os.environ.get(var_name)
return raw_key
# === Tavily ===
tavily_cfg = config.get('search', {}).get('tavily', {})
tavily_api_key = resolve_api_key(tavily_cfg.get('api_key'))
tavily_base_url = tavily_cfg.get('base_url', 'https://api.tavily.com')
tavily_max_results = tavily_cfg.get('default_max_results', 10)
tavily_depth = tavily_cfg.get('default_search_depth', 'basic')
# === OpenCodeZen ===
opencodezen_key = None
opencodezen_base_url = 'https://opencode.ai/zen/go/v1'
opencodezen_model = 'deepseek-v4-flash'
for provider in config.get('custom_providers', []):
if provider.get('name') == 'OpenCodeZen':
opencodezen_key = resolve_api_key(provider.get('api_key'))
opencodezen_base_url = provider.get('base_url', opencodezen_base_url)
opencodezen_model = provider.get('model', opencodezen_model)
break
if not opencodezen_key:
opencodezen_key = env_vars.get('OPENCODE_ZEN_API_KEY')
# === Search Hub 自身配置(可选,可覆盖 Hermes 配置)===
hub_config = {}
if os.path.exists(SEARCH_HUB_CONFIG):
with open(SEARCH_HUB_CONFIG, 'r') as f:
hub_config = yaml.safe_load(f) or {}
# 应用本地配置覆盖
local_sources = hub_config.get('sources', {})
# Tavily 覆盖
if 'tavily' in local_sources:
tavily_local = local_sources['tavily']
if tavily_local.get('api_key'):
tavily_api_key = tavily_local['api_key']
if tavily_local.get('base_url'):
tavily_base_url = tavily_local['base_url']
# OpenCodeZen/AI 覆盖
if 'ai' in local_sources:
ai_local = local_sources['ai']
if ai_local.get('api_key'):
opencodezen_key = ai_local['api_key']
if ai_local.get('base_url'):
opencodezen_base_url = ai_local['base_url']
if ai_local.get('model'):
opencodezen_model = ai_local['model']
# 百度配置(来自本地 config.yaml
baidu_api_key = ''
baidu_intelligent_url = 'https://qianfan.baidubce.com/v2/ai_search/chat/completions'
baidu_web_search_url = 'https://qianfan.baidubce.com/v2/ai_search/web_search'
if 'baidu' in local_sources:
baidu_local = local_sources['baidu']
if baidu_local.get('api_key'):
baidu_api_key = baidu_local['api_key']
if baidu_local.get('intelligent_url'):
baidu_intelligent_url = baidu_local['intelligent_url']
if baidu_local.get('web_search_url'):
baidu_web_search_url = baidu_local['web_search_url']
# === SearXNG ===
searxng_base_url = 'http://localhost:8888'
if 'searxng' in local_sources:
searxng_local = local_sources['searxng']
if searxng_local.get('base_url'):
searxng_base_url = searxng_local['base_url']
return {
'tavily': {
'api_key': tavily_api_key,
'base_url': tavily_base_url,
'max_results': tavily_max_results,
'depth': tavily_depth,
},
'opencodezen': {
'api_key': opencodezen_key,
'base_url': opencodezen_base_url,
'model': opencodezen_model,
},
'hub': hub_config,
'baidu': {
'api_key': baidu_api_key,
'intelligent_url': baidu_intelligent_url,
'web_search_url': baidu_web_search_url,
},
'searxng': {
'base_url': searxng_base_url,
},
}