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

72
docs/api.md Normal file
View File

@@ -0,0 +1,72 @@
# API 文档
搜索入口: `http://localhost:8650`
## 搜索
```
POST /api/search
Content-Type: application/json
{
"query": "搜索词",
"source": "auto", // auto / tavily / baidu / searxng / tavily,baidu
"max_results": 10
}
```
## 搜索源列表
```
GET /api/sources
```
## 服务状态
```
GET /api/status
```
## AI 总结(流式 SSE
```
POST /api/ai-summarize/stream
Content-Type: application/json
{
"query": "搜索词",
"results": [{"title": "...", "url": "...", "content": "..."}]
}
```
响应为 Server-Sent Events:
```
event: delta
data: {"content": "..."}
event: meta
data: {"model": "...", "elapsed": 1.23}
event: error
data: {"error": "..."}
```
## 管理 API
### 获取源配置
```
GET /api/admin/sources/{name}
```
### 更新源配置
```
POST /api/admin/sources/{name}
Content-Type: application/json
{"api_key": "sk-xxx", "base_url": "https://..."}
```
## 完整 API 列表
访问管理面板或 `GET /api/docs` 获取完整列表及 curl 示例。

78
docs/architecture.md Normal file
View File

@@ -0,0 +1,78 @@
# Search Hub 架构
## 项目定位
统一多搜索引擎聚合服务,提供 Web UI + RESTful API + 管理面板。
## 目录结构
```
search-hub/
├── app.py # Flask 主入口 — API 路由 / 页面路由 / 启动
├── config.py # 配置加载Hermes 配置 → config.yaml → 管理面板)
├── config.yaml # 搜索源密钥 / base_url 等配置
├── requirements.txt # Python 依赖
├── providers/ # 搜索源适配器层
│ ├── base.py # 抽象基类 BaseProvider + SearchResult
│ ├── tavily_provider.py # Tavily API
│ ├── baidu_provider.py # 百度千帆(网页搜索 / 智能检索)
│ ├── duckduckgo_provider.py # DuckDuckGo免费默认关闭
│ ├── searxng_provider.py # SearXNG 自托管元搜索引擎
│ └── ai_provider.py # AI 总结服务(非搜索源)
├── hub/ # 核心逻辑层
│ ├── router.py # 搜索路由器 — 多源路由 / 去重合并 / 自动选择
│ └── config_manager.py # 配置管理 — 读写 config.yaml / 字段 schema / 密钥脱敏
├── templates/ # Jinja2 模板
│ ├── index.html # 搜索页面Deepseek 风格 UI
│ └── admin.html # 管理面板 + API 文档
├── static/ # 前端资源
│ ├── css/style.css # 全局样式
│ ├── js/app.js # 搜索页交互逻辑
│ └── js/admin.js # 管理面板交互逻辑
└── docs/ # 项目文档
├── architecture.md # 本文件 — 技术架构与设计决策
└── providers.md # 搜索源接入指南
```
## 数据流
```
用户输入
app.py: /api/search
hub/router.py: SearchRouter.search()
│ ├─ source='auto' → 按优先级遍历可用源,去重合并
│ ├─ source='tavily' → 单一源搜索
│ └─ source='a,b,c' → 多源并发搜索
providers/*.search() ← 各搜索源适配器调用外部 API
SearchResult 列表 → 去重 → 排序 → JSON 响应
```
## 配置优先级
管理面板保存 > config.yaml > Hermes 配置
修改配置后调用 `_reload_providers()` 热加载,无需重启。
## 搜索源优先级
| 源 | 优先级 | API Key | 说明 |
|---|---|---|---|
| Tavily | 10 | 需要 | 默认首选 |
| 百度搜索 | 20 | 需要 | 千帆网页搜索 |
| 百度智能检索 | 21 | 需要 | 千帆 AI 检索 |
| SearXNG | 25 | 不需要 | 自托管元搜索 |
| DuckDuckGo | 30 | 不需要 | 国内不可用,默认关闭 |
| AI 总结 | 50 | 需要 | 仅用于总结,非搜索源 |

107
docs/providers.md Normal file
View File

@@ -0,0 +1,107 @@
# 搜索源接入指南
## 新增一个搜索源
### 1. 创建 Provider 类
`providers/` 下新建文件,继承 `BaseProvider`:
```python
from providers.base import BaseProvider, SearchResult
class MyProvider(BaseProvider):
name = 'mysearch' # 唯一标识
display_name = 'MySearch' # 展示名称
needs_api_key = False # 是否需要 API key
enabled = True # 是否默认启用
priority = 35 # 优先级(越小越优先)
def __init__(self, config: dict):
super().__init__(config)
# 从 config 中读取配置
mc = config.get('mysearch', {})
self.api_key = mc.get('api_key')
self.base_url = mc.get('base_url', 'https://api.mysearch.com')
def is_available(self) -> bool:
return True # 或 return bool(self.api_key)
def search(self, query: str, max_results: int = 10) -> list:
# 调用外部搜索 API
results = []
# ... 解析响应 ...
results.append(SearchResult(
title='标题',
url='https://example.com',
content='摘要内容',
score=0.8,
source=self.name,
published_date='2024-01-01',
))
return results[:max_results]
```
### 2. 注册到 app.py
两处注册:初始化时 + `_reload_providers()`
```python
from providers.my_provider import MyProvider
_providers = {
# ... 已有源 ...
'mysearch': MyProvider(_raw_config),
}
```
### 3. 添加配置加载config.py
`load_config()` 返回值中加入配置:
```python
'mysearch': {
'api_key': my_api_key,
'base_url': my_base_url,
},
```
### 4. 添加字段 Schemahub/config_manager.py
`get_source_schema()` 中加入字段定义:
```python
'mysearch': [
{'key': 'api_key', 'label': 'API Key', 'type': 'password', 'required': True},
{'key': 'base_url', 'label': 'API 地址', 'type': 'text', 'required': False},
],
```
### 5. 添加本地配置config.yaml
```yaml
sources:
mysearch:
api_key: "sk-xxx"
base_url: "https://api.mysearch.com"
```
## Provider 基类属性
| 属性 | 类型 | 说明 |
|---|---|---|
| `name` | str | 唯一标识,用于路由 |
| `display_name` | str | UI 展示名 |
| `needs_api_key` | bool | 管理面板是否显示编辑按钮 |
| `enabled` | bool | auto 模式是否自动使用 |
| `priority` | int | 越小越优先auto 模式排序用) |
## SearchResult 字段
| 字段 | 说明 |
|---|---|
| `title` | 标题 |
| `url` | 链接 |
| `content` | 摘要/内容 |
| `score` | 相关性分数0~1用于去重排序 |
| `source` | 来源标识 |
| `published_date` | 发布日期(可选) |