[架构重构] 移除本地Ollama融合,云端直出JSON直存DB,Q&A三模型降级
1. 视频摘要链路:云端VLM直出结构化JSON → Edge format_cloud_result格式化校验 → 直存NAS DB(移除run_text_fusion本地融合) 2. 智能问答链路:Gemini→NVIDIA→Ollama降级,新增chat()纯文本问答方法 3. 适配器重构:base/gemini/nvidia/ollama adapter新增chat();gemini多图单请求结构化JSON;nvidia逐帧调用聚合 4. 端点变更:/api/edge/chat → /api/edge/chat/ask,调orchestrator.run_qa() 5. chat_handler改经Edge Q&A编排,不再直连Ollama 6. 配置更新:ollama_url → qa_url,Ollama role注释改为Q&A兜底 7. README同步更新架构描述、拓扑图、时序图、模块表
This commit is contained in:
90
README.md
90
README.md
@@ -14,11 +14,11 @@
|
||||
### 1.1 首期范围(已基本完成)
|
||||
|
||||
- **FAM-Core**(NAS 端单进程):Task-Scheduler / Dispatcher / Event-Receiver / Chat-Handler / Member-Manager / Video-Server 六个子模块
|
||||
- **FAM-Edge**(Oracle 端单进程):接收视频上传 → FFmpeg 快速抽帧 → OpenCV 关键帧筛选 → 云端 VLM 视觉分析(Gemini/NVIDIA NIM)→ 本地 Ollama 文本融合 → 结果同步返回 全链路
|
||||
- **FAM-Edge**(Oracle 端单进程):接收视频上传 → FFmpeg 快速抽帧 → OpenCV 关键帧筛选 → 云端 VLM 视觉分析(Gemini/NVIDIA NIM)直出结构化 JSON → Edge 仅做格式化/校验 → 结果同步返回 全链路(**本地模型不参与视频分析**)
|
||||
- **FAM-UI**(NAS 端):Streamlit 直读 DB,事件列表 + 成员命名页 + AI 对话页 + 对话历史
|
||||
- **数据库六张表**:process_tasks / monitor_events / event_details / chat_history / family_members / daily_summaries(预留)
|
||||
- **任务状态机**:PENDING → PROCESSING → SUCCESS/FAILED,含退避重试与僵尸任务回收
|
||||
- **AI 对话**:查 event_details 拼上下文 → 经 FAM-Edge 代理调 Oracle 本地 Ollama 纯文本 → 返回回答并写 chat_history
|
||||
- **AI 对话**:查 event_details 拼上下文 → 经 FAM-Edge 问答编排(Gemini → NVIDIA → 本地 Ollama 兜底)生成回答 → 返回并写 chat_history
|
||||
- **交互式成员命名**:VLM 按特征提取"人物A/B/C"落库,用户命名后批量回溯更新历史,后续分析直接用真名
|
||||
|
||||
### 1.2 不在首期范围(推迟 v1.1+)
|
||||
@@ -39,19 +39,19 @@
|
||||
| **Tailscale 端口不通** | NAS tailscaled 以 userspace 模式运行(无 TUN 网卡),Oracle 无法反向访问 NAS;当前 NAS→Oracle 走公网 IP | 推送模式已规避反向访问,但流量走公网 |
|
||||
| **历史视频积压** | 正式目录 `/volume1/surveillance/Generic_ONVIF-001` 有约 285 个历史视频(~100GB),288 个历史任务已标记 FAILED 避免全量上传 | 切回生产目录前需确认回补策略 |
|
||||
|
||||
**规划方向**:**云端大模型负责视觉识别、甲骨文本地大模型负责文本问答**。按任务类型分工:
|
||||
**规划方向(新框架)**:**云端大模型负责视觉识别与结构化输出、本地大模型仅做智能问答兜底**。按任务类型分工:
|
||||
|
||||
| 任务 | 执行方 | 模型 | 说明 |
|
||||
|------|--------|------|------|
|
||||
| **视觉分析**(看图识人/动作/衣着) | 云端 | Gemini → NVIDIA NIM(fallback 降级) | 云端 GPU 推理,支持多图,质量与速度均优于本地 ARM |
|
||||
| **文本融合**(多帧视觉结果 → 结构化 JSON) | 本地 | Ollama qwen2.5:7b | 纯文本能力,不涉及图片 |
|
||||
| **AI 对话**("汤圆今天干嘛了") | 本地 | Ollama qwen2.5:7b | 纯文本问答 |
|
||||
| **视觉分析 + 结构化输出**(看图识人/动作/衣着 → 直出 JSON) | 云端 | Gemini → NVIDIA NIM(fallback 降级) | 云端 VLM 直接产出 `global_summary` / `entities_json` / `frame_details`,Edge 仅做**格式化校验**后直存 NAS,**本地模型不介入** |
|
||||
| **结果格式化**(云端 JSON → 入库 schema) | Edge 进程 | 无模型调用 | `format_cloud_result`:字段归一化、补 `source_providers`/`compute_provider`、推导 `entities`、缺失 `global_summary` 时事实拼接;纯数据转换,非 LLM 二次汇总 |
|
||||
| **AI 对话**("汤圆今天干嘛了") | 云端优先 + 本地兜底 | Gemini → NVIDIA NIM → 本地 Ollama | 两云端任一成功即用;**仅当 Gemini 与 NVIDIA 都失败**才回退本地 Ollama qwen2.5:7b |
|
||||
|
||||
- **Google Gemini**(`gemini-1.5-flash`,API Key 已验证,单帧 2~3s 且真正支持多图)
|
||||
- **NVIDIA NIM**(`meta/llama-3.2-11b-vision-instruct`,OpenAI 兼容 API,云端 GPU 推理,响应快)
|
||||
- **本地 Ollama**(qwen2.5:7b)**专职文本任务**(融合 + 对话),不再参与视觉分析;视觉链路两云端全失败 → 任务 FAILED 走重试
|
||||
- **Google Gemini**(`gemini-flash-latest`,API Key 已验证,支持多图单请求,视觉 + 问答均参与)
|
||||
- **NVIDIA NIM**(`meta/llama-3.2-11b-vision-instruct`,OpenAI 兼容 API,云端 GPU 推理,单请求限 1 图故逐帧调用;视觉 + 问答均参与)
|
||||
- **本地 Ollama**(qwen2.5:7b)**仅参与智能问答,且仅作兜底**:视觉链路两云端全失败 → 任务 FAILED 走重试,**绝不回退本地模型做视觉/融合**
|
||||
|
||||
Orchestrator 视觉阶段按 `fallback` 模式顺序降级:Gemini → NVIDIA NIM;记录每模型实际执行耗时与成功状态。`analyze_frames` 改为逐帧请求。
|
||||
Orchestrator 视觉阶段按 `fallback` 模式顺序降级:Gemini → NVIDIA NIM;云端模型直出结构化 JSON 后由 `format_cloud_result` 格式化。问答阶段按 `gemini → nvidia → ollama` 顺序,仅末位本地模型作兜底。
|
||||
|
||||
---
|
||||
|
||||
@@ -97,10 +97,10 @@ Orchestrator 视觉阶段按 `fallback` 模式顺序降级:Gemini → NVIDIA N
|
||||
┌──────────────────────── Oracle Cloud (129.146.203.203) ──────────────────────────┐
|
||||
│ FAM-Edge (Flask :5000, gunicorn --timeout 1800, 单 worker) │
|
||||
│ ├─ POST /api/edge/video/push(multipart 视频,同步分析,结果随响应返回) │
|
||||
│ ├─ AI-Orchestrator: 健康检查 → 抽帧 → 选帧 → 压缩 → 云端VLM视觉 → 本地Ollama文本融合 │
|
||||
│ ├─ POST /api/edge/chat(代理转发本地 Ollama 纯文本问答) │
|
||||
│ ├─ AI-Orchestrator: 健康检查 → 抽帧 → 选帧 → 压缩 → 云端VLM视觉直出结构化JSON → 格式化校验(无本地融合) │
|
||||
│ ├─ POST /api/edge/chat/ask(问答编排: Gemini→NVIDIA→本地Ollama 兜底) │
|
||||
│ ▼ │
|
||||
│ 云端: Gemini / NVIDIA NIM (视觉) 本地: Ollama :11434 (qwen2.5:7b, 文本融合+对话) │
|
||||
│ 云端: Gemini / NVIDIA NIM (视觉直出结构化 + 问答) 本地: Ollama :11434 (qwen2.5:7b, 仅问答兜底) │
|
||||
└───────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
@@ -112,8 +112,8 @@ Orchestrator 视觉阶段按 `fallback` 模式顺序降级:Gemini → NVIDIA N
|
||||
- a. 保存上传视频到临时目录(超时 60s)
|
||||
- b. FFmpeg 快速 seek(`-ss <ts> -frames:v 1`)粗抽候选帧,帧数随视频时长自适应
|
||||
- c. OpenCV MSE 帧差分析筛选关键帧 → 压缩(长边 ≤ 1024px,JPEG 质量 80)
|
||||
- d. 云端视觉模型按 `orchestrator.mode`(fallback)顺序降级:Gemini(timeout 15s)→ NVIDIA NIM(timeout 20s);首个返回非 None 结果即采用,两云端全失败 → 任务 FAILED 走重试(不回退本地 Ollama,本地仅负责文本)
|
||||
- e. 本地 Ollama 文本融合(超时 300s,输入各帧视觉描述 + 时间戳 + known_members 上下文)→ JSON 结构化结果
|
||||
- d. 云端视觉模型按 `orchestrator.mode`(fallback)顺序降级:Gemini(timeout 30s)→ NVIDIA NIM(timeout 20s);首个**直出结构化 JSON** 成功的模型即采用,两云端全失败 → 任务 FAILED 走重试(绝不回退本地 Ollama,本地模型不参与视频分析)
|
||||
- e. `format_cloud_result` 格式化校验(无模型调用):字段归一化、补 `source_providers=[provider]` / `compute_provider=[provider]`、缺失 `entities_json` 由 `frame_details` 推导、缺失 `global_summary` 时事实拼接 → 合法入库 schema
|
||||
- f. `event_end_time` = event_start_time + 视频时长(Edge 推算)
|
||||
- g. `finally` 清理临时文件
|
||||
4. Edge 把结果 JSON 直接作为 HTTP 响应返回(无 webhook)
|
||||
@@ -135,7 +135,7 @@ Orchestrator 视觉阶段按 `fallback` 模式顺序降级:Gemini → NVIDIA N
|
||||
| Task-Scheduler | `scheduler/scheduler.py` | 60s 轮询视频目录(`os.walk` 递归,支持 AM/PM 子目录),`video_path` 去重,稳定文件建 PENDING 任务 |
|
||||
| Dispatcher | `dispatcher/dispatcher.py` | 30s 轮询 PENDING;multipart 上传视频至 Edge push 端点;收响应后经 `apply_success_event` 落库;僵尸 PROCESSING 回收;退避重试 |
|
||||
| Event-Receiver | `event_receiver/event_receiver.py` | `/api/core/callback/event`(webhook 兼容保留);核心逻辑抽为 `apply_success_event(data)` 供 Dispatcher 推送模式复用;未命名 abstract_label 自动 upsert `family_members` |
|
||||
| Chat-Handler | `chat_handler/chat_handler.py` | `/api/chat/ask` 查 event_details 拼上下文 → 经 Edge 代理调 Ollama → 写 chat_history;明细 > 50 条按小时聚合 |
|
||||
| Chat-Handler | `chat_handler/chat_handler.py` | `/api/chat/ask` 查 event_details 拼上下文 → 经 Edge `/api/edge/chat/ask` 问答编排(Gemini→NVIDIA→本地 Ollama 兜底)→ 写 chat_history;明细 > 50 条按小时聚合 |
|
||||
| Member-Manager | `member_manager/member_manager.py` | `/api/member/unnamed` / `/api/member/name` / `/api/member/list`;命名后批量回溯 UPDATE event_details(MariaDB 不支持 `$[*]` JSON 路径,Python 层逐行更新) |
|
||||
| Video-Server | `video_server/video_server.py` | `/media/<path>?token=xxx` 静态视频服务(推送模式下主链路不再使用,保留备用) |
|
||||
| 公共层 | `db_layer.py` / `config_loader.py` / `logger.py` | PyMySQL 连接(unix_socket);datetime 空串归一化 NULL + NOT NULL 列兜底;文件日志 |
|
||||
@@ -146,11 +146,11 @@ Orchestrator 视觉阶段按 `fallback` 模式顺序降级:Gemini → NVIDIA N
|
||||
|------|------|------|
|
||||
| API-Gateway | `api_gateway/api_gateway.py` | `POST /api/edge/video/push`(multipart 上传 + 同步分析 + 结果返回);`POST /api/edge/video/analyze`(旧拉取模式,兼容保留);`POST /api/edge/chat`(Ollama 代理);`GET /health`;单并发控制(处理中返回 429) |
|
||||
| Video-Preprocessor | `video_preprocessor/preprocessor.py` | `save_upload` 保存上传视频;FFmpeg 快速 seek 粗抽候选帧(帧数自适应);OpenCV MSE 帧差筛选关键帧(首末帧必选);压缩;`compute_timestamps` 用 start+偏移算绝对时间戳;`video_duration` 供 event_end_time 推算 |
|
||||
| AI-Orchestrator | `ai_orchestrator/orchestrator.py` | 模型健康检查 → 云端视觉适配器按 `orchestrator.mode`(fallback 顺序降级)调度 → 本地 Ollama 文本融合 → JSON 解析三层容错 + schema 校验;`process_push_task` 为推送模式入口(不触发 webhook);记录各模型实际执行耗时与成功状态到 `compute_provider` 数组 |
|
||||
| Model-Adapters | `model_adapters/` | `BaseModelAdapter` 抽象基类(`__init__` / `health_check` / `analyze_frames` / `get_timeout` / 熔断器实例);`build_adapter` 工厂函数按 `provider` 字段分发实例化;视觉适配器(gemini/nvidia)参与视觉分析,文本适配器(ollama)专职融合与对话 |
|
||||
| └ OllamaAdapter | `model_adapters/ollama_adapter.py` | requests 直调本地 REST `/api/chat`,`num_predict` 可配;**role: text**(文本融合 + AI 对话,不参与视觉分析) |
|
||||
| └ GeminiAdapter | `model_adapters/gemini_adapter.py` | requests 直调 Google REST `:generateContent`,支持多图;**role: vision** |
|
||||
| └ NvidiaVisionAdapter | `model_adapters/nvidia_adapter.py` | **基于 openai SDK**(NIM 兼容 OpenAI API 规范),`base_url=https://integrate.api.nvidia.com/v1`,`api_key` 从 `${NVIDIA_API_KEY}` 展开;`analyze_frames` 用 OpenAI 标准 `image_url`(Base64 内联)格式打包多张关键帧;`health_check` 调 `client.models.list()`;**role: vision** |
|
||||
| AI-Orchestrator | `ai_orchestrator/orchestrator.py` | 模型健康检查 → 云端视觉适配器按 `orchestrator.mode`(fallback 顺序降级)调度,**直出结构化 JSON** → `format_cloud_result` 格式化校验(无本地融合)→ JSON schema 校验;`run_qa` 实现问答编排(Gemini→NVIDIA→本地 Ollama 兜底);`process_push_task` 为推送模式入口(不触发 webhook);记录各模型实际执行耗时与成功状态到 `compute_provider` 数组 |
|
||||
| Model-Adapters | `model_adapters/` | `BaseModelAdapter` 抽象基类(`__init__` / `health_check` / `analyze_frames` / `chat` / `get_timeout` / 熔断器实例);`build_adapter` 工厂函数按 `provider` 字段分发实例化;视觉适配器(gemini/nvidia)直出结构化 JSON,文本适配器(ollama)仅智能问答兜底 |
|
||||
| └ OllamaAdapter | `model_adapters/ollama_adapter.py` | requests 直调本地 REST `/api/generate`,`num_predict` 可配;**role: text, usage: qa_fallback**(仅智能问答兜底,不参与视觉分析、不参与融合) |
|
||||
| └ GeminiAdapter | `model_adapters/gemini_adapter.py` | requests 直调 Google REST `:generateContent`,**多图单请求直出结构化 JSON**;**role: vision**;`chat()` 参与问答 |
|
||||
| └ NvidiaVisionAdapter | `model_adapters/nvidia_adapter.py` | **基于 openai SDK**(NIM 兼容 OpenAI API 规范),`base_url=https://integrate.api.nvidia.com/v1`,`api_key` 从 `${NVIDIA_API_KEY}` 展开;**逐帧返回结构化单帧 JSON 并聚合为 frame_details**(NIM 限 1 图/请求);`health_check` 调 `client.models.list()`;**role: vision**;`chat()` 参与问答 |
|
||||
| Storage-Cleaner | `storage_cleaner/` | `finally` 删除临时视频与帧图片 |
|
||||
|
||||
### 3.3 FAM-UI(NAS 端)
|
||||
@@ -195,7 +195,7 @@ chat_history 独立表
|
||||
|
||||
### 4.3 compute_provider / source_providers
|
||||
|
||||
- `monitor_events.compute_provider`:JSON 数组,记录本次任务实际成功调用的模型,如 `["ollama"]` 或 `["ollama","gemini"]`
|
||||
- `monitor_events.compute_provider`:JSON 数组,记录本次任务实际成功调用(**视觉分析**)的云端模型,如 `["gemini"]` 或 `["nvidia"]`;本地 Ollama 不参与视频分析,不会出现在该字段
|
||||
- `event_details.source_providers`:该条明细被哪些模型识别到(可能少于 compute_provider)
|
||||
- 多模型交叉验证:多模型一致 → 可信度高;仅单一模型描述 → source_providers 仅含该模型;冲突 → 多数派为准
|
||||
|
||||
@@ -227,9 +227,9 @@ chat_history 独立表
|
||||
"entities_json": [{"person": "汤圆", "action": "...", "clothing": "..."}],
|
||||
"frame_details": [
|
||||
{"frame_index": 1, "frame_timestamp": "...", "person": "...", "action": "...",
|
||||
"clothing": "...", "is_attention_event": false, "source_providers": ["ollama"]}
|
||||
"clothing": "...", "is_attention_event": false, "source_providers": ["gemini"]}
|
||||
],
|
||||
"compute_provider": ["ollama"]
|
||||
"compute_provider": ["gemini"]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -237,7 +237,8 @@ chat_history 独立表
|
||||
- 429:已有任务处理中(单并发);503:全部模型不健康
|
||||
|
||||
**POST /api/edge/video/analyze** — 旧拉取模式(Edge 拉 video_url + webhook 回调),兼容保留,主链路不再使用
|
||||
**POST /api/edge/chat** — Ollama 聊天代理(FAM-Core Chat-Handler 调用)
|
||||
**POST /api/edge/chat/ask** — 智能问答编排(FAM-Core Chat-Handler 调用):请求 `{"prompt"}` → 响应 `{"answer","provider"}`;内部按 Gemini → NVIDIA → 本地 Ollama 顺序,仅两云端都失败才用本地兜底
|
||||
**POST /api/edge/chat** — Ollama 直连代理(兼容旧调用,保留)
|
||||
**GET /health** — 服务与模型健康状态(任务处理中可能无响应,单 worker 忙)
|
||||
|
||||
### 5.2 FAM-Core(NAS :8000)
|
||||
@@ -270,9 +271,9 @@ chat_history 独立表
|
||||
- **压缩**:长边 > 1024px 才缩放,JPEG 质量 80
|
||||
- **异常兜底**:ffprobe 失败退化为 60s 间隔抽帧;帧差异常退化为等距 5 帧
|
||||
|
||||
### 6.2 Ollama 调用(ARM CPU 实测调优,专职文本任务)
|
||||
### 6.2 本地 Ollama(仅智能问答兜底,ARM CPU)
|
||||
|
||||
本地 Ollama(qwen2.5:7b,纯文本模型)**不参与视觉分析**,专职承担文本融合与 AI 对话两项纯文本任务。视觉分析全部由云端模型承担。以下参数作为文本任务的调优依据保留。
|
||||
本地 Ollama(qwen2.5:7b,纯文本模型)**不参与视觉分析、不参与云端结果融合**。它只在**智能问答**场景下、且 Gemini 与 NVIDIA 两云端模型都失败时才被启用作为兜底。视觉分析与结构化输出全部由云端模型承担。以下参数作为问答任务的调优依据保留。
|
||||
|
||||
| 参数 | 值 | 依据 |
|
||||
|------|-----|------|
|
||||
@@ -308,8 +309,8 @@ chat_history 独立表
|
||||
| `nvidia` | `NvidiaVisionAdapter` | **vision** | **openai SDK**(NIM 兼容 OpenAI API 规范) | 待实现 |
|
||||
|
||||
**role 语义**:
|
||||
- `vision`:参与视觉分析阶段,按 fallback 顺序降级
|
||||
- `text`:参与文本融合与 AI 对话,不参与视觉分析
|
||||
- `vision`:参与视觉分析阶段,按 fallback 顺序降级,直出结构化 JSON
|
||||
- `text`:仅参与智能问答(`usage: qa_fallback`),且为 Gemini/NVIDIA 都失败时的兜底,不参与视觉分析、不参与云端结果融合
|
||||
|
||||
**NvidiaVisionAdapter 关键实现**(`fam_edge/adapters/nvidia_adapter.py`):
|
||||
|
||||
@@ -328,24 +329,39 @@ chat_history 独立表
|
||||
**视觉分析降级链路(fallback 模式,仅云端 role=vision 模型)**:
|
||||
|
||||
```
|
||||
Gemini (gemini-1.5-flash, role=vision)
|
||||
Gemini (gemini-flash-latest, role=vision) —— 多图单请求直出结构化 JSON
|
||||
│ 失败 / 熔断 OPEN / 超时
|
||||
▼
|
||||
NVIDIA NIM (llama-3.2-11b-vision-instruct, role=vision)
|
||||
NVIDIA NIM (llama-3.2-11b-vision-instruct, role=vision) —— 逐帧结构化聚合
|
||||
│ 失败 / 熔断 OPEN / 超时
|
||||
▼
|
||||
任务 FAILED(走重试,不回退本地 Ollama —— 本地仅负责文本)
|
||||
任务 FAILED(走重试,绝不回退本地 Ollama —— 本地仅负责问答兜底)
|
||||
```
|
||||
|
||||
**文本阶段链路**:
|
||||
**格式化阶段(无模型调用,仅数据转换)**:
|
||||
|
||||
```
|
||||
各帧视觉描述(来自云端成功 provider)+ 时间戳 + known_members 上下文
|
||||
云端成功 provider 直出的结构化 JSON(frame_details / 可选 global_summary / entities_json)
|
||||
▼
|
||||
本地 Ollama (qwen2.5:7b, role=text) 文本融合 → JSON 结构化结果
|
||||
│ 失败 / 超时
|
||||
format_cloud_result:字段归一化 → 补 source_providers=[provider] / compute_provider=[provider]
|
||||
→ 缺失 entities_json 由 frame_details 推导 → 缺失 global_summary 则事实拼接 → schema 校验
|
||||
▼
|
||||
任务 FAILED(走重试)
|
||||
合法入库结构,随响应返回 NAS 直接落库(本地模型不介入)
|
||||
```
|
||||
|
||||
**智能问答降级链路(chat 场景)**:
|
||||
|
||||
```
|
||||
Gemini (role=vision, 也参与问答)
|
||||
│ 失败 / 熔断 OPEN / 超时
|
||||
▼
|
||||
NVIDIA NIM (role=vision, 也参与问答)
|
||||
│ 失败 / 熔断 OPEN / 超时
|
||||
▼
|
||||
本地 Ollama (qwen2.5:7b, role=text, usage=qa_fallback) —— 仅当两云端都失败才启用
|
||||
│ 失败
|
||||
▼
|
||||
返回"所有模型均不可用"
|
||||
```
|
||||
|
||||
**熔断器策略**(按 provider 独立,仅云端模型启用):
|
||||
|
||||
Reference in New Issue
Block a user