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

IMAGE_API_GUIDE.md:
- 新增删除图片接口文档
- 新增一键去重接口文档
- 新增企业微信机器人相关接口文档
- 更新上传接口返回格式(含 duplicate 状态)
- 更新注意事项(去重/删除相关)
2026-04-14 13:44:35 +08:00

13 KiB
Raw Blame History

EduBrain 图片功能集成指南

本文档介绍如何在 ADK Agent 项目中集成 EduBrain 的图片 OCR 和搜索功能。

服务地址

BASE_URL = http://localhost:8765

所有接口前缀:/api/v1/images


一、接口总览

方法 路径 说明 请求类型
POST /recognize-direct 上传并识别单张图片 multipart/form-data
POST /batch-recognize 批量上传并识别 multipart/form-data
POST /import-paths 从服务器路径导入 JSON
GET /search AI 搜索图片SSE 流式) query params
GET `` 获取图片列表(分页) query params
GET /{image_id} 获取单张图片详情 path param
DELETE /{image_id} 删除图片 path param
POST /dedup 一键去重

二、上传并识别图片

接口

POST /api/v1/images/recognize-direct
Content-Type: multipart/form-data

参数

字段 类型 必填 说明
file File 图片文件,支持 .png .jpg .jpeg .bmp .webp

返回

正常识别:

{
    "id": 386,
    "file_path": "data/images/test.png",
    "original_filename": "test.png",
    "ocr_text": "识别出的文字内容...",
    "tags": [],
    "confidence": 1.0,
    "provider": "deepseek",
    "status": "completed",
    "blocks": [
        {"text": "第一段文字", "bbox": [x1,y1,x2,y2], "confidence": 0.98}
    ]
}

内容重复(自动去重):

{
    "id": 386,
    "file_path": "data/images/test.png",
    "original_filename": "test.png",
    "status": "duplicate",
    "duplicate_of": 100,
    "ocr_text": "识别出的文字内容..."
}

duplicate_of 指向数据库中已存在的相同内容记录 ID。

Python 调用示例

import httpx

async def recognize_image(file_path: str) -> dict:
    async with httpx.AsyncClient(timeout=120.0) as client:
        with open(file_path, "rb") as f:
            resp = await client.post(
                f"{BASE_URL}/api/v1/images/recognize-direct",
                files={"file": (file_path, f)}
            )
        result = resp.json()
        if result.get("status") == "duplicate":
            print(f"内容已存在,重复于 ID: {result['duplicate_of']}")
        return result

三、批量上传并识别

接口

POST /api/v1/images/batch-recognize
Content-Type: multipart/form-data

参数

字段 类型 必填 说明
files File[] 多个图片文件

返回

{
    "total": 3,
    "success": 2,
    "failed": 0,
    "duplicates": 1,
    "results": [
        {
            "id": 387,
            "filename": "img1.png",
            "file_path": "data/images/img1.png",
            "status": "completed",
            "ocr_text": "识别内容...",
            "tags": [],
            "confidence": 1.0,
            "provider": "deepseek"
        },
        {
            "id": 388,
            "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 识别失败: ..."
        }
    ]
}

四、从服务器路径导入

接口

POST /api/v1/images/import-paths
Content-Type: application/json

请求体

{
    "paths": ["/data/images/001.png", "/data/images/002.jpg"],
    "recursive": false
}

返回

与批量上传相同格式。


五、AI 搜索图片SSE 流式)⚠️ 重点

接口

GET /api/v1/images/search?keyword=搜索词&limit=5&sort=time_desc

这不是普通 HTTP 请求,而是 SSEServer-Sent Events流式接口。

参数

参数 类型 必填 默认值 说明
keyword string - 搜索关键词
limit int 3 最多返回多少条匹配结果(可在设置中配置默认值)
sort string time_desc 排序方式:time_desc(时间倒序)、time_asc(时间正序)、random(随机)

搜索原理

用户搜索 "孩子不想上学"
  → 从数据库取所有已识别图片(按 sort 排序)
  → 每批 N 张发给 MiniMax M2.7 判断N 可在设置中配置,默认 10
  → 并发池最多 10 个 LLM 请求同时执行
  → 匹配结果通过 SSE 实时推送(找到就立刻返回)
  → 找够 limit 条或遍历完所有图片后结束

SSE 事件格式

连接后,服务端会持续推送 JSON 事件,每条格式为:

data: {"type": "事件类型", ...其他字段}\n\n

事件类型

1. start - 搜索开始

{"type": "start", "keyword": "孩子不想上学", "limit": 5}

2. progress - 进度更新

{"type": "progress", "checked": 170, "total": 385, "found": 3}
字段 类型 说明
checked int 已检查的图片数量
total int 数据库中图片总数
found int 已找到的匹配数量

3. result - 匹配结果(实时推送)

