Files
sentinel-home-ai/README.md
ericwyuan bdf561a415 fix(fam-core): 登录密码不再硬编码兜底(fail-closed) + 会话有效期改 2 小时
之前 FAM_AUTH_USER/FAM_AUTH_PASS 没配置时会退回代码里写死的 ericwyuan/
iLoveJava5——这两个值跟 NAS SSH 密码是同一个,且这个登录页已经通过 frp 暴露在
公网上。.env 万一没配置好(这个项目里已经发生过好几次"忘了 source .env"的
情况),公网入口就会用一个和 SSH 密码相同、且写在源码里的已知密码兜底,双重
风险叠一起。

现在改成 fail-closed:FAM_AUTH_USER/FAM_AUTH_PASS 只要有一个没配置,直接拒绝
所有登录(打一条 ERROR 日志提醒去配 .env),不再有任何硬编码默认值。

会话有效期从 7 天改成 2 小时(按用户要求)。

新增 test_auth.py 11 个用例覆盖:未配置时 fail-closed、正确/错误凭据校验、
只配了一半也要拒绝、session 过期判定、白名单路径匹配。

已实测验证:当前 .env 里 FAM_AUTH_USER/PASS 仍是配置好的(还是 ericwyuan/
iLoveJava5,这个值本身没改——只是不再硬编码在代码里,运维如果想换成不同于
SSH 的密码,现在改 .env 就行,不用改代码),登录/拒绝错误密码都验证正常,
cookie Max-Age=7200 确认生效。
2026-08-23 07:56:33 +08:00

51 KiB
Raw Blame History

Sentinel Home AI — 家庭多模态智能监控系统

多模型容灾降级 + 交互式命名 + AI 对话的家庭监控系统。 本文档为项目需求文档与 README 的整合版按当前代码实际状态v1.0 E2E 已打通)编写。 最后更新2026-08-22运动事件驱动架构整段素材分割运动片段再分析


1. 项目简介与设计目标

系统持续分析家庭监控摄像头Synology Surveillance Station录制的视频抽取关键帧后调用视觉语言模型VLM分析画面中的人物、动作、衣着落库为结构化事件用户通过 Web UI 浏览事件、给"人物A/B/C"命名(批量回溯历史记录)、以及用自然语言向 AI 询问"汤圆今天干嘛了"。

1.1 首期范围(已基本完成)

新架构 v32026-08-22 重构:运动事件驱动)NAS 不再处理视频,仅作管理后台 + 运动事件推送视频分析全部上云Oracle不再分析整段视频——整段素材按 SS 运动事件分割成运动片段后只分析片段。

  • FAM-CoreNAS 端单进程):Oracle-Sync(每 30 分钟拉增量镜像)+ MotionNotifier(轮询 SS 运动事件推送 Oracle+ Chat-Handler + Member-ManagerCPU 占用极低
  • FAM-EdgeOracle 端单进程rclone 实时同步 Google 硬盘视频(整段素材)→ 按 ss_motion_events 运动事件start_time/durationffmpeg 分割运动片段只把运动片段送云端 VLMGemini 用 Files API / NVIDIA 整视频 video_url)→ 结构化 JSON 落本地 SQLite → 对外提供 /api/oracle/sync 增量拉取接口(本地模型不参与视频分析
  • FAM-UINAS 端Vue3 SPA 读本地同步镜像sync_videos / sync_events / sync_people事件时间轴 + 人物管理 + AI 对话 + 对话历史 + 统计
  • 数据库Oracle 侧 SQLitevideos/events/people/ss_motion_events/sync_cursorNAS 侧 MariaDB 镜像sync_videos / sync_events / sync_people / sync_cursor+ chat_history
  • 数据流向Google 硬盘 ──rclone──► 甲骨文整段素材 ──按运动事件分割片段──► 片段云端分析 ──► Oracle SQLite ──每 30 分钟 NAS 拉取──► NAS MariaDB 镜像 ──► FAM-UI运动事件由 NAS 轮询 SS 推送 OracleNAS → Oracle 单向)
  • AI 对话:查 sync_events 拼上下文 → 经 FAM-Edge 问答编排Gemini → NVIDIA → 本地 Ollama 兜底)生成回答 → 返回并写 chat_history
  • 人物命名:用户命名/合并某 label → 回推 Oracle /api/oracle/people/correctmanual 优先)→ 下一周期同步回 NASOracle 独立 person_service 汇总全量人物 → LLM 合并为规范名 → 回灌视频提示

