owner888 / smart-book
Smart Book AI Server - RAG-based book Q&A with Gemini AI, supporting EPUB parsing, vector search, and MCP protocol
Installs: 1
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/owner888/smart-book
Requires
- php: >=8.0
- ext-curl: *
- ext-json: *
- ext-mbstring: *
- ext-zip: *
- workerman/workerman: ^5.1
Requires (Dev)
- phpunit/phpunit: ^10.0
Suggests
- ext-redis: Required for Redis caching
- workerman/redis: Required for async Redis caching
README
基于 Workerman 的智能书籍问答服务,支持 RAG(检索增强生成)。
安装
通过 Composer 安装
composer require owner888/smart-book
手动安装
git clone https://github.com/owner888/smart-book.git
cd smart-book
composer install
功能特性
- 📚 RAG 问答 - 基于书籍内容的智能问答
- 🔍 混合检索 - 关键词 + 向量双重检索
- 💬 流式响应 - 支持 SSE 和 WebSocket 实时流式输出
- 🚀 高性能 - 基于 Workerman 异步架构
- 💾 智能缓存 - Redis 缓存 + 语义相似度匹配
快速开始
1. 安装依赖
composer install
2. 配置环境
cp .env.example .env
# 编辑 .env 文件,填入 GEMINI_API_KEY
3. 启动服务
php server.php start # 前台运行 php server.php start -d # 后台运行 php server.php stop # 停止 php server.php restart # 重启 php server.php status # 状态
4. 访问
- Web 界面: http://localhost:8088
- API: http://localhost:8088/api
- WebSocket: ws://localhost:8081
- MCP Server: http://localhost:8089/mcp
目录结构
smart-book/
├── config/ # 配置文件
│ ├── app.php # 应用配置(端口、AI、书籍路径)
│ ├── database.php # 数据库/Redis 配置
│ └── prompts.php # AI 提示词配置
│
├── src/ # 源代码
│ ├── AI/ # AI 客户端
│ │ ├── GeminiClient.php # Gemini API 同步客户端
│ │ ├── AsyncGeminiClient.php # Gemini API 异步客户端
│ │ ├── AsyncCurlManager.php # curl_multi 异步管理器
│ │ └── AIService.php # AI 服务统一管理
│ │
│ ├── Cache/ # 缓存服务
│ │ ├── CacheService.php # Redis 缓存(问答/语义索引)
│ │ └── RedisVectorStore.php # Redis 8.0 向量存储
│ │
│ ├── RAG/ # RAG 系统
│ │ ├── EmbeddingClient.php # 向量嵌入客户端
│ │ ├── VectorStore.php # 本地向量存储
│ │ ├── DocumentChunker.php # 文档分块器
│ │ └── BookRAGAssistant.php # RAG 书籍助手
│ │
│ ├── Parser/ # 文档解析器
│ │ └── EpubParser.php # EPUB 解析器
│ │
│ └── Http/ # HTTP 处理
│ └── Handlers.php # 请求处理函数
│
├── static/ # 静态资源
│ ├── css/main.css
│ └── js/main.js
│
├── tests/ # 测试文件
│ ├── test_ai.php
│ ├── test_rag.php
│ └── ...
│
├── bootstrap.php # 初始化/自动加载
├── server.php # 服务入口
├── chat.html # Web 聊天界面
├── .env.example # 环境变量模板
└── composer.json # 依赖配置
API 接口
HTTP API
| 接口 | 方法 | 说明 |
|---|---|---|
/api/health |
GET | 健康检查 |
/api/ask |
POST | RAG 问答(非流式) |
/api/chat |
POST | 通用聊天(非流式) |
/api/stream/ask |
POST | RAG 问答(SSE 流式) |
/api/stream/chat |
POST | 通用聊天(SSE 流式) |
/api/cache/stats |
GET | 缓存统计 |
WebSocket API
连接 ws://localhost:8081,发送 JSON:
{"action": "ask", "question": "孙悟空的师父是谁?"}
{"action": "chat", "messages": [{"role": "user", "content": "你好"}]}
MCP Server API (Streamable HTTP)
MCP Server 运行在端口 8089,实现 MCP 2025-11-25 Streamable HTTP Transport 协议。
端点: http://localhost:8089/mcp
| 方法 | 说明 |
|---|---|
GET |
获取服务器信息和能力 |
POST |
JSON-RPC 请求(支持批量) |
DELETE |
终止会话 |
会话管理:
- 服务器通过
Mcp-Session-IdHTTP Header 管理会话 initialize请求会创建新会话并返回 Session ID- 后续请求应携带 Session ID 以保持状态
JSON-RPC 请求示例:
# 1. 初始化会话 curl -X POST http://localhost:8089/mcp \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","clientInfo":{"name":"test","version":"1.0"}},"id":1}' # 2. 获取工具列表 curl -X POST http://localhost:8089/mcp \ -H "Content-Type: application/json" \ -H "Mcp-Session-Id: <session_id>" \ -d '{"jsonrpc":"2.0","method":"tools/list","id":2}' # 3. 调用工具 - 列出书籍 curl -X POST http://localhost:8089/mcp \ -H "Content-Type: application/json" \ -H "Mcp-Session-Id: <session_id>" \ -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"list_books","arguments":{}},"id":3}' # 4. 调用工具 - 搜索书籍 curl -X POST http://localhost:8089/mcp \ -H "Content-Type: application/json" \ -H "Mcp-Session-Id: <session_id>" \ -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"search_book","arguments":{"query":"孙悟空"}},"id":4}'
可用工具:
| 工具名 | 说明 | 参数 |
|---|---|---|
list_books |
列出所有可用书籍 | 无 |
get_book_info |
获取当前书籍信息 | 无 |
select_book |
选择要使用的书籍 | book: 书籍文件名 |
search_book |
在书籍中搜索内容 | query: 搜索词, top_k: 结果数量(可选) |
stdio 模式:
也支持 stdio 协议,用于 Cline/Cherry Studio 等客户端:
php mcp-stdio.php
技术架构
Smart Book 系统架构
┌─────────────────────────────────────────────────────────────────────────────┐
│ Smart Book AI Server │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ chat.html │ │ HTTP Client │ │ WebSocket Client│ │
│ │ (Web UI) │ │ (curl/fetch) │ │ (Browser) │ │
│ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │ │
│ └───────────────────────┼───────────────────────┘ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Workerman Server │ │
│ │ ┌─────────────────────┐ ┌─────────────────────┐ │ │
│ │ │ HTTP Worker │ │ WebSocket Worker │ │ │
│ │ │ (Port: 8088) │ │ (Port: 8089) │ │ │
│ │ └──────────┬──────────┘ └──────────┬──────────┘ │ │
│ └─────────────┼───────────────────────────────┼───────────────────────┘ │
│ │ │ │
│ └───────────────┬───────────────┘ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ src/Http/Handlers.php │ │
│ │ (路由分发 / 请求处理) │ │
│ └────────────┬────────────────────────────────────┬───────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌────────────────────────┐ ┌────────────────────────┐ │
│ │ src/AI/ │ │ src/Cache/ │ │
│ │ ┌──────────────────┐ │ │ ┌──────────────────┐ │ │
│ │ │ GeminiClient │ │ │ │ CacheService │ │ │
│ │ │ (同步 API) │ │ │ │ (Redis 缓存) │ │ │
│ │ └──────────────────┘ │ │ └──────────────────┘ │ │
│ │ ┌──────────────────┐ │ │ ┌──────────────────┐ │ │
│ │ │ AsyncGeminiClient│ │ │ │ RedisVectorStore │ │ │
│ │ │ (异步流式) │ │ │ │ (Redis 8.0 向量) │ │ │
│ │ └──────────────────┘ │ │ └──────────────────┘ │ │
│ │ ┌──────────────────┐ │ └────────────────────────┘ │
│ │ │ AsyncCurlManager │ │ │
│ │ │ (curl_multi) │ │ │
│ │ └──────────────────┘ │ │
│ └────────────┬───────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ src/RAG/ │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ │
│ │ │ EmbeddingClient │ │ VectorStore │ │ DocumentChunker │ │ │
│ │ │ (text-embedding) │ │ (混合检索) │ │ (文档分块) │ │ │
│ │ └──────────────────┘ └──────────────────┘ └──────────────────┘ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ BookRAGAssistant │ │ │
│ │ │ (RAG 问答编排 / 上下文构建) │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────┐ ┌────────────────────────┐ │
│ │ src/Parser/ │ │ config/prompts.php │ │
│ │ ┌──────────────────┐ │ │ ┌──────────────────┐ │ │
│ │ │ EpubParser │ │ │ │ AI 提示词配置 │ │ │
│ │ │ (EPUB 解析) │ │ │ │ (与 Python 同步) │ │ │
│ │ └──────────────────┘ │ │ └──────────────────┘ │ │
│ └────────────────────────┘ └────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ External Services │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Google Gemini API │ │ Redis │ │
│ │ (LLM + Embedding) │ │ (缓存 + 向量存储) │ │
│ └─────────────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
RAG 问答流程
用户提问 ──► 语义缓存检查 ──► 命中? ──► 返回缓存
│ │
│ 未命中 │
▼ │
生成问题 Embedding │
│ │
▼ │
混合检索 (关键词+向量) │
│ │
▼ │
构建上下文 Prompt │
│ │
▼ │
调用 Gemini API │
│ │
▼ │
流式返回 + 缓存结果 ◄──┘
数据流式传输架构
┌─────────────────────────────────────────────────────────────────────────────┐
│ 流式传输架构 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ Browser │ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ SSE (Server-Sent Events) │ │
│ │ POST /api/stream/ask │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ HTTP Response Headers: │
│ │ Content-Type: text/event-stream │
│ │ Cache-Control: no-cache │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ Handlers.php │ │
│ │ │ │
│ │ handleStreamAskAsync() │ │
│ │ │ │ │
│ │ ├──► sendSSE('sources', json) // 发送检索来源 │ │
│ │ │ │ │
│ │ └──► AsyncGeminiClient::chatStreamAsync() │ │
│ │ │ │ │
│ │ ├──► onChunk(text) ──► sendSSE('content', text) │ │
│ │ │ // 实时发送 AI 输出 │ │
│ │ │ │ │
│ │ └──► onComplete() ──► sendSSE('done', '') │ │
│ │ // 发送完成信号 │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ AsyncCurlManager │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────────────┐ │ │
│ │ │ curl_multi_init() │ │ │
│ │ │ │ │ │ │
│ │ │ ▼ │ │ │
│ │ │ Workerman Timer (10ms 轮询) │ │ │
│ │ │ │ │ │ │
│ │ │ ▼ │ │ │
│ │ │ curl_multi_exec() ──► CURLOPT_WRITEFUNCTION │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ ▼ │ │ │
│ │ │ │ 解析 SSE 数据流 │ │ │
│ │ │ │ data: {"candidates":...} │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ ▼ │ │ │
│ │ │ │ onData callback ──► onChunk() │ │ │
│ │ │ │ │ │ │
│ │ │ ▼ │ │ │
│ │ │ curl_multi_info_read() ──► onComplete callback │ │ │
│ │ └────────────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ Gemini API (streamGenerateContent) │ │
│ │ │ │
│ │ SSE 响应格式: │ │
│ │ data: {"candidates":[{"content":{"parts":[{"text":"..."}]}}]} │ │
│ │ data: {"candidates":[{"content":{"parts":[{"text":"..."}]}}]} │ │
│ │ ... │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
SSE 事件类型:
┌──────────────┬─────────────────────────────────────┐
│ Event │ Data │
├──────────────┼─────────────────────────────────────┤
│ sources │ [{"text":"...", "score":95.2}, ...] │
│ cached │ {"hit":true, "similarity":98.5} │
│ content │ "AI 生成的文本片段..." │
│ done │ "" │
│ error │ "错误信息" │
└──────────────┴─────────────────────────────────────┘
WebSocket 消息类型:
┌──────────────┬─────────────────────────────────────┐
│ Type │ Data │
├──────────────┼─────────────────────────────────────┤
│ sources │ {"type":"sources", "sources":[...]} │
│ content │ {"type":"content", "content":"..."}│
│ done │ {"type":"done"} │
│ error │ {"error":"错误信息"} │
└──────────────┴─────────────────────────────────────┘
配置说明
.env 配置
# AI API GEMINI_API_KEY=your_api_key_here # 服务端口 HTTP_PORT=8088 WS_PORT=8089 # Redis REDIS_HOST=127.0.0.1 REDIS_PORT=6379
config/app.php
return [ 'server' => [ 'http_port' => getenv('HTTP_PORT') ?: 8088, 'ws_port' => getenv('WS_PORT') ?: 8089, ], 'ai' => [ 'gemini' => [ 'api_key' => getenv('GEMINI_API_KEY'), 'model' => 'gemini-2.5-flash', ], ], 'books' => [ 'default' => [ 'path' => '/path/to/book.epub', 'cache' => '/path/to/index.json', ], ], ];
依赖
- PHP 8.0+
- workerman/workerman
- workerman/redis (异步 Redis)
- ext-curl
- ext-zip
- ext-mbstring
License
MIT