博闻 Bowen · MCP Web Search
博闻是魂镜的 MCP 联网搜索服务,支持多搜索引擎、多 API 密钥智能轮询、两级缓存和双传输模式。
文化命名出自《礼记·中庸》「博闻强识而让」,与辰正、一炁等同属魂镜文化命名体系。工程路径保留
mcp/web-search。
架构概览
flowchart TD
subgraph L1["① 传输层"]
ST["stdio<br/>stdin/stdout JSON-RPC"]
SSE["HTTP SSE<br/>/sse + /messages"]
HTTP["简化 HTTP<br/>/tools/list + /tools/call"]
end
subgraph L2["② 核心处理器"]
H["Handler<br/>传输层共享"]
C["Cache Manager<br/>L1 LRU → L2 Redis"]
end
subgraph L3["③ 负载均衡"]
LB["Load Balancer"]
KP["KeyPool<br/>EWMA 动态权重"]
CB["Circuit Breaker<br/>closed / open / half-open"]
end
subgraph L4["④ 搜索提供商"]
T[Tavily]
B[Bing]
V[Brave]
end
subgraph L5["⑤ 可观测性"]
M["Prometheus Metrics<br/>7 指标"]
S["/status + /health"]
A["管理 API<br/>enable / disable / clear"]
end
ST --> H
SSE --> H
HTTP --> H
H --> C
H --> LB
LB --> KP
KP --> CB
LB --> T
LB --> B
LB --> V
H --> M
H --> S
H --> A
核心特性
| 特性 | 说明 |
|---|---|
| 多 Provider | Tavily、Bing、Brave 三家搜索,统一响应格式 |
| 多 Key 轮询 | 每 Provider 可配多个 API Key,按 EWMA 动态权重(延迟×成功率)自动排序 |
| 熔断器 | 每个 Key 独立熔断(closed → open → half-open),失败 key 自动隔离并恢复探测 |
| 故障转移 | 一个 Key 失败自动换 Key,一个 Provider 耗尽自动切其他 Provider |
| 双传输 | HTTP SSE(远程 Client 直连)+ stdio(子进程集成),由 --port / --stdio CLI 标志决定 |
| 两级缓存 | L1 内存 LRU(毫秒级)+ L2 Redis(可选,跨实例共享),L2 命中自动升温到 L1 |
| 配置热重载 | fsnotify 监听 YAML 变更,动态调整 provider 启用/禁用、缓存 TTL、重试次数,无需重启 |
| 管理 API | 运行时启用/禁用 provider、清空缓存、查询密钥状态 |
| SearchMode | first(首个成功返回,默认低延迟)+ merge(并发搜所有引擎,URL 去重排序) |
| 可观测性 | 暗色风格 HTML 监控面板(:9090)、Prometheus 指标(:9090/metrics)、结构化彩色日志 |
快速开始
前置条件
- Go 1.22+
- 至少一个搜索 API Key(Tavily / Bing / Brave)
- (可选)Redis 用于二级缓存
环境变量快速配置
# 必填——搜索 API Key(逗号分隔支持多 key 轮询)
export TAVILY_API_KEYS="tvly-key1,tvly-key2"
export BRAVE_API_KEYS="brave-key1"
完整环境变量见项目根目录 .env.example 中的「博闻」段。
启动
# HTTP SSE 模式(供 MCP Client 远程连接,或供灵台注册为工具)
go run ./cmd/server --port 8080
# stdio 模式(供 Claude Desktop / Cursor 等 MCP Client 子进程集成)
go run ./cmd/server --stdio
# 指定 YAML 配置文件(可选,不传则自动探测 config/mcp/web-search.yaml)
go run ./cmd/server --config ../../config/mcp/web-search.yaml
验证
# 存活探针
curl http://localhost:8080/health
# → {"status":"ok"}
# 系统状态
curl http://localhost:8080/status
# → {"version":"2.0.0","uptime":"...","providers":[...],"cache":{...}}
# 工具列表(MCP tools/list)
curl -X POST http://localhost:8080/tools/list
# → {"tools":[{"name":"web_search",...}]}
# 搜索调用
curl -X POST http://localhost:8080/tools/call \
-H "Content-Type: application/json" \
-d '{"tool":"web_search","input":{"query":"hello world"}}'
# 监控面板(浏览器打开)
open http://localhost:9090
端口规划
| 端口 | 用途 | 说明 |
|---|---|---|
| 8080 | MCP 服务端口 | tools/list、tools/call、SSE、管理 API、/health、/status |
| 9090 | 监控端口 | HTML 仪表盘(/)、Prometheus 指标(/metrics)、存活探针(/health) |
MCP 服务端点(端口 8080)
MCP 协议端点
| 路径 | 方法 | 说明 |
|---|---|---|
POST /tools/list |
POST | 列出可用工具(web_search),含 inputSchema |
POST /tools/call |
POST | 调用 web_search 工具执行搜索 |
GET /sse |
GET | SSE 事件流(MCP 标准握手) |
POST /messages |
POST | MCP JSON-RPC 消息(与 /sse 配对使用) |
GET / |
GET | 端点指引(纯文本) |
web_search 工具
inputSchema:
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
query |
string | (必填) | 搜索关键词 |
max_results |
integer | 5 | 最大返回结果数(1–20) |
mode |
string | "first" |
搜索模式:first(首个成功返回) / merge(并发搜所有引擎后合并去重) |
providers |
string[] | 全部 | 优先使用的搜索引擎 |
响应(CallToolResult):
{
"content": [{
"type": "json",
"text": "{\"provider\":\"tavily\",\"query\":\"...\",\"results\":[...],\"total_count\":5,\"cached\":false}"
}]
}
管理端点
| 路径 | 方法 | 说明 |
|---|---|---|
GET /health |
GET | 存活探针 → {"status":"ok"} |
GET /status |
GET | 系统状态:provider 列表、活跃密钥数、缓存命中率、uptime |
GET /admin/keys |
GET | 各 provider 启用状态 |
PUT /admin/provider/{name}/enable |
PUT | 启用指定 provider |
PUT /admin/provider/{name}/disable |
PUT | 禁用指定 provider |
POST /admin/cache/clear |
POST | 清空缓存 |
监控面板(端口 9090)
HTML 仪表盘 GET /
暗色风格的可视化面板,展示:
- 四张概览卡片:Provider 数、活跃密钥数、缓存命中率(绿/黄/红三档)、缓存条目总数
- Provider 状态表格:🟢/🔴 状态指示灯、名称、启用/禁用开关、活跃密钥计数
- 缓存统计表格:L1(内存 LRU)命中、L2(Redis)命中、未命中、综合命中率
- 自动刷新:每 30 秒自动刷新
- 导航链接:一键跳转 /status、/admin/keys、/health
Prometheus 原始指标 GET /metrics
| 指标名 | 类型 | 标签 | 说明 |
|---|---|---|---|
bowen_search_total |
Counter | provider |
搜索请求总数 |
bowen_search_duration_seconds |
Histogram | — | 搜索耗时分布 |
bowen_search_errors_total |
Counter | reason |
搜索错误数 |
bowen_cache_hits_total |
Counter | — | 缓存命中总数 |
bowen_cache_hits_l2_total |
Counter | — | Redis 二级缓存命中数 |
bowen_cache_misses_total |
Counter | — | 缓存未命中数 |
bowen_active_keys |
Gauge | provider |
各 provider 活跃密钥数 |
所有监控端点也可在端口 9090 访问,与 MCP 服务端口独立。
配置文件
非敏感配置放在项目根目录 config/mcp/web-search.yaml(从 config/mcp/web-search.example.yaml 复制)。
不传 --config 时自动按项目惯例探测:开发模式探测 ../../config/mcp/web-search.yaml,容器内探测 /etc/bowen/config.yaml。
server:
port: 8080 # SSE 模式监听端口
read_timeout: 30s # HTTP 读取超时
write_timeout: 30s # HTTP 写入超时
providers:
tavily:
enabled: true # 是否启用(热重载:支持动态变更)
timeout: 10s
max_results: 10
health_check:
interval: 60s
failure_threshold: 3
bing:
enabled: true
timeout: 10s
max_results: 20
brave:
enabled: true
timeout: 10s
max_results: 10
balancer:
strategy: weighted_round_robin
retry_count: 2 # 热重载:支持动态变更
circuit_breaker:
enabled: true
failure_threshold: 5
cooldown: 30s
cache:
enabled: true
ttl: 5m # 热重载:支持动态变更
max_items: 1000
redis:
enabled: false # 需要自行部署 Redis
addr: 127.0.0.1:6379
password: ""
db: 0
metrics:
enabled: true
port: 9090
path: /metrics
配置优先级:环境变量 > YAML 文件 > 内置默认值
热重载:指定 --config 后,修改 YAML 文件自动生效(500ms 防抖),支持动态变更:
| 配置项 | 变更后自动生效 |
|---|---|
providers.{name}.enabled |
运行时启用/禁用搜索源 |
cache.ttl |
更新新缓存条目的过期时间 |
balancer.retry_count |
更新重试次数 |
传输模式
博闻支持两种 MCP 传输模式,不在配置中写死,由启动 CLI 参数决定:
| 模式 | 启动方式 | 适用场景 |
|---|---|---|
| SSE | --port 8080 |
灵台后端注册(简化 HTTP 端点 /tools/list + /tools/call)、远程 MCP Client 直连(/sse + /messages) |
| Stdio | --stdio |
Claude Desktop / Cursor 等 MCP Client 以子进程方式启动,通过 stdin/stdout 走 JSON-RPC 通信 |
两种模式下核心业务逻辑完全共享(internal/mcp/handler.go),只有传输层不同。
环境变量
所有敏感信息通过环境变量注入(参见 .env.example 博闻段):
| 变量 | 必填 | 说明 |
|---|---|---|
TAVILY_API_KEYS |
是 | Tavily 搜索 Key,多 key 逗号分隔 |
BRAVE_API_KEYS |
是 | Brave Search Key |
MCP_WEB_SEARCH_REDIS_ENABLED |
否 | Redis 二级缓存开关 |
MCP_WEB_SEARCH_REDIS_ADDR |
否 | Redis 地址(默认 127.0.0.1:6379) |
MCP_WEB_SEARCH_REDIS_PASSWORD |
否 | Redis 密码 |
MCP_WEB_SEARCH_REDIS_DB |
否 | Redis 数据库编号(默认 0) |
开发
# 运行所有测试
make test
# 编译
make build # → bin/bowen-server
# 本地 SSE 模式
make run-sse
# 本地 stdio 模式
make run-stdio
# 代码检查
make lint
开发环境日志自动使用彩色输出(ANSI 颜色代码):
INFO→ 绿色WARN→ 黄色ERROR→ 红色DEBUG→ 灰色
生产环境(ENV=production)自动切换为 JSON 格式,便于日志采集系统采集。
项目结构
mcp/web-search/
├── cmd/server/main.go # 入口(--port / --stdio)
├── internal/
│ ├── config/
│ │ ├── config.go # YAML + 环境变量配置加载
│ │ ├── yaml.go # YAML 解析
│ │ └── reloader.go # 配置热重载(fsnotify)
│ ├── logger/logger.go # slog 双模式(dev 彩色 / prod JSON)
│ ├── models/types.go # 核心数据类型
│ ├── providers/ # 搜索提供商适配
│ │ ├── provider.go # SearchProvider 接口
│ │ ├── tavily.go / bing.go / brave.go
│ │ └── registry.go # 注册中心
│ ├── balancer/ # 负载均衡
│ │ ├── keypool.go # 密钥池(EWMA)
│ │ ├── circuitbreaker.go # 熔断器
│ │ └── balancer.go # LoadBalancer
│ ├── cache/cache.go # 两级缓存
│ ├── mcp/ # MCP 协议实现
│ │ ├── handler.go # 核心处理逻辑
│ │ ├── stdio.go # stdin/stdout JSON-RPC
│ │ ├── sse.go # HTTP SSE 传输
│ │ ├── http.go # 简化 HTTP 端点
│ │ ├── status.go # /health + /status
│ │ ├── admin.go # 管理 API
│ │ └── monitor.go # HTML 监控面板
│ └── metrics/metrics.go # Prometheus 指标
├── Makefile
├── go.mod / go.sum
Docker
# 构建镜像
docker build -t bowen-web-search -f ../../docker/Dockerfile.web-search .
# 运行
docker run \
-e TAVILY_API_KEYS=tvly-xxx \
-e BRAVE_API_KEYS=brave-xxx \
-p 8080:8080 -p 9090:9090 \
bowen-web-search
Docker 镜像可通过魂镜的 docker/docker-compose.app.yml 编排启动。
集成到灵台
后端通过 MCP 注册 API 自动接入博闻:
# 注册博闻为 MCP Server
curl -X POST http://localhost:8000/api/v1/tools/mcp-servers \
-H "Content-Type: application/json" \
-d '{
"name": "bowen-web-search",
"endpoint_url": "http://localhost:8080",
"status": "enabled"
}'
注册后灵台会拉取工具清单,Agent 即可在对话中直接调用 web_search 工具。
搜索模式对比
| 模式 | 延迟 | 召回 | 使用场景 |
|---|---|---|---|
first(默认) |
低(首个成功即返回) | 单 provider | 快速查天气、新闻、百科等简单查询 |
merge |
中(等最慢 provider) | 高(多源合并去重) | 研究、对比多个来源、需要全面 coverage |
技术栈
| 类别 | 选型 |
|---|---|
| 语言 | Go 1.22+ |
| Web | 标准库 net/http |
| 日志 | 标准库 log/slog + 自定义彩色 Handler |
| 配置 | gopkg.in/yaml.v3 + 环境变量 |
| 内存缓存 | hashicorp/golang-lru/v2 |
| Redis | redis/go-redis/v9(可选) |
| 监控 | prometheus/client_golang |
| 热重载 | fsnotify |
| 测试 | 标准库 testing |
| 容器化 | Docker 多阶段构建 |