1.2 不在首期范围(推迟 v1.1+

  • Nginx 静态服务Video-Server 用 Flask send_from_directory 替代,且推送模式下已不再必需)
  • 资源保护策略 BCPU/内存过载返回 503
  • 心跳监控(用整体超时 + 僵尸任务回收兜底)
  • API 业务接口鉴权(/api/edge/*/api/core/* 不加鉴权)
  • Cron 兜底清理(finally 清理足够)
  • daily_summaries 每日摘要(表已建,逻辑未实现)

1.3 已知遗留问题v1.1 优先解决)

问题 现状 影响
llava-phi3 多图单请求失效(已替换) 原本地模型 llava-phi3 analyze_frames 多图单请求输出长度仅 3~4已替换为 qwen2.5:7b(纯文本模型,专职问答兜底) 历史遗留描述,新方案下视觉走云端,本地不参与视觉分析,该问题随之消除
吞吐不足 历史本地模型全流程约 19 分钟(关键帧筛选 10min + VLM 5.5min + 融合 3min30min/360MB 视频实测 929s 已切换到云端 VLM 直出结构化 JSON无本地融合步骤吞吐瓶颈转移至云端调用延迟整体明显改善
Tailscale 端口不通 NAS tailscaled 以 userspace 模式运行(无 TUN 网卡Oracle 无法反向访问 NAS当前 NAS→Oracle 走公网 IP 推送模式已规避反向访问,但流量走公网
历史视频积压(已处理) 正式目录 /volume1/surveillance/Generic_ONVIF-001 原有约 285 个历史视频(~100GB已切回生产目录并 forward-only 处理:历史任务占位 FAILEDscheduler dedup 自动跳过,仅处理新增视频 已解决;生产目录已生效,新视频正常入链

规划方向(新框架)云端大模型负责视觉识别与结构化输出、本地大模型仅做智能问答兜底。按任务类型分工:

任务 执行方 模型 说明
视觉分析 + 结构化输出(看图识人/动作/衣着 → 直出 JSON 云端 Gemini → NVIDIA NIMfallback 降级) 云端 VLM 直接产出 global_summary / entities_json / frame_detailsEdge 仅做格式化校验后直存 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 Geminigemini-flash-latestAPI Key 已验证,支持多图单请求,视觉 + 问答均参与)
  • NVIDIA NIMmeta/llama-3.2-11b-vision-instructOpenAI 兼容 API云端 GPU 推理,单请求限 1 图故逐帧调用;视觉 + 问答均参与)
  • 本地 Ollamaqwen2.5:7b仅参与智能问答,且仅作兜底:视觉链路两云端全失败 → 任务 FAILED 走重试,绝不回退本地模型做视觉/融合

Orchestrator 视觉阶段按 fallback 模式顺序降级Gemini → NVIDIA NIM云端模型直出结构化 JSON 后由 format_cloud_result 格式化。问答阶段按 gemini → nvidia → ollama 顺序,仅末位本地模型作兜底。


2. 系统架构

2.1 物理节点与部署

节点 角色 硬件 IP 服务与端口
Synology NAS FAM-Core + FAM-UI + 数据库 DS220+ (Geminilake), DSM 7 家庭局域网 192.168.50.64 / Tailscale 100.70.234.39 FAM-Core :8000, FAM-UIVue3 构建产物,由 FAM-Core 托管), MariaDB :3306, Surveillance Station :5000
Oracle Cloud FAM-Edge Ampere A1 2C12G ARM64无 GPU, Ubuntu 20.04 公网 129.146.203.203 / Tailscale 100.74.137.126 FAM-Edge :5000systemd 守护), Ollama :11434仅本地
家庭网络 用户入口 普通终端 192.168.50.0/24 浏览器访问 http://192.168.50.64:8000

网络要点(推送模式)

  • 服务间通信只有两条出站:NAS → Oracle 公网 IP:5000(① GET /api/oracle/sync 拉增量 + POST /api/oracle/people/correct 命名回推 ② POST /api/ss/motion 推送运动侦测事件。Edge 不需要反向访问 NAS
  • 运动事件获取NAS 轮询驱动)FAM-Core MotionNotifier 每 60s 轮询本机 Surveillance Station 事件列表 APISYNO.SurveillanceStation.EventCenter.Event),增量推送 Oracle。/api/ss/webhook 端点在代码中保留为可选低延迟补充SS 行動規則 Webhook 未配置则不触发)
  • Oracle 端 Ollama 端口 11434 不对外暴露,聊天请求经 FAM-Edge /api/edge/chat/ask 代理转发
  • Tailscale 两节点已安装在线,但 NAS tailscaled 为 userspace 模式且防火墙端口不通,暂走公网 IP

2.2 部署拓扑与数据流(新架构 v2Oracle 分析 + NAS 镜像)

┌──────────── Google 硬盘 ────────────┐
│  oraclenas@...gserviceaccount.com    │
│  (Cloud Sync 落盘目录)               │
└──────────────┬───────────────────────┘
               │ rclone 定时同步systemd timer
               ▼
┌──────────────────────── Oracle Cloud (129.146.203.203) ────────────────────────┐
│  FAM-Edge (Flask :5000)                                                        │
│   ├─ Video-Queue: 30s 轮询 /opt/fam-edge/gdrive_videos 登记整段素材            │
│   ├─ Video-Processor: 素材按 ss_motion_events 分割运动片段 → 只分析片段         │
│   │    (ffmpeg -c:v copy -c:a aac) → Gemini → 失败 NVIDIA → 再失败 FAILED     │
│   ├─ Person-Service: 汇总人物 → LLM 合并规范名 → 回灌视频提示                  │
│   ├─ OracleDB (SQLite): videos / events / people / ss_motion_events            │
│   └─ API: /api/oracle/sync (增量拉取) · /api/ss/motion (运动事件) ·            │
│         /api/oracle/people/correct (命名校正) · /api/edge/chat/ask (问答)      │
└───────────────────────────────┬───────────────────────────────────────────────┘
                                 │ HTTP GET /api/oracle/sync?since=&token= (每 30 分钟)
                                 ▼
┌─────────────────────────────── NAS (192.168.50.64) ────────────────────────────┐
│  FAM-Core (Flask :8000)                                                        │
│   ├─ Oracle-Sync: 唯一后台线程,拉增量写 MariaDB 镜像 + 维护 sync_cursor         │
│   ├─ Chat-Handler: /api/chat/ask查 sync_events 拼上下文 → 走 Oracle 编排)    │
│   ├─ Member-Manager: /api/member/name|merge回推 Oracle + 即时拉回)           │
│   ▼                                                                             │
│  MariaDB (sentinel_home_ai): sync_videos / sync_events / sync_people /          │
│                           sync_cursor / chat_history                            │
│  FAM-UI (Vue3 SPAFAM-Core 托管 dist) 读同步镜像                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

2.2.1 运动监测链路v3NAS 轮询驱动)

摄像头动作事件由 NAS 端 MotionNotifier 轮询 Surveillance Station 事件列表获取简单稳定事件不遗漏Webhook 端点在代码中保留为可选低延迟补充):

Surveillance Station (NAS 本机)
  │ SYNO.SurveillanceStation.EventCenter.Event method=List
  │   camera_ids=2, event_types=10, start_time/end_time下划线风格
  ▼
NAS FAM-Core  MotionNotifiermotion_notifier.py每 60s
  │  ├─ 增量游标MariaDB sync_cursor.motion_last_event_idDB 续用/重启补推)
  │  ├─ 推送失败批次不前进游标(下轮重试,不丢事件)
  │  └─ 定期空 events 心跳(证明推送链路存活)
  │ HTTP POST /api/ss/motion?token=ORACLE_SYNC_TOKEN真实 event_id/start_time/duration
  ▼
Oracle FAM-Edge  :5000  /api/ss/motion (api_gateway.py)
  │  → OracleDB.ss_motion_eventsevent_id UNIQUE自动去重start_time/duration 为 Unix epoch
  ▼
Oracle video_processor整段素材按运动事件【分割运动片段】→ 只分析片段(见 2.3

设计要点:甲骨文不反向访问 NAS只分割已结束事件SS 事件 duration 在动作进行中为 0、结束才回填真实时长。 失败-open心跳超过 max_heartbeat_age_sec900s未更新时预过滤/分割 fail-open不误判"无运动"。

网络要点(新架构)

  • NAS → Oracle 出站共两条:① GET /api/oracle/sync(拉取增量)+ POST /api/oracle/people/correct(命名回推);② POST /api/ss/motion(运动侦测事件推送)。均走 Oracle 公网 IP:5000token 鉴权
  • Oracle Ollama :11434 不对外暴露,问答经 FAM-Edge /api/edge/chat/ask 代理
  • Tailscale 两节点在线但 NAS 无法反向访问 Oracle故全部走 NAS 主动出站拉取模式

2.3 主链路时序(新架构 v3运动事件驱动

  1. Google 硬盘新视频(整段素材)→ rclone 定时同步到 Oracle /opt/fam-edge/gdrive_videos
  2. Video-Queue 轮询发现新文件 → 登记到 Oracle videospending
  3. Video-Processor 处理素材:按文件名解析开始时间 → 查窗口内 ss_motion_events 已结束运动事件 → ffmpeg 分割运动片段(-c:v copy -c:a aac 保留音频)→ 片段登记 videospendingmotion_event_id 关联)并入队;素材标记"已分割 N 段"(仍有未结束事件则保持 pending 下轮再分割)
  4. Video-Processor 处理运动片段:只分析片段(不分析整段)→ Gemini或 NVIDIA 兜底)直出 {global_summary, events[], people_mentioned[]} → 写 Oracle videos + events + people
  5. Person-Service 每 30 分钟汇总全量人物 → LLM 合并为规范名 → 更新 people.canonical_name → 生成 known_members_context 回灌后续片段提示
  6. NAS Oracle-Sync 每 30 分钟 GET /api/oracle/sync?since=<cursor> → upsert 到本地 sync_* 镜像表 → 推进 sync_cursor
  7. FAM-UI 读本地镜像展示(时间轴/人物/统计字段契约不变);用户命名 → POST /api/oracle/people/correct 回推 Oracle下一周期同步生效

运动监测支流NAS 轮询驱动)

  • MotionNotifier 每 60s 轮询 SS EventCenter.Event.Listcamera_ids=2, event_types=10→ 增量(游标)推送 POST /api/ss/motion 落库 ss_motion_events(真实 event_id/start_time/duration
  • 整段素材处理时按 ss_motion_events 分割运动片段只分割已结束事件duration=0 的进行中事件等结束后的下轮);片段分析结果的时间点(events.ts)为绝对时间,前端时间轴与帧图(/api/proxy/frame,绝对 ts event_start_time 偏移取帧)天然兼容

容错设计

  • Oracle 单视频串行(max_concurrent=1)避免多视频抢占云端配额
  • 运动片段分析失败(两云端均不可用)标记 failed,下一周期 cursor 仍包含它会被重试
  • 素材分割幂等:按 motion_event_id 去重,片段文件已存在则跳过分割
  • NAS 同步失败仅记日志下一周期30 分钟)自动重试,不阻塞 UI
  • fam-core 日志双写stdout + fam-core/logs/fam-core.log

3. 模块设计

3.1 FAM-CoreNAS 端)

NAS 不再处理视频仅作管理后台。常驻后台线程Oracle-Sync增量镜像+ MotionNotifier轮询 SS 运动事件)。

模块 文件 职责
Oracle-Sync oracle_sync/oracle_sync.py 后台线程:每 30 分钟 GET /api/oracle/sync?since=<cursor>&token= 拉增量 → upsert 到 sync_videos/sync_events/sync_people → 推进 sync_cursorpush_name_correct() 回推命名校正;trigger_now() 立即同步
Chat-Handler chat_handler/chat_handler.py /api/chat/asksync_events 拼上下文 → 经 Oracle /api/edge/chat/ask 问答编排Gemini→NVIDIA→本地 Ollama 兜底)→ 写 chat_history
Member-Manager member_manager/member_manager.py /api/member/unnamed / /api/member/list / /api/member/name / /api/member/merge;命名/合并回推 Oracle 并即时拉回本地镜像
MotionNotifier motion_notifier/motion_notifier.py 轮询主路径poll_enabled=true):每 poll_interval_sec(60s) 查 SS EventCenter.Event.Listcamera_ids/event_types=10→ 增量推送 Oracle /api/ss/motion;游标存 MariaDB重启续用/补推停机期间事件,失败批次不前进);心跳线程定期空 POST 证明链路存活;启动时拉取 SS 摄像头「名→id」映射并配置兜底
Motion-BP motion_bp.py POST /api/ss/webhook 可选低延迟补充SS 行動規則 Webhook 未配置则不触发,兼容 JSON/表单/单条/数组)→ 映射 → 推送 OracleGET /api/ss/status 查询状态
UI-API ui_api.py /api/ui/* 只读接口videos/stats/people/people-clips/attention-events/named-members/model-stats/service-status供 Vue 前端渲染
公共层 db_layer.py / config_loader.py / logger.py PyMySQL 连接unix_socket同步镜像 CRUDget_sync_people_clips() 人物→运动片段查询;文件日志

已删除Task-Scheduler / Dispatcher / Poller / Event-Receiver / Video-Server视频上传、切片、抽帧、关键帧落盘等职责全部迁移至 Oracle 端NAS CPU 占用大幅降低)。

3.2 FAM-EdgeOracle 端)

整段素材按运动事件分割运动片段,只分析运动片段(不再整段送云端)。

模块 文件 职责
API-Gateway api_gateway/api_gateway.py GET /api/oracle/sync增量拉取since+token 校验);POST /api/oracle/people/correct(命名校正);POST /api/edge/chat/ask(问答编排);POST /api/ss/motion运动事件接收token 校验,落库 ss_motion_events + 刷新心跳);`GET /api/oracle/frame
Video-Queue video_queue.py 生产-消费队列:生产者 30s 轮询 rclone 同步落地目录登记整段素材入队(含重启恢复);消费者(max_concurrent 个线程)取队列调 Video-Processor素材分割出的运动片段自动入队模型超时 = 原配置 ×timeout_multiplier;失败重试上限 max_retries
Video-Processor video_processor.py 素材→分割/片段→分析双分支:素材按 ss_motion_events 已结束运动事件 ffmpeg 分割运动片段(-c:v copy -c:a aac 保留音频,motion_event_id 幂等),片段只送云端 VLM 分析;按 vision_order 调适配器;首个成功即落库 Oracle videos+events+people;全失败标 failed
Person-Service person_service.py 汇总全量人物 → LLM 合并为规范名 → set_canonical;生成 known_members_context 回灌片段提示manual 命名优先不被覆盖
OracleDB oracle_db.py SQLitevideosmotion_event_id/camera_id 列)/ events / people / sync_cursor / ss_motion_eventsget_sync_delta(since) 增量导出;record_motion_events / get_motion_events_in_range(已结束运动事件窗口查询,分割用)/ has_motion_in_range_local / 心跳
Model-Adapters model_adapters/ BaseModelAdapter.analyze_video(video_path, known_members_context, event_start_time)GeminiFiles API/ NVIDIA整视频 video_url/ Ollama纯文本不参与视频——输入均为运动片段
QA-Orchestrator qa.py 遍历所有适配器 chat()Gemini→NVIDIA→Ollama 三级降级(仅问答)

3.3 FAM-UINAS 端)

Vue3 + Vite + Tailwind SPAfam-ui/src/views/*.vue,构建产物 fam-ui/dist/ 由 FAM-Core static_app.py 托管Vue Router history 模式),页面均读本地同步镜像:

页面 功能
🕒 事件时间轴 运动片段会话列表(motion_*.mp4,只显示有内容的会话)+ 选中会话的事件时间线(时间点 + 描述 + 人物/关注徽章 + 事件帧图);支持 ?video=<id> 定位跳转
👤 人物管理 按规范名/标签聚合,命名/合并(回推 Oracle人物卡含「运动片段」区块(该人物出现过的运动片段:缩略图/时间/摘要/事件数,点击跳时间轴)
💬 AI 对话 输入框 + 调 /api/chat/ask;按 queried_person 预设快捷提问
📝 对话历史 chat_history 倒序展示
📈 统计图表 模型来源占比 / 关注事件 / 同步状态

4. 数据库设计

库名 sentinel_home_aiMariaDB 10.11.11utf8mb4。完整 DDL 见 scripts/ddl.sql

4.1 表清单

新架构 v2Oracle 侧用 SQLitevideos/events/people/sync_cursorNAS 侧 MariaDB 仅保留 同步镜像表 + 问答历史process_tasks/monitor_events/event_details/family_members 等旧表已不再写入(保留历史数据,未删除)。

OracleSQLiteoracle_db.py

用途 关键字段
videos 视频会话(整段素材 + 运动片段均在此表) id, filename(UNIQUE), camera_name, status, summary_json, events_json, people_json, compute_provider, event_start_time, duration_sec, motion_event_id(运动片段关联的 SS 事件 id, camera_id, updated_at
events 视频内时间点事件 id, video_id, ts, description, person_list_json, person_appearances_json, is_attention_event
people 规范人物Oracle 维护) id, label(UNIQUE), canonical_name, appearances, source(llm/manual), features_json, display_uid
ss_motion_events NAS 推送的运动事件(分割素材的依据) id, event_id(UNIQUE), camera_id, event_type(10=运动), start_time(epoch), duration, thumbnail_url, received_at
sync_cursor 同步游标 + 心跳 key, valuesync_cursor=上次 server_timemotion_heartbeat_at=推送链路心跳)

NASMariaDB同步镜像scripts/ddl.sql

用途 关键字段
sync_videos 视频会话镜像(对齐 Oracle videos含运动片段 id, filename, camera_name, status, summary_json, events_json, people_json, compute_provider, processed_at
sync_events 事件镜像(对齐 Oracle events id, video_id, ts, description, person_list_json, is_attention_event
sync_people 人物镜像(对齐 Oracle people id, label, canonical_name, appearances, source, features_json, display_uid
sync_cursor 同步游标 + 运动游标 key='last_since'key='motion_last_event_id'MotionNotifier 增量游标)
chat_history AI 问答记录 chat_id, user_question, ai_answer, context_summary, queried_date, queried_person

4.2 表关系

Oracle:  videos (1) ─── (N) events        people 独立label/canonical_name
NAS 镜像: sync_videos (1) ─── (N) sync_events   sync_people 独立
chat_history 独立表(问答上下文摘要留存)

命名回溯:用户命名某 labelPOST /api/oracle/people/correctcanonical_namemanual 优先)→ 下一周期同步回 NAS sync_peopleOracle person_service 用规范名回灌视频提示,后续事件 person_list_json 直接带真名。

4.3 compute_provider

  • sync_videos.compute_provider:字符串,记录该视频实际成功调用的视觉模型(gemini / nvidia);本地 Ollama 不参与视频分析,不会出现在该字段
  • 问答链路Gemini→NVIDIA→Ollama 兜底)的 provider 体现在 /api/edge/chat/ask 响应的 provider 字段

4.4 兼容性注意

  • MariaDB 10.11 严格模式:空字符串不能插 DATETIME 列1292 错误)。同步表时间字段统一用 VARCHAR(32) 文本存储 Oracle 的 ISO 字符串,规避类型转换问题
  • MariaDB 不支持 MySQL 的 $[*] JSON 通配路径,人物统计按 person_list_json LIKE '%name%' 字符串匹配在 Python 层完成

5. API 规范(实际实现)

5.1 FAM-EdgeOracle :5000

GET /api/oracle/syncNAS 每 30 分钟拉增量,新架构主接口)

  • 请求:?since=<ISO 文本>&token=<ORACLE_SYNC_TOKEN>since 为空拉全量)
  • 响应200
{
  "videos": [
    {"id": 1, "filename": "2026-08-21_081500.mp4", "camera_name": "客厅",
     "status": "done", "summary_json": "...", "events_json": "[...]",
     "people_json": "[...]", "compute_provider": "gemini",
     "event_start_time": "2026-08-21 08:15:00", "updated_at": "2026-08-21 08:40:12"}
  ],
  "events": [
    {"id": 10, "video_id": 1, "ts": "00:01:23", "description": "汤圆在客厅玩耍",
     "person_list_json": "[\"汤圆\"]", "is_attention_event": 0, "updated_at": "2026-08-21 08:40:12"}
  ],
  "people": [
    {"id": 1, "label": "人物A", "canonical_name": "汤圆", "source": "manual",
     "appearances": 12, "updated_at": "2026-08-21 08:41:00"}
  ],
  "server_time": "2026-08-21 08:41:30"
}
  • 401token 校验失败

POST /api/oracle/people/correct — 命名校正回推:{"label":"人物A","canonical_name":"汤圆","token":...}manual 优先,不被 LLM 覆盖)→ {"status":"ok"} POST /api/edge/chat/ask — 智能问答编排FAM-Core Chat-Handler 调用):请求 {"prompt","max_tokens"} → 响应 {"answer","provider"};内部按 Gemini → NVIDIA → 本地 Ollama 顺序,仅两云端都失败才用本地兜底 POST /api/ss/motion — 运动事件接收NAS MotionNotifier 推送):{"token", "events":[{event_id, camera_id, event_type, start_time, duration, thumbnail_url}]} → 幂等落库 ss_motion_events + 刷新心跳;events: [] 空数组即心跳 GET /api/oracle/frame — 事件帧图:?video_id=&ts=&w= → jpegts 为绝对时间,frame_servicets event_start_time 偏移从视频文件取帧) GET /api/oracle/avatar — 人物头像:?label=&w= → jpeg从该人物候选事件 person_appearances bbox 裁剪) GET /health — 服务状态(含已处理视频数)

5.2 FAM-CoreNAS :8000

端点 方法 说明
/health GET 服务健康(免登录
/api/login POST 登录:{"username","password"} → 校验 FAM_AUTH_USER/FAM_AUTH_PASSNAS .env 配置,无硬编码默认值,未配置则拒绝所有登录)→ 种 HttpOnly cookie fam_session2 小时)
/api/logout POST 退出登录(清 cookie
/api/auth/check GET 登录态检查:{"authed": true|false}免登录
/api/status GET Oracle-Sync + MotionNotifier 状态sync running/cursormotion poll_enabled/pushed_total/heartbeat
/api/chat/ask POST 用户问答:{"question","queried_person","queried_date"}{"answer","context_summary","chat_id"}(上下文来自 sync_events
/api/chat/history GET 对话历史(?date=?person=&limit=
/api/member/unnamed GET 未命名人物列表label / 出现次数 / 首见时间)
/api/member/list GET 全部人物label + canonical_name + 是否命名)
/api/member/name POST 命名:{"label","canonical_name"} → 回推 Oracle 并即时拉回本地镜像
/api/member/merge POST 合并:{"source","target"} → 将 source 并入 target 身份(统一 canonical_name
/api/ss/webhook POST 可选 + 免登录:接收 Surveillance Station Webhook参数 event_time/device_name/event_name/server_name/thumbnail_url),映射后推送 Oracle /api/ss/motionSS 行動規則未配置则不触发SS 推送无法携带登录态故列入白名单)
/api/ss/status GET 运动监测状态:poll_enabled / camera_loaded / camera_map / pushed_total / last_error / 心跳
/api/ui/videos GET 运动片段会话列表(?date=&page=只返回 motion_ 前缀或有事件的会话0 段素材不展示)
/api/ui/videos/<id> GET 会话详情:{video, events[]}(事件含 person_list_json / person_appearances_json / is_attention_event
/api/ui/stats GET 统计卡videos / events / people / attention?date= 过滤,口径与列表一致)
/api/ui/people GET 人物列表(按 canonical_name 聚合display / labels / appearances / first_seen / features_json / display_uid
/api/ui/people/clips GET 人物运动片段:?label=&limit= → 该人物出现过的运动片段video_id / event_start_time / duration / summary / first_ts 缩略图定位 / clip_events
/api/ui/attention-events GET 需关注事件(日期 + 涉及人物,已去重清洗)
/api/ui/named-members GET 已命名成员真名列表AI 对话快捷选择)
/api/ui/model-stats GET 云端模型调用统计(按模型聚合 + 最近明细)
/api/ui/service-status GET NAS 同步状态 + Oracle 实时活动代理token 不下发浏览器)
/api/proxy/frame GET 事件帧图代理:?video_id=&ts=&w= → Oracle /api/oracle/frame(浏览器不直连 Oracle
/api/proxy/avatar GET 人物头像代理:?label=&w= → Oracle /api/oracle/avatar

已删除:/api/core/callback/event/media/<path>(视频处理职责已迁移至 Oracle

5.3 云端结构化输出 JSON Schema

运动片段直传云端 VLM模型直接产出结构化 JSONanalyze_video 返回):

{
  "global_summary": "客厅监控摘要……",
  "events": [
    {"timestamp": "00:01:23", "description": "汤圆在客厅玩耍",
     "people": ["汤圆"], "is_attention_event": false}
  ],
  "people_mentioned": ["汤圆"]
}
  • Gemini 用 Files API 上传运动片段后 generateContentNVIDIA 用整视频 video_url + num_frames=128(模型内部自行采样帧);均不依赖 OpenCV 人脸
  • person_service 汇总全量 people_mentioned → LLM 合并为规范名 → 生成 known_members_context 回灌后续片段提示,使模型用真名指代
  • action / 描述由 AI 自由生成无枚举过滤,is_attention_event 由 AI 自行判断

6. 关键技术

6.1 视频预处理历史记录关键帧抽帧方案v3 已废弃)

⚠️ 本小节为旧架构v1/v2 本地抽帧分析)记录。v3 运动事件驱动架构不再抽帧 整段素材按运动事件 ffmpeg 分割运动片段(-c:v copy),片段直传云端 VLM 分析。 保留此处仅作历史参考。

  • 粗抽候选帧FFmpeg 快速 seek逐帧 ffmpeg -ss <ts> -frames:v 1),替代 fps 滤镜全解码30min 视频从 180s+ 降到 33s6-8x 提速)
  • 帧数自适应:候选帧 clamp(时长分钟×2, 30, 120);关键帧上限 clamp(时长/150s, 8, 30)30min→12 帧60min→24 帧,封顶 30
  • 帧差筛选OpenCV MSE首末帧必选MSE > 阈值 500 的保留,不足 min_key_frames=5 补足
  • 压缩:长边 > 1024px 才缩放JPEG 质量 80
  • 异常兜底ffprobe 失败退化为 60s 间隔抽帧;帧差异常退化为等距 5 帧

6.2 本地 Ollama仅智能问答兜底ARM CPU

本地 Ollamaqwen2.5:7b纯文本模型不参与视觉分析、不参与云端结果融合。它只在智能问答场景下、且 Gemini 与 NVIDIA 两云端模型都失败时才被启用作为兜底。视觉分析与结构化输出全部由云端模型承担。以下参数作为问答任务的调优依据保留。

参数 依据
OLLAMA_KEEP_ALIVE=-1 模型常驻内存 消除 55s 冷启动(常驻约 4.3GB12GB 内存够用)
num_predict=512 限制生成 token ARM 约 5 tok/s过长生成会拖慢问答响应
视觉/模型 timeout 600s 实测 1024px 帧视觉编码 ~36s + 生成 ~12s/60token问答链路改用 config 中各模型 timeout
gunicornEdge --timeout 1800 同步分析模式,默认 30s 会杀 worker
push_timeoutNAS 1800s 覆盖最坏情况30min 视频实测 929s

已知问题:原 llava-phi3 多图单请求基本失效N 张图一次调用输出长度仅 3~4已替换为 qwen2.5:7b(纯文本模型,专职问答兜底,不涉及视觉多图问题;视频分析已全部由云端 VLM 承担)。

6.3 模型适配器架构

抽象基类 BaseModelAdapter:定义统一接口,每个模型实现自己的适配器。

方法 职责
__init__(provider_name, config) 读取 api_key / base_url / model_name / timeout / 熔断器配置
health_check() -> bool 轻量请求验证连通性与 Key 有效性
analyze_frames(frame_paths, frame_timestamps, known_members_context) -> Optional[str] 接收多张关键帧路径 + 时间戳 + 已知成员特征,返回 VLM 文本描述(失败返回 None
get_timeout() -> int 返回该模型单次调用超时阈值
熔断器实例 每个云端模型独立熔断器

模型清单由 config.yamlmodels 数组动态决定,新增模型 = 实现适配器 + 配置加一项,主流程不动。

已实现 / 规划的适配器(按 role 区分职责):

provider 适配器类 role SDK / 协议 状态
ollama OllamaAdapter text requests 直调 REST /api/chat 已实现
gemini GeminiAdapter vision requests 直调 REST :generateContent 待实现
nvidia NvidiaVisionAdapter vision openai SDKNIM 兼容 OpenAI API 规范) 待实现

role 语义

  • vision:参与视觉分析阶段,按 fallback 顺序降级,直出结构化 JSON
  • text:仅参与智能问答(usage: qa_fallback),且为 Gemini/NVIDIA 都失败时的兜底,不参与视觉分析、不参与云端结果融合

NvidiaVisionAdapter 关键实现fam_edge/adapters/nvidia_adapter.py

  • 基于 openai Python SDKbase_url=https://integrate.api.nvidia.com/v1api_key${NVIDIA_API_KEY} 展开
  • health_check:调 client.models.list() 轻量验证 Key
  • analyze_frames:构建 OpenAI 标准 content 列表1 个 text item + N 个 image_url item图片以 data:image/jpeg;base64,... 格式内联),调 chat.completions.createtemperature=0.2max_tokens=1024
  • prompt 中文模板:要求按时间顺序分析人物数量/衣着/微动作/互动,重点标注异常高危行为

Orchestrator 调度模式config.yamlorchestrator.mode

模式 行为
fallback(默认) 顺序降级:按 models 数组顺序依次尝试,首个健康且返回非 None 的模型结果即采用;失败则降级到下一个;全部失败返回 503
ensemble 并行交叉验证:所有 enabled 模型并行调用,结果一致 → 可信度高;多数派为准;仅单一模型描述 → source_providers 仅含该模型

视觉分析降级链路fallback 模式,仅云端 role=vision 模型)

Gemini (gemini-flash-latest, role=vision) —— 多图单请求直出结构化 JSON
   │ 失败 / 熔断 OPEN / 超时
   ▼
NVIDIA NIM (llama-3.2-11b-vision-instruct, role=vision) —— 逐帧结构化聚合
   │ 失败 / 熔断 OPEN / 超时
   ▼
任务 FAILED走重试绝不回退本地 Ollama —— 本地仅负责问答兜底)

格式化阶段(无模型调用,仅数据转换)

云端成功 provider 直出的结构化 JSONframe_details / 可选 global_summary / entities_json
   ▼
format_cloud_result字段归一化 → 补 source_providers=[provider] / compute_provider=[provider]
   → 缺失 entities_json 由 frame_details 推导 → 缺失 global_summary 则事实拼接 → schema 校验
   ▼
合法入库结构,随响应返回 NAS 直接落库(本地模型不介入)

智能问答降级链路chat 场景)

Gemini (role=vision, 也参与问答)
   │ 失败 / 熔断 OPEN / 超时
   ▼
NVIDIA NIM (role=vision, 也参与问答)
   │ 失败 / 熔断 OPEN / 超时
   ▼
本地 Ollama (qwen2.5:7b, role=text, usage=qa_fallback) —— 仅当两云端都失败才启用
   │ 失败
   ▼
返回"所有模型均不可用"

熔断器策略(按 provider 独立,仅云端模型启用):

provider role threshold cooldown enabled
Gemini vision 3 次连续失败 600s true
NVIDIA NIM vision 3 次连续失败 600s true
Ollama text false本地不熔断

健康探测Ollama GET /api/tagsGemini GET /v1/models?key=...NVIDIA client.models.list()。视觉模型全部不健康返回 503。

多模型标签兼容compute_providerevent_details.source_providers 的 JSON 数组值新增 "nvidia" 标签(与 "ollama" / "gemini" 并列);validate_schema 校验非空数组。

执行可观测性Orchestrator 在每次调用记录各模型的 provider / 耗时 / success 三元组到日志,并落库到 monitor_events.compute_provider 数组。

6.4 任务可靠性

  • 状态机 PENDING → PROCESSING → SUCCESS/FAILED退避重试 min(60×(retry+1)×2, 600)smax_retries=3
  • 僵尸任务回收PROCESSING 超过 push_timeout + 120s 自动重置 PENDING应对 fam-core 重启 / Edge 重启 / in-flight 请求丢失)
  • failure_stage ENUMdownload / extract / vlm_visual / vlm_fusion / callback

7. 性能基准(实测)

7.1 单图推理(原 llava-phi3, Oracle ARM 2C12G — 已替换为 qwen2.5:7b

场景 总耗时
冷启动(首次加载 2.9GB 模型) 109s已由 keep-alive 消除)
预热 + num_predict=30 4.3s
1024px 真实帧 + num_predict=60 ~48.6s(视觉编码 36s 固定成本 + ~5 tok/s

7.2 真实视频全流程30min / 360MB / 1080p H.264

步骤 耗时
视频上传 NAS→Oracle公网5.1 MB/s 70s
FFmpeg 快速 seek 抽 60 候选帧 33s
OpenCV 关键帧筛选→12 帧) 5s
压缩 0.6s
Ollama 视觉分析12 帧) 820savg 68s/帧0.9 tok/s
总计 929s15.5min

7.3 E2E 验证2026-08-20

task_id=28930s 测试片段)全链路打通:推送 5.7MB → Edge 分析 1129s → 同步返回 → monitor_events event_id=1 + event_details 2 条落库,event_end_time 推算正确start+29s


8. 部署说明

8.1 部署位置与启动命令

组件 节点 路径 启动
FAM-Core NAS /volume1/web/sentinel-home-ai/fam-core/ bash start_core.shgunicorn -w 1 :8000source .env 注入 DSM_/ORACLE_SYNC_TOKEN/FAM_AUTH_
FAM-UI NAS /volume1/web/sentinel-home-ai/fam-ui/dist/ Vue3 构建产物,由 FAM-Core static_app.py 托管(无需独立进程);本地改代码后 npm run build 并 tar 部署 dist
FAM-Edge Oracle /opt/fam-edge/ systemd fam-edge.service 守护Restart=always部署代码后 sudo systemctl restart fam-edge(勿手动 setsid会端口冲突
Ollama Oracle systemd 托管 环境变量 OLLAMA_KEEP_ALIVE=-1

外网访问frp 内网穿透)NAS 跑 frpc/etc/frp/frpc.tomlS99frpc.sh 守护),映射到 Oracle 129.146.203.203frps :7000

  • 3000 → NAS Gitea、8500 → NAS WordPress(8088)、8000 → NAS FAM-Core本系统
  • 外网入口 http://129.146.203.203:8000需登录(见 §5.2 /api/login

登录校验2026-08-22 新增2026-08-23 改为 fail-closedFAM-Core 全站拦截(auth.py)——页面未登录 302 /login内置深色登录页SPA 零改动),/api/* 未登录 401凭据 FAM_AUTH_USER/FAM_AUTH_PASSNAS .env 配置,无硬编码默认值——这两个变量跟 NAS SSH 密码是同一个值,公网入口不能有"没配置就退回已知密码"的兜底,.env 没配好这两项时直接拒绝所有登录);白名单免登录:/login /api/login /api/logout /api/auth/check /health /api/ss/webhook /assets/*。登录态为进程内 token + HttpOnly cookie2 小时),过期或重启 fam-core 需重新登录。

Oracle 部署方式:本地 git 提交 push Gitea → tar 管道到 /opt/fam-edge--strip-components=1 解临时目录再 cp避免动 data/venv/gdrive_videos

8.2 依赖

  • FAM-CoreNAS, Python 3.10 venvFlask, Gunicorn, PyMySQL45KB 纯 Python 替代 19MB mysql-connector, PyYAML, requests
  • FAM-UINAS, NodeVue3 + Vite + Tailwindfam-ui/,构建产物 dist 不入库);不再依赖 Streamlit
  • FAM-EdgeOracle, Python venvFlask, Gunicorn, requests, PyYAML, opencv-python, numpy, openaiNVIDIA NIM 兼容 OpenAI API 规范Gemini 用 requests 直调 REST
  • 系统级FFmpeg两端Oracle 端用于运动片段分割 + 帧图/头像、Ollama + qwen2.5:7bOracle、MariaDB 10.11NAS、rcloneOracle同步 Google Drive 素材)

8.3 配置文件要点

fam-core/config/config.yamlNAS —— 同步 + 问答 + 运动监测):

server:
  port: 8000
database:                       # MariaDBunix_socket 优先)
  unix_socket: "/run/mysqld/mysqld10.sock"
oracle_sync:                    # 增量镜像线程
  base_url: "http://129.146.203.203:5000"
  token: "${ORACLE_SYNC_TOKEN}" # 与 Oracle 端 sync_api.token 一致
  interval_sec: 1800            # 每 30 分钟拉一次增量
chat_handler:
  qa_url: "http://129.146.203.203:5000/api/edge/chat/ask"
  timeout: 120
motion_notifier:                # 运动监测(轮询主路径)
  enabled: true
  poll_enabled: true
  dsm_host: "192.168.50.64"     # Surveillance Station
  dsm_port: 5000
  dsm_account: "${DSM_ACCOUNT}"
  dsm_password: "${DSM_PASSWORD}"
  camera_ids: [2]
  camera_name_to_id: {"Generic_ONVIF-001": 2}
  oracle_base_url: "http://129.146.203.203:5000"
  oracle_token: "${ORACLE_SYNC_TOKEN}"
  poll_interval_sec: 60
  poll_window_hours: 2
  batch_size: 100
  heartbeat_interval_sec: 300   # 心跳(证明推送链路存活,< Oracle 侧 900s 阈值)

fam-edge/config/config.yamlOracle —— 素材分割 + 片段分析 + 同步 + 人物服务):

server:
  port: 5000
gdrive_sync:                    # rclone 同步落地目录(整段素材)
  enabled: true
  local_dir: "/opt/fam-edge/gdrive_videos"
  watch_interval_sec: 30
  camera_name: "客厅"
  parse_start_from_filename: true
oracle_db:
  path: "/opt/fam-edge/data/oracle.db"
sync_api:
  token: "${ORACLE_SYNC_TOKEN}"
person_service:
  schedule_interval_sec: 1800   # 每 30 分钟重新汇总人物
  model: "gemini"
video_processing:
  max_concurrent: 1             # 单视频串行,避免抢占云端配额
  timeout: 900
  vision_order: ["gemini", "nvidia"]
motion_segment:                 # 运动片段分割(运动事件驱动架构)
  clips_dir: "/opt/fam-edge/motion_clips"
  keep_audio: true              # 保留音频pcm_alaw -> aac 64k 转码)
  min_duration_sec: 1
  unfinished_grace_sec: 10      # start+duration 距当前 ≤10s 视为已结束
models:
  - provider: "gemini"
    role: "vision"
    model_name: "gemini-flash-latest"
    api_key: "${GEMINI_API_KEY}"
    extra_api_keys: ["${GEMINI_API_KEY_2}", "${GEMINI_API_KEY_3}", "${GEMINI_API_KEY_4}"]
    timeout: 600
  - provider: "nvidia"
    role: "vision"
    model_name: "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning"   # 整视频 video_url 输入(内部采样帧)
    base_url: "https://integrate.api.nvidia.com/v1"
    api_key: "${NVIDIA_API_KEY}"
    timeout: 600
  - provider: "ollama"
    role: "text"
    usage: "qa_fallback"        # 仅智能问答兜底,不参与视频
    model_name: "qwen2.5:7b"
```    api_key: "${NVIDIA_API_KEY}"
    timeout: 20
    circuit_breaker:
      enabled: true
      threshold: 3
      cooldown: 600

  # 3. 本地 Ollama纯文本仅 Q&A 兜底,不参与视觉分析、不参与云端结果融合)
  - provider: "ollama"
    role: "text"                # 仅问答兜底
    usage: "qa_fallback"        # Gemini/NVIDIA 都失败时才启用
    enabled: true
    model_name: "qwen2.5:7b"
    base_url: "http://localhost:11434"
    timeout: 120
    num_predict: 512
    circuit_breaker:
      enabled: false

环境变量Oracle 节点,写入 ~/.bashrc 或 systemd 环境变量文件):

export GEMINI_API_KEY="AQ.Ab8RN6I0l8hC7hLnNHRY6qOXdch5CTWczDNlS4c1XrneGHipUQ"
export NVIDIA_API_KEY="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"

NVIDIA NIM 单图连通性验证

pip3 install openai
python3 -c "
import os
from openai import OpenAI
client = OpenAI(
    base_url='https://integrate.api.nvidia.com/v1',
    api_key=os.environ['NVIDIA_API_KEY']
)
response = client.chat.completions.create(
    model='meta/llama-3.2-11b-vision-instruct',
    messages=[{'role': 'user', 'content': 'Hello, are you ready?'}],
    max_tokens=30
)
print('NVIDIA NIM 连接成功:', response.choices[0].message.content)
"

8.4 运维注意事项

  • NAS 部署目录不是 git 仓库(文件拷贝部署),同步代码用 stdin 管道:tar czf - <子目录> | ssh ... 'tar xzf - -C <目标>'
  • NAS scp 子系统被禁用,同样用 stdin 管道传文件
  • Oracle fam-edge 由 systemd fam-edge.service 守护Restart=always:部署代码后必须 sudo systemctl restart fam-edge;手动 setsid 启动会和守护打架(端口 Connection in use
  • NAS 远端 kill gunicorn 用 ps aux | grep "[f]am-core/venv/bin/gunicorn" 字符类技巧pkill/pgrep 会匹配 SSH 自身命令行导致断连)
  • fam-core 启动模块路径是 src.fam_core.app:app(不是 fam_core.app:appstart_core.sh 会 source 仓库根 .env 注入 DSM_*/ORACLE_SYNC_TOKEN
  • Edge 单 worker 处理任务期间 /health 可能不响应,属正常
  • 运动数据清理:切换架构/重新提取时清 Oracle videos/events/people + motion_clips/(保留 ss_motion_events 与素材)与 NAS sync_* 镜像,重启两端自动重新分割分析
  • 前端构建产物 fam-ui/dist 不入库.gitignore改前端后需 npm run build 并单独 tar 部署 dist

9. 快速开始

# 1. 初始化数据库NAS
python scripts/init_db.py

# 2. 启动 FAM-CoreNAS含运动监测轮询 + Oracle-Sync
cd /volume1/web/sentinel-home-ai && bash start_core.sh

# 3. 启动 FAM-EdgeOraclesystemd 守护)
sudo systemctl restart fam-edge

# 4. 前端Vue3构建后由 FAM-Core 托管,无需独立进程)
cd fam-ui && npm run build   # 产物 fam-ui/dist/

# 或使用脚本
./scripts/start_core.sh && ./scripts/start_edge.sh

10. 服务器访问信息

10.1 Synology NASFAM-Core + FAM-UI + MariaDB

项目
IP 192.168.50.64
SSH 端口 2222scp 禁用,用 stdin 管道传文件)
SSH 用户 / 密码 ericwyuan / iLoveJava5
系统 Synology DS220+ (Geminilake), DSM 7
Tailscale IP 100.70.234.39userspace 模式,端口不通待修)
登录命令 ssh -p 2222 ericwyuan@192.168.50.64

