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

v1.0.0 2026-01-08 09:09 UTC

This package is auto-updated.

Last update: 2026-01-11 12:15:40 UTC


README

Latest Version on Packagist License PHP Version

基于 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. 访问

目录结构

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-Id HTTP 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