init: Search Hub - 统一多搜索引擎聚合服务
This commit is contained in:
72
docs/api.md
Normal file
72
docs/api.md
Normal 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
78
docs/architecture.md
Normal 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
107
docs/providers.md
Normal 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. 添加字段 Schema(hub/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` | 发布日期(可选) |
|
||||
Reference in New Issue
Block a user