10.2 MariaDBNAS

项目
版本 MariaDB 10.11.11
Socket /run/mysqld/mysqld10.sock
Root 密码 iLoveJava5!
数据库名 sentinel_home_ai
连接 /usr/local/mariadb10/bin/mysql -S /run/mysqld/mysqld10.sock -u root -p(非交互 PATH 下用 fam-core venv 的 PyMySQL + unix_socket 查询)

10.3 Oracle CloudFAM-Edge + Ollama + FFmpeg

项目
公网 IP 129.146.203.203
SSH 用户 ubuntu密钥 ~/.ssh/oracle_sentinel
系统 aarch64 (Ampere A1 2C12G), Ubuntu 20.04 LTS
Tailscale 100.74.137.126(已安装在线,与 NAS 端口不通)
登录命令 ssh -i ~/.ssh/oracle_sentinel ubuntu@129.146.203.203

10.4 Gitea 代码仓库

项目
URL http://192.168.50.64:3000/ericwyuan/sentinel-home-ai
账号 / 密码 ericwyuan / iLoveJava5

10.5 Gemini APIGoogle AI Studio

项目
API Key AQ.Ab8RN6I0l8hC7hLnNHRY6qOXdch5CTWczDNlS4c1XrneGHipUQ
模型 gemini-flash-latest生产配置v1beta 下 gemini-1.5-flash 会 404故用别名
端点 https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-latest:generateContent
验证状态 2026-08-20 测试可用
curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-latest:generateContent" \
  -H 'Content-Type: application/json' \
  -H 'X-goog-api-key: AQ.Ab8RN6I0l8hC7hLnNHRY6qOXdch5CTWczDNlS4c1XrneGHipUQ' \
  -X POST \
  -d '{"contents":[{"parts":[{"text":"Explain how AI works in a few words"}]}]}'

