docs: 更新 README.md 和 IMAGE_API_GUIDE.md
README.md: - 新增企业微信机器人功能说明(搜索/图片入库/集成启动) - 新增图片去重和管理功能说明 - 新增可配置参数说明 - 更新 API 概览(删除/去重/机器人管理接口) - 更新项目结构和技术栈 IMAGE_API_GUIDE.md: - 新增删除图片接口文档 - 新增一键去重接口文档 - 新增企业微信机器人相关接口文档 - 更新上传接口返回格式(含 duplicate 状态) - 更新注意事项(去重/删除相关)
This commit is contained in:
69
README.md
69
README.md
@@ -1,6 +1,6 @@
|
||||
# EduBrain - 中文直播教育知识库系统
|
||||
|
||||
基于 FastAPI 的中文直播教育知识库系统,支持直播文字稿导入、OCR 截图识别、AI 语义搜索等功能。
|
||||
基于 FastAPI 的中文直播教育知识库系统,支持直播文字稿导入、OCR 截图识别、AI 语义搜索、企业微信机器人等功能。
|
||||
|
||||
## 功能特性
|
||||
|
||||
@@ -13,13 +13,23 @@
|
||||
### 图片 OCR
|
||||
- **OCR 识别**: DeepSeek Vision 模型识别图片文字
|
||||
- **关键词提取**: MiniMax M2.7 自动提取内容标签
|
||||
- **批量处理**: 支持拖拽上传、选择文件、选择文件夹,按文件名去重
|
||||
- **批量处理**: 支持拖拽上传、选择文件、选择文件夹
|
||||
- **上传去重**: OCR 识别后自动检查内容是否已存在,避免重复录入
|
||||
- **图片管理**: 网页端管理所有图片,支持一键去重、手动删除
|
||||
- **BM25 索引**: 内置倒排索引搜索引擎,jieba 中文分词
|
||||
|
||||
### AI 搜索
|
||||
- **知识库搜索**: 向量语义搜索 + 全文检索混合排序
|
||||
- **图片搜索**: MiniMax M2.7 语义匹配,SSE 流式实时返回结果
|
||||
- **并发池**: 最多 10 个 LLM 请求并发,每批 10 张图片判断
|
||||
- **并发池**: 最多 10 个 LLM 请求并发,每批数量可配置
|
||||
- **可配置参数**: 搜索返回数量、LLM 批量判断数量均可在网页端设置
|
||||
|
||||
### 企业微信机器人
|
||||
- **WebSocket 长连接**: 基于官方 SDK,无需公网 IP
|
||||
- **关键词搜索**: 用户发文字 → 搜索图片 → 返回匹配结果
|
||||
- **图片自动入库**: 用户发图片 → 自动 OCR 识别 → 录入知识库(自动去重)
|
||||
- **集成启动**: 后端启动时自动检测配置并拉起机器人
|
||||
- **网页端管理**: 在设置页面配置 Bot ID / Secret,支持在线重启
|
||||
|
||||
### 其他
|
||||
- **MCP 协议**: 支持 Model Context Protocol,可被 AI Agent 调用
|
||||
@@ -36,6 +46,7 @@
|
||||
| OCR | DeepSeek Vision / PaddleOCR / 阿里云 / 腾讯云 |
|
||||
| LLM | MiniMax M2.7 / DeepSeek / Qwen3-8B |
|
||||
| 搜索 | BM25 倒排索引 + 向量语义搜索 |
|
||||
| 企业微信 | wecom-aibot-python-sdk (WebSocket 长连接) |
|
||||
| 部署 | Docker + Docker Compose |
|
||||
|
||||
## 项目结构
|
||||
@@ -43,7 +54,7 @@
|
||||
```
|
||||
edu-brain/
|
||||
├── app/
|
||||
│ ├── main.py # FastAPI 入口,路由注册,生命周期管理
|
||||
│ ├── main.py # FastAPI 入口,路由注册,生命周期管理,BotManager
|
||||
│ ├── config.py # pydantic-settings 配置管理
|
||||
│ ├── database.py # 数据库连接(PG/SQLite 双后端)
|
||||
│ ├── models/base.py # ORM 模型(KnowledgePage, OCRImage 等)
|
||||
@@ -51,9 +62,9 @@ edu-brain/
|
||||
│ ├── api/v1/ # API 路由
|
||||
│ │ ├── pages.py # 知识页面 CRUD
|
||||
│ │ ├── search.py # 语义搜索
|
||||
│ │ ├── images.py # 图片 OCR + AI 搜索(SSE)
|
||||
│ │ ├── images.py # 图片 OCR + AI 搜索(SSE)+ 去重 + 删除
|
||||
│ │ ├── import_export.py # 文件导入导出
|
||||
│ │ └── settings.py # 系统设置
|
||||
│ │ └── settings.py # 系统设置 + 机器人管理
|
||||
│ ├── services/
|
||||
│ │ ├── ocr_service.py # OCR 识别服务(多 Provider)
|
||||
│ │ ├── llm_service.py # LLM 服务(标签提取/搜索匹配)
|
||||
@@ -62,6 +73,7 @@ edu-brain/
|
||||
│ │ ├── embedding_service.py # 嵌入模型服务
|
||||
│ │ ├── import_service.py # 文件导入服务
|
||||
│ │ └── page_service.py # 页面 CRUD 服务
|
||||
│ ├── wework_bot.py # 企业微信智能机器人服务
|
||||
│ └── mcp/server.py # MCP Server 实现
|
||||
├── static/index.html # 前端单页应用
|
||||
├── data/images/ # 图片存储目录
|
||||
@@ -106,6 +118,11 @@ DEEPSEEK_OCR_MODEL=deepseek-ocr:latest
|
||||
MINIMAX_API_KEY=your-minimax-api-key
|
||||
MINIMAX_BASE_URL=https://api.minimaxi.com/v1
|
||||
MINIMAX_CHAT_MODEL=MiniMax-M2.7
|
||||
|
||||
# 企业微信机器人(可选,不配则不启动)
|
||||
WEWORK_BOT_ENABLED=true
|
||||
WEWORK_BOT_ID=your-bot-id
|
||||
WEWORK_BOT_SECRET=your-bot-secret
|
||||
```
|
||||
|
||||
### 3. 启动服务
|
||||
@@ -114,6 +131,8 @@ MINIMAX_CHAT_MODEL=MiniMax-M2.7
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 8765
|
||||
```
|
||||
|
||||
启动后会自动检测企业微信机器人配置,如果 `WEWORK_BOT_ENABLED=true` 且 `WEWORK_BOT_ID` / `WEWORK_BOT_SECRET` 已配置,机器人会自动在后台线程启动。
|
||||
|
||||
访问 http://localhost:8765 即可使用。
|
||||
|
||||
### 4. Docker 部署
|
||||
@@ -127,16 +146,39 @@ docker-compose up -d
|
||||
```
|
||||
用户上传图片
|
||||
→ DeepSeek Vision OCR 识别文字
|
||||
→ 检查 OCR 内容是否已存在(去重)
|
||||
→ MiniMax M2.7 提取关键词标签
|
||||
→ 存入 SQLite + BM25 索引
|
||||
|
||||
用户搜索 "孩子不想上学"
|
||||
→ 从 DB 倒序取图片(每批 10 张)
|
||||
→ 并发池(最多 10 个请求)发给 MiniMax M2.7 判断
|
||||
→ 从 DB 倒序取图片(每批 N 张,可配置)
|
||||
→ 并发池发给 MiniMax M2.7 判断
|
||||
→ 匹配结果通过 SSE 实时推送到前端
|
||||
→ 找够指定数量或遍历完 DB 后结束
|
||||
```
|
||||
|
||||
## 企业微信机器人
|
||||
|
||||
### 功能
|
||||
|
||||
| 场景 | 说明 |
|
||||
|------|------|
|
||||
| 单聊发关键词 | 搜索图片知识库,返回匹配结果(图片+预览文字) |
|
||||
| 单聊发图片 | 自动 OCR 识别并录入知识库(自动去重) |
|
||||
| 进入会话 | 自动发送欢迎语 |
|
||||
|
||||
### 配置
|
||||
|
||||
1. 在企业微信管理后台创建智能机器人,开启 API 模式(长连接)
|
||||
2. 获取 Bot ID 和 Secret
|
||||
3. 在 `.env` 中配置或通过网页端设置页面配置
|
||||
|
||||
### 注意事项
|
||||
|
||||
- 图片消息**仅支持单聊**,群聊中发图片不会触发机器人
|
||||
- 机器人支持多人同时使用(asyncio 异步架构)
|
||||
- 配置修改后可通过网页端「重启机器人」按钮生效
|
||||
|
||||
## API 概览
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
@@ -144,12 +186,18 @@ docker-compose up -d
|
||||
| POST | `/api/v1/images/recognize-direct` | 上传并识别图片 |
|
||||
| POST | `/api/v1/images/batch-recognize` | 批量上传并识别 |
|
||||
| GET | `/api/v1/images/search` | AI 搜索图片(SSE 流式) |
|
||||
| GET | `/api/v1/images` | 获取图片列表(分页) |
|
||||
| GET | `/api/v1/images/{id}` | 获取图片识别结果 |
|
||||
| DELETE | `/api/v1/images/{id}` | 删除图片 |
|
||||
| POST | `/api/v1/images/dedup` | 一键去重 |
|
||||
| GET | `/api/v1/pages` | 知识页面列表 |
|
||||
| POST | `/api/v1/pages` | 创建知识页面 |
|
||||
| POST | `/api/v1/search` | 语义搜索知识库 |
|
||||
| POST | `/api/v1/import/file` | 导入文件 |
|
||||
| GET | `/api/v1/settings` | 获取系统设置 |
|
||||
| PUT | `/api/v1/settings` | 更新系统设置 |
|
||||
| GET | `/api/v1/settings/bot/status` | 获取机器人状态 |
|
||||
| POST | `/api/v1/settings/bot/restart` | 重启机器人 |
|
||||
|
||||
## 配置说明
|
||||
|
||||
@@ -173,4 +221,9 @@ docker-compose up -d
|
||||
| `dashscope` | 阿里云 DashScope | `DASHSCOPE_API_KEY` |
|
||||
| `local_bge` | 本地 BGE 模型 | `LOCAL_BGE_MODEL_PATH` |
|
||||
|
||||
### 网页端可配置参数
|
||||
|
||||
| 参数 | 说明 | 默认值 | 范围 |
|
||||
|------|------|--------|------|
|
||||
| 搜索返回数量 | 每次搜索最多返回多少条匹配结果 | 3 | 1-10 |
|
||||
| LLM 批量判断数量 | 每批发给 LLM 判断的图片数量 | 10 | 1-50 |
|
||||
|
||||
@@ -20,8 +20,10 @@ BASE_URL = http://localhost:8765
|
||||
| POST | `/batch-recognize` | 批量上传并识别 | multipart/form-data |
|
||||
| POST | `/import-paths` | 从服务器路径导入 | JSON |
|
||||
| GET | `/search` | AI 搜索图片(SSE 流式) | query params |
|
||||
| GET | `/{image_id}` | 获取单张图片详情 | path param |
|
||||
| GET | `` | 获取图片列表(分页) | query params |
|
||||
| GET | `/{image_id}` | 获取单张图片详情 | path param |
|
||||
| DELETE | `/{image_id}` | 删除图片 | path param |
|
||||
| POST | `/dedup` | 一键去重 | 无 |
|
||||
|
||||
---
|
||||
|
||||
@@ -42,6 +44,8 @@ Content-Type: multipart/form-data
|
||||
|
||||
### 返回
|
||||
|
||||
**正常识别:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 386,
|
||||
@@ -58,6 +62,21 @@ Content-Type: multipart/form-data
|
||||
}
|
||||
```
|
||||
|
||||
**内容重复(自动去重):**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 386,
|
||||
"file_path": "data/images/test.png",
|
||||
"original_filename": "test.png",
|
||||
"status": "duplicate",
|
||||
"duplicate_of": 100,
|
||||
"ocr_text": "识别出的文字内容..."
|
||||
}
|
||||
```
|
||||
|
||||
> `duplicate_of` 指向数据库中已存在的相同内容记录 ID。
|
||||
|
||||
### Python 调用示例
|
||||
|
||||
```python
|
||||
@@ -70,7 +89,10 @@ async def recognize_image(file_path: str) -> dict:
|
||||
f"{BASE_URL}/api/v1/images/recognize-direct",
|
||||
files={"file": (file_path, f)}
|
||||
)
|
||||
return resp.json()
|
||||
result = resp.json()
|
||||
if result.get("status") == "duplicate":
|
||||
print(f"内容已存在,重复于 ID: {result['duplicate_of']}")
|
||||
return result
|
||||
```
|
||||
|
||||
---
|
||||
@@ -96,7 +118,8 @@ Content-Type: multipart/form-data
|
||||
{
|
||||
"total": 3,
|
||||
"success": 2,
|
||||
"failed": 1,
|
||||
"failed": 0,
|
||||
"duplicates": 1,
|
||||
"results": [
|
||||
{
|
||||
"id": 387,
|
||||
@@ -110,8 +133,15 @@ Content-Type: multipart/form-data
|
||||
},
|
||||
{
|
||||
"id": 388,
|
||||
"filename": "img2.jpg",
|
||||
"file_path": "data/images/img2.jpg",
|
||||
"filename": "img2.png",
|
||||
"status": "duplicate",
|
||||
"duplicate_of": 100,
|
||||
"ocr_text": "重复内容..."
|
||||
},
|
||||
{
|
||||
"id": 389,
|
||||
"filename": "img3.jpg",
|
||||
"file_path": "data/images/img3.jpg",
|
||||
"status": "failed",
|
||||
"message": "OCR 识别失败: ..."
|
||||
}
|
||||
@@ -160,7 +190,7 @@ GET /api/v1/images/search?keyword=搜索词&limit=5&sort=time_desc
|
||||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|------|------|------|--------|------|
|
||||
| keyword | string | 是 | - | 搜索关键词 |
|
||||
| limit | int | 否 | 5 | 最多返回多少条匹配结果 |
|
||||
| limit | int | 否 | 3 | 最多返回多少条匹配结果(可在设置中配置默认值) |
|
||||
| sort | string | 否 | time_desc | 排序方式:`time_desc`(时间倒序)、`time_asc`(时间正序)、`random`(随机) |
|
||||
|
||||
### 搜索原理
|
||||
@@ -168,7 +198,7 @@ GET /api/v1/images/search?keyword=搜索词&limit=5&sort=time_desc
|
||||
```
|
||||
用户搜索 "孩子不想上学"
|
||||
→ 从数据库取所有已识别图片(按 sort 排序)
|
||||
→ 每批 10 张发给 MiniMax M2.7 判断是否匹配
|
||||
→ 每批 N 张发给 MiniMax M2.7 判断(N 可在设置中配置,默认 10)
|
||||
→ 并发池最多 10 个 LLM 请求同时执行
|
||||
→ 匹配结果通过 SSE 实时推送(找到就立刻返回)
|
||||
→ 找够 limit 条或遍历完所有图片后结束
|
||||
@@ -413,7 +443,55 @@ GET /api/v1/images?page=1&page_size=20&status=completed
|
||||
|
||||
---
|
||||
|
||||
## 八、图片文件访问
|
||||
## 八、删除图片
|
||||
|
||||
### 接口
|
||||
|
||||
```
|
||||
DELETE /api/v1/images/{image_id}
|
||||
```
|
||||
|
||||
### 返回
|
||||
|
||||
成功返回 `204 No Content`。
|
||||
|
||||
### 说明
|
||||
|
||||
删除操作会同时清理:
|
||||
1. BM25 倒排索引中的记录
|
||||
2. 磁盘上的图片文件
|
||||
3. 数据库中的记录
|
||||
|
||||
---
|
||||
|
||||
## 九、一键去重
|
||||
|
||||
### 接口
|
||||
|
||||
```
|
||||
POST /api/v1/images/dedup
|
||||
```
|
||||
|
||||
### 返回
|
||||
|
||||
```json
|
||||
{
|
||||
"total_checked": 385,
|
||||
"duplicates_found": 12,
|
||||
"deleted": 12,
|
||||
"kept": 373
|
||||
}
|
||||
```
|
||||
|
||||
### 说明
|
||||
|
||||
- 按 OCR 识别文本精确匹配去重
|
||||
- 每组重复内容保留最早录入的记录
|
||||
- 删除操作与单条删除相同(清理索引 + 磁盘文件 + 数据库记录)
|
||||
|
||||
---
|
||||
|
||||
## 十、图片文件访问
|
||||
|
||||
识别后的图片可通过静态文件路径访问:
|
||||
|
||||
@@ -428,7 +506,51 @@ http://localhost:8765/data/images/001.png
|
||||
|
||||
---
|
||||
|
||||
## 九、注意事项
|
||||
## 十一、企业微信机器人相关接口
|
||||
|
||||
### 获取机器人状态
|
||||
|
||||
```
|
||||
GET /api/v1/settings/bot/status
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"enabled": true,
|
||||
"configured": true,
|
||||
"running": true,
|
||||
"bot_id": "aibPl3zE65xj..."
|
||||
}
|
||||
```
|
||||
|
||||
### 重启机器人
|
||||
|
||||
```
|
||||
POST /api/v1/settings/bot/restart
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "机器人已启动"
|
||||
}
|
||||
```
|
||||
|
||||
### 系统设置(含机器人配置)
|
||||
|
||||
```
|
||||
GET /api/v1/settings
|
||||
PUT /api/v1/settings
|
||||
```
|
||||
|
||||
机器人相关字段:
|
||||
- `wework_bot_enabled` (bool): 是否启用
|
||||
- `wework_bot_id` (string): Bot ID
|
||||
- `wework_bot_secret` (string): Secret(更新时传入明文,获取时返回脱敏值)
|
||||
|
||||
---
|
||||
|
||||
## 十二、注意事项
|
||||
|
||||
1. **搜索接口是 SSE 流式**,不能用普通的 `requests.get()` 或 `httpx.get()` 直接获取 JSON,必须用流式读取(`stream()` / `iter_lines()`)
|
||||
2. **搜索耗时较长**:385 张图片全部遍历约需 2-5 分钟,建议设置较长的超时(300 秒)
|
||||
@@ -436,3 +558,5 @@ http://localhost:8765/data/images/001.png
|
||||
4. **limit 参数控制返回数量**:找到 `limit` 条匹配后自动停止搜索
|
||||
5. **OCR 识别耗时**:单张图片约 5-15 秒,建议设置 120 秒超时
|
||||
6. **文件格式**:仅支持 .png .jpg .jpeg .bmp .webp
|
||||
7. **上传自动去重**:所有上传接口(recognize-direct、batch-recognize、import-paths)都会在 OCR 识别后检查内容是否已存在
|
||||
8. **删除不可恢复**:删除操作会同时清理索引、磁盘文件和数据库记录
|
||||
|
||||
Reference in New Issue
Block a user