Files
sentinel-home-ai/.workbuddy/memory/2026-08-21.md
ericwyuan 2c5bf950c5 feat: 事件时间轴缩略帧 + 人物管理头像 + 人物合并硬规则校验
## 新架构:Oracle 集中计算 + NAS 代理展示

### Oracle 端 (fam-edge)
- 新增 frame_service: ffmpeg 视频抽帧 + VLM 人物定位裁剪头像(磁盘缓存)
- 新增 /api/oracle/frame: 按 video_id+ts 抽帧返回 jpeg(带 token)
- 新增 /api/oracle/avatar: 按 label 生成人物头像(VLM 定位人物 + 兜底整帧居中)
- 新增 person_identifier: 人物身份识别模块
- Gemini 适配器支持 flash/flash-lite 双模型切换,429 自动降级
- frame_service VLM 全模型 429 时进入 10 分钟熔断,避免每次请求白打配额
- 兜底头像不落缓存,配额恢复后自动重试 VLM 精确定位

### 人物合并硬规则校验(框架级修复)
- person_service: LLM 合并结果落库前加硬冲突检测
  - 性别冲突 → 绝不合并
  - 年龄档跨未成年/成年 → 绝不合并(防止把爷爷/宝宝并进同一人)
- oracle_db: upsert_person 入口剥离括号后缀(人物A(别名:人物B) → 人物A),消灭垃圾人物行
- 修复 set_canonical 丢弃 source 参数的 bug(旧代码硬编码 'manual' 导致错误合并被永久固化)
- get_events_for_label: 只提取该身份组的特征文本,头像定位更精准

### NAS 端 (fam-core)
- 新增 img_proxy: /api/proxy/frame 和 /api/proxy/avatar 代理 Oracle 图片
- app.py 注册 img_bp 蓝图
- oracle_sync / db_layer / member_manager 同步人物表

### UI 端 (fam-ui)
- 事件时间轴: 每条事件卡片加时间点缩略帧
- 人物管理: 每人卡片加头像(150x150 圆角)
- parse_persons: 剥离括号备注,与 Oracle 归一化一致
- 新增 EventItem 组件、Timeline 页改造
- Chat / ServiceStatus 页相应调整

### 数据库
- scripts/ddl.sql: 同步表结构更新
- Oracle people 表: features_json / display_uid / source 字段完善
2026-08-23 00:13:56 +08:00