10.6 NVIDIA NIM 托管 API

项目
API Key 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
Base URL https://integrate.api.nvidia.com/v1
默认模型 meta/llama-3.2-11b-vision-instruct(可替换 qwen/qwen2-vl-72b-instruct
SDK openai Python SDKNIM 兼容 OpenAI API 规范,直接复用)
验证状态 已验证2026-08-20 部署验证:/api/edge/chat/ask 实测 provider=nvidiaGemini 超时后 NVIDIA 兜底成功;单图连通性验证脚本见 8.3

环境变量

export NVIDIA_API_KEY="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"

11. 提交规范AI Agent 必读)

工作流程(强制)

  1. 开工前:仓库根目录 git pull --rebase
  2. 每完成一项验收子任务:立即 git add + git commit + git push,一任务一 commit不批量合并
  3. 遇到阻塞:先 commit 可工作部分message 加 [WIP] 前缀
  4. 修 Bug:单独 commit格式 fix(模块): 问题简述

commit message 格式

  • 任务:[阶段X.Y子任务号] 子任务名称 - 完成内容简述
  • Bugfix(模块): 问题简述
  • 性能/文档:perf(...) / docs(...)

禁止:不 commit 直接继续;一次 commit 多个子任务;git push --force;跳过 hooks--no-verify)。

