docs: 更新 README.md 和 IMAGE_API_GUIDE.md

README.md:
- 新增企业微信机器人功能说明(搜索/图片入库/集成启动)
- 新增图片去重和管理功能说明
- 新增可配置参数说明
- 更新 API 概览(删除/去重/机器人管理接口)
- 更新项目结构和技术栈

IMAGE_API_GUIDE.md:
- 新增删除图片接口文档
- 新增一键去重接口文档
- 新增企业微信机器人相关接口文档
- 更新上传接口返回格式(含 duplicate 状态)
- 更新注意事项(去重/删除相关)
This commit is contained in:
EduBrain Dev
2026-04-14 13:44:35 +08:00
parent e8a861fda2
commit 01ecc3fa6d
2 changed files with 205 additions and 28 deletions

View File

@@ -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 条或遍历完所有图片后结束
@@ -260,7 +290,7 @@ async def search_images(
) -> AsyncGenerator[dict, None]:
"""
AI 搜索图片SSE 流式)
Yields:
dict: 每个 SSE 事件解析后的字典
"""
@@ -281,23 +311,23 @@ async def search_images(
async def demo_search():
results = []
async for event in search_images("孩子不想上学", limit=5):
event_type = event.get("type")
if event_type == "start":
print(f"开始搜索: {event['keyword']}")
elif event_type == "progress":
print(f"进度: {event['checked']}/{event['total']},已找到 {event['found']} 条")
elif event_type == "result":
results.append(event)
print(f"找到匹配: [{event['id']}] {event['ocr_text_preview'][:50]}")
elif event_type == "done":
print(f"搜索完成: 共找到 {event['total_found']} 条,检查了 {event['total_checked']} 张")
return results
```
@@ -310,10 +340,10 @@ from google.adk import Tool
class ImageSearchTool(Tool):
"""搜索图片知识库"""
name = "search_images"
description = "搜索已识别的图片库,返回与查询语义相关的图片及其 Base64 数据。支持自然语言查询。"
async def execute(self, keyword: str, limit: int = 5) -> str:
results = []
async for event in search_images(keyword, limit=limit):
@@ -327,10 +357,10 @@ class ImageSearchTool(Tool):
})
elif event["type"] == "done":
pass # 搜索结束
if not results:
return f"未找到与 '{keyword}' 相关的图片"
# 返回 JSON方便 Agent 后续处理(如发送到企业微信)
return json.dumps({
"keyword": keyword,
@@ -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. **删除不可恢复**:删除操作会同时清理索引、磁盘文件和数据库记录