# FAM-Edge 配置文件 (Oracle 端) - 新架构 v2 # # 新架构(2026-08-21 重构): # 1. 不再切片/抽帧:整视频直传云端 VLM(Gemini 用 Files API,NVIDIA 用整视频 video_url) # 2. 视频来源:rclone 从 Google 硬盘实时同步到本地 local_dir,监听目录处理新视频 # 3. Oracle 自建 SQLite 库存储所有视频摘要/事件/人物,并对外提供同步接口供 NAS 拉取 # 4. 独立 person_service 汇总全量人物 -> LLM 合并为规范人物表 -> 回灌视频提示 # 5. NAS 仅作管理后台,每 30 分钟从甲骨文拉增量镜像到本地 MariaDB # Oracle 端 HTTP 服务 server: host: "0.0.0.0" port: 5000 # Google 硬盘同步(rclone 负责同步落地,本段仅描述监听行为) gdrive_sync: enabled: true local_dir: "/opt/fam-edge/gdrive_videos" # rclone 同步落地目录(video_processing 监听此目录) watch_interval_sec: 30 # 监听新视频的轮询间隔 camera_name: "客厅" # 摄像头名称(注入视频提示) # 文件名解析开始时间:监控文件名含时间戳时使用(如 2026-08-21_081500.mp4) parse_start_from_filename: true # Oracle 本地库(视频摘要/事件/人物) oracle_db: path: "/opt/fam-edge/data/oracle.db" # NAS 拉取同步接口鉴权 token(与 NAS oracle_sync.token 一致,走 .env,不明文入库) sync_api: token: "${ORACLE_SYNC_TOKEN}" # 人物识别服务(2026-08-22 停用):已被闭集识别 person_identifier 取代 # (汤圆/媳妇走性别年龄规则免费识别,爷爷/爸爸走视觉大模型比对参考图,见 # person_identifier 配置块)。原 LLM 合并逻辑不可靠(用户原话"现在的识别全是 # 错的"),继续跑只会用不可靠的猜测持续覆盖 people 表的 canonical_name,跟新 # 系统的结果打架——停用,不删除代码(关键冲突检测逻辑 _features_conflict 仍 # 保留供参考/未来复用)。 person_service: enabled: false schedule_interval_sec: 1800 model: "gemini" # 视频处理 video_processing: max_concurrent: 1 # 消费者线程数(串行处理,避免云端并发超额) timeout: 900 # 兜底单视频分析超时 timeout_multiplier: 2 # 模型消费超时倍数:在 models[i].timeout 原值上 ×2(大视频上传+分析耗时) max_retries: 10 # 单视频失败最大重试次数(配额/过载等瞬时故障给足重试机会) retry_interval_sec: 3600 # 失败重试最小间隔:距上次失败 ≥1h 才重新入队,等配额恢复 file_validate: true # 登记入队前用 ffprobe 校验文件可解码;失败标记 invalid 不入队 stable_window_sec: 60 # 文件 mtime 稳定窗口:写入中(rclone 同步未完成)的文件跳过本轮 # 降级顺序:先 gemini 整视频,失败再 nvidia 整视频;两者都失败 -> 标记 failed vision_order: ["gemini", "nvidia"] # 运动事件(2026-08-22 运动事件驱动架构): # - NAS 端 fam-core MotionNotifier 轮询 SS 事件后,POST 推送到甲骨文 # /api/ss/motion,落库 ss_motion_events(start_time/duration 为 Unix epoch); # 并定期用空 events 调接口当心跳,证明推送链路存活。 # - video_processor 不再整段分析:整段素材按 ss_motion_events 中【已结束】的 # 运动事件分割成运动片段(motion_segment 配置),只分析运动片段。 dsm_motion_prefilter: max_heartbeat_age_sec: 900 # 心跳超过 15 分钟没更新 -> 判定推送链路可能已死 # 运动片段分割(运动事件驱动架构,2026-08-22 新增): # 整段素材视频(rclone 同步落地)按 ss_motion_events 分割成运动片段再分析。 # 只分割已结束事件(start_time+duration 落在当前时刻附近),进行中的事件 # 等结束后的下一轮再分割;素材保持 pending 直到窗口内事件全部结束。 motion_segment: clips_dir: "/opt/fam-edge/motion_clips" # 分割产物目录 keep_audio: true # 保留音频(pcm_alaw -> aac 64k 转码);false 则 -an 去音频 min_duration_sec: 1 # 短于该时长的事件不分割 unfinished_grace_sec: 10 # start_time+duration 距当前 ≤ 该秒视为"已结束"容差 # 闭集人物识别(2026-08-22 新增,家里固定 4 人:爷爷/爸爸/媳妇/汤圆): # 原来靠大模型自己编的"人物A/B/C"临时 uid + 文字特征描述跨视频合并,验证下来 # 不可靠(用户原话"现在的识别全是错的")。人脸向量方案也验证过,家庭监控这种 # 大广角/远距离画面下同人内部相似度经常比不同人还低,此路不通。 # 现在改成:汤圆(幼儿/儿童特征)/媳妇(唯一成年女性) 直接用 Gemini 已产出的 # 性别/年龄字段判断,免费且验证下来接近 100% 准;爷爷/爸爸(两个成年男性,纯 # 外观规则/人脸向量都区分不开) 改用视觉大模型"看参考图比对"——NVIDIA 实测 # 6/6 全对且配额与主分析链路完全独立,设为优先,Gemini flash-lite(7/8) 兜底。 # 参考图目录结构:{ref_dir}/爷爷/*.jpg、{ref_dir}/爸爸/*.jpg(已用确认过身份的 # 历史截图种好,见 PROGRESS.md 记录)。 person_identifier: enabled: true ref_dir: "/opt/fam-edge/data/person_refs" max_ref_per_person: 6 # 连续两次分类调用之间的最小间隔(不分 provider 统一限速):正常处理新片段时 # 调用本来就稀疏,这个主要是给历史数据批量回填用的,避免短时间内密集调用打爆配额 min_call_interval_sec: 2 nvidia: api_key: "${NVIDIA_API_KEY}" model_name: "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning" fallback_models: [] # 目前只验证过这一个能用的 NVIDIA 视觉模型,先留好扩展位 timeout: 60 max_retries: 3 # 每个模型对瞬时故障(429/5xx/超时)最多重试几次(含首次) retry_backoff_sec: 3 # 重试前基础等待秒数,指数退避(3s -> 6s -> 12s) gemini: api_key: "${GEMINI_API_KEY}" extra_api_keys: ["${GEMINI_API_KEY_2}", "${GEMINI_API_KEY_3}", "${GEMINI_API_KEY_4}"] model_name: "gemini-flash-lite-latest" timeout: 60 max_retries: 2 retry_backoff_sec: 3 # 智能问答(2026-08-23 抽离到独立 ai-gateway 服务,OpenAI 兼容协议): # fam-edge 这边只是转发客户端,问答本体的模型降级链/key 轮换/熔断都在 # ai-gateway 自己的 config.yaml 里配置,这里只填怎么连它。 # token 走 .env AI_GATEWAY_TOKEN,跟 ai-gateway 侧配置的值必须一致。 ai_gateway: base_url: "http://127.0.0.1:5100" # 同机部署,走本地回环,不走公网 token: "${AI_GATEWAY_TOKEN}" timeout: 60 # 视频分析模型链 models: - provider: "gemini" role: "vision" enabled: true model_name: "gemini-flash-latest" # 主模型(每日免费配额 20 请求,按模型独立) fallback_models: - "gemini-flash-lite-latest" api_key: "${GEMINI_API_KEY}" # 多 Key 轮换(各自独立 Google Cloud 项目,配额互不影响):主 key 配额用尽时 # 依次尝试这些 key,每个 key 都会重新走一遍模型 fallback 链。key 本身放 .env, # 这里只放环境变量名,不直接写密钥。 extra_api_keys: - "${GEMINI_API_KEY_2}" - "${GEMINI_API_KEY_3}" - "${GEMINI_API_KEY_4}" # 每个 key 对应的 Google Cloud 项目名(顺序对应上面 api_key + extra_api_keys), # 用于模型统计页展示是哪个项目在跑,不填则退回 "key1/key2/..." key_labels: - "智能摄像头-1" - "智能摄像头-2" - "智能摄像头-3" - "智能摄像头-4" timeout: 600 # 模型级独立超时(最终值,不参与编排层 ×2 放大) # gemini-flash-lite 实测 ~22-34s,按用户要求放宽至 8 分钟(480s),避免大视频/排队时过早切断 model_timeouts: "gemini-flash-lite-latest": 480 circuit_breaker: enabled: true threshold: 5 cooldown: 300 - provider: "nvidia" role: "vision" enabled: true # 模型可用性实测记录(2026-08-21,用真实短视频逐个探测 chat.completions 接口): # omni(本行 model_name):video_url 只认 base64 data URI,NVCF asset_id 引用 # 方式对它直接 500("Only base64 data URLs are supported for now")——本适配器 # 已改为 base64 内嵌整段视频,见 max_base64_mb。确认可用(真实返回结构化 JSON)。 # nemotron-nano-12b-v2-vl:走 asset_id 引用需要 NVCF-ASSET-DIR/ # NVCF-FUNCTION-ASSET-IDS 请求头,这两个头的值由 NVCF 服务端按内部路径生成, # 客户端传什么都 400 "Invalid NVCF-ASSET-DIR",标准 OpenAI 兼容调用打不通,已移除。 # meta/llama-3.2-11b-vision-instruct:明确不支持视频输入 # ("At most 0 video(s) may be provided"),只能单图,已移除。 # base64 方案受请求体大小限制(实测约 25MB 上限),真实监控视频压缩后通常在 # 20MB 上下,超过 max_base64_mb 直接跳过(不做注定失败的慢速编码),不是本地 # 故意限制过窄——这是当前唯一能打通的 NVIDIA 视频理解路径。 model_name: "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning" fallback_models: [] base_url: "https://integrate.api.nvidia.com/v1" api_key: "${NVIDIA_API_KEY}" timeout: 600 max_base64_mb: 20 # 超过此大小直接跳过 NVIDIA,不做注定失败的编码+上传 switch_interval_sec: 5 # 模型切换间隔:一个失败后等待再试下一个(未来加模型时用) model_timeouts: # 模型级独立超时(最终值,不参与 ×2) "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning": 300 circuit_breaker: enabled: true threshold: 5 cooldown: 300