自检清单:任务对应哪一行验收标准?是否已 commit + pushmessage 是否合规?开工前是否 pull --rebase


12. 当前进度与 v1.1 计划

已完成(截至 2026-08-22v3 运动事件驱动架构)

  • 运动事件驱动v32026-08-22不再分析整段视频。NAS MotionNotifier 轮询 SS 事件列表60s游标续用/失败重试/心跳)推送 ss_motion_eventsOracle 整段素材按运动事件 ffmpeg 分割运动片段(-c:v copy -c:a aac,只分割已结束事件,motion_event_id 幂等),只分析运动片段;前端契约不变
  • 数据迁移2026-08-22整段提取的旧数据Oracle videos/events/people + NAS 镜像已清空并按运动视频重新提取历史素材NAS 轮询启动前)无运动事件,时间轴已过滤其空会话
  • 人物管理重设计2026-08-22人物卡新增「运动片段」区块缩略图/时间/摘要/事件数,点击跳时间轴定位);新增 GET /api/ui/people/clipsTimeline 支持 ?video= 定位
  • 前端从 Streamlit 迁移为 Vue3 + Vite + Tailwind SPA(由 FAM-Core 托管 dist
  • 运动事件链路NAS→Oracle 单向推送)+ 心跳 fail-openGemini 多 Key 轮换 + 模型调用统计
  • 双端部署NAS start_core.shsource .envOracle systemd fam-edge.service 守护

详细进度见 PROGRESS.md

v1.1 待办

# 任务 优先级
1 单元测试JSON parser / circuit breaker / schema 校验)
2 Tailscale 修复NAS userspace 模式升级,流量不走公网)
3 daily_summaries 每日摘要
4 运动片段音频策略验证(当前保留 aac 转码)

文档结束