{
    "type": "result",
    "id": 42,
    "file_path": "data/images/001.png",
    "image_url": "001.png",
    "image_base64": "/9j/4QE3RXhpZgAATU0AKgAAAAgABgEAAAQ...",
    "ocr_text": "完整的 OCR 识别文本...",
    "ocr_text_preview": "前150个字符的预览...",
    "tags": ["亲子关系", "沟通"],
    "confidence": 1.0,
    "provider": "deepseek",
    "created_at": "2026-04-11T21:59:56"
}
字段 类型 说明
id int 图片记录 ID
file_path string 图片文件路径(服务器本地路径)
image_url string 图片文件名(访问地址为 BASE_URL/data/images/{image_url}
image_base64 string 图片的 Base64 编码,可直接用于发送到企业微信等
ocr_text string 完整 OCR 文本
ocr_text_preview string OCR 文本预览前150字符
tags string[] 关键词标签
confidence float OCR 置信度0-1
provider string OCR 提供商
created_at string 创建时间ISO 8601

4. done - 搜索结束

{"type": "done", "total_found": 5, "total_checked": 385}
字段 类型 说明
total_found int 总共找到的匹配数量
total_checked int 总共检查的图片数量

Python 调用示例(推荐)

import httpx
import json
from typing import AsyncGenerator

BASE_URL = "http://localhost:8765"

async def search_images(
    keyword: str,
    limit: int = 5,
    sort: str = "time_desc",
) -> AsyncGenerator[dict, None]:
    """
    AI 搜索图片SSE 流式)

    Yields:
        dict: 每个 SSE 事件解析后的字典
    """
    async with httpx.AsyncClient(timeout=300.0) as client:
        async with client.stream(
            "GET",
            f"{BASE_URL}/api/v1/images/search",
            params={"keyword": keyword, "limit": limit, "sort": sort},
        ) as resp:
            async for line in resp.aiter_lines():
                if not line.startswith("data: "):
                    continue
                data = json.loads(line[6:])
                yield data


# ── 使用示例 ──

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

ADK Agent 集成示例

在 ADK Agent 中,可以将搜索功能封装为一个 Tool

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):
            if event["type"] == "result":
                results.append({
                    "id": event["id"],
                    "image_base64": event["image_base64"],
                    "image_url": event["image_url"],
                    "ocr_text_preview": event["ocr_text_preview"],
                    "tags": event.get("tags", []),
                })
            elif event["type"] == "done":
                pass  # 搜索结束

        if not results:
            return f"未找到与 '{keyword}' 相关的图片"

        # 返回 JSON方便 Agent 后续处理(如发送到企业微信)
        return json.dumps({
            "keyword": keyword,
            "total": len(results),
            "images": results,
        }, ensure_ascii=False)

提示image_base64 字段包含图片的完整 Base64 编码,可直接用于企业微信发送图片接口。 企业微信发送图片需要先通过「上传临时素材」接口上传 base64 获取 media_id,再发送图片消息。


六、获取图片详情

接口

GET /api/v1/images/{image_id}

返回

{
    "id": 1,
    "file_path": "data/images/001.png",
    "ocr_text": "完整 OCR 文本...",
    "confidence": 1.0,
    "provider": "deepseek",
    "status": "completed",
    "tags": null,
    "story_summary": null,
    "created_at": "2026-04-11T20:00:00",
    "updated_at": "2026-04-11T20:00:05"
}

七、获取图片列表

接口

GET /api/v1/images?page=1&page_size=20&status=completed

参数

参数 类型 必填 默认值 说明
page int 1 页码
page_size int 20 每页数量
status string - 筛选状态:completedpendingprocessingfailed

返回

{
    "total": 385,
    "page": 1,
    "page_size": 20,
    "items": [
        {
            "id": 385,
            "file_path": "data/images/001.png",
            "ocr_text": "...",
            "confidence": 1.0,
            "provider": "deepseek",
            "status": "completed",
            "tags": null,
            "story_summary": null,
            "created_at": "2026-04-11T21:59:56",
            "updated_at": "2026-04-11T21:59:59"
        }
    ]
}

八、删除图片

接口

DELETE /api/v1/images/{image_id}

返回

成功返回 204 No Content

说明

删除操作会同时清理:

  1. BM25 倒排索引中的记录
  2. 磁盘上的图片文件
  3. 数据库中的记录

九、一键去重

接口

POST /api/v1/images/dedup

返回

{
    "total_checked": 385,
    "duplicates_found": 12,
    "deleted": 12,
    "kept": 373
}

说明

  • 按 OCR 识别文本精确匹配去重
  • 每组重复内容保留最早录入的记录
  • 删除操作与单条删除相同(清理索引 + 磁盘文件 + 数据库记录)

十、图片文件访问

识别后的图片可通过静态文件路径访问:

GET /data/images/{文件名}

例如:

http://localhost:8765/data/images/001.png

十一、企业微信机器人相关接口

获取机器人状态

GET /api/v1/settings/bot/status
{
    "enabled": true,
    "configured": true,
    "running": true,
    "bot_id": "aibPl3zE65xj..."
}

重启机器人

POST /api/v1/settings/bot/restart
{
    "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 秒)
  3. 搜索结果实时推送:不需要等所有图片检查完毕,匹配结果会逐条推送
  4. limit 参数控制返回数量:找到 limit 条匹配后自动停止搜索
  5. OCR 识别耗时:单张图片约 5-15 秒,建议设置 120 秒超时
  6. 文件格式:仅支持 .png .jpg .jpeg .bmp .webp
  7. 上传自动去重所有上传接口recognize-direct、batch-recognize、import-paths都会在 OCR 识别后检查内容是否已存在
  8. 删除不可恢复:删除操作会同时清理索引、磁盘文件和数据库记录