181 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-08-21 工作记录
## #51 部署验证 - rclone 同步 Google 硬盘 + 全链路跑通
### Oraclefam-edge部署完成
- rclone v1.75.0 装到 /usr/local/binaarch64zip 解压)
- 服务账号 JSON 存 /opt/fam-edge/gdrive-sa.jsonchmod 600用户提供
- rclone remote `gdrive:`drive scope + service_account_file
- 同步脚本 /opt/fam-edge/rclone_sync.sh + systemd timer每 5 分钟):
`rclone sync --drive-shared-with-me gdrive:SS/Generic_ONVIF-001 /opt/fam-edge/gdrive_videos`
- 首次全量同步 20 个视频 7.52GB~2 分钟完成)
- 共享目录是 `SS`(群晖监控站),摄像头 `Generic_ONVIF-001` 按 YYYYMMDDAM/PM 分目录
### 踩坑与修复3 个 commit
1. `watch_processor._scan_files` 改 os.walk 递归(原 listdir 只看根目录,视频在子目录)
2. `.env` 的 GEMINI/NVIDIA key 是旧无效值 → 用 env.sh 的真实 key 修复config_loader 只读 .env
3. **Gemini 上传**:必须用 `/upload/v1beta/files` 端点(/v1beta/files 是元数据端点)+ resumable 协议370MB 整视频 ~55s 上传成功
4. **NVIDIA**base64 塞 payload 超 25MB 上限 → 改 Assets APIPOST 拿 assetId+uploadUrlPUT 上传PUT 必须**全小写 content-type 且值=POST contentType(video/mp4)**,否则 S3 预签名 SignatureDoesNotMatch
5. `parse_vlm_json` schema 从旧架构entities_json/frame_details改为新架构global_summary/events/people_mentioned兼容旧结构转换
### 端到端验证通过
- 21 视频全部登记video 1/2 已 doneprovider=gemini凌晨视频无事件=分析正确)
- NAS 重启 fam-core 触发立即同步sync_videos=21cursor 推进正确
- 剩余 19 个视频串行处理中Gemini 免费配额 429 会降级 flash-lite之后可考虑 NVIDIA
### 待观察
- Gemini 免费 key 配额限制(~20 req/day后续 19 个视频可能大量 429
- rclone timer 每 5 分钟增量同步验证
- NAS 30 分钟自动同步已生效
## 生产-消费队列 + 模型超时×2新需求commit ce27e4a
- 新增 `fam-edge/src/fam_edge/video_queue.py`VideoQueue 生产-消费队列
- 生产者线程30s 轮询 rclone 落地目录,新文件登记 pending 并入队;启动时补入队 DB 未处理完的(重启恢复,实测 14 个)
- 消费者线程max_concurrent=1从队列取 video_id → VideoProcessor.process_video(timeout_multiplier=2)
- 防重复入队_queued set重试上限max_retries=2oracle_db.videos 加 retry_count 列mark_video_failed 递增)
- `video_processor.process_video` 加 timeout_multiplier遍历 adapter 时临时 adapter.timeout = 原配置 ×倍数finally 恢复gemini 600→1200s 生效日志确认)
- config.yaml video_processing 加 timeout_multiplier: 2 / max_retries: 2
- 删除旧 watch_processor.pyapp.py 改启动 VideoQueue
- oracle_dbretry_count 列(建表+ALTER 兼容旧库、get_video_by_id、PRAGMA busy_timeout=10000
- 部署验证:启动恢复入队 14 个、video_id=8 gemini 600→1200s、生产者登记 id=22 新视频、done 持续增长
## 模型调用统计界面 + lite 超时实测×4commit b87b38b + 3e94c2a
- Oracle 端oracle_db 新增 model_calls 表provider/model/video_id/started_at/duration_sec/success/error/created_atBaseModelAdapter 加 model_call_hook + _emit_model_callgemini(_generate_video 每模型每attempt)/nvidia(analyze_video) 每次请求记录get_sync_delta 下发 model_callscreated_at >= since + NAS 幂等 upsert 防漏)
- gemini 支持模型级 model_timeouts最终值不参与 ×2实测 lite 370MB 视频耗时 22.6s(生产 27~34slite 限制 = 22.6×4 ≈ 90sflash 仍 1200s600×2。日志确认flash 本轮 1200s / lite 本轮 90s
- NAS 端sync_model_calls 镜像表ddl 8.1db_layer upsert_sync_model_calls/get_sync_model_calls/get_sync_model_calls_statsoracle_sync 拉 model_callslast_count 变 4 元组)
- fam-ui 新增 "🤖 模型统计" 页:按模型聚合(成功/失败/成功率/平均耗时/最后调用)+ 最近 100 条调用明细(请求时间/模型/耗时/状态/失败原因/视频)
- 验证Oracle model_calls 正常记录flash 429_quota 失败、lite 成功 27~34sNAS 同步 4 条fam-ui 200
## 整体排查 + lite 超时 8 分钟commit 83eefc1
- **NVIDIA 400 排查结论**`nvidia/nemotron-nano-12b-v2-vl` 对整视频分析**稳定复现 400/500 服务端内部错误**"not enough values to unpack (expected 2, got 1)"),所有视频、有无 num_frames 均复现 → NVIDIA 兜底在当前模型/端点**不可用**asset 上传 OKchat.completions 必失败)。建议从 vision_order 移除或换模型
- gemini-flash-lite 超时 90s → **480s8 分钟)**config model_timeouts 已改并部署(日志确认 480s
- 处理进度done 33 / failed 1video24 待重试)/ pending 12
## NVIDIA 多模型降级链 + 实测结论commit b7b5fe6
- nvidia_adapter 改造model_chainasset 上传一次,逐个模型 video_url 引用尝试)+ switch_interval_sec 切换间隔(默认 5s+ model_timeouts 每模型独立超时 + compute_provider 带模型名nvidia:{model}
- **实测全部 NVIDIA 云端视频候选不可用**omni 500asset_id 引用失败、12b 400、llama-3.2-11b-vision 400不支持视频、cosmos-reason2/phi-3-vision/gemma-3/kosmos-2/fuyu-8b/paligemma 404base64 超 25MBpublic URL 方案被用户否决(不暴露视频)
- configmodel_name=omni + fallback 12b/llama-11b链机制保留未来可用模型出现只需改配置
- 手动验证:模型链 [1/3]→[2/3]→[3/3] 逐个尝试+5s 间隔+失败原因记录,全部失败返回 None
- ⚠️ 安全提醒Oracle 曾短暂起 http.server:8000 暴露视频目录做 POC已按用户要求关闭
## 前端修复SQL 1054 + 视频缩略图commit 4148507
- **SQL 1054 修复**fam-ui 事件查询 `SELECT ... camera_name FROM sync_events`(该列在 sync_videos→ 改 JOIN sync_videos 取 v.camera_name
- **视频缩略图**Oracle 装 opencv-python-headless(5.0)video_processor 处理成功后抽首帧(宽≤640, JPEG q65)存 /opt/fam-edge/thumbs/{video_id}.jpgapi_gateway 新增 GET /api/oracle/video/{id}/thumbtoken 校验,无 token 401已 done 46 视频 backfill 46/46
- **fam-ui**config 加 oracle_url(129.146.203.203:5000)+oracle_token(${ORACLE_SYNC_TOKEN})事件时间轴视频会话头显示缩略图img onerror 优雅降级start_ui.sh 补 source 项目 .env 注入 token
- 验证NAS→Oracle thumb HTTP 200/40KBfam-ui 进程 token env 就绪UI 8501 正常
- 人物管理无照片架构局限LLM 仅输出人物名,无图像锚点),如需人物照片需从视频定位+裁剪,待后续
## 每个事件对应时间点画面截图commit 05f727a
- oracle_db.mark_video_processed 返回 event_ids与 events 一一对应)
- video_processor._generate_event_thumbs事件 ts - 视频 event_start_time = 偏移秒 → cv2 跳帧(CAP_PROP_POS_MSEC)截图,存 /opt/fam-edge/thumbs/ev_{event_id}.jpg宽≤640, q65解析失败/负偏移取首帧)
- api_gateway 新增 GET /api/oracle/event/{event_id}/thumbtoken 校验)
- fam-ui 事件查询加 e.idrender_event_list 每条事件显示对应截图onerror 隐藏降级)
- backfill已 done 视频 102 个事件截图全部生成;验证 NAS→Oracle HTTP 200/51KB、无 token 401
## 事件截图时间对不上修复commit 5233296 + 3fcd00d
- **根因**:模型输出"绝对北京时间"靠自身推算1 小时视频内误差可达分钟级 → 截图按不准的时间定位帧必然图文不符
- **修复**:① prompt 改为要求输出"视频内相对时间 HH:MM:SS"(模型对相对位置判断准);② 后端 _parse_event_ts 解析相对时间 → 绝对时间 = start + offset 精确落库(兼容旧绝对格式);③ 截图直接用 offset 跳帧
- **连带 bug**_parse_event_start_from_filename 只认带分隔符日期2026-08-21监控文件名是纯数字 20260820-140416 → event_start_time 空 → 新增纯数字格式解析
- 启发式:相对时间 >6h 视为模型误输出绝对时间,不强行定位
- 验证:重置 video 46 重分析 → event_start=14:04:16 ✓,事件 ts 14:05:37/14:06:21/14:13:02截图 offset 36/48/81/125/526s 精确 ✓
- 旧视频(除重分析的)仍用旧时间戳截图;如需全部修正需批量重分析(成本高,用户确认后再做)
## 全部旧视频重分析 + 人物管理图片commit 92ef9fe
- **全量重分析**:清空 events(104) + 旧事件截图(108) + 重置全部 45+1 个视频 pending → 队列后台串行重跑(新相对时间逻辑,预计 1.5-2.5hGemini flash 429 → lite 兜底)
- **人物管理图片**fam-ui 每个人物身份取其一 label 出现事件的截图作头像(查 sync_events.person_list_json LIKE → 显示 Oracle event thumb
- 修正video 46 起初被排除重跑但 events 被清 → 一并重置统一重跑
## 队列健壮性三项commit 6afdda5用户需求清单
1. **失败重试间隔 30s→1h**videos 表加 last_fail_atmark_video_failed 记录video_queue._retry_allowed 对 failed 要求 retry_count<max_retries 且距上次失败 ≥ retry_interval_sec(3600) 才重新入队(配额类瞬时故障等恢复,避免重复打爆)
2. **max_retries 2→10**:默认值与 config 均改 10瞬时故障更多机会
3. **入队前文件校验**validate_video()OpenCV大小>0/可打开/可读帧/元数据 fps-frames-duration-分辨率);新文件先过 mtime 稳定窗口(stable_window_sec=60 防 rclone 半成品)再校验,失败登记 status='invalid' 不入队可追溯_retry_allowed 排除process_video 处理前二次确认(失败标 failed:invalid_file:xxx
- 验证:正常视频 ok+meta(30min/2880x1620/14.28fps)、损坏 cannot_open、空文件 file_empty部署后日志"重试上限 10"
- 重分析 42 个 pending 继续后台跑
## 代码审查 18 项处理commit 034dca9 + 4e85a98
- **已解决(新增)**#18 密钥明文直配 config.yamltoken/gemini/nvidia弃 .env 依赖,验证 token_ok/key 长度正常);#5 appearances 改 distinct 视频数覆盖校准set_person_appearances防 reconcile 累加膨胀);#9 oracle_sync _pull_lock 防 trigger_now 与后台并发双拉;#11 LLM 合并命名解析 target 已有 canonical统一显示名#12 熔断器状态转换加锁;#10 问答人名匹配改 JSON_CONTAINS 精确匹配MariaDB 语法验证 OK#4 OracleDB _write_lock 复合写串行化;#6 每消费者独立 VideoProcessor消 adapter.timeout 共享竞争);#15 done 视频文件被覆盖 mtime>processed_at 自动重置重分析
- **累计已解决**#1/#2/#3/#4/#5/#6/#9/#10/#11/#12/#15/#18;部分缓解 #7/#14/#17
- **未解决(说明)**#8 删除传播tombstone 大工程,暂缓);#13 Gemini 文件/NVIDIA asset 残留清理(暂缓);#16 gunicorn 线程数(运维配置,暂缓)
- 重分析进度done 13 / pending 33约剩 1h
## Prompt 集中化重构commit a72b286用户提供详细设计文档
- 新增 fam-edge/src/fam_edge/ai_orchestrator/prompts.pybuild_video_prompt含 3 秒密度抽取/7 维度描述/people_mentioned 一致性强制/输出硬约束/边界情况/camera_name 注入、build_chat_prompt、build_person_merge_prompt唯一性+保守不合并__init__ 导出
- gemini/nvidia adapter._build_video_prompt 改调共享函数(消除两份发散),注入 camera_name
- person_service._llm_merge 改调 build_person_merge_prompt
- fam-core chat_handler 新增 prompts.py跨模块独立维护风格一致删除内联 CHAT_SYSTEM_PROMPTchat_ask 改调 build_chat_prompt
- 部署验证prompt 生成正常3 秒密度/camera/唯一性均含fam-edge activefam-core health OK
- 注意3 秒密度可能让有人时段 events 数百条Gemini maxOutputTokens=4096 可能截断——若出现 JSON 解析失败需提高 max_tokens
## 最新代码调试 + 人物标识清洗commit 0d73c23
- 调试结果服务全正常fam-edge active、NAS core/ui 200、rclone timer active重分析 done 26/pending 20新 prompt 正常——有人视频 5-16 事件、无人时段(晚 19:31/凌晨 5 点)events=0 合理、无 JSON 解析失败
- 发现并修复:模型输出 people 含 known_members 上下文格式串"人物A别名/标识人物B"污染人物表 → prompts.py 明确"people 只填标识本身不带括号注释" + video_processor._clean_person 后端清洗events.people 与 people_mentioned 均清洗)+ 清理存量脏 label人物A别名/标识人物B→ 人物A
- people 表现状A/B/C/D 四标签LLM 合并 B/C/D → canonical 人物A
## 前端日期标签与视频文件对不上commit da76319
**用户反馈**:前端显示"2026-08-21 · 画面静止/无人员活动",和下面的视频文件对不上。
**根因链**(排查发现):
1. 前端事件时间轴/统计按 `processed_at`**分析处理时间**)做日期分组——全量重分析都在 8/21 完成,导致 8/15~8/20 录制的视频全部堆在"2026-08-21"标签下,与文件名(录制时间)错位
2. Oracle videos 表 8/21 11:29~12:59 被整表重建created 全在此区间),重分析串行进行
3. 20 个视频 event_start_time 为空video 30-45 在 13:00~13:25 处理时服务器跑的是旧文件名解析代码(无纯数字正则 YYYYMMDD-HHMMSS且被后续部署重启中断pending28/29 从未处理
4. NAS 镜像状态与 Oracle 错位35-45 NAS 显示 done、Oracle 实为 pending——Oracle 重建后 NAS 增量未对齐
**修复**
1. **日期维度统一改视频实际录制时间**fam-ui事件时间轴统计/列表/关注事件统计)与 db_layerget_sync_videos/get_sync_stats/query_sync_events_for_person_date全部改用 `COALESCE(NULLIF(event_start_time,''), processed_at)`(录制时间优先,回退处理时间)
2. **Oracle backfill**:对 12 个缺失 start 的视频按文件名解析回填30/33-42/45→ Oracle 46 个视频全部有 event_start_time
3. **NAS 强制全量重同步**:删 sync_cursor last_since + 重启 fam-core → since='' 全量拉取 → EMPTY_START 20→0、状态与 Oracle 对齐done 28/failed 4/pending 14
**验证**done 视频按录制日期分布 8/15:10、8/16:8、8/17:2、8/18:2、8/19:2、8/20:1、8/21:38/15/8/20 筛选正确fam-ui 200 无报错。
**遗留**Oracle 端 pending 14 个在队列继续串行处理video 28 处理中failed 4 个24/25/26 等Gemini 429等 1h 重试间隔自动重试。
## Google 硬盘删除联动 + DB 摘要保留(用户需求确认,无代码改动)
- **用户需求**①谷歌删了甲骨文也删文件层②Oracle/NAS 数据库生成的摘要不能删DB 层)
- **验证结论**
- 文件层rclone sync 本就是镜像语义远程删→本地删实测放临时文件→sync→Deleted:1 确认生效
- DB 层video_queue 生产者只扫描"目录存在的文件",文件消失不影响 Oracle videos/events/people 记录NAS 镜像照常同步,前端摘要保留;截图接口 404 由前端 onerror 降级
- **加固**rclone_sync.sh 加 `--max-delete 200`(防 Google API 临时故障级联误删本地,单次最多删 200 个),脚本验证 exit=0、75 文件不受影响
- 注意:当前是**单向镜像**Google 为源→本地),不建议反向传播(本地删→谷歌删会误删原始监控),如需 rclone bisync 真双向需用户确认风险
## 实时服务状态界面 + 7 天活动记录commit 0d0a7f6 + fec9a3e
- **Oracle**service_activity 表service/action/detail/ts写入时清 7 天前record_activity/get_recent_activities/get_queue_statusVideoQueue 打点(register/reanalyze/process_start/process_done/process_fail)+_current 当前处理跟踪+status()PersonService 打点(merge_done/merge_skip)api_gateway 新增 GET /api/oracle/activitytoken 鉴权,返回 queue/db.by_status/rclone/person/model_calls/最近50条活动
- **rclone_sync.sh**:同步结果写 activity 表service=rclone, transferred/deleted/exitTRANS 提取正则修过一次(输出带 B 单位)
- **fam-ui**:新增"🖥 服务状态"页导航第7项状态卡队列运行/排队/完成/待处理/失败 + 当前处理视频 + rclone/人物/NAS同步/模型 4 张服务卡)+ 最近活动时间流(服务徽章),直连 Oracle /api/oracle/activity复用 oracle_url+token
- **踩坑**video_queue.status() 用 Dict 注解未导入 → worker 启动 NameError → 补 typing.Dict
- 验证接口数据正常queue running/queued 19/当前 video 56rclone sync_done/person merge_done 打点生效401 鉴权NAS→Oracle HTTP 200fam-ui 200
## 模型调用时间差 8 小时修复commit 02fc80b
- **根因**gemini/nvidia adapter 的 model_calls.started_at 用 `datetime.now()`Oracle 服务器 UTC而 created_at 用 _now_iso()(北京时间)→ 前端模型统计/服务状态页时间差 8h
- **修复**:两个 adapter started_at 改 `datetime.now(timezone(timedelta(hours=8)))`Oracle 历史 215 条 +8hNAS 镜像 sync_model_calls 210 条 DATE_ADD +8h
- 验证:最新记录 started 16:05:02 与 created 对齐(北京时间)✓
## 人物管理无照片修复commit 27bfd6a
- **根因**NAS sync_events 残留重分析前旧事件223 条 vs Oracle 115 条id 1-108 旧事件未删除——#8 删除传播未做的副作用)。人物页头像查 `ORDER BY e.id LIMIT 1` 取到旧事件 id=1 → Oracle ev_1.jpg 不存在(重分析后事件从 109 起)→ 404 → onerror 隐藏 → 无照片
- **修复**:① NAS 清空 sync_events + 重置 cursor 全量重拉 → 精确 115 条镜像;② api_gateway event_thumb 兜底ev 缺失时查所属 video 返回 thumbs/{video_id}.jpg视频首帧③ 清理 Oracle people 脏 label人物A别名/标识人物B→人物A
- 验证NAS→Oracle ev109 HTTP 200/51KB ✓;人物页查询到的都是新事件 id → 截图存在
## 人物头像改为"出现事件画面"commit 2ba3478用户否决视频首帧方案
- **用户意见**:不能用视频首帧当头像(首帧可能无人/非本人)——该人物在众多视频中多次出现,肯定能在他出现的事件里找到画面
- **新方案**Oracle 新增 GET /api/oracle/person/avatar?label=Xtoken 鉴权events.person_list_json 按 `%"label"%` JSON 数组精确匹配,按 id DESC 遍历返回第一个 ev_{id}.jpg 存在的截图(人物出现事件中最新的有截图画面);撤销 event_thumb 的视频首帧兜底(改回 404
- **fam-ui 人物页**:头像改调 avatar 接口(先 requests 探测 200 再渲染 imglabel 用 urllib.parse.quote 编码)
- 验证人物A/B avatar 均 20039-41KB 真实事件画面)、无 token 401、NAS→Oracle 200、UI 200
## Oracle 磁盘扩容(用户控制台扩盘 + 重启生效)
- **背景**rclone 持续同步视频导致 45G 盘写满(剩 46Mgdrive_videos 31G/75 个视频)。用户说已扩容但服务器 lsblk 一直 46.6G(在线未生效)
- **处理**growpart/resize2fs 均 NOCHANGE块设备没变临时清理 /tmp 残留+apt+journal 释放 ~900M禁用 rclone timer 防写满;按用户要求重启服务器
- **重启后扩容生效**sda 46.6G→**150G**Ubuntu cloud-init 开机自动扩展分区+文件系统 → df 146G 可用 90G39%
- **恢复**enable --now rclone-sync.timer同步恢复视频 75→121 个持续下载中fam-edge active
- 经验Oracle 在线扩容偶尔不立即生效重启可触发Ubuntu 自动 growfs扩容后无需手动 growpart
- 提醒视频持续增长121 个已占 56G150G 约可再装 240 个视频,长期需考虑清理策略(如只保留分析完的摘要+删本地视频,需改 rclone 策略避免重新拉回)或再扩盘
## 人物模块重构 v3 - 大模型特征值替代 OpenCVcommit ff01d14 + 68337f8 + 677c5bd用户提供设计文档
- **核心**prompt 增加 person_appearances(uid+7特征+action)VLM 直接产出结构化特征,跨视频靠特征合并,彻底移除 cv2
- **改动**12 文件prompts.py(6原则+schema+规则8条+合并prompt重写)、gemini _normalize 透传、oracle_db(events.person_appearances_json/people.features_json/display_uid + _merge_features + upsert_person/mark_video_processed)、video_processor(validate_video 改 ffprobe 去 cv2、_store_result 聚合 uid 特征落 people、删 thumb/event_thumbs)、person_service(_aggregate_features/_collect_features_text 特征文本合并)、api_gateway 删 3 图接口、NAS db_layer+ddl 加字段、fam-ui 特征卡替代头像+事件人物特征块
- **踩坑 2 个**
1. max_tokens 4096→163843秒密度+特征使 JSON 巨大被截断解析失败
2. **json_parser.validate_schema 白名单丢弃 person_appearances**(在 _normalize 之前执行)→ 补透传
- **验证**video 46 重分析 → lite 输出 16 事件含完整特征(性别男/中年/中等/短发/蓝色POLO衫/无辨识events 带特征 6、people.features_json 落库NAS 同步 6 条+人物A特征卡数据 ✓
- 部署Oracle 卸载 opencv/numpyffprobe 已有NAS ALTER 加 3 列
- 遗留NVIDIA 模型链仍不可用gemini 429 时 lite 兜底);历史视频无特征(前端显示"特征待大模型补充",下段分